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