diff options
| author | Ulf Lilleengen <[email protected]> | 2025-03-21 13:32:14 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-03-21 13:32:14 +0000 |
| commit | e29be82c8bc1b7e2dbf9f58dad4229d12968e1b4 (patch) | |
| tree | 4ae9e7b784c82b96bcf870c3f61c62abad8a3811 /embassy-mspm0/src | |
| parent | fecb7a2b2b6f1953a2fe57557cb83d063ab5eea4 (diff) | |
| parent | 91684a11c8a15b62a773a1ace40791fcf80fdad2 (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.rs | 270 | ||||
| -rw-r--r-- | embassy-mspm0/src/gpio.rs | 1060 | ||||
| -rw-r--r-- | embassy-mspm0/src/int_group/c110x.rs | 25 | ||||
| -rw-r--r-- | embassy-mspm0/src/int_group/g350x.rs | 51 | ||||
| -rw-r--r-- | embassy-mspm0/src/int_group/g351x.rs | 52 | ||||
| -rw-r--r-- | embassy-mspm0/src/int_group/l130x.rs | 46 | ||||
| -rw-r--r-- | embassy-mspm0/src/int_group/l222x.rs | 49 | ||||
| -rw-r--r-- | embassy-mspm0/src/lib.rs | 107 | ||||
| -rw-r--r-- | embassy-mspm0/src/time_driver.rs | 423 | ||||
| -rw-r--r-- | embassy-mspm0/src/timer.rs | 48 |
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 | |||
| 4 | use core::fmt::{Debug, Display, LowerHex}; | ||
| 5 | |||
| 6 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 7 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 8 | |||
| 9 | #[collapse_debuginfo(yes)] | ||
| 10 | macro_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)] | ||
| 22 | macro_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)] | ||
| 34 | macro_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)] | ||
| 46 | macro_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)] | ||
| 58 | macro_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)] | ||
| 70 | macro_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)] | ||
| 82 | macro_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)] | ||
| 94 | macro_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)] | ||
| 106 | macro_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)] | ||
| 118 | macro_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)] | ||
| 132 | macro_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)] | ||
| 146 | macro_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)] | ||
| 160 | macro_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)] | ||
| 174 | macro_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)] | ||
| 189 | macro_rules! unwrap { | ||
| 190 | ($($x:tt)*) => { | ||
| 191 | ::defmt::unwrap!($($x)*) | ||
| 192 | }; | ||
| 193 | } | ||
| 194 | |||
| 195 | #[cfg(not(feature = "defmt"))] | ||
| 196 | #[collapse_debuginfo(yes)] | ||
| 197 | macro_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)] | ||
| 217 | pub struct NoneError; | ||
| 218 | |||
| 219 | pub trait Try { | ||
| 220 | type Ok; | ||
| 221 | type Error; | ||
| 222 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 223 | } | ||
| 224 | |||
| 225 | impl<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 | |||
| 235 | impl<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 | |||
| 245 | pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||
| 246 | |||
| 247 | impl<'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 | |||
| 253 | impl<'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 | |||
| 259 | impl<'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")] | ||
| 266 | impl<'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 | |||
| 3 | use core::convert::Infallible; | ||
| 4 | use core::future::Future; | ||
| 5 | use core::pin::Pin as FuturePin; | ||
| 6 | use core::task::{Context, Poll}; | ||
| 7 | |||
| 8 | use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | |||
| 11 | use crate::pac::gpio::vals::*; | ||
| 12 | use crate::pac::gpio::{self}; | ||
| 13 | #[cfg(all(feature = "rt", feature = "mspm0c110x"))] | ||
| 14 | use crate::pac::interrupt; | ||
| 15 | use 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))] | ||
| 20 | pub enum Level { | ||
| 21 | /// Logical low. | ||
| 22 | Low, | ||
| 23 | /// Logical high. | ||
| 24 | High, | ||
| 25 | } | ||
| 26 | |||
| 27 | impl 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 | |||
| 36 | impl 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)] | ||
| 47 | pub 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)] | ||
| 58 | pub 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. | ||
| 76 | pub struct Flex<'d> { | ||
| 77 | pin: PeripheralRef<'d, AnyPin>, | ||
| 78 | } | ||
| 79 | |||
| 80 | impl<'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 | |||
| 333 | impl<'d> Drop for Flex<'d> { | ||
| 334 | #[inline] | ||
| 335 | fn drop(&mut self) { | ||
| 336 | self.set_as_disconnected(); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | /// GPIO input driver. | ||
| 341 | pub struct Input<'d> { | ||
| 342 | pin: Flex<'d>, | ||
| 343 | } | ||
| 344 | |||
| 345 | impl<'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`]. | ||
| 417 | pub struct Output<'d> { | ||
| 418 | pin: Flex<'d>, | ||
| 419 | } | ||
| 420 | |||
| 421 | impl<'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`]. | ||
| 487 | pub struct OutputOpenDrain<'d> { | ||
| 488 | pin: Flex<'d>, | ||
| 489 | } | ||
| 490 | |||
| 491 | impl<'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 | ||
| 601 | pub struct AnyPin { | ||
| 602 | pin_port: u8, | ||
| 603 | } | ||
| 604 | |||
| 605 | impl 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 | |||
| 616 | impl_peripheral!(AnyPin); | ||
| 617 | |||
| 618 | impl Pin for AnyPin {} | ||
| 619 | impl 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)] | ||
| 628 | pub 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 | |||
| 642 | impl<'d> embedded_hal::digital::ErrorType for Flex<'d> { | ||
| 643 | type Error = Infallible; | ||
| 644 | } | ||
| 645 | |||
| 646 | impl<'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 | |||
| 658 | impl<'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 | |||
| 670 | impl<'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 | |||
| 682 | impl<'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 | |||
| 709 | impl<'d> embedded_hal::digital::ErrorType for Input<'d> { | ||
| 710 | type Error = Infallible; | ||
| 711 | } | ||
| 712 | |||
| 713 | impl<'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 | |||
| 725 | impl<'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 | |||
| 752 | impl<'d> embedded_hal::digital::ErrorType for Output<'d> { | ||
| 753 | type Error = Infallible; | ||
| 754 | } | ||
| 755 | |||
| 756 | impl<'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 | |||
| 768 | impl<'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 | |||
| 780 | impl<'d> embedded_hal::digital::ErrorType for OutputOpenDrain<'d> { | ||
| 781 | type Error = Infallible; | ||
| 782 | } | ||
| 783 | |||
| 784 | impl<'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 | |||
| 796 | impl<'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 | |||
| 808 | impl<'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 | |||
| 820 | impl<'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. | ||
| 850 | const DISCONNECT_PF: u8 = 0; | ||
| 851 | |||
| 852 | /// The pin function for the GPIO peripheral. | ||
| 853 | /// | ||
| 854 | /// This is fixed to `1` for every part. | ||
| 855 | const GPIO_PF: u8 = 1; | ||
| 856 | |||
| 857 | macro_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. | ||
| 877 | static PORTA_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 878 | #[cfg(gpio_pb)] | ||
| 879 | static PORTB_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 880 | #[cfg(gpio_pc)] | ||
| 881 | static PORTC_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; | ||
| 882 | |||
| 883 | pub(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"] | ||
| 930 | struct InputFuture<'d> { | ||
| 931 | pin: PeripheralRef<'d, AnyPin>, | ||
| 932 | } | ||
| 933 | |||
| 934 | impl<'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 | |||
| 964 | impl<'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 | |||
| 991 | pub(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")] | ||
| 1010 | fn 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 | |||
| 1024 | struct BitIter(u32); | ||
| 1025 | |||
| 1026 | impl 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] | ||
| 1043 | fn GPIOA() { | ||
| 1044 | gpioa_interrupt(); | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | #[cfg(feature = "rt")] | ||
| 1048 | pub(crate) fn gpioa_interrupt() { | ||
| 1049 | irq_handler(pac::GPIOA, &PORTA_WAKERS); | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | #[cfg(all(feature = "rt", gpio_pb))] | ||
| 1053 | pub(crate) fn gpiob_interrupt() { | ||
| 1054 | irq_handler(pac::GPIOB, &PORTB_WAKERS); | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | #[cfg(all(feature = "rt", gpio_pc))] | ||
| 1058 | pub(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 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::pac::interrupt; | ||
| 3 | |||
| 4 | #[cfg(feature = "rt")] | ||
| 5 | #[interrupt] | ||
| 6 | fn 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 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::pac::interrupt; | ||
| 3 | |||
| 4 | #[cfg(feature = "rt")] | ||
| 5 | #[interrupt] | ||
| 6 | fn 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] | ||
| 30 | fn 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 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::pac::interrupt; | ||
| 3 | |||
| 4 | #[cfg(feature = "rt")] | ||
| 5 | #[interrupt] | ||
| 6 | fn 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] | ||
| 30 | fn 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 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::pac::interrupt; | ||
| 3 | |||
| 4 | #[cfg(feature = "rt")] | ||
| 5 | #[interrupt] | ||
| 6 | fn 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] | ||
| 29 | fn 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 @@ | |||
| 1 | use crate::pac; | ||
| 2 | use crate::pac::interrupt; | ||
| 3 | |||
| 4 | #[cfg(feature = "rt")] | ||
| 5 | #[interrupt] | ||
| 6 | fn 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] | ||
| 29 | fn 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. | ||
| 6 | pub(crate) mod fmt; | ||
| 7 | |||
| 8 | pub mod gpio; | ||
| 9 | pub mod timer; | ||
| 10 | |||
| 11 | #[cfg(feature = "_time-driver")] | ||
| 12 | mod 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")] | ||
| 27 | mod int_group; | ||
| 28 | |||
| 29 | pub(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 | ||
| 39 | pub(crate) use _generated::gpio_pincm; | ||
| 40 | pub use _generated::{peripherals, Peripherals}; | ||
| 41 | pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | ||
| 42 | #[cfg(feature = "unstable-pac")] | ||
| 43 | pub use mspm0_metapac as pac; | ||
| 44 | #[cfg(not(feature = "unstable-pac"))] | ||
| 45 | pub(crate) use mspm0_metapac as pac; | ||
| 46 | |||
| 47 | pub use crate::_generated::interrupt; | ||
| 48 | |||
| 49 | /// `embassy-mspm0` global configuration. | ||
| 50 | #[non_exhaustive] | ||
| 51 | #[derive(Clone, Copy)] | ||
| 52 | pub struct Config { | ||
| 53 | // TODO | ||
| 54 | } | ||
| 55 | |||
| 56 | impl Default for Config { | ||
| 57 | fn default() -> Self { | ||
| 58 | Self { | ||
| 59 | // TODO | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | pub 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 @@ | |||
| 1 | use core::cell::{Cell, RefCell}; | ||
| 2 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | ||
| 3 | use core::task::Waker; | ||
| 4 | |||
| 5 | use critical_section::{CriticalSection, Mutex}; | ||
| 6 | use embassy_time_driver::Driver; | ||
| 7 | use embassy_time_queue_utils::Queue; | ||
| 8 | use mspm0_metapac::interrupt; | ||
| 9 | use mspm0_metapac::tim::vals::{Cm, Cvae, CxC, EvtCfg, PwrenKey, Ratio, Repeat, ResetKey}; | ||
| 10 | use mspm0_metapac::tim::{Counterregs16, Tim}; | ||
| 11 | |||
| 12 | use crate::peripherals; | ||
| 13 | use crate::timer::SealedTimer; | ||
| 14 | |||
| 15 | // Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers. | ||
| 16 | #[cfg(time_driver_timg0)] | ||
| 17 | type T = peripherals::TIMG0; | ||
| 18 | #[cfg(time_driver_timg1)] | ||
| 19 | type T = peripherals::TIMG1; | ||
| 20 | #[cfg(time_driver_timg2)] | ||
| 21 | type T = peripherals::TIMG2; | ||
| 22 | #[cfg(time_driver_timg3)] | ||
| 23 | type T = peripherals::TIMG3; | ||
| 24 | #[cfg(time_driver_timg4)] | ||
| 25 | type T = peripherals::TIMG4; | ||
| 26 | #[cfg(time_driver_timg5)] | ||
| 27 | type T = peripherals::TIMG5; | ||
| 28 | #[cfg(time_driver_timg6)] | ||
| 29 | type T = peripherals::TIMG6; | ||
| 30 | #[cfg(time_driver_timg7)] | ||
| 31 | type T = peripherals::TIMG7; | ||
| 32 | #[cfg(time_driver_timg8)] | ||
| 33 | type T = peripherals::TIMG8; | ||
| 34 | #[cfg(time_driver_timg9)] | ||
| 35 | type T = peripherals::TIMG9; | ||
| 36 | #[cfg(time_driver_timg10)] | ||
| 37 | type T = peripherals::TIMG10; | ||
| 38 | #[cfg(time_driver_timg11)] | ||
| 39 | type T = peripherals::TIMG11; | ||
| 40 | #[cfg(time_driver_timg14)] | ||
| 41 | type T = peripherals::TIMG14; | ||
| 42 | #[cfg(time_driver_tima0)] | ||
| 43 | type T = peripherals::TIMA0; | ||
| 44 | #[cfg(time_driver_tima1)] | ||
| 45 | type T = peripherals::TIMA1; | ||
| 46 | |||
| 47 | // TODO: RTC | ||
| 48 | |||
| 49 | fn regs() -> Tim { | ||
| 50 | unsafe { Tim::from_ptr(T::regs()) } | ||
| 51 | } | ||
| 52 | |||
| 53 | fn 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. | ||
| 59 | fn 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). | ||
| 76 | struct 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 | |||
| 84 | impl 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 | |||
| 295 | impl 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 | |||
| 323 | embassy_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 | |||
| 329 | pub(crate) fn init(cs: CriticalSection) { | ||
| 330 | DRIVER.init(cs); | ||
| 331 | } | ||
| 332 | |||
| 333 | #[cfg(time_driver_timg0)] | ||
| 334 | #[interrupt] | ||
| 335 | fn TIMG0() { | ||
| 336 | DRIVER.on_interrupt(); | ||
| 337 | } | ||
| 338 | |||
| 339 | #[cfg(time_driver_timg1)] | ||
| 340 | #[interrupt] | ||
| 341 | fn TIMG1() { | ||
| 342 | DRIVER.on_interrupt(); | ||
| 343 | } | ||
| 344 | |||
| 345 | #[cfg(time_driver_timg2)] | ||
| 346 | #[interrupt] | ||
| 347 | fn TIMG2() { | ||
| 348 | DRIVER.on_interrupt(); | ||
| 349 | } | ||
| 350 | |||
| 351 | #[cfg(time_driver_timg3)] | ||
| 352 | #[interrupt] | ||
| 353 | fn TIMG3() { | ||
| 354 | DRIVER.on_interrupt(); | ||
| 355 | } | ||
| 356 | |||
| 357 | #[cfg(time_driver_timg4)] | ||
| 358 | #[interrupt] | ||
| 359 | fn TIMG4() { | ||
| 360 | DRIVER.on_interrupt(); | ||
| 361 | } | ||
| 362 | |||
| 363 | #[cfg(time_driver_timg5)] | ||
| 364 | #[interrupt] | ||
| 365 | fn TIMG5() { | ||
| 366 | DRIVER.on_interrupt(); | ||
| 367 | } | ||
| 368 | |||
| 369 | #[cfg(time_driver_timg6)] | ||
| 370 | #[interrupt] | ||
| 371 | fn TIMG6() { | ||
| 372 | DRIVER.on_interrupt(); | ||
| 373 | } | ||
| 374 | |||
| 375 | #[cfg(time_driver_timg7)] | ||
| 376 | #[interrupt] | ||
| 377 | fn TIMG7() { | ||
| 378 | DRIVER.on_interrupt(); | ||
| 379 | } | ||
| 380 | |||
| 381 | #[cfg(time_driver_timg8)] | ||
| 382 | #[interrupt] | ||
| 383 | fn TIMG8() { | ||
| 384 | DRIVER.on_interrupt(); | ||
| 385 | } | ||
| 386 | |||
| 387 | #[cfg(time_driver_timg9)] | ||
| 388 | #[interrupt] | ||
| 389 | fn TIMG9() { | ||
| 390 | DRIVER.on_interrupt(); | ||
| 391 | } | ||
| 392 | |||
| 393 | #[cfg(time_driver_timg10)] | ||
| 394 | #[interrupt] | ||
| 395 | fn TIMG10() { | ||
| 396 | DRIVER.on_interrupt(); | ||
| 397 | } | ||
| 398 | |||
| 399 | #[cfg(time_driver_timg11)] | ||
| 400 | #[interrupt] | ||
| 401 | fn TIMG11() { | ||
| 402 | DRIVER.on_interrupt(); | ||
| 403 | } | ||
| 404 | |||
| 405 | // TODO: TIMG12 and TIMG13 | ||
| 406 | |||
| 407 | #[cfg(time_driver_timg14)] | ||
| 408 | #[interrupt] | ||
| 409 | fn TIMG14() { | ||
| 410 | DRIVER.on_interrupt(); | ||
| 411 | } | ||
| 412 | |||
| 413 | #[cfg(time_driver_tima0)] | ||
| 414 | #[interrupt] | ||
| 415 | fn TIMA0() { | ||
| 416 | DRIVER.on_interrupt(); | ||
| 417 | } | ||
| 418 | |||
| 419 | #[cfg(time_driver_tima1)] | ||
| 420 | #[interrupt] | ||
| 421 | fn 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))] | ||
| 6 | pub enum TimerBits { | ||
| 7 | /// 16 bits. | ||
| 8 | Bits16, | ||
| 9 | /// 32 bits. | ||
| 10 | Bits32, | ||
| 11 | } | ||
| 12 | |||
| 13 | #[allow(private_bounds)] | ||
| 14 | pub trait Timer: SealedTimer + 'static { | ||
| 15 | /// Amount of bits this timer has. | ||
| 16 | const BITS: TimerBits; | ||
| 17 | } | ||
| 18 | |||
| 19 | pub(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 | |||
| 30 | macro_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 | } | ||
