aboutsummaryrefslogtreecommitdiff
path: root/embassy-mspm0/src
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2025-03-21 13:32:14 +0000
committerGitHub <[email protected]>2025-03-21 13:32:14 +0000
commite29be82c8bc1b7e2dbf9f58dad4229d12968e1b4 (patch)
tree4ae9e7b784c82b96bcf870c3f61c62abad8a3811 /embassy-mspm0/src
parentfecb7a2b2b6f1953a2fe57557cb83d063ab5eea4 (diff)
parent91684a11c8a15b62a773a1ace40791fcf80fdad2 (diff)
Merge pull request #3966 from i509VCB/mspm0-init
Embassy for MSPM0
Diffstat (limited to 'embassy-mspm0/src')
-rw-r--r--embassy-mspm0/src/fmt.rs270
-rw-r--r--embassy-mspm0/src/gpio.rs1060
-rw-r--r--embassy-mspm0/src/int_group/c110x.rs25
-rw-r--r--embassy-mspm0/src/int_group/g350x.rs51
-rw-r--r--embassy-mspm0/src/int_group/g351x.rs52
-rw-r--r--embassy-mspm0/src/int_group/l130x.rs46
-rw-r--r--embassy-mspm0/src/int_group/l222x.rs49
-rw-r--r--embassy-mspm0/src/lib.rs107
-rw-r--r--embassy-mspm0/src/time_driver.rs423
-rw-r--r--embassy-mspm0/src/timer.rs48
10 files changed, 2131 insertions, 0 deletions
diff --git a/embassy-mspm0/src/fmt.rs b/embassy-mspm0/src/fmt.rs
new file mode 100644
index 000000000..8ca61bc39
--- /dev/null
+++ b/embassy-mspm0/src/fmt.rs
@@ -0,0 +1,270 @@
1#![macro_use]
2#![allow(unused)]
3
4use core::fmt::{Debug, Display, LowerHex};
5
6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features.");
8
9#[collapse_debuginfo(yes)]
10macro_rules! assert {
11 ($($x:tt)*) => {
12 {
13 #[cfg(not(feature = "defmt"))]
14 ::core::assert!($($x)*);
15 #[cfg(feature = "defmt")]
16 ::defmt::assert!($($x)*);
17 }
18 };
19}
20
21#[collapse_debuginfo(yes)]
22macro_rules! assert_eq {
23 ($($x:tt)*) => {
24 {
25 #[cfg(not(feature = "defmt"))]
26 ::core::assert_eq!($($x)*);
27 #[cfg(feature = "defmt")]
28 ::defmt::assert_eq!($($x)*);
29 }
30 };
31}
32
33#[collapse_debuginfo(yes)]
34macro_rules! assert_ne {
35 ($($x:tt)*) => {
36 {
37 #[cfg(not(feature = "defmt"))]
38 ::core::assert_ne!($($x)*);
39 #[cfg(feature = "defmt")]
40 ::defmt::assert_ne!($($x)*);
41 }
42 };
43}
44
45#[collapse_debuginfo(yes)]
46macro_rules! debug_assert {
47 ($($x:tt)*) => {
48 {
49 #[cfg(not(feature = "defmt"))]
50 ::core::debug_assert!($($x)*);
51 #[cfg(feature = "defmt")]
52 ::defmt::debug_assert!($($x)*);
53 }
54 };
55}
56
57#[collapse_debuginfo(yes)]
58macro_rules! debug_assert_eq {
59 ($($x:tt)*) => {
60 {
61 #[cfg(not(feature = "defmt"))]
62 ::core::debug_assert_eq!($($x)*);
63 #[cfg(feature = "defmt")]
64 ::defmt::debug_assert_eq!($($x)*);
65 }
66 };
67}
68
69#[collapse_debuginfo(yes)]
70macro_rules! debug_assert_ne {
71 ($($x:tt)*) => {
72 {
73 #[cfg(not(feature = "defmt"))]
74 ::core::debug_assert_ne!($($x)*);
75 #[cfg(feature = "defmt")]
76 ::defmt::debug_assert_ne!($($x)*);
77 }
78 };
79}
80
81#[collapse_debuginfo(yes)]
82macro_rules! todo {
83 ($($x:tt)*) => {
84 {
85 #[cfg(not(feature = "defmt"))]
86 ::core::todo!($($x)*);
87 #[cfg(feature = "defmt")]
88 ::defmt::todo!($($x)*);
89 }
90 };
91}
92
93#[collapse_debuginfo(yes)]
94macro_rules! unreachable {
95 ($($x:tt)*) => {
96 {
97 #[cfg(not(feature = "defmt"))]
98 ::core::unreachable!($($x)*);
99 #[cfg(feature = "defmt")]
100 ::defmt::unreachable!($($x)*);
101 }
102 };
103}
104
105#[collapse_debuginfo(yes)]
106macro_rules! panic {
107 ($($x:tt)*) => {
108 {
109 #[cfg(not(feature = "defmt"))]
110 ::core::panic!($($x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::panic!($($x)*);
113 }
114 };
115}
116
117#[collapse_debuginfo(yes)]
118macro_rules! trace {
119 ($s:literal $(, $x:expr)* $(,)?) => {
120 {
121 #[cfg(feature = "log")]
122 ::log::trace!($s $(, $x)*);
123 #[cfg(feature = "defmt")]
124 ::defmt::trace!($s $(, $x)*);
125 #[cfg(not(any(feature = "log", feature="defmt")))]
126 let _ = ($( & $x ),*);
127 }
128 };
129}
130
131#[collapse_debuginfo(yes)]
132macro_rules! debug {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::debug!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::debug!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145#[collapse_debuginfo(yes)]
146macro_rules! info {
147 ($s:literal $(, $x:expr)* $(,)?) => {
148 {
149 #[cfg(feature = "log")]
150 ::log::info!($s $(, $x)*);
151 #[cfg(feature = "defmt")]
152 ::defmt::info!($s $(, $x)*);
153 #[cfg(not(any(feature = "log", feature="defmt")))]
154 let _ = ($( & $x ),*);
155 }
156 };
157}
158
159#[collapse_debuginfo(yes)]
160macro_rules! warn {
161 ($s:literal $(, $x:expr)* $(,)?) => {
162 {
163 #[cfg(feature = "log")]
164 ::log::warn!($s $(, $x)*);
165 #[cfg(feature = "defmt")]
166 ::defmt::warn!($s $(, $x)*);
167 #[cfg(not(any(feature = "log", feature="defmt")))]
168 let _ = ($( & $x ),*);
169 }
170 };
171}
172
173#[collapse_debuginfo(yes)]
174macro_rules! error {
175 ($s:literal $(, $x:expr)* $(,)?) => {
176 {
177 #[cfg(feature = "log")]
178 ::log::error!($s $(, $x)*);
179 #[cfg(feature = "defmt")]
180 ::defmt::error!($s $(, $x)*);
181 #[cfg(not(any(feature = "log", feature="defmt")))]
182 let _ = ($( & $x ),*);
183 }
184 };
185}
186
187#[cfg(feature = "defmt")]
188#[collapse_debuginfo(yes)]
189macro_rules! unwrap {
190 ($($x:tt)*) => {
191 ::defmt::unwrap!($($x)*)
192 };
193}
194
195#[cfg(not(feature = "defmt"))]
196#[collapse_debuginfo(yes)]
197macro_rules! unwrap {
198 ($arg:expr) => {
199 match $crate::fmt::Try::into_result($arg) {
200 ::core::result::Result::Ok(t) => t,
201 ::core::result::Result::Err(e) => {
202 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
203 }
204 }
205 };
206 ($arg:expr, $($msg:expr),+ $(,)? ) => {
207 match $crate::fmt::Try::into_result($arg) {
208 ::core::result::Result::Ok(t) => t,
209 ::core::result::Result::Err(e) => {
210 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
211 }
212 }
213 }
214}
215
216#[derive(Debug, Copy, Clone, Eq, PartialEq)]
217pub struct NoneError;
218
219pub trait Try {
220 type Ok;
221 type Error;
222 fn into_result(self) -> Result<Self::Ok, Self::Error>;
223}
224
225impl<T> Try for Option<T> {
226 type Ok = T;
227 type Error = NoneError;
228
229 #[inline]
230 fn into_result(self) -> Result<T, NoneError> {
231 self.ok_or(NoneError)
232 }
233}
234
235impl<T, E> Try for Result<T, E> {
236 type Ok = T;
237 type Error = E;
238
239 #[inline]
240 fn into_result(self) -> Self {
241 self
242 }
243}
244
245pub(crate) struct Bytes<'a>(pub &'a [u8]);
246
247impl<'a> Debug for Bytes<'a> {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 write!(f, "{:#02x?}", self.0)
250 }
251}
252
253impl<'a> Display for Bytes<'a> {
254 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
255 write!(f, "{:#02x?}", self.0)
256 }
257}
258
259impl<'a> LowerHex for Bytes<'a> {
260 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
261 write!(f, "{:#02x?}", self.0)
262 }
263}
264
265#[cfg(feature = "defmt")]
266impl<'a> defmt::Format for Bytes<'a> {
267 fn format(&self, fmt: defmt::Formatter) {
268 defmt::write!(fmt, "{:02x}", self.0)
269 }
270}
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs
new file mode 100644
index 000000000..e1eb7eecf
--- /dev/null
+++ b/embassy-mspm0/src/gpio.rs
@@ -0,0 +1,1060 @@
1#![macro_use]
2
3use core::convert::Infallible;
4use core::future::Future;
5use core::pin::Pin as FuturePin;
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker;
10
11use crate::pac::gpio::vals::*;
12use crate::pac::gpio::{self};
13#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
14use crate::pac::interrupt;
15use crate::pac::{self};
16
17/// Represents a digital input or output level.
18#[derive(Debug, Eq, PartialEq, Clone, Copy)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub enum Level {
21 /// Logical low.
22 Low,
23 /// Logical high.
24 High,
25}
26
27impl From<bool> for Level {
28 fn from(val: bool) -> Self {
29 match val {
30 true => Self::High,
31 false => Self::Low,
32 }
33 }
34}
35
36impl From<Level> for bool {
37 fn from(level: Level) -> bool {
38 match level {
39 Level::Low => false,
40 Level::High => true,
41 }
42 }
43}
44
45/// Represents a pull setting for an input.
46#[derive(Debug, Clone, Copy, Eq, PartialEq)]
47pub enum Pull {
48 /// No pull.
49 None,
50 /// Internal pull-up resistor.
51 Up,
52 /// Internal pull-down resistor.
53 Down,
54}
55
56/// A GPIO bank with up to 32 pins.
57#[derive(Debug, Clone, Copy, Eq, PartialEq)]
58pub enum Port {
59 /// Port A.
60 PortA = 0,
61
62 /// Port B.
63 #[cfg(gpio_pb)]
64 PortB = 1,
65
66 /// Port C.
67 #[cfg(gpio_pc)]
68 PortC = 2,
69}
70
71/// GPIO flexible pin.
72///
73/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
74/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
75/// mode.
76pub struct Flex<'d> {
77 pin: PeripheralRef<'d, AnyPin>,
78}
79
80impl<'d> Flex<'d> {
81 /// Wrap the pin in a `Flex`.
82 ///
83 /// The pin remains disconnected. The initial output level is unspecified, but can be changed
84 /// before the pin is put into output mode.
85 #[inline]
86 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd) -> Self {
87 into_ref!(pin);
88
89 // Pin will be in disconnected state.
90 Self { pin: pin.map_into() }
91 }
92
93 /// Set the pin's pull.
94 #[inline]
95 pub fn set_pull(&mut self, pull: Pull) {
96 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
97
98 pincm.modify(|w| {
99 w.set_pipd(matches!(pull, Pull::Down));
100 w.set_pipu(matches!(pull, Pull::Up));
101 });
102 }
103
104 /// Put the pin into input mode.
105 ///
106 /// The pull setting is left unchanged.
107 #[inline]
108 pub fn set_as_input(&mut self) {
109 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
110
111 pincm.modify(|w| {
112 w.set_pf(GPIO_PF);
113 w.set_hiz1(false);
114 w.set_pc(true);
115 w.set_inena(true);
116 });
117
118 self.pin.block().doeclr31_0().write(|w| {
119 w.set_dio(self.pin.bit_index(), true);
120 });
121 }
122
123 /// Put the pin into output mode.
124 ///
125 /// The pin level will be whatever was set before (or low by default). If you want it to begin
126 /// at a specific level, call `set_high`/`set_low` on the pin first.
127 #[inline]
128 pub fn set_as_output(&mut self) {
129 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
130
131 pincm.modify(|w| {
132 w.set_pf(GPIO_PF);
133 w.set_hiz1(false);
134 w.set_pc(true);
135 w.set_inena(false);
136 });
137
138 self.pin.block().doeset31_0().write(|w| {
139 w.set_dio(self.pin.bit_index(), true);
140 });
141 }
142
143 /// Put the pin into input + open-drain output mode.
144 ///
145 /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
146 /// it to high, in which case you can read the input to figure out whether another device
147 /// is driving the line low.
148 ///
149 /// The pin level will be whatever was set before (or low by default). If you want it to begin
150 /// at a specific level, call `set_high`/`set_low` on the pin first.
151 ///
152 /// The internal weak pull-up and pull-down resistors will be disabled.
153 #[inline]
154 pub fn set_as_input_output(&mut self) {
155 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
156
157 pincm.modify(|w| {
158 w.set_pf(GPIO_PF);
159 w.set_hiz1(true);
160 w.set_pc(true);
161 w.set_inena(false);
162 });
163
164 self.set_pull(Pull::None);
165 }
166
167 /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
168 /// amount of power possible.
169 ///
170 /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
171 /// really. Drivers should `set_as_disconnected()` pins when dropped.
172 ///
173 /// Note that this also disables the internal weak pull-up and pull-down resistors.
174 #[inline]
175 pub fn set_as_disconnected(&mut self) {
176 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
177
178 pincm.modify(|w| {
179 w.set_pf(DISCONNECT_PF);
180 w.set_hiz1(false);
181 w.set_pc(false);
182 w.set_inena(false);
183 });
184
185 self.set_pull(Pull::None);
186 self.set_inversion(false);
187 }
188
189 /// Configure the logic inversion of this pin.
190 ///
191 /// Logic inversion applies to both the input and output path of this pin.
192 #[inline]
193 pub fn set_inversion(&mut self, invert: bool) {
194 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
195
196 pincm.modify(|w| {
197 w.set_inv(invert);
198 });
199 }
200
201 // TODO: drive strength, hysteresis, wakeup enable, wakeup compare
202
203 /// Put the pin into the PF mode, unchecked.
204 ///
205 /// This puts the pin into the PF mode, with the request number. This is completely unchecked,
206 /// it can attach the pin to literally any peripheral, so use with care. In addition the pin
207 /// peripheral is connected in the iomux.
208 ///
209 /// The peripheral attached to the pin depends on the part in use. Consult the datasheet
210 /// or technical reference manual for additional details.
211 #[inline]
212 pub fn set_pf_unchecked(&mut self, pf: u8) {
213 // Per SLAU893, PF is only 5 bits
214 assert!((pf & 0x3F) != 0, "PF is out of range");
215
216 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
217
218 pincm.modify(|w| {
219 w.set_pf(pf);
220 // If the PF is manually set, connect the pin
221 w.set_pc(true);
222 });
223 }
224
225 /// Get whether the pin input level is high.
226 #[inline]
227 pub fn is_high(&self) -> bool {
228 !self.is_low()
229 }
230
231 /// Get whether the pin input level is low.
232 #[inline]
233 pub fn is_low(&self) -> bool {
234 self.pin.block().din31_0().read().dio(self.pin.bit_index())
235 }
236
237 /// Returns current pin level
238 #[inline]
239 pub fn get_level(&self) -> Level {
240 self.is_high().into()
241 }
242
243 /// Set the output as high.
244 #[inline]
245 pub fn set_high(&mut self) {
246 self.pin.block().doutset31_0().write(|w| {
247 w.set_dio(self.pin.bit_index() as usize, true);
248 });
249 }
250
251 /// Set the output as low.
252 #[inline]
253 pub fn set_low(&mut self) {
254 self.pin.block().doutclr31_0().write(|w| {
255 w.set_dio(self.pin.bit_index(), true);
256 });
257 }
258
259 /// Toggle pin output
260 #[inline]
261 pub fn toggle(&mut self) {
262 self.pin.block().douttgl31_0().write(|w| {
263 w.set_dio(self.pin.bit_index(), true);
264 })
265 }
266
267 /// Set the output level.
268 #[inline]
269 pub fn set_level(&mut self, level: Level) {
270 match level {
271 Level::Low => self.set_low(),
272 Level::High => self.set_high(),
273 }
274 }
275
276 /// Get the current pin input level.
277 #[inline]
278 pub fn get_output_level(&self) -> Level {
279 self.is_high().into()
280 }
281
282 /// Is the output level high?
283 #[inline]
284 pub fn is_set_high(&self) -> bool {
285 !self.is_set_low()
286 }
287
288 /// Is the output level low?
289 #[inline]
290 pub fn is_set_low(&self) -> bool {
291 (self.pin.block().dout31_0().read().0 & self.pin.bit_index() as u32) == 0
292 }
293
294 /// Wait until the pin is high. If it is already high, return immediately.
295 #[inline]
296 pub async fn wait_for_high(&mut self) {
297 if self.is_high() {
298 return;
299 }
300
301 self.wait_for_rising_edge().await
302 }
303
304 /// Wait until the pin is low. If it is already low, return immediately.
305 #[inline]
306 pub async fn wait_for_low(&mut self) {
307 if self.is_low() {
308 return;
309 }
310
311 self.wait_for_falling_edge().await
312 }
313
314 /// Wait for the pin to undergo a transition from low to high.
315 #[inline]
316 pub async fn wait_for_rising_edge(&mut self) {
317 InputFuture::new(self.pin.reborrow(), Polarity::RISE).await
318 }
319
320 /// Wait for the pin to undergo a transition from high to low.
321 #[inline]
322 pub async fn wait_for_falling_edge(&mut self) {
323 InputFuture::new(self.pin.reborrow(), Polarity::FALL).await
324 }
325
326 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
327 #[inline]
328 pub async fn wait_for_any_edge(&mut self) {
329 InputFuture::new(self.pin.reborrow(), Polarity::RISE_FALL).await
330 }
331}
332
333impl<'d> Drop for Flex<'d> {
334 #[inline]
335 fn drop(&mut self) {
336 self.set_as_disconnected();
337 }
338}
339
340/// GPIO input driver.
341pub struct Input<'d> {
342 pin: Flex<'d>,
343}
344
345impl<'d> Input<'d> {
346 /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
347 #[inline]
348 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, pull: Pull) -> Self {
349 let mut pin = Flex::new(pin);
350 pin.set_as_input();
351 pin.set_pull(pull);
352 Self { pin }
353 }
354
355 /// Get whether the pin input level is high.
356 #[inline]
357 pub fn is_high(&self) -> bool {
358 self.pin.is_high()
359 }
360
361 /// Get whether the pin input level is low.
362 #[inline]
363 pub fn is_low(&self) -> bool {
364 self.pin.is_low()
365 }
366
367 /// Get the current pin input level.
368 #[inline]
369 pub fn get_level(&self) -> Level {
370 self.pin.get_level()
371 }
372
373 /// Configure the logic inversion of this pin.
374 ///
375 /// Logic inversion applies to the input path of this pin.
376 #[inline]
377 pub fn set_inversion(&mut self, invert: bool) {
378 self.pin.set_inversion(invert)
379 }
380
381 /// Wait until the pin is high. If it is already high, return immediately.
382 #[inline]
383 pub async fn wait_for_high(&mut self) {
384 self.pin.wait_for_high().await
385 }
386
387 /// Wait until the pin is low. If it is already low, return immediately.
388 #[inline]
389 pub async fn wait_for_low(&mut self) {
390 self.pin.wait_for_low().await
391 }
392
393 /// Wait for the pin to undergo a transition from low to high.
394 #[inline]
395 pub async fn wait_for_rising_edge(&mut self) {
396 self.pin.wait_for_rising_edge().await
397 }
398
399 /// Wait for the pin to undergo a transition from high to low.
400 #[inline]
401 pub async fn wait_for_falling_edge(&mut self) {
402 self.pin.wait_for_falling_edge().await
403 }
404
405 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
406 #[inline]
407 pub async fn wait_for_any_edge(&mut self) {
408 self.pin.wait_for_any_edge().await
409 }
410}
411
412/// GPIO output driver.
413///
414/// Note that pins will **return to their floating state** when `Output` is dropped.
415/// If pins should retain their state indefinitely, either keep ownership of the
416/// `Output`, or pass it to [`core::mem::forget`].
417pub struct Output<'d> {
418 pin: Flex<'d>,
419}
420
421impl<'d> Output<'d> {
422 /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.
423 #[inline]
424 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level) -> Self {
425 let mut pin = Flex::new(pin);
426 pin.set_as_output();
427 pin.set_level(initial_output);
428 Self { pin }
429 }
430
431 /// Set the output as high.
432 #[inline]
433 pub fn set_high(&mut self) {
434 self.pin.set_high();
435 }
436
437 /// Set the output as low.
438 #[inline]
439 pub fn set_low(&mut self) {
440 self.pin.set_low();
441 }
442
443 /// Set the output level.
444 #[inline]
445 pub fn set_level(&mut self, level: Level) {
446 self.pin.set_level(level)
447 }
448
449 /// Is the output pin set as high?
450 #[inline]
451 pub fn is_set_high(&self) -> bool {
452 self.pin.is_set_high()
453 }
454
455 /// Is the output pin set as low?
456 #[inline]
457 pub fn is_set_low(&self) -> bool {
458 self.pin.is_set_low()
459 }
460
461 /// What level output is set to
462 #[inline]
463 pub fn get_output_level(&self) -> Level {
464 self.pin.get_output_level()
465 }
466
467 /// Toggle pin output
468 #[inline]
469 pub fn toggle(&mut self) {
470 self.pin.toggle();
471 }
472
473 /// Configure the logic inversion of this pin.
474 ///
475 /// Logic inversion applies to the input path of this pin.
476 #[inline]
477 pub fn set_inversion(&mut self, invert: bool) {
478 self.pin.set_inversion(invert)
479 }
480}
481
482/// GPIO output open-drain driver.
483///
484/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.
485/// If pins should retain their state indefinitely, either keep ownership of the
486/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].
487pub struct OutputOpenDrain<'d> {
488 pin: Flex<'d>,
489}
490
491impl<'d> OutputOpenDrain<'d> {
492 /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].
493 #[inline]
494 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level) -> Self {
495 let mut pin = Flex::new(pin);
496 pin.set_level(initial_output);
497 pin.set_as_input_output();
498 Self { pin }
499 }
500
501 /// Get whether the pin input level is high.
502 #[inline]
503 pub fn is_high(&self) -> bool {
504 !self.pin.is_low()
505 }
506
507 /// Get whether the pin input level is low.
508 #[inline]
509 pub fn is_low(&self) -> bool {
510 self.pin.is_low()
511 }
512
513 /// Get the current pin input level.
514 #[inline]
515 pub fn get_level(&self) -> Level {
516 self.pin.get_level()
517 }
518
519 /// Set the output as high.
520 #[inline]
521 pub fn set_high(&mut self) {
522 self.pin.set_high();
523 }
524
525 /// Set the output as low.
526 #[inline]
527 pub fn set_low(&mut self) {
528 self.pin.set_low();
529 }
530
531 /// Set the output level.
532 #[inline]
533 pub fn set_level(&mut self, level: Level) {
534 self.pin.set_level(level);
535 }
536
537 /// Get whether the output level is set to high.
538 #[inline]
539 pub fn is_set_high(&self) -> bool {
540 self.pin.is_set_high()
541 }
542
543 /// Get whether the output level is set to low.
544 #[inline]
545 pub fn is_set_low(&self) -> bool {
546 self.pin.is_set_low()
547 }
548
549 /// Get the current output level.
550 #[inline]
551 pub fn get_output_level(&self) -> Level {
552 self.pin.get_output_level()
553 }
554
555 /// Toggle pin output
556 #[inline]
557 pub fn toggle(&mut self) {
558 self.pin.toggle()
559 }
560
561 /// Configure the logic inversion of this pin.
562 ///
563 /// Logic inversion applies to the input path of this pin.
564 #[inline]
565 pub fn set_inversion(&mut self, invert: bool) {
566 self.pin.set_inversion(invert)
567 }
568
569 /// Wait until the pin is high. If it is already high, return immediately.
570 #[inline]
571 pub async fn wait_for_high(&mut self) {
572 self.pin.wait_for_high().await
573 }
574
575 /// Wait until the pin is low. If it is already low, return immediately.
576 #[inline]
577 pub async fn wait_for_low(&mut self) {
578 self.pin.wait_for_low().await
579 }
580
581 /// Wait for the pin to undergo a transition from low to high.
582 #[inline]
583 pub async fn wait_for_rising_edge(&mut self) {
584 self.pin.wait_for_rising_edge().await
585 }
586
587 /// Wait for the pin to undergo a transition from high to low.
588 #[inline]
589 pub async fn wait_for_falling_edge(&mut self) {
590 self.pin.wait_for_falling_edge().await
591 }
592
593 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
594 #[inline]
595 pub async fn wait_for_any_edge(&mut self) {
596 self.pin.wait_for_any_edge().await
597 }
598}
599
600/// Type-erased GPIO pin
601pub struct AnyPin {
602 pin_port: u8,
603}
604
605impl AnyPin {
606 /// Create an [AnyPin] for a specific pin.
607 ///
608 /// # Safety
609 /// - `pin_port` should not in use by another driver.
610 #[inline]
611 pub unsafe fn steal(pin_port: u8) -> Self {
612 Self { pin_port }
613 }
614}
615
616impl_peripheral!(AnyPin);
617
618impl Pin for AnyPin {}
619impl SealedPin for AnyPin {
620 #[inline]
621 fn pin_port(&self) -> u8 {
622 self.pin_port
623 }
624}
625
626/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].
627#[allow(private_bounds)]
628pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static {
629 fn degrade(self) -> AnyPin {
630 AnyPin {
631 pin_port: self.pin_port(),
632 }
633 }
634
635 /// The index of this pin in PINCM (pin control management) registers.
636 #[inline]
637 fn pin_cm(&self) -> u8 {
638 self._pin_cm()
639 }
640}
641
642impl<'d> embedded_hal::digital::ErrorType for Flex<'d> {
643 type Error = Infallible;
644}
645
646impl<'d> embedded_hal::digital::InputPin for Flex<'d> {
647 #[inline]
648 fn is_high(&mut self) -> Result<bool, Self::Error> {
649 Ok((*self).is_high())
650 }
651
652 #[inline]
653 fn is_low(&mut self) -> Result<bool, Self::Error> {
654 Ok((*self).is_low())
655 }
656}
657
658impl<'d> embedded_hal::digital::OutputPin for Flex<'d> {
659 #[inline]
660 fn set_low(&mut self) -> Result<(), Self::Error> {
661 Ok(self.set_low())
662 }
663
664 #[inline]
665 fn set_high(&mut self) -> Result<(), Self::Error> {
666 Ok(self.set_high())
667 }
668}
669
670impl<'d> embedded_hal::digital::StatefulOutputPin for Flex<'d> {
671 #[inline]
672 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
673 Ok((*self).is_set_high())
674 }
675
676 #[inline]
677 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
678 Ok((*self).is_set_low())
679 }
680}
681
682impl<'d> embedded_hal_async::digital::Wait for Flex<'d> {
683 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
684 self.wait_for_high().await;
685 Ok(())
686 }
687
688 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
689 self.wait_for_low().await;
690 Ok(())
691 }
692
693 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
694 self.wait_for_rising_edge().await;
695 Ok(())
696 }
697
698 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
699 self.wait_for_falling_edge().await;
700 Ok(())
701 }
702
703 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
704 self.wait_for_any_edge().await;
705 Ok(())
706 }
707}
708
709impl<'d> embedded_hal::digital::ErrorType for Input<'d> {
710 type Error = Infallible;
711}
712
713impl<'d> embedded_hal::digital::InputPin for Input<'d> {
714 #[inline]
715 fn is_high(&mut self) -> Result<bool, Self::Error> {
716 Ok((*self).is_high())
717 }
718
719 #[inline]
720 fn is_low(&mut self) -> Result<bool, Self::Error> {
721 Ok((*self).is_low())
722 }
723}
724
725impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
726 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
727 self.wait_for_high().await;
728 Ok(())
729 }
730
731 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
732 self.wait_for_low().await;
733 Ok(())
734 }
735
736 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
737 self.wait_for_rising_edge().await;
738 Ok(())
739 }
740
741 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
742 self.wait_for_falling_edge().await;
743 Ok(())
744 }
745
746 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
747 self.wait_for_any_edge().await;
748 Ok(())
749 }
750}
751
752impl<'d> embedded_hal::digital::ErrorType for Output<'d> {
753 type Error = Infallible;
754}
755
756impl<'d> embedded_hal::digital::OutputPin for Output<'d> {
757 #[inline]
758 fn set_low(&mut self) -> Result<(), Self::Error> {
759 Ok(self.set_low())
760 }
761
762 #[inline]
763 fn set_high(&mut self) -> Result<(), Self::Error> {
764 Ok(self.set_high())
765 }
766}
767
768impl<'d> embedded_hal::digital::StatefulOutputPin for Output<'d> {
769 #[inline]
770 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
771 Ok((*self).is_set_high())
772 }
773
774 #[inline]
775 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
776 Ok((*self).is_set_low())
777 }
778}
779
780impl<'d> embedded_hal::digital::ErrorType for OutputOpenDrain<'d> {
781 type Error = Infallible;
782}
783
784impl<'d> embedded_hal::digital::InputPin for OutputOpenDrain<'d> {
785 #[inline]
786 fn is_high(&mut self) -> Result<bool, Self::Error> {
787 Ok((*self).is_high())
788 }
789
790 #[inline]
791 fn is_low(&mut self) -> Result<bool, Self::Error> {
792 Ok((*self).is_low())
793 }
794}
795
796impl<'d> embedded_hal::digital::OutputPin for OutputOpenDrain<'d> {
797 #[inline]
798 fn set_low(&mut self) -> Result<(), Self::Error> {
799 Ok(self.set_low())
800 }
801
802 #[inline]
803 fn set_high(&mut self) -> Result<(), Self::Error> {
804 Ok(self.set_high())
805 }
806}
807
808impl<'d> embedded_hal::digital::StatefulOutputPin for OutputOpenDrain<'d> {
809 #[inline]
810 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
811 Ok((*self).is_set_high())
812 }
813
814 #[inline]
815 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
816 Ok((*self).is_set_low())
817 }
818}
819
820impl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {
821 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
822 self.wait_for_high().await;
823 Ok(())
824 }
825
826 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
827 self.wait_for_low().await;
828 Ok(())
829 }
830
831 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
832 self.wait_for_rising_edge().await;
833 Ok(())
834 }
835
836 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
837 self.wait_for_falling_edge().await;
838 Ok(())
839 }
840
841 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
842 self.wait_for_any_edge().await;
843 Ok(())
844 }
845}
846
847/// The pin function to disconnect peripherals from the pin.
848///
849/// This is also the pin function used to connect to analog peripherals, such as an ADC.
850const DISCONNECT_PF: u8 = 0;
851
852/// The pin function for the GPIO peripheral.
853///
854/// This is fixed to `1` for every part.
855const GPIO_PF: u8 = 1;
856
857macro_rules! impl_pin {
858 ($name: ident, $port: expr, $pin_num: expr) => {
859 impl crate::gpio::Pin for crate::peripherals::$name {}
860 impl crate::gpio::SealedPin for crate::peripherals::$name {
861 #[inline]
862 fn pin_port(&self) -> u8 {
863 ($port as u8) * 32 + $pin_num
864 }
865 }
866
867 impl From<crate::peripherals::$name> for crate::gpio::AnyPin {
868 fn from(val: crate::peripherals::$name) -> Self {
869 crate::gpio::Pin::degrade(val)
870 }
871 }
872 };
873}
874
875// TODO: Possible micro-op for C110X, not every pin is instantiated even on the 20 pin parts.
876// This would mean cfg guarding to just cfg guarding every pin instance.
877static PORTA_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
878#[cfg(gpio_pb)]
879static PORTB_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
880#[cfg(gpio_pc)]
881static PORTC_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
882
883pub(crate) trait SealedPin {
884 fn pin_port(&self) -> u8;
885
886 fn port(&self) -> Port {
887 match self.pin_port() / 32 {
888 0 => Port::PortA,
889 #[cfg(gpio_pb)]
890 1 => Port::PortB,
891 #[cfg(gpio_pc)]
892 2 => Port::PortC,
893 _ => unreachable!(),
894 }
895 }
896
897 fn waker(&self) -> &AtomicWaker {
898 match self.port() {
899 Port::PortA => &PORTA_WAKERS[self.bit_index()],
900 #[cfg(gpio_pb)]
901 Port::PortB => &PORTB_WAKERS[self.bit_index()],
902 #[cfg(gpio_pc)]
903 Port::PortC => &PORTC_WAKERS[self.bit_index()],
904 }
905 }
906
907 fn _pin_cm(&self) -> u8 {
908 // Some parts like the MSPM0L222x have pincm mappings all over the place.
909 crate::gpio_pincm(self.pin_port())
910 }
911
912 fn bit_index(&self) -> usize {
913 (self.pin_port() % 32) as usize
914 }
915
916 #[inline]
917 fn block(&self) -> gpio::Gpio {
918 match self.pin_port() / 32 {
919 0 => pac::GPIOA,
920 #[cfg(gpio_pb)]
921 1 => pac::GPIOB,
922 #[cfg(gpio_pc)]
923 2 => pac::GPIOC,
924 _ => unreachable!(),
925 }
926 }
927}
928
929#[must_use = "futures do nothing unless you `.await` or poll them"]
930struct InputFuture<'d> {
931 pin: PeripheralRef<'d, AnyPin>,
932}
933
934impl<'d> InputFuture<'d> {
935 fn new(pin: PeripheralRef<'d, AnyPin>, polarity: Polarity) -> Self {
936 let block = pin.block();
937
938 // First clear the bit for this event. Otherwise previous edge events may be recorded.
939 block.cpu_int().iclr().write(|w| {
940 w.set_dio(pin.bit_index(), true);
941 });
942
943 // Selecting which polarity events happens is a RMW operation.
944 //
945 // Guard with a critical section in case two different threads try to select events at the
946 // same time.
947 critical_section::with(|_cs| {
948 // Tell the hardware which pin event we want to receive.
949 if pin.bit_index() >= 16 {
950 block.polarity31_16().modify(|w| {
951 w.set_dio(pin.bit_index() - 16, polarity);
952 });
953 } else {
954 block.polarity15_0().modify(|w| {
955 w.set_dio(pin.bit_index(), polarity);
956 });
957 };
958 });
959
960 Self { pin }
961 }
962}
963
964impl<'d> Future for InputFuture<'d> {
965 type Output = ();
966
967 fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
968 // We need to register/re-register the waker for each poll because any
969 // calls to wake will deregister the waker.
970 let waker = self.pin.waker();
971 waker.register(cx.waker());
972
973 // The interrupt handler will mask the interrupt if the event has occurred.
974 if self.pin.block().cpu_int().ris().read().dio(self.pin.bit_index()) {
975 return Poll::Ready(());
976 }
977
978 // Unmasking the interrupt is a RMW operation.
979 //
980 // Guard with a critical section in case two different threads try to unmask at the same time.
981 critical_section::with(|_cs| {
982 self.pin.block().cpu_int().imask().modify(|w| {
983 w.set_dio(self.pin.bit_index(), true);
984 });
985 });
986
987 Poll::Pending
988 }
989}
990
991pub(crate) fn init(gpio: gpio::Gpio) {
992 gpio.gprcm().rstctl().write(|w| {
993 w.set_resetstkyclr(true);
994 w.set_resetassert(true);
995 w.set_key(ResetKey::KEY);
996 });
997
998 gpio.gprcm().pwren().write(|w| {
999 w.set_enable(true);
1000 w.set_key(PwrenKey::KEY);
1001 });
1002
1003 gpio.evt_mode().modify(|w| {
1004 // The CPU will clear it's own interrupts
1005 w.set_cpu_cfg(EvtCfg::SOFTWARE);
1006 });
1007}
1008
1009#[cfg(feature = "rt")]
1010fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {
1011 // Only consider pins which have interrupts unmasked.
1012 let bits = gpio.cpu_int().mis().read().0;
1013
1014 for i in BitIter(bits) {
1015 wakers[i as usize].wake();
1016
1017 // Notify the future that an edge event has occurred by masking the interrupt for this pin.
1018 gpio.cpu_int().imask().modify(|w| {
1019 w.set_dio(i as usize, false);
1020 });
1021 }
1022}
1023
1024struct BitIter(u32);
1025
1026impl Iterator for BitIter {
1027 type Item = u32;
1028
1029 fn next(&mut self) -> Option<Self::Item> {
1030 match self.0.trailing_zeros() {
1031 32 => None,
1032 b => {
1033 self.0 &= !(1 << b);
1034 Some(b)
1035 }
1036 }
1037 }
1038}
1039
1040// C110x has a dedicated interrupt just for GPIOA, as it does not have a GROUP1 interrupt.
1041#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
1042#[interrupt]
1043fn GPIOA() {
1044 gpioa_interrupt();
1045}
1046
1047#[cfg(feature = "rt")]
1048pub(crate) fn gpioa_interrupt() {
1049 irq_handler(pac::GPIOA, &PORTA_WAKERS);
1050}
1051
1052#[cfg(all(feature = "rt", gpio_pb))]
1053pub(crate) fn gpiob_interrupt() {
1054 irq_handler(pac::GPIOB, &PORTB_WAKERS);
1055}
1056
1057#[cfg(all(feature = "rt", gpio_pc))]
1058pub(crate) fn gpioc_interrupt() {
1059 irq_handler(pac::GPIOC, &PORTC_WAKERS);
1060}
diff --git a/embassy-mspm0/src/int_group/c110x.rs b/embassy-mspm0/src/int_group/c110x.rs
new file mode 100644
index 000000000..e6a9ddb99
--- /dev/null
+++ b/embassy-mspm0/src/int_group/c110x.rs
@@ -0,0 +1,25 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // TODO: Decompose to direct u8
12 let iidx = group.iidx().read().stat().to_bits();
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::DEBUGSS => todo!("implement DEBUGSS"),
22 Group0::FLASHCTL => todo!("implement FLASHCTL"),
23 Group0::SYSCTL => todo!("implement SYSCTL"),
24 }
25}
diff --git a/embassy-mspm0/src/int_group/g350x.rs b/embassy-mspm0/src/int_group/g350x.rs
new file mode 100644
index 000000000..706ba2078
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g350x.rs
@@ -0,0 +1,51 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::WWDT1 => todo!("implement WWDT1"),
22 Group0::DEBUGSS => todo!("implement DEBUGSS"),
23 Group0::FLASHCTL => todo!("implement FLASHCTL"),
24 Group0::SYSCTL => todo!("implement SYSCTL"),
25 }
26}
27
28#[cfg(feature = "rt")]
29#[interrupt]
30fn GROUP1() {
31 use mspm0_metapac::Group1;
32
33 let group = pac::CPUSS.int_group(1);
34
35 // Must subtract by 1 since NO_INTR is value 0
36 let iidx = group.iidx().read().stat().to_bits() - 1;
37
38 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
39 debug!("Invalid IIDX for group 1: {}", iidx);
40 return;
41 };
42
43 match group {
44 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
45 Group1::GPIOB => crate::gpio::gpiob_interrupt(),
46 Group1::COMP0 => todo!("implement COMP0"),
47 Group1::COMP1 => todo!("implement COMP1"),
48 Group1::COMP2 => todo!("implement COMP2"),
49 Group1::TRNG => todo!("implement TRNG"),
50 }
51}
diff --git a/embassy-mspm0/src/int_group/g351x.rs b/embassy-mspm0/src/int_group/g351x.rs
new file mode 100644
index 000000000..e785018a7
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g351x.rs
@@ -0,0 +1,52 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::WWDT1 => todo!("implement WWDT1"),
22 Group0::DEBUGSS => todo!("implement DEBUGSS"),
23 Group0::FLASHCTL => todo!("implement FLASHCTL"),
24 Group0::SYSCTL => todo!("implement SYSCTL"),
25 }
26}
27
28#[cfg(feature = "rt")]
29#[interrupt]
30fn GROUP1() {
31 use mspm0_metapac::Group1;
32
33 let group = pac::CPUSS.int_group(1);
34
35 // Must subtract by 1 since NO_INTR is value 0
36 let iidx = group.iidx().read().stat().to_bits() - 1;
37
38 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
39 debug!("Invalid IIDX for group 1: {}", iidx);
40 return;
41 };
42
43 match group {
44 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
45 Group1::GPIOB => crate::gpio::gpiob_interrupt(),
46 Group1::COMP0 => todo!("implement COMP0"),
47 Group1::COMP1 => todo!("implement COMP1"),
48 Group1::COMP2 => todo!("implement COMP2"),
49 Group1::TRNG => todo!("implement TRNG"),
50 Group1::GPIOC => crate::gpio::gpioc_interrupt(),
51 }
52}
diff --git a/embassy-mspm0/src/int_group/l130x.rs b/embassy-mspm0/src/int_group/l130x.rs
new file mode 100644
index 000000000..8be5adcad
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l130x.rs
@@ -0,0 +1,46 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::DEBUGSS => todo!("implement DEBUGSS"),
22 Group0::FLASHCTL => todo!("implement FLASHCTL"),
23 Group0::SYSCTL => todo!("implement SYSCTL"),
24 }
25}
26
27#[cfg(feature = "rt")]
28#[interrupt]
29fn GROUP1() {
30 use mspm0_metapac::Group1;
31
32 let group = pac::CPUSS.int_group(1);
33
34 // Must subtract by 1 since NO_INTR is value 0
35 let iidx = group.iidx().read().stat().to_bits() - 1;
36
37 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
38 debug!("Invalid IIDX for group 1: {}", iidx);
39 return;
40 };
41
42 match group {
43 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
44 Group1::COMP0 => todo!("implement COMP0"),
45 }
46}
diff --git a/embassy-mspm0/src/int_group/l222x.rs b/embassy-mspm0/src/int_group/l222x.rs
new file mode 100644
index 000000000..eeb2ce70d
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l222x.rs
@@ -0,0 +1,49 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::DEBUGSS => todo!("implement DEBUGSS"),
22 Group0::FLASHCTL => todo!("implement FLASHCTL"),
23 Group0::SYSCTL => todo!("implement SYSCTL"),
24 }
25}
26
27#[cfg(feature = "rt")]
28#[interrupt]
29fn GROUP1() {
30 use mspm0_metapac::Group1;
31
32 let group = pac::CPUSS.int_group(1);
33
34 // Must subtract by 1 since NO_INTR is value 0
35 let iidx = group.iidx().read().stat().to_bits() - 1;
36
37 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
38 debug!("Invalid IIDX for group 1: {}", iidx);
39 return;
40 };
41
42 match group {
43 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
44 Group1::GPIOB => crate::gpio::gpiob_interrupt(),
45 Group1::COMP0 => todo!("implement COMP0"),
46 Group1::TRNG => todo!("implement TRNG"),
47 Group1::GPIOC => crate::gpio::gpioc_interrupt(),
48 }
49}
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
new file mode 100644
index 000000000..1191b1010
--- /dev/null
+++ b/embassy-mspm0/src/lib.rs
@@ -0,0 +1,107 @@
1#![no_std]
2// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
3#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
4
5// This mod MUST go first, so that the others see its macros.
6pub(crate) mod fmt;
7
8pub mod gpio;
9pub mod timer;
10
11#[cfg(feature = "_time-driver")]
12mod time_driver;
13
14// Interrupt group handlers.
15#[cfg_attr(feature = "mspm0c110x", path = "int_group/c110x.rs")]
16#[cfg_attr(feature = "mspm0g110x", path = "int_group/g110x.rs")]
17#[cfg_attr(feature = "mspm0g150x", path = "int_group/g150x.rs")]
18#[cfg_attr(feature = "mspm0g151x", path = "int_group/g151x.rs")]
19#[cfg_attr(feature = "mspm0g310x", path = "int_group/g310x.rs")]
20#[cfg_attr(feature = "mspm0g350x", path = "int_group/g350x.rs")]
21#[cfg_attr(feature = "mspm0g351x", path = "int_group/g351x.rs")]
22#[cfg_attr(feature = "mspm0l110x", path = "int_group/l110x.rs")]
23#[cfg_attr(feature = "mspm0l122x", path = "int_group/l122x.rs")]
24#[cfg_attr(feature = "mspm0l130x", path = "int_group/l130x.rs")]
25#[cfg_attr(feature = "mspm0l134x", path = "int_group/l134x.rs")]
26#[cfg_attr(feature = "mspm0l222x", path = "int_group/l222x.rs")]
27mod int_group;
28
29pub(crate) mod _generated {
30 #![allow(dead_code)]
31 #![allow(unused_imports)]
32 #![allow(non_snake_case)]
33 #![allow(missing_docs)]
34
35 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
36}
37
38// Reexports
39pub(crate) use _generated::gpio_pincm;
40pub use _generated::{peripherals, Peripherals};
41pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
42#[cfg(feature = "unstable-pac")]
43pub use mspm0_metapac as pac;
44#[cfg(not(feature = "unstable-pac"))]
45pub(crate) use mspm0_metapac as pac;
46
47pub use crate::_generated::interrupt;
48
49/// `embassy-mspm0` global configuration.
50#[non_exhaustive]
51#[derive(Clone, Copy)]
52pub struct Config {
53 // TODO
54}
55
56impl Default for Config {
57 fn default() -> Self {
58 Self {
59 // TODO
60 }
61 }
62}
63
64pub fn init(_config: Config) -> Peripherals {
65 critical_section::with(|cs| {
66 let peripherals = Peripherals::take_with_cs(cs);
67
68 // TODO: Further clock configuration
69
70 pac::SYSCTL.mclkcfg().modify(|w| {
71 // Enable MFCLK
72 w.set_usemftick(true);
73 // MDIV must be disabled if MFCLK is enabled.
74 w.set_mdiv(0);
75 });
76
77 // Enable MFCLK for peripheral use
78 //
79 // TODO: Optional?
80 pac::SYSCTL.genclken().modify(|w| {
81 w.set_mfpclken(true);
82 });
83
84 pac::SYSCTL.borthreshold().modify(|w| {
85 w.set_level(0);
86 });
87
88 gpio::init(pac::GPIOA);
89 #[cfg(gpio_pb)]
90 gpio::init(pac::GPIOB);
91 #[cfg(gpio_pc)]
92 gpio::init(pac::GPIOC);
93
94 _generated::enable_group_interrupts(cs);
95
96 #[cfg(feature = "mspm0c110x")]
97 unsafe {
98 use crate::_generated::interrupt::typelevel::Interrupt;
99 crate::interrupt::typelevel::GPIOA::enable();
100 }
101
102 #[cfg(feature = "_time-driver")]
103 time_driver::init(cs);
104
105 peripherals
106 })
107}
diff --git a/embassy-mspm0/src/time_driver.rs b/embassy-mspm0/src/time_driver.rs
new file mode 100644
index 000000000..937ce58d4
--- /dev/null
+++ b/embassy-mspm0/src/time_driver.rs
@@ -0,0 +1,423 @@
1use core::cell::{Cell, RefCell};
2use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
3use core::task::Waker;
4
5use critical_section::{CriticalSection, Mutex};
6use embassy_time_driver::Driver;
7use embassy_time_queue_utils::Queue;
8use mspm0_metapac::interrupt;
9use mspm0_metapac::tim::vals::{Cm, Cvae, CxC, EvtCfg, PwrenKey, Ratio, Repeat, ResetKey};
10use mspm0_metapac::tim::{Counterregs16, Tim};
11
12use crate::peripherals;
13use crate::timer::SealedTimer;
14
15// Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers.
16#[cfg(time_driver_timg0)]
17type T = peripherals::TIMG0;
18#[cfg(time_driver_timg1)]
19type T = peripherals::TIMG1;
20#[cfg(time_driver_timg2)]
21type T = peripherals::TIMG2;
22#[cfg(time_driver_timg3)]
23type T = peripherals::TIMG3;
24#[cfg(time_driver_timg4)]
25type T = peripherals::TIMG4;
26#[cfg(time_driver_timg5)]
27type T = peripherals::TIMG5;
28#[cfg(time_driver_timg6)]
29type T = peripherals::TIMG6;
30#[cfg(time_driver_timg7)]
31type T = peripherals::TIMG7;
32#[cfg(time_driver_timg8)]
33type T = peripherals::TIMG8;
34#[cfg(time_driver_timg9)]
35type T = peripherals::TIMG9;
36#[cfg(time_driver_timg10)]
37type T = peripherals::TIMG10;
38#[cfg(time_driver_timg11)]
39type T = peripherals::TIMG11;
40#[cfg(time_driver_timg14)]
41type T = peripherals::TIMG14;
42#[cfg(time_driver_tima0)]
43type T = peripherals::TIMA0;
44#[cfg(time_driver_tima1)]
45type T = peripherals::TIMA1;
46
47// TODO: RTC
48
49fn regs() -> Tim {
50 unsafe { Tim::from_ptr(T::regs()) }
51}
52
53fn regs_counter(tim: Tim) -> Counterregs16 {
54 unsafe { Counterregs16::from_ptr(tim.counterregs(0).as_ptr()) }
55}
56
57/// Clock timekeeping works with something we call "periods", which are time intervals
58/// of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods.
59fn calc_now(period: u32, counter: u16) -> u64 {
60 ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
61}
62
63/// The TIMx driver uses one of the `TIMG` or `TIMA` timer instances to implement a timer with a 32.768 kHz
64/// tick rate. (TODO: Allow setting the tick rate)
65///
66/// This driver defines a period to be 2^15 ticks. 16-bit timers of course count to 2^16 ticks.
67///
68/// To generate a period every 2^15 ticks, the CC0 value is set to 2^15 and the load value set to 2^16.
69/// Incrementing the period on a CCU0 and load results in the a period of 2^15 ticks.
70///
71/// For a specific timestamp, load the lower 16 bits into the CC1 value. When the period where the timestamp
72/// should be enabled is reached, then the CCU1 (CC1 up) interrupt runs to actually wake the timer.
73///
74/// TODO: Compensate for per part variance. This can supposedly be done with the FCC system.
75/// TODO: Allow using 32-bit timers (TIMG12 and TIMG13).
76struct TimxDriver {
77 /// Number of 2^15 periods elapsed since boot.
78 period: AtomicU32,
79 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
80 alarm: Mutex<Cell<u64>>,
81 queue: Mutex<RefCell<Queue>>,
82}
83
84impl TimxDriver {
85 #[inline(never)]
86 fn init(&'static self, _cs: CriticalSection) {
87 // Clock config
88 // TODO: Configurable tick rate up to 4 MHz (32 kHz for now)
89 let regs = regs();
90
91 // Reset timer
92 regs.gprcm(0).rstctl().write(|w| {
93 w.set_resetassert(true);
94 w.set_key(ResetKey::KEY);
95 w.set_resetstkyclr(true);
96 });
97
98 // Power up timer
99 regs.gprcm(0).pwren().write(|w| {
100 w.set_enable(true);
101 w.set_key(PwrenKey::KEY);
102 });
103
104 // Following the instructions according to SLAU847D 23.2.1: TIMCLK Configuration
105
106 // 1. Select TIMCLK source
107 regs.clksel().modify(|w| {
108 // Use LFCLK for a 32.768kHz tick rate
109 w.set_lfclk_sel(true);
110 // TODO: Allow MFCLK for configurable tick rate up to 4 MHz
111 // w.set_mfclk_sel(ClkSel::ENABLE);
112 });
113
114 // 2. Divide by TIMCLK, we don't need to divide further for the 32kHz tick rate
115 regs.clkdiv().modify(|w| {
116 w.set_ratio(Ratio::DIV_BY_1);
117 });
118
119 // 3. To be generic across timer instances, we do not use the prescaler.
120 // TODO: mspm0-sdk always sets this, regardless of timer width?
121 regs.commonregs(0).cps().modify(|w| {
122 w.set_pcnt(0);
123 });
124
125 regs.pdbgctl().modify(|w| {
126 w.set_free(true);
127 });
128
129 // 4. Enable the TIMCLK.
130 regs.commonregs(0).cclkctl().modify(|w| {
131 w.set_clken(true);
132 });
133
134 regs.counterregs(0).ctrctl().modify(|w| {
135 // allow counting during debug
136 w.set_repeat(Repeat::REPEAT_3);
137 w.set_cvae(Cvae::ZEROVAL);
138 w.set_cm(Cm::UP);
139
140 // Must explicitly set CZC, CAC and CLC to 0 in order for all the timers to count.
141 //
142 // The reset value of these registers is 0x07, which is a reserved value.
143 //
144 // Looking at a bit representation of the reset value, this appears to be an AND
145 // of 2-input QEI mode and CCCTL_3 ACOND. Given that TIMG14 and TIMA0 have no QEI
146 // and 4 capture and compare channels, this works by accident for those timer units.
147 w.set_czc(CxC::CCTL0);
148 w.set_cac(CxC::CCTL0);
149 w.set_clc(CxC::CCTL0);
150 });
151
152 // Setup the period
153 let ctr = regs_counter(regs);
154
155 // Middle
156 ctr.cc(0).modify(|w| {
157 w.set_ccval(0x7FFF);
158 });
159
160 ctr.load().modify(|w| {
161 w.set_ld(u16::MAX);
162 });
163
164 // Enable the period interrupts
165 //
166 // This does not appear to ever be set for CPU_INT in the TI SDK and is not technically needed.
167 regs.evt_mode().modify(|w| {
168 w.set_evt_cfg(0, EvtCfg::SOFTWARE);
169 });
170
171 regs.int_event(0).imask().modify(|w| {
172 w.set_l(true);
173 w.set_ccu0(true);
174 });
175
176 unsafe { T::enable_interrupt() };
177
178 // Allow the counter to start counting.
179 regs.counterregs(0).ctrctl().modify(|w| {
180 w.set_en(true);
181 });
182 }
183
184 #[inline(never)]
185 fn next_period(&self) {
186 let r = regs();
187
188 // We only modify the period from the timer interrupt, so we know this can't race.
189 let period = self.period.load(Ordering::Relaxed) + 1;
190 self.period.store(period, Ordering::Relaxed);
191 let t = (period as u64) << 15;
192
193 critical_section::with(move |cs| {
194 r.int_event(0).imask().modify(move |w| {
195 let alarm = self.alarm.borrow(cs);
196 let at = alarm.get();
197
198 if at < t + 0xC000 {
199 // just enable it. `set_alarm` has already set the correct CC1 val.
200 w.set_ccu1(true);
201 }
202 })
203 });
204 }
205
206 #[inline(never)]
207 fn on_interrupt(&self) {
208 let r = regs();
209
210 critical_section::with(|cs| {
211 let mis = r.int_event(0).mis().read();
212
213 // Advance to next period if overflowed
214 if mis.l() {
215 self.next_period();
216
217 r.int_event(0).iclr().write(|w| {
218 w.set_l(true);
219 });
220 }
221
222 if mis.ccu0() {
223 self.next_period();
224
225 r.int_event(0).iclr().write(|w| {
226 w.set_ccu0(true);
227 });
228 }
229
230 if mis.ccu1() {
231 r.int_event(0).iclr().write(|w| {
232 w.set_ccu1(true);
233 });
234
235 self.trigger_alarm(cs);
236 }
237 });
238 }
239
240 fn trigger_alarm(&self, cs: CriticalSection) {
241 let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
242
243 while !self.set_alarm(cs, next) {
244 next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
245 }
246 }
247
248 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
249 let r = regs();
250 let ctr = regs_counter(r);
251
252 self.alarm.borrow(cs).set(timestamp);
253
254 let t = self.now();
255
256 if timestamp <= t {
257 // If alarm timestamp has passed the alarm will not fire.
258 // Disarm the alarm and return `false` to indicate that.
259 r.int_event(0).imask().modify(|w| w.set_ccu1(false));
260
261 self.alarm.borrow(cs).set(u64::MAX);
262
263 return false;
264 }
265
266 // Write the CC1 value regardless of whether we're going to enable it now or not.
267 // This way, when we enable it later, the right value is already set.
268 ctr.cc(1).write(|w| {
269 w.set_ccval(timestamp as u16);
270 });
271
272 // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
273 let diff = timestamp - t;
274 r.int_event(0).imask().modify(|w| w.set_ccu1(diff < 0xC000));
275
276 // Reevaluate if the alarm timestamp is still in the future
277 let t = self.now();
278 if timestamp <= t {
279 // If alarm timestamp has passed since we set it, we have a race condition and
280 // the alarm may or may not have fired.
281 // Disarm the alarm and return `false` to indicate that.
282 // It is the caller's responsibility to handle this ambiguity.
283 r.int_event(0).imask().modify(|w| w.set_ccu1(false));
284
285 self.alarm.borrow(cs).set(u64::MAX);
286
287 return false;
288 }
289
290 // We're confident the alarm will ring in the future.
291 true
292 }
293}
294
295impl Driver for TimxDriver {
296 fn now(&self) -> u64 {
297 let regs = regs();
298
299 let period = self.period.load(Ordering::Relaxed);
300 // Ensure the compiler does not read the counter before the period.
301 compiler_fence(Ordering::Acquire);
302
303 let counter = regs_counter(regs).ctr().read().cctr() as u16;
304
305 calc_now(period, counter)
306 }
307
308 fn schedule_wake(&self, at: u64, waker: &Waker) {
309 critical_section::with(|cs| {
310 let mut queue = self.queue.borrow(cs).borrow_mut();
311
312 if queue.schedule_wake(at, waker) {
313 let mut next = queue.next_expiration(self.now());
314
315 while !self.set_alarm(cs, next) {
316 next = queue.next_expiration(self.now());
317 }
318 }
319 });
320 }
321}
322
323embassy_time_driver::time_driver_impl!(static DRIVER: TimxDriver = TimxDriver {
324 period: AtomicU32::new(0),
325 alarm: Mutex::new(Cell::new(u64::MAX)),
326 queue: Mutex::new(RefCell::new(Queue::new()))
327});
328
329pub(crate) fn init(cs: CriticalSection) {
330 DRIVER.init(cs);
331}
332
333#[cfg(time_driver_timg0)]
334#[interrupt]
335fn TIMG0() {
336 DRIVER.on_interrupt();
337}
338
339#[cfg(time_driver_timg1)]
340#[interrupt]
341fn TIMG1() {
342 DRIVER.on_interrupt();
343}
344
345#[cfg(time_driver_timg2)]
346#[interrupt]
347fn TIMG2() {
348 DRIVER.on_interrupt();
349}
350
351#[cfg(time_driver_timg3)]
352#[interrupt]
353fn TIMG3() {
354 DRIVER.on_interrupt();
355}
356
357#[cfg(time_driver_timg4)]
358#[interrupt]
359fn TIMG4() {
360 DRIVER.on_interrupt();
361}
362
363#[cfg(time_driver_timg5)]
364#[interrupt]
365fn TIMG5() {
366 DRIVER.on_interrupt();
367}
368
369#[cfg(time_driver_timg6)]
370#[interrupt]
371fn TIMG6() {
372 DRIVER.on_interrupt();
373}
374
375#[cfg(time_driver_timg7)]
376#[interrupt]
377fn TIMG7() {
378 DRIVER.on_interrupt();
379}
380
381#[cfg(time_driver_timg8)]
382#[interrupt]
383fn TIMG8() {
384 DRIVER.on_interrupt();
385}
386
387#[cfg(time_driver_timg9)]
388#[interrupt]
389fn TIMG9() {
390 DRIVER.on_interrupt();
391}
392
393#[cfg(time_driver_timg10)]
394#[interrupt]
395fn TIMG10() {
396 DRIVER.on_interrupt();
397}
398
399#[cfg(time_driver_timg11)]
400#[interrupt]
401fn TIMG11() {
402 DRIVER.on_interrupt();
403}
404
405// TODO: TIMG12 and TIMG13
406
407#[cfg(time_driver_timg14)]
408#[interrupt]
409fn TIMG14() {
410 DRIVER.on_interrupt();
411}
412
413#[cfg(time_driver_tima0)]
414#[interrupt]
415fn TIMA0() {
416 DRIVER.on_interrupt();
417}
418
419#[cfg(time_driver_tima1)]
420#[interrupt]
421fn TIMA1() {
422 DRIVER.on_interrupt();
423}
diff --git a/embassy-mspm0/src/timer.rs b/embassy-mspm0/src/timer.rs
new file mode 100644
index 000000000..4441e5640
--- /dev/null
+++ b/embassy-mspm0/src/timer.rs
@@ -0,0 +1,48 @@
1#![macro_use]
2
3/// Amount of bits of a timer.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum TimerBits {
7 /// 16 bits.
8 Bits16,
9 /// 32 bits.
10 Bits32,
11}
12
13#[allow(private_bounds)]
14pub trait Timer: SealedTimer + 'static {
15 /// Amount of bits this timer has.
16 const BITS: TimerBits;
17}
18
19pub(crate) trait SealedTimer {
20 /// Registers for this timer.
21 ///
22 /// This is a raw pointer to the register block. The actual register block layout varies depending on the
23 /// timer type.
24 fn regs() -> *mut ();
25
26 /// Enable the interrupt corresponding to this timer.
27 unsafe fn enable_interrupt();
28}
29
30macro_rules! impl_timer {
31 ($name: ident, $bits: ident) => {
32 impl crate::timer::SealedTimer for crate::peripherals::$name {
33 fn regs() -> *mut () {
34 crate::pac::$name.as_ptr()
35 }
36
37 unsafe fn enable_interrupt() {
38 use embassy_hal_internal::interrupt::InterruptExt;
39 crate::interrupt::$name.unpend();
40 crate::interrupt::$name.enable();
41 }
42 }
43
44 impl crate::timer::Timer for crate::peripherals::$name {
45 const BITS: crate::timer::TimerBits = crate::timer::TimerBits::$bits;
46 }
47 };
48}