diff options
| -rw-r--r-- | embassy-nrf/src/gpio.rs | 194 |
1 files changed, 152 insertions, 42 deletions
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index a06ee6053..4269cbe16 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs | |||
| @@ -41,24 +41,7 @@ impl<'d, T: Pin> Input<'d, T> { | |||
| 41 | pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { | 41 | pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { |
| 42 | unborrow!(pin); | 42 | unborrow!(pin); |
| 43 | 43 | ||
| 44 | pin.conf().write(|w| { | 44 | init_input(&pin, pull); |
| 45 | w.dir().input(); | ||
| 46 | w.input().connect(); | ||
| 47 | match pull { | ||
| 48 | Pull::None => { | ||
| 49 | w.pull().disabled(); | ||
| 50 | } | ||
| 51 | Pull::Up => { | ||
| 52 | w.pull().pullup(); | ||
| 53 | } | ||
| 54 | Pull::Down => { | ||
| 55 | w.pull().pulldown(); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | w.drive().s0s1(); | ||
| 59 | w.sense().disabled(); | ||
| 60 | w | ||
| 61 | }); | ||
| 62 | 45 | ||
| 63 | Self { | 46 | Self { |
| 64 | pin, | 47 | pin, |
| @@ -93,6 +76,7 @@ pub enum Level { | |||
| 93 | High, | 76 | High, |
| 94 | } | 77 | } |
| 95 | 78 | ||
| 79 | // These numbers match DRIVE_A exactly so hopefully the compiler will unify them. | ||
| 96 | #[derive(Clone, Copy, Debug, PartialEq)] | 80 | #[derive(Clone, Copy, Debug, PartialEq)] |
| 97 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 81 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 98 | #[repr(u8)] | 82 | #[repr(u8)] |
| @@ -129,30 +113,7 @@ impl<'d, T: Pin> Output<'d, T> { | |||
| 129 | ) -> Self { | 113 | ) -> Self { |
| 130 | unborrow!(pin); | 114 | unborrow!(pin); |
| 131 | 115 | ||
| 132 | match initial_output { | 116 | init_output(&pin, initial_output, drive); |
| 133 | Level::High => pin.set_high(), | ||
| 134 | Level::Low => pin.set_low(), | ||
| 135 | } | ||
| 136 | |||
| 137 | let drive = match drive { | ||
| 138 | OutputDrive::Standard => DRIVE_A::S0S1, | ||
| 139 | OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1, | ||
| 140 | OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1, | ||
| 141 | OutputDrive::HighDrive => DRIVE_A::H0H1, | ||
| 142 | OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1, | ||
| 143 | OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1, | ||
| 144 | OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1, | ||
| 145 | OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1, | ||
| 146 | }; | ||
| 147 | |||
| 148 | pin.conf().write(|w| { | ||
| 149 | w.dir().output(); | ||
| 150 | w.input().disconnect(); | ||
| 151 | w.pull().disabled(); | ||
| 152 | w.drive().variant(drive); | ||
| 153 | w.sense().disabled(); | ||
| 154 | w | ||
| 155 | }); | ||
| 156 | 117 | ||
| 157 | Self { | 118 | Self { |
| 158 | pin, | 119 | pin, |
| @@ -205,6 +166,101 @@ impl<'d, T: Pin> StatefulOutputPin for Output<'d, T> { | |||
| 205 | } | 166 | } |
| 206 | } | 167 | } |
| 207 | 168 | ||
| 169 | /// GPIO flexible pin. | ||
| 170 | /// | ||
| 171 | /// This pin can either be a disconnected, input, or output pin. | ||
| 172 | pub struct FlexPin<'d, T: Pin> { | ||
| 173 | pub(crate) pin: T, | ||
| 174 | phantom: PhantomData<&'d mut T>, | ||
| 175 | } | ||
| 176 | |||
| 177 | impl<'d, T: Pin> FlexPin<'d, T> { | ||
| 178 | /// Wrap the pin in a `FlexPin`. | ||
| 179 | /// | ||
| 180 | /// The pin remains disconnected. | ||
| 181 | pub fn new(pin: impl Unborrow<Target = T> + 'd) -> Self { | ||
| 182 | unborrow!(pin); | ||
| 183 | // Pin will be in disconnected state. | ||
| 184 | Self { | ||
| 185 | pin, | ||
| 186 | phantom: PhantomData, | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | pub fn to_input(&self, pull: Pull) { | ||
| 191 | self.pin.conf().reset(); // TODO is this necessary? | ||
| 192 | init_input(&self.pin, pull); | ||
| 193 | } | ||
| 194 | |||
| 195 | pub fn to_output(&self, initial_output: Level, drive: OutputDrive) { | ||
| 196 | self.pin.conf().reset(); // TODO is this necessary? | ||
| 197 | init_output(&self.pin, initial_output, drive); | ||
| 198 | } | ||
| 199 | |||
| 200 | pub fn disconnect(&self) { | ||
| 201 | self.pin.conf().reset(); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | impl<'d, T: Pin> Drop for FlexPin<'d, T> { | ||
| 206 | fn drop(&mut self) { | ||
| 207 | self.pin.conf().reset(); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Implement [`InputPin`] for [`FlexPin`]; | ||
| 212 | /// | ||
| 213 | /// If the pin is not in input mode the result is unspecified. | ||
| 214 | impl<'d, T: Pin> InputPin for FlexPin<'d, T> { | ||
| 215 | type Error = Infallible; | ||
| 216 | |||
| 217 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 218 | self.is_low().map(|v| !v) | ||
| 219 | } | ||
| 220 | |||
| 221 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 222 | Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0) | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | impl<'d, T: Pin> OutputPin for FlexPin<'d, T> { | ||
| 227 | type Error = Infallible; | ||
| 228 | |||
| 229 | /// Set the output as high. | ||
| 230 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 231 | unsafe { | ||
| 232 | self.pin | ||
| 233 | .block() | ||
| 234 | .outset | ||
| 235 | .write(|w| w.bits(1u32 << self.pin.pin())); | ||
| 236 | } | ||
| 237 | Ok(()) | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Set the output as low. | ||
| 241 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 242 | unsafe { | ||
| 243 | self.pin | ||
| 244 | .block() | ||
| 245 | .outclr | ||
| 246 | .write(|w| w.bits(1u32 << self.pin.pin())); | ||
| 247 | } | ||
| 248 | Ok(()) | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | impl<'d, T: Pin> StatefulOutputPin for FlexPin<'d, T> { | ||
| 253 | /// Is the output pin set as high? | ||
| 254 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 255 | self.is_set_low().map(|v| !v) | ||
| 256 | } | ||
| 257 | |||
| 258 | /// Is the output pin set as low? | ||
| 259 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 260 | Ok(self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0) | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 208 | pub(crate) mod sealed { | 264 | pub(crate) mod sealed { |
| 209 | use super::*; | 265 | use super::*; |
| 210 | 266 | ||
| @@ -314,6 +370,60 @@ impl sealed::Pin for AnyPin { | |||
| 314 | } | 370 | } |
| 315 | } | 371 | } |
| 316 | 372 | ||
| 373 | // ===================== | ||
| 374 | |||
| 375 | /// Set up a pin for input | ||
| 376 | #[inline] | ||
| 377 | fn init_input<T: Pin>(pin: &T, pull: Pull) { | ||
| 378 | pin.conf().write(|w| { | ||
| 379 | w.dir().input(); | ||
| 380 | w.input().connect(); | ||
| 381 | match pull { | ||
| 382 | Pull::None => { | ||
| 383 | w.pull().disabled(); | ||
| 384 | } | ||
| 385 | Pull::Up => { | ||
| 386 | w.pull().pullup(); | ||
| 387 | } | ||
| 388 | Pull::Down => { | ||
| 389 | w.pull().pulldown(); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | w.drive().s0s1(); | ||
| 393 | w.sense().disabled(); | ||
| 394 | w | ||
| 395 | }); | ||
| 396 | } | ||
| 397 | |||
| 398 | /// Set up a pin for output | ||
| 399 | #[inline] | ||
| 400 | fn init_output<T: Pin>(pin: &T, initial_output: Level, drive: OutputDrive) { | ||
| 401 | match initial_output { | ||
| 402 | Level::High => pin.set_high(), | ||
| 403 | Level::Low => pin.set_low(), | ||
| 404 | } | ||
| 405 | |||
| 406 | let drive = match drive { | ||
| 407 | OutputDrive::Standard => DRIVE_A::S0S1, | ||
| 408 | OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1, | ||
| 409 | OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1, | ||
| 410 | OutputDrive::HighDrive => DRIVE_A::H0H1, | ||
| 411 | OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1, | ||
| 412 | OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1, | ||
| 413 | OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1, | ||
| 414 | OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1, | ||
| 415 | }; | ||
| 416 | |||
| 417 | pin.conf().write(|w| { | ||
| 418 | w.dir().output(); | ||
| 419 | w.input().disconnect(); | ||
| 420 | w.pull().disabled(); | ||
| 421 | w.drive().variant(drive); | ||
| 422 | w.sense().disabled(); | ||
| 423 | w | ||
| 424 | }); | ||
| 425 | } | ||
| 426 | |||
| 317 | // ==================== | 427 | // ==================== |
| 318 | 428 | ||
| 319 | pub trait OptionalPin: sealed::OptionalPin + Sized { | 429 | pub trait OptionalPin: sealed::OptionalPin + Sized { |
