diff options
| -rw-r--r-- | embassy-nrf/src/gpio.rs | 191 |
1 files changed, 154 insertions, 37 deletions
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index a06ee6053..14ac61822 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)] |
| @@ -134,25 +118,7 @@ impl<'d, T: Pin> Output<'d, T> { | |||
| 134 | Level::Low => pin.set_low(), | 118 | Level::Low => pin.set_low(), |
| 135 | } | 119 | } |
| 136 | 120 | ||
| 137 | let drive = match drive { | 121 | init_output(&pin, 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 | 122 | ||
| 157 | Self { | 123 | Self { |
| 158 | pin, | 124 | pin, |
| @@ -205,6 +171,108 @@ impl<'d, T: Pin> StatefulOutputPin for Output<'d, T> { | |||
| 205 | } | 171 | } |
| 206 | } | 172 | } |
| 207 | 173 | ||
| 174 | /// GPIO flexible pin. | ||
| 175 | /// | ||
| 176 | /// This pin can either be a disconnected, input, or output pin. The level register bit will remain | ||
| 177 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output | ||
| 178 | /// mode. | ||
| 179 | pub struct FlexPin<'d, T: Pin> { | ||
| 180 | pub(crate) pin: T, | ||
| 181 | phantom: PhantomData<&'d mut T>, | ||
| 182 | } | ||
| 183 | |||
| 184 | impl<'d, T: Pin> FlexPin<'d, T> { | ||
| 185 | /// Wrap the pin in a `FlexPin`. | ||
| 186 | /// | ||
| 187 | /// The pin remains disconnected. The initial output level is unspecified, but can be changed | ||
| 188 | /// before the pin is put into output mode. | ||
| 189 | pub fn new(pin: impl Unborrow<Target = T> + 'd) -> Self { | ||
| 190 | unborrow!(pin); | ||
| 191 | // Pin will be in disconnected state. | ||
| 192 | Self { | ||
| 193 | pin, | ||
| 194 | phantom: PhantomData, | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | /// Put the pin into input mode. | ||
| 199 | pub fn set_as_input(&mut self, pull: Pull) { | ||
| 200 | init_input(&self.pin, pull); | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Put the pin into output mode. | ||
| 204 | /// | ||
| 205 | /// The pin level will be whatever was set before (or low by default). If you want it to begin | ||
| 206 | /// at a specific level, call `set_high`/`set_low` on the pin first. | ||
| 207 | pub fn set_as_output(&mut self, drive: OutputDrive) { | ||
| 208 | init_output(&self.pin, drive); | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Put the pin into disconnected mode. | ||
| 212 | pub fn set_as_disconnected(&mut self) { | ||
| 213 | self.pin.conf().reset(); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | impl<'d, T: Pin> Drop for FlexPin<'d, T> { | ||
| 218 | fn drop(&mut self) { | ||
| 219 | self.pin.conf().reset(); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Implement [`InputPin`] for [`FlexPin`]; | ||
| 224 | /// | ||
| 225 | /// If the pin is not in input mode the result is unspecified. | ||
| 226 | impl<'d, T: Pin> InputPin for FlexPin<'d, T> { | ||
| 227 | type Error = Infallible; | ||
| 228 | |||
| 229 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 230 | self.is_low().map(|v| !v) | ||
| 231 | } | ||
| 232 | |||
| 233 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 234 | Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0) | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | impl<'d, T: Pin> OutputPin for FlexPin<'d, T> { | ||
| 239 | type Error = Infallible; | ||
| 240 | |||
| 241 | /// Set the output as high. | ||
| 242 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 243 | unsafe { | ||
| 244 | self.pin | ||
| 245 | .block() | ||
| 246 | .outset | ||
| 247 | .write(|w| w.bits(1u32 << self.pin.pin())); | ||
| 248 | } | ||
| 249 | Ok(()) | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Set the output as low. | ||
| 253 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 254 | unsafe { | ||
| 255 | self.pin | ||
| 256 | .block() | ||
| 257 | .outclr | ||
| 258 | .write(|w| w.bits(1u32 << self.pin.pin())); | ||
| 259 | } | ||
| 260 | Ok(()) | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | impl<'d, T: Pin> StatefulOutputPin for FlexPin<'d, T> { | ||
| 265 | /// Is the output pin set as high? | ||
| 266 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 267 | self.is_set_low().map(|v| !v) | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Is the output pin set as low? | ||
| 271 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 272 | Ok(self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0) | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 208 | pub(crate) mod sealed { | 276 | pub(crate) mod sealed { |
| 209 | use super::*; | 277 | use super::*; |
| 210 | 278 | ||
| @@ -314,6 +382,55 @@ impl sealed::Pin for AnyPin { | |||
| 314 | } | 382 | } |
| 315 | } | 383 | } |
| 316 | 384 | ||
| 385 | // ===================== | ||
| 386 | |||
| 387 | /// Set up a pin for input | ||
| 388 | #[inline] | ||
| 389 | fn init_input<T: Pin>(pin: &T, pull: Pull) { | ||
| 390 | pin.conf().write(|w| { | ||
| 391 | w.dir().input(); | ||
| 392 | w.input().connect(); | ||
| 393 | match pull { | ||
| 394 | Pull::None => { | ||
| 395 | w.pull().disabled(); | ||
| 396 | } | ||
| 397 | Pull::Up => { | ||
| 398 | w.pull().pullup(); | ||
| 399 | } | ||
| 400 | Pull::Down => { | ||
| 401 | w.pull().pulldown(); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | w.drive().s0s1(); | ||
| 405 | w.sense().disabled(); | ||
| 406 | w | ||
| 407 | }); | ||
| 408 | } | ||
| 409 | |||
| 410 | /// Set up a pin for output | ||
| 411 | #[inline] | ||
| 412 | fn init_output<T: Pin>(pin: &T, drive: OutputDrive) { | ||
| 413 | let drive = match drive { | ||
| 414 | OutputDrive::Standard => DRIVE_A::S0S1, | ||
| 415 | OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1, | ||
| 416 | OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1, | ||
| 417 | OutputDrive::HighDrive => DRIVE_A::H0H1, | ||
| 418 | OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1, | ||
| 419 | OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1, | ||
| 420 | OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1, | ||
| 421 | OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1, | ||
| 422 | }; | ||
| 423 | |||
| 424 | pin.conf().write(|w| { | ||
| 425 | w.dir().output(); | ||
| 426 | w.input().disconnect(); | ||
| 427 | w.pull().disabled(); | ||
| 428 | w.drive().variant(drive); | ||
| 429 | w.sense().disabled(); | ||
| 430 | w | ||
| 431 | }); | ||
| 432 | } | ||
| 433 | |||
| 317 | // ==================== | 434 | // ==================== |
| 318 | 435 | ||
| 319 | pub trait OptionalPin: sealed::OptionalPin + Sized { | 436 | pub trait OptionalPin: sealed::OptionalPin + Sized { |
