diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-07-09 02:42:38 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-07-09 02:42:38 +0200 |
| commit | 921bc4d97ba290045e026389d7ab68399bb1ceab (patch) | |
| tree | e325a0d1151022e14563311c708366d709253375 | |
| parent | d2a622b3d0ed4dfd2932f9059a70dc82a64d761e (diff) | |
| parent | ccf57cfab63d9562d617a667ef6dfa2c9edd572f (diff) | |
Merge pull request #852 from embassy-rs/rp-flex
rp: add Flex gpio
| -rwxr-xr-x | ci.sh | 1 | ||||
| -rw-r--r-- | embassy-rp/src/gpio.rs | 376 | ||||
| -rw-r--r-- | tests/rp/.cargo/config.toml | 20 | ||||
| -rw-r--r-- | tests/rp/Cargo.toml | 48 | ||||
| -rw-r--r-- | tests/rp/build.rs | 16 | ||||
| -rw-r--r-- | tests/rp/link_ram.x | 255 | ||||
| -rw-r--r-- | tests/rp/src/bin/gpio.rs | 192 |
7 files changed, 804 insertions, 104 deletions
| @@ -104,6 +104,7 @@ cargo batch \ | |||
| 104 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ | 104 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ |
| 105 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ | 105 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ |
| 106 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ | 106 | --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ |
| 107 | --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ | ||
| 107 | 108 | ||
| 108 | 109 | ||
| 109 | function run_elf { | 110 | function run_elf { |
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ae771e849..131330e79 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs | |||
| @@ -30,156 +30,147 @@ pub enum Bank { | |||
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | pub struct Input<'d, T: Pin> { | 32 | pub struct Input<'d, T: Pin> { |
| 33 | pin: T, | 33 | pin: Flex<'d, T>, |
| 34 | phantom: PhantomData<&'d mut T>, | ||
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | impl<'d, T: Pin> Input<'d, T> { | 36 | impl<'d, T: Pin> Input<'d, T> { |
| 38 | pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { | 37 | pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { |
| 39 | unborrow!(pin); | 38 | let mut pin = Flex::new(pin); |
| 40 | 39 | pin.set_as_input(); | |
| 41 | unsafe { | 40 | pin.set_pull(pull); |
| 42 | pin.pad_ctrl().write(|w| { | 41 | Self { pin } |
| 43 | w.set_ie(true); | ||
| 44 | match pull { | ||
| 45 | Pull::Up => w.set_pue(true), | ||
| 46 | Pull::Down => w.set_pde(true), | ||
| 47 | Pull::None => {} | ||
| 48 | } | ||
| 49 | }); | ||
| 50 | |||
| 51 | // disable output in SIO, to use it as input | ||
| 52 | pin.sio_oe().value_clr().write_value(1 << pin.pin()); | ||
| 53 | |||
| 54 | pin.io().ctrl().write(|w| { | ||
| 55 | w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); | ||
| 56 | }); | ||
| 57 | } | ||
| 58 | |||
| 59 | Self { | ||
| 60 | pin, | ||
| 61 | phantom: PhantomData, | ||
| 62 | } | ||
| 63 | } | 42 | } |
| 64 | 43 | ||
| 65 | pub fn is_high(&self) -> bool { | 44 | pub fn is_high(&self) -> bool { |
| 66 | !self.is_low() | 45 | self.pin.is_high() |
| 67 | } | 46 | } |
| 68 | 47 | ||
| 69 | pub fn is_low(&self) -> bool { | 48 | pub fn is_low(&self) -> bool { |
| 70 | let val = 1 << self.pin.pin(); | 49 | self.pin.is_low() |
| 71 | unsafe { self.pin.sio_in().read() & val == 0 } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl<'d, T: Pin> Drop for Input<'d, T> { | ||
| 76 | fn drop(&mut self) { | ||
| 77 | // todo | ||
| 78 | } | 50 | } |
| 79 | } | 51 | } |
| 80 | 52 | ||
| 81 | pub struct Output<'d, T: Pin> { | 53 | pub struct Output<'d, T: Pin> { |
| 82 | pin: T, | 54 | pin: Flex<'d, T>, |
| 83 | phantom: PhantomData<&'d mut T>, | ||
| 84 | } | 55 | } |
| 85 | 56 | ||
| 86 | impl<'d, T: Pin> Output<'d, T> { | 57 | impl<'d, T: Pin> Output<'d, T> { |
| 58 | #[inline] | ||
| 87 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self { | 59 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self { |
| 88 | unborrow!(pin); | 60 | let mut pin = Flex::new(pin); |
| 89 | 61 | match initial_output { | |
| 90 | unsafe { | 62 | Level::High => pin.set_high(), |
| 91 | match initial_output { | 63 | Level::Low => pin.set_low(), |
| 92 | Level::High => pin.sio_out().value_set().write_value(1 << pin.pin()), | ||
| 93 | Level::Low => pin.sio_out().value_clr().write_value(1 << pin.pin()), | ||
| 94 | } | ||
| 95 | pin.sio_oe().value_set().write_value(1 << pin.pin()); | ||
| 96 | |||
| 97 | pin.io().ctrl().write(|w| { | ||
| 98 | w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); | ||
| 99 | }); | ||
| 100 | } | 64 | } |
| 101 | 65 | ||
| 102 | Self { | 66 | pin.set_as_output(); |
| 103 | pin, | 67 | Self { pin } |
| 104 | phantom: PhantomData, | ||
| 105 | } | ||
| 106 | } | 68 | } |
| 107 | 69 | ||
| 108 | /// Set the output as high. | 70 | /// Set the output as high. |
| 71 | #[inline] | ||
| 109 | pub fn set_high(&mut self) { | 72 | pub fn set_high(&mut self) { |
| 110 | let val = 1 << self.pin.pin(); | 73 | self.pin.set_high() |
| 111 | unsafe { self.pin.sio_out().value_set().write_value(val) }; | ||
| 112 | } | 74 | } |
| 113 | 75 | ||
| 114 | /// Set the output as low. | 76 | /// Set the output as low. |
| 77 | #[inline] | ||
| 115 | pub fn set_low(&mut self) { | 78 | pub fn set_low(&mut self) { |
| 116 | let val = 1 << self.pin.pin(); | 79 | self.pin.set_low() |
| 117 | unsafe { self.pin.sio_out().value_clr().write_value(val) }; | ||
| 118 | } | 80 | } |
| 119 | 81 | ||
| 120 | /// Is the output pin set as high? | 82 | /// Is the output pin set as high? |
| 83 | #[inline] | ||
| 121 | pub fn is_set_high(&self) -> bool { | 84 | pub fn is_set_high(&self) -> bool { |
| 122 | !self.is_set_low() | 85 | self.pin.is_set_high() |
| 123 | } | 86 | } |
| 124 | 87 | ||
| 125 | /// Is the output pin set as low? | 88 | /// Is the output pin set as low? |
| 89 | #[inline] | ||
| 126 | pub fn is_set_low(&self) -> bool { | 90 | pub fn is_set_low(&self) -> bool { |
| 127 | // Reading from SIO: GPIO_OUT gives the last value written. | 91 | self.pin.is_set_low() |
| 128 | let val = 1 << self.pin.pin(); | ||
| 129 | unsafe { (self.pin.sio_out().value().read() & val) == 0 } | ||
| 130 | } | 92 | } |
| 131 | 93 | ||
| 132 | /// Toggle pin output | 94 | /// Toggle pin output |
| 133 | #[inline] | 95 | #[inline] |
| 134 | pub fn toggle(&mut self) { | 96 | pub fn toggle(&mut self) { |
| 135 | let val = 1 << self.pin.pin(); | 97 | self.pin.toggle() |
| 136 | unsafe { | ||
| 137 | self.pin.sio_out().value_xor().write_value(val); | ||
| 138 | } | ||
| 139 | } | 98 | } |
| 140 | } | 99 | } |
| 141 | 100 | ||
| 142 | impl<'d, T: Pin> Drop for Output<'d, T> { | 101 | /// GPIO output open-drain. |
| 143 | fn drop(&mut self) { | 102 | pub struct OutputOpenDrain<'d, T: Pin> { |
| 144 | let val = 1 << self.pin.pin(); | 103 | pin: Flex<'d, T>, |
| 145 | unsafe { | 104 | } |
| 146 | self.pin.sio_out().value_clr().write_value(val); | 105 | |
| 147 | self.pin.sio_oe().value_clr().write_value(val); | 106 | impl<'d, T: Pin> OutputOpenDrain<'d, T> { |
| 148 | self.pin.io().ctrl().write(|w| { | 107 | #[inline] |
| 149 | w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0); | 108 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self { |
| 150 | }); | 109 | let mut pin = Flex::new(pin); |
| 151 | }; | 110 | pin.set_low(); |
| 111 | match initial_output { | ||
| 112 | Level::High => pin.set_as_input(), | ||
| 113 | Level::Low => pin.set_as_output(), | ||
| 114 | } | ||
| 115 | Self { pin } | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Set the output as high. | ||
| 119 | #[inline] | ||
| 120 | pub fn set_high(&mut self) { | ||
| 121 | // For Open Drain High, disable the output pin. | ||
| 122 | self.pin.set_as_input() | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Set the output as low. | ||
| 126 | #[inline] | ||
| 127 | pub fn set_low(&mut self) { | ||
| 128 | // For Open Drain Low, enable the output pin. | ||
| 129 | self.pin.set_as_output() | ||
| 130 | } | ||
| 131 | |||
| 132 | /// Is the output level high? | ||
| 133 | #[inline] | ||
| 134 | pub fn is_set_high(&self) -> bool { | ||
| 135 | !self.is_set_low() | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Is the output level low? | ||
| 139 | #[inline] | ||
| 140 | pub fn is_set_low(&self) -> bool { | ||
| 141 | self.pin.is_set_as_output() | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Toggle pin output | ||
| 145 | #[inline] | ||
| 146 | pub fn toggle(&mut self) { | ||
| 147 | self.pin.toggle_set_as_output() | ||
| 152 | } | 148 | } |
| 153 | } | 149 | } |
| 154 | 150 | ||
| 155 | /// GPIO output open-drain. | 151 | /// GPIO flexible pin. |
| 156 | pub struct OutputOpenDrain<'d, T: Pin> { | 152 | /// |
| 153 | /// This pin can be either an input or output pin. The output level register bit will remain | ||
| 154 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output | ||
| 155 | /// mode. | ||
| 156 | pub struct Flex<'d, T: Pin> { | ||
| 157 | pin: T, | 157 | pin: T, |
| 158 | phantom: PhantomData<&'d mut T>, | 158 | phantom: PhantomData<&'d mut T>, |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | impl<'d, T: Pin> OutputOpenDrain<'d, T> { | 161 | impl<'d, T: Pin> Flex<'d, T> { |
| 162 | #[inline] | 162 | #[inline] |
| 163 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self { | 163 | pub fn new(pin: impl Unborrow<Target = T> + 'd) -> Self { |
| 164 | unborrow!(pin); | 164 | unborrow!(pin); |
| 165 | 165 | ||
| 166 | unsafe { | 166 | unsafe { |
| 167 | let val = 1 << pin.pin(); | 167 | pin.pad_ctrl().write(|w| { |
| 168 | w.set_ie(true); | ||
| 169 | }); | ||
| 170 | |||
| 168 | pin.io().ctrl().write(|w| { | 171 | pin.io().ctrl().write(|w| { |
| 169 | w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); | 172 | w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); |
| 170 | }); | 173 | }); |
| 171 | pin.sio_out().value_clr().write_value(val); | ||
| 172 | |||
| 173 | match initial_output { | ||
| 174 | Level::High => { | ||
| 175 | // For Open Drain High, disable the output pin. | ||
| 176 | pin.sio_oe().value_clr().write_value(val); | ||
| 177 | } | ||
| 178 | Level::Low => { | ||
| 179 | // For Open Drain Low, enable the output pin. | ||
| 180 | pin.sio_oe().value_set().write_value(val); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | } | 174 | } |
| 184 | 175 | ||
| 185 | Self { | 176 | Self { |
| @@ -188,29 +179,79 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { | |||
| 188 | } | 179 | } |
| 189 | } | 180 | } |
| 190 | 181 | ||
| 191 | /// Set the output as high. | ||
| 192 | #[inline] | 182 | #[inline] |
| 193 | pub fn set_high(&mut self) { | 183 | fn bit(&self) -> u32 { |
| 194 | // For Open Drain High, disable the output pin. | 184 | 1 << self.pin.pin() |
| 185 | } | ||
| 186 | |||
| 187 | /// Set the pin's pull. | ||
| 188 | #[inline] | ||
| 189 | pub fn set_pull(&mut self, pull: Pull) { | ||
| 195 | unsafe { | 190 | unsafe { |
| 196 | self.pin.sio_oe().value_clr().write_value(1 << self.pin.pin()); | 191 | self.pin.pad_ctrl().write(|w| { |
| 192 | w.set_ie(true); | ||
| 193 | match pull { | ||
| 194 | Pull::Up => w.set_pue(true), | ||
| 195 | Pull::Down => w.set_pde(true), | ||
| 196 | Pull::None => {} | ||
| 197 | } | ||
| 198 | }); | ||
| 197 | } | 199 | } |
| 198 | } | 200 | } |
| 199 | 201 | ||
| 202 | /// Put the pin into input mode. | ||
| 203 | /// | ||
| 204 | /// The pull setting is left unchanged. | ||
| 205 | #[inline] | ||
| 206 | pub fn set_as_input(&mut self) { | ||
| 207 | unsafe { self.pin.sio_oe().value_clr().write_value(self.bit()) } | ||
| 208 | } | ||
| 209 | |||
| 210 | /// Put the pin into output mode. | ||
| 211 | /// | ||
| 212 | /// The pin level will be whatever was set before (or low by default). If you want it to begin | ||
| 213 | /// at a specific level, call `set_high`/`set_low` on the pin first. | ||
| 214 | #[inline] | ||
| 215 | pub fn set_as_output(&mut self) { | ||
| 216 | unsafe { self.pin.sio_oe().value_set().write_value(self.bit()) } | ||
| 217 | } | ||
| 218 | |||
| 219 | #[inline] | ||
| 220 | fn is_set_as_output(&self) -> bool { | ||
| 221 | unsafe { (self.pin.sio_oe().value().read() & self.bit()) != 0 } | ||
| 222 | } | ||
| 223 | |||
| 224 | #[inline] | ||
| 225 | pub fn toggle_set_as_output(&mut self) { | ||
| 226 | unsafe { self.pin.sio_oe().value_xor().write_value(self.bit()) } | ||
| 227 | } | ||
| 228 | |||
| 229 | #[inline] | ||
| 230 | pub fn is_high(&self) -> bool { | ||
| 231 | !self.is_low() | ||
| 232 | } | ||
| 233 | |||
| 234 | #[inline] | ||
| 235 | pub fn is_low(&self) -> bool { | ||
| 236 | unsafe { self.pin.sio_in().read() & self.bit() == 0 } | ||
| 237 | } | ||
| 238 | |||
| 239 | /// Set the output as high. | ||
| 240 | #[inline] | ||
| 241 | pub fn set_high(&mut self) { | ||
| 242 | unsafe { self.pin.sio_out().value_set().write_value(self.bit()) } | ||
| 243 | } | ||
| 244 | |||
| 200 | /// Set the output as low. | 245 | /// Set the output as low. |
| 201 | #[inline] | 246 | #[inline] |
| 202 | pub fn set_low(&mut self) { | 247 | pub fn set_low(&mut self) { |
| 203 | // For Open Drain Low, enable the output pin. | 248 | unsafe { self.pin.sio_out().value_clr().write_value(self.bit()) } |
| 204 | unsafe { | ||
| 205 | self.pin.sio_oe().value_set().write_value(1 << self.pin.pin()); | ||
| 206 | } | ||
| 207 | } | 249 | } |
| 208 | 250 | ||
| 209 | /// Is the output level high? | 251 | /// Is the output level high? |
| 210 | #[inline] | 252 | #[inline] |
| 211 | pub fn is_set_high(&self) -> bool { | 253 | pub fn is_set_high(&self) -> bool { |
| 212 | let val = 1 << self.pin.pin(); | 254 | unsafe { (self.pin.sio_out().value().read() & self.bit()) == 0 } |
| 213 | unsafe { (self.pin.sio_oe().value().read() & val) == 0 } | ||
| 214 | } | 255 | } |
| 215 | 256 | ||
| 216 | /// Is the output level low? | 257 | /// Is the output level low? |
| @@ -222,9 +263,18 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { | |||
| 222 | /// Toggle pin output | 263 | /// Toggle pin output |
| 223 | #[inline] | 264 | #[inline] |
| 224 | pub fn toggle(&mut self) { | 265 | pub fn toggle(&mut self) { |
| 225 | let val = 1 << self.pin.pin(); | 266 | unsafe { self.pin.sio_out().value_xor().write_value(self.bit()) } |
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | impl<'d, T: Pin> Drop for Flex<'d, T> { | ||
| 271 | #[inline] | ||
| 272 | fn drop(&mut self) { | ||
| 226 | unsafe { | 273 | unsafe { |
| 227 | self.pin.sio_out().value_xor().write_value(val); | 274 | self.pin.pad_ctrl().write(|_| {}); |
| 275 | self.pin.io().ctrl().write(|w| { | ||
| 276 | w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0); | ||
| 277 | }); | ||
| 228 | } | 278 | } |
| 229 | } | 279 | } |
| 230 | } | 280 | } |
| @@ -428,6 +478,48 @@ mod eh02 { | |||
| 428 | Ok(self.toggle()) | 478 | Ok(self.toggle()) |
| 429 | } | 479 | } |
| 430 | } | 480 | } |
| 481 | |||
| 482 | impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Flex<'d, T> { | ||
| 483 | type Error = Infallible; | ||
| 484 | |||
| 485 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 486 | Ok(self.is_high()) | ||
| 487 | } | ||
| 488 | |||
| 489 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 490 | Ok(self.is_low()) | ||
| 491 | } | ||
| 492 | } | ||
| 493 | |||
| 494 | impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Flex<'d, T> { | ||
| 495 | type Error = Infallible; | ||
| 496 | |||
| 497 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 498 | Ok(self.set_high()) | ||
| 499 | } | ||
| 500 | |||
| 501 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 502 | Ok(self.set_low()) | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> { | ||
| 507 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 508 | Ok(self.is_set_high()) | ||
| 509 | } | ||
| 510 | |||
| 511 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 512 | Ok(self.is_set_low()) | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d, T> { | ||
| 517 | type Error = Infallible; | ||
| 518 | #[inline] | ||
| 519 | fn toggle(&mut self) -> Result<(), Self::Error> { | ||
| 520 | Ok(self.toggle()) | ||
| 521 | } | ||
| 522 | } | ||
| 431 | } | 523 | } |
| 432 | 524 | ||
| 433 | #[cfg(feature = "unstable-traits")] | 525 | #[cfg(feature = "unstable-traits")] |
| @@ -471,4 +563,80 @@ mod eh1 { | |||
| 471 | Ok(self.is_set_low()) | 563 | Ok(self.is_set_low()) |
| 472 | } | 564 | } |
| 473 | } | 565 | } |
| 566 | |||
| 567 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> { | ||
| 568 | fn toggle(&mut self) -> Result<(), Self::Error> { | ||
| 569 | Ok(self.toggle()) | ||
| 570 | } | ||
| 571 | } | ||
| 572 | |||
| 573 | impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d, T> { | ||
| 574 | type Error = Infallible; | ||
| 575 | } | ||
| 576 | |||
| 577 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> { | ||
| 578 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 579 | Ok(self.set_high()) | ||
| 580 | } | ||
| 581 | |||
| 582 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 583 | Ok(self.set_low()) | ||
| 584 | } | ||
| 585 | } | ||
| 586 | |||
| 587 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> { | ||
| 588 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 589 | Ok(self.is_set_high()) | ||
| 590 | } | ||
| 591 | |||
| 592 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 593 | Ok(self.is_set_low()) | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> { | ||
| 598 | fn toggle(&mut self) -> Result<(), Self::Error> { | ||
| 599 | Ok(self.toggle()) | ||
| 600 | } | ||
| 601 | } | ||
| 602 | |||
| 603 | impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> { | ||
| 604 | type Error = Infallible; | ||
| 605 | } | ||
| 606 | |||
| 607 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> { | ||
| 608 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 609 | Ok(self.is_high()) | ||
| 610 | } | ||
| 611 | |||
| 612 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 613 | Ok(self.is_low()) | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> { | ||
| 618 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 619 | Ok(self.set_high()) | ||
| 620 | } | ||
| 621 | |||
| 622 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 623 | Ok(self.set_low()) | ||
| 624 | } | ||
| 625 | } | ||
| 626 | |||
| 627 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> { | ||
| 628 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 629 | Ok(self.is_set_high()) | ||
| 630 | } | ||
| 631 | |||
| 632 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 633 | Ok(self.is_set_low()) | ||
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> { | ||
| 638 | fn toggle(&mut self) -> Result<(), Self::Error> { | ||
| 639 | Ok(self.toggle()) | ||
| 640 | } | ||
| 641 | } | ||
| 474 | } | 642 | } |
diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml new file mode 100644 index 000000000..0330025e4 --- /dev/null +++ b/tests/rp/.cargo/config.toml | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | [unstable] | ||
| 2 | build-std = ["core"] | ||
| 3 | build-std-features = ["panic_immediate_abort"] | ||
| 4 | |||
| 5 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 6 | #runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" | ||
| 7 | runner = "teleprobe local run --chip RP2040 --elf" | ||
| 8 | |||
| 9 | rustflags = [ | ||
| 10 | # Code-size optimizations. | ||
| 11 | "-Z", "trap-unreachable=no", | ||
| 12 | "-C", "inline-threshold=5", | ||
| 13 | "-C", "no-vectorize-loops", | ||
| 14 | ] | ||
| 15 | |||
| 16 | [build] | ||
| 17 | target = "thumbv6m-none-eabi" | ||
| 18 | |||
| 19 | [env] | ||
| 20 | DEFMT_LOG = "trace" | ||
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml new file mode 100644 index 000000000..b3067fffd --- /dev/null +++ b/tests/rp/Cargo.toml | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-rp-tests" | ||
| 4 | version = "0.1.0" | ||
| 5 | |||
| 6 | [dependencies] | ||
| 7 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } | ||
| 8 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } | ||
| 9 | |||
| 10 | defmt = "0.3.0" | ||
| 11 | defmt-rtt = "0.3.0" | ||
| 12 | |||
| 13 | cortex-m = "0.7.3" | ||
| 14 | cortex-m-rt = "0.7.0" | ||
| 15 | embedded-hal = "0.2.6" | ||
| 16 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } | ||
| 17 | embedded-hal-async = { version = "0.1.0-alpha.1" } | ||
| 18 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } | ||
| 19 | |||
| 20 | [profile.dev] | ||
| 21 | debug = 2 | ||
| 22 | debug-assertions = true | ||
| 23 | opt-level = 's' | ||
| 24 | overflow-checks = true | ||
| 25 | |||
| 26 | [profile.release] | ||
| 27 | codegen-units = 1 | ||
| 28 | debug = 2 | ||
| 29 | debug-assertions = false | ||
| 30 | incremental = false | ||
| 31 | lto = "fat" | ||
| 32 | opt-level = 's' | ||
| 33 | overflow-checks = false | ||
| 34 | |||
| 35 | # do not optimize proc-macro crates = faster builds from scratch | ||
| 36 | [profile.dev.build-override] | ||
| 37 | codegen-units = 8 | ||
| 38 | debug = false | ||
| 39 | debug-assertions = false | ||
| 40 | opt-level = 0 | ||
| 41 | overflow-checks = false | ||
| 42 | |||
| 43 | [profile.release.build-override] | ||
| 44 | codegen-units = 8 | ||
| 45 | debug = false | ||
| 46 | debug-assertions = false | ||
| 47 | opt-level = 0 | ||
| 48 | overflow-checks = false | ||
diff --git a/tests/rp/build.rs b/tests/rp/build.rs new file mode 100644 index 000000000..6f4872249 --- /dev/null +++ b/tests/rp/build.rs | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | use std::error::Error; | ||
| 2 | use std::path::PathBuf; | ||
| 3 | use std::{env, fs}; | ||
| 4 | |||
| 5 | fn main() -> Result<(), Box<dyn Error>> { | ||
| 6 | let out = PathBuf::from(env::var("OUT_DIR").unwrap()); | ||
| 7 | fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap(); | ||
| 8 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 9 | println!("cargo:rerun-if-changed=link_ram.x"); | ||
| 10 | |||
| 11 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 12 | println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); | ||
| 13 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 14 | |||
| 15 | Ok(()) | ||
| 16 | } | ||
diff --git a/tests/rp/link_ram.x b/tests/rp/link_ram.x new file mode 100644 index 000000000..86a11e875 --- /dev/null +++ b/tests/rp/link_ram.x | |||
| @@ -0,0 +1,255 @@ | |||
| 1 | /* ##### EMBASSY NOTE | ||
| 2 | Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in | ||
| 3 | Adjusted to put everything in RAM | ||
| 4 | */ | ||
| 5 | |||
| 6 | /* # Developer notes | ||
| 7 | |||
| 8 | - Symbols that start with a double underscore (__) are considered "private" | ||
| 9 | |||
| 10 | - Symbols that start with a single underscore (_) are considered "semi-public"; they can be | ||
| 11 | overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { | ||
| 12 | static mut __sbss }`). | ||
| 13 | |||
| 14 | - `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a | ||
| 15 | symbol if not dropped if it appears in or near the front of the linker arguments and "it's not | ||
| 16 | needed" by any of the preceding objects (linker arguments) | ||
| 17 | |||
| 18 | - `PROVIDE` is used to provide default values that can be overridden by a user linker script | ||
| 19 | |||
| 20 | - On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* | ||
| 21 | the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization | ||
| 22 | routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see | ||
| 23 | "Address (..) is out of bounds" in the disassembly produced by `objdump`. | ||
| 24 | */ | ||
| 25 | |||
| 26 | /* Provides information about the memory layout of the device */ | ||
| 27 | MEMORY { | ||
| 28 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 29 | } | ||
| 30 | |||
| 31 | /* # Entry point = reset vector */ | ||
| 32 | EXTERN(__RESET_VECTOR); | ||
| 33 | EXTERN(Reset); | ||
| 34 | ENTRY(Reset); | ||
| 35 | |||
| 36 | /* # Exception vectors */ | ||
| 37 | /* This is effectively weak aliasing at the linker level */ | ||
| 38 | /* The user can override any of these aliases by defining the corresponding symbol themselves (cf. | ||
| 39 | the `exception!` macro) */ | ||
| 40 | EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ | ||
| 41 | |||
| 42 | EXTERN(DefaultHandler); | ||
| 43 | |||
| 44 | PROVIDE(NonMaskableInt = DefaultHandler); | ||
| 45 | EXTERN(HardFaultTrampoline); | ||
| 46 | PROVIDE(MemoryManagement = DefaultHandler); | ||
| 47 | PROVIDE(BusFault = DefaultHandler); | ||
| 48 | PROVIDE(UsageFault = DefaultHandler); | ||
| 49 | PROVIDE(SecureFault = DefaultHandler); | ||
| 50 | PROVIDE(SVCall = DefaultHandler); | ||
| 51 | PROVIDE(DebugMonitor = DefaultHandler); | ||
| 52 | PROVIDE(PendSV = DefaultHandler); | ||
| 53 | PROVIDE(SysTick = DefaultHandler); | ||
| 54 | |||
| 55 | PROVIDE(DefaultHandler = DefaultHandler_); | ||
| 56 | PROVIDE(HardFault = HardFault_); | ||
| 57 | |||
| 58 | /* # Interrupt vectors */ | ||
| 59 | EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ | ||
| 60 | |||
| 61 | /* # Pre-initialization function */ | ||
| 62 | /* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, | ||
| 63 | then the function this points to will be called before the RAM is initialized. */ | ||
| 64 | PROVIDE(__pre_init = DefaultPreInit); | ||
| 65 | |||
| 66 | /* # Sections */ | ||
| 67 | SECTIONS | ||
| 68 | { | ||
| 69 | PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); | ||
| 70 | |||
| 71 | /* ## Sections in RAM */ | ||
| 72 | /* ### Vector table */ | ||
| 73 | .vector_table ORIGIN(RAM) : | ||
| 74 | { | ||
| 75 | /* Initial Stack Pointer (SP) value */ | ||
| 76 | LONG(_stack_start); | ||
| 77 | |||
| 78 | /* Reset vector */ | ||
| 79 | KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */ | ||
| 80 | __reset_vector = .; | ||
| 81 | |||
| 82 | /* Exceptions */ | ||
| 83 | KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */ | ||
| 84 | __eexceptions = .; | ||
| 85 | |||
| 86 | /* Device specific interrupts */ | ||
| 87 | KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */ | ||
| 88 | } > RAM | ||
| 89 | |||
| 90 | PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table)); | ||
| 91 | |||
| 92 | /* ### .text */ | ||
| 93 | .text _stext : | ||
| 94 | { | ||
| 95 | __stext = .; | ||
| 96 | *(.Reset); | ||
| 97 | |||
| 98 | *(.text .text.*); | ||
| 99 | |||
| 100 | /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`, | ||
| 101 | so must be placed close to it. */ | ||
| 102 | *(.HardFaultTrampoline); | ||
| 103 | *(.HardFault.*); | ||
| 104 | |||
| 105 | . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ | ||
| 106 | __etext = .; | ||
| 107 | } > RAM | ||
| 108 | |||
| 109 | /* ### .rodata */ | ||
| 110 | .rodata : ALIGN(4) | ||
| 111 | { | ||
| 112 | . = ALIGN(4); | ||
| 113 | __srodata = .; | ||
| 114 | *(.rodata .rodata.*); | ||
| 115 | |||
| 116 | /* 4-byte align the end (VMA) of this section. | ||
| 117 | This is required by LLD to ensure the LMA of the following .data | ||
| 118 | section will have the correct alignment. */ | ||
| 119 | . = ALIGN(4); | ||
| 120 | __erodata = .; | ||
| 121 | } > RAM | ||
| 122 | |||
| 123 | /* ## Sections in RAM */ | ||
| 124 | /* ### .data */ | ||
| 125 | .data : ALIGN(4) | ||
| 126 | { | ||
| 127 | . = ALIGN(4); | ||
| 128 | __sdata = .; | ||
| 129 | __edata = .; | ||
| 130 | *(.data .data.*); | ||
| 131 | . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ | ||
| 132 | } > RAM | ||
| 133 | /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to | ||
| 134 | * use the .data loading mechanism by pushing __edata. Note: do not change | ||
| 135 | * output region or load region in those user sections! */ | ||
| 136 | . = ALIGN(4); | ||
| 137 | |||
| 138 | /* LMA of .data */ | ||
| 139 | __sidata = LOADADDR(.data); | ||
| 140 | |||
| 141 | /* ### .gnu.sgstubs | ||
| 142 | This section contains the TrustZone-M veneers put there by the Arm GNU linker. */ | ||
| 143 | /* Security Attribution Unit blocks must be 32 bytes aligned. */ | ||
| 144 | /* Note that this pads the RAM usage to 32 byte alignment. */ | ||
| 145 | .gnu.sgstubs : ALIGN(32) | ||
| 146 | { | ||
| 147 | . = ALIGN(32); | ||
| 148 | __veneer_base = .; | ||
| 149 | *(.gnu.sgstubs*) | ||
| 150 | . = ALIGN(32); | ||
| 151 | __veneer_limit = .; | ||
| 152 | } > RAM | ||
| 153 | |||
| 154 | /* ### .bss */ | ||
| 155 | .bss (NOLOAD) : ALIGN(4) | ||
| 156 | { | ||
| 157 | . = ALIGN(4); | ||
| 158 | __sbss = .; | ||
| 159 | *(.bss .bss.*); | ||
| 160 | *(COMMON); /* Uninitialized C statics */ | ||
| 161 | . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ | ||
| 162 | } > RAM | ||
| 163 | /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to | ||
| 164 | * use the .bss zeroing mechanism by pushing __ebss. Note: do not change | ||
| 165 | * output region or load region in those user sections! */ | ||
| 166 | . = ALIGN(4); | ||
| 167 | __ebss = .; | ||
| 168 | |||
| 169 | /* ### .uninit */ | ||
| 170 | .uninit (NOLOAD) : ALIGN(4) | ||
| 171 | { | ||
| 172 | . = ALIGN(4); | ||
| 173 | __suninit = .; | ||
| 174 | *(.uninit .uninit.*); | ||
| 175 | . = ALIGN(4); | ||
| 176 | __euninit = .; | ||
| 177 | } > RAM | ||
| 178 | |||
| 179 | /* Place the heap right after `.uninit` in RAM */ | ||
| 180 | PROVIDE(__sheap = __euninit); | ||
| 181 | |||
| 182 | /* ## .got */ | ||
| 183 | /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in | ||
| 184 | the input files and raise an error if relocatable code is found */ | ||
| 185 | .got (NOLOAD) : | ||
| 186 | { | ||
| 187 | KEEP(*(.got .got.*)); | ||
| 188 | } | ||
| 189 | |||
| 190 | /* ## Discarded sections */ | ||
| 191 | /DISCARD/ : | ||
| 192 | { | ||
| 193 | /* Unused exception related info that only wastes space */ | ||
| 194 | *(.ARM.exidx); | ||
| 195 | *(.ARM.exidx.*); | ||
| 196 | *(.ARM.extab.*); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | /* Do not exceed this mark in the error messages below | */ | ||
| 201 | /* # Alignment checks */ | ||
| 202 | ASSERT(ORIGIN(RAM) % 4 == 0, " | ||
| 203 | ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); | ||
| 204 | |||
| 205 | ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " | ||
| 206 | BUG(cortex-m-rt): .data is not 4-byte aligned"); | ||
| 207 | |||
| 208 | ASSERT(__sidata % 4 == 0, " | ||
| 209 | BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); | ||
| 210 | |||
| 211 | ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " | ||
| 212 | BUG(cortex-m-rt): .bss is not 4-byte aligned"); | ||
| 213 | |||
| 214 | ASSERT(__sheap % 4 == 0, " | ||
| 215 | BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); | ||
| 216 | |||
| 217 | /* # Position checks */ | ||
| 218 | |||
| 219 | /* ## .vector_table */ | ||
| 220 | ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, " | ||
| 221 | BUG(cortex-m-rt): the reset vector is missing"); | ||
| 222 | |||
| 223 | ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, " | ||
| 224 | BUG(cortex-m-rt): the exception vectors are missing"); | ||
| 225 | |||
| 226 | ASSERT(SIZEOF(.vector_table) > 0x40, " | ||
| 227 | ERROR(cortex-m-rt): The interrupt vectors are missing. | ||
| 228 | Possible solutions, from most likely to less likely: | ||
| 229 | - Link to a svd2rust generated device crate | ||
| 230 | - Check that you actually use the device/hal/bsp crate in your code | ||
| 231 | - Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency | ||
| 232 | may be enabling it) | ||
| 233 | - Supply the interrupt handlers yourself. Check the documentation for details."); | ||
| 234 | |||
| 235 | /* ## .text */ | ||
| 236 | ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, " | ||
| 237 | ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section | ||
| 238 | Set _stext to an address greater than the end of .vector_table (See output of `nm`)"); | ||
| 239 | |||
| 240 | ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), " | ||
| 241 | ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory. | ||
| 242 | Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'"); | ||
| 243 | |||
| 244 | /* # Other checks */ | ||
| 245 | ASSERT(SIZEOF(.got) == 0, " | ||
| 246 | ERROR(cortex-m-rt): .got section detected in the input object files | ||
| 247 | Dynamic relocations are not supported. If you are linking to C code compiled using | ||
| 248 | the 'cc' crate then modify your build script to compile the C code _without_ | ||
| 249 | the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); | ||
| 250 | /* Do not exceed this mark in the error messages above | */ | ||
| 251 | |||
| 252 | |||
| 253 | /* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */ | ||
| 254 | /* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */ | ||
| 255 | INCLUDE device.x \ No newline at end of file | ||
diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs new file mode 100644 index 000000000..0be9d9f24 --- /dev/null +++ b/tests/rp/src/bin/gpio.rs | |||
| @@ -0,0 +1,192 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{assert, *}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy_rp::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull}; | ||
| 8 | use embassy_rp::Peripherals; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy::main] | ||
| 12 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 13 | info!("Hello World!"); | ||
| 14 | |||
| 15 | let (mut a, mut b) = (p.PIN_0, p.PIN_1); | ||
| 16 | |||
| 17 | // Test initial output | ||
| 18 | { | ||
| 19 | let b = Input::new(&mut b, Pull::None); | ||
| 20 | |||
| 21 | { | ||
| 22 | let _a = Output::new(&mut a, Level::Low); | ||
| 23 | delay(); | ||
| 24 | assert!(b.is_low()); | ||
| 25 | } | ||
| 26 | { | ||
| 27 | let _a = Output::new(&mut a, Level::High); | ||
| 28 | delay(); | ||
| 29 | assert!(b.is_high()); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | // Test input no pull | ||
| 34 | { | ||
| 35 | let b = Input::new(&mut b, Pull::None); | ||
| 36 | // no pull, the status is undefined | ||
| 37 | |||
| 38 | let mut a = Output::new(&mut a, Level::Low); | ||
| 39 | delay(); | ||
| 40 | assert!(b.is_low()); | ||
| 41 | a.set_high(); | ||
| 42 | delay(); | ||
| 43 | assert!(b.is_high()); | ||
| 44 | } | ||
| 45 | |||
| 46 | // Test input pulldown | ||
| 47 | { | ||
| 48 | let b = Input::new(&mut b, Pull::Down); | ||
| 49 | delay(); | ||
| 50 | assert!(b.is_low()); | ||
| 51 | |||
| 52 | let mut a = Output::new(&mut a, Level::Low); | ||
| 53 | delay(); | ||
| 54 | assert!(b.is_low()); | ||
| 55 | a.set_high(); | ||
| 56 | delay(); | ||
| 57 | assert!(b.is_high()); | ||
| 58 | } | ||
| 59 | |||
| 60 | // Test input pullup | ||
| 61 | { | ||
| 62 | let b = Input::new(&mut b, Pull::Up); | ||
| 63 | delay(); | ||
| 64 | assert!(b.is_high()); | ||
| 65 | |||
| 66 | let mut a = Output::new(&mut a, Level::Low); | ||
| 67 | delay(); | ||
| 68 | assert!(b.is_low()); | ||
| 69 | a.set_high(); | ||
| 70 | delay(); | ||
| 71 | assert!(b.is_high()); | ||
| 72 | } | ||
| 73 | |||
| 74 | // OUTPUT OPEN DRAIN | ||
| 75 | { | ||
| 76 | let mut b = OutputOpenDrain::new(&mut b, Level::High); | ||
| 77 | let mut a = Flex::new(&mut a); | ||
| 78 | a.set_as_input(); | ||
| 79 | |||
| 80 | // When an OutputOpenDrain is high, it doesn't drive the pin. | ||
| 81 | a.set_pull(Pull::Up); | ||
| 82 | delay(); | ||
| 83 | assert!(a.is_high()); | ||
| 84 | a.set_pull(Pull::Down); | ||
| 85 | delay(); | ||
| 86 | assert!(a.is_low()); | ||
| 87 | |||
| 88 | b.set_low(); | ||
| 89 | |||
| 90 | // When an OutputOpenDrain is low, it drives the pin low. | ||
| 91 | a.set_pull(Pull::Up); | ||
| 92 | delay(); | ||
| 93 | assert!(a.is_low()); | ||
| 94 | a.set_pull(Pull::Down); | ||
| 95 | delay(); | ||
| 96 | assert!(a.is_low()); | ||
| 97 | |||
| 98 | b.set_high(); | ||
| 99 | |||
| 100 | a.set_pull(Pull::Up); | ||
| 101 | delay(); | ||
| 102 | assert!(a.is_high()); | ||
| 103 | a.set_pull(Pull::Down); | ||
| 104 | delay(); | ||
| 105 | assert!(a.is_low()); | ||
| 106 | } | ||
| 107 | |||
| 108 | // FLEX | ||
| 109 | // Test initial output | ||
| 110 | { | ||
| 111 | //Flex pin configured as input | ||
| 112 | let mut b = Flex::new(&mut b); | ||
| 113 | b.set_as_input(); | ||
| 114 | |||
| 115 | { | ||
| 116 | //Flex pin configured as output | ||
| 117 | let mut a = Flex::new(&mut a); //Flex pin configured as output | ||
| 118 | a.set_low(); // Pin state must be set before configuring the pin, thus we avoid unknown state | ||
| 119 | a.set_as_output(); | ||
| 120 | delay(); | ||
| 121 | assert!(b.is_low()); | ||
| 122 | } | ||
| 123 | { | ||
| 124 | //Flex pin configured as output | ||
| 125 | let mut a = Flex::new(&mut a); | ||
| 126 | a.set_high(); | ||
| 127 | a.set_as_output(); | ||
| 128 | |||
| 129 | delay(); | ||
| 130 | assert!(b.is_high()); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | // Test input no pull | ||
| 135 | { | ||
| 136 | let mut b = Flex::new(&mut b); | ||
| 137 | b.set_as_input(); // no pull by default. | ||
| 138 | |||
| 139 | let mut a = Flex::new(&mut a); | ||
| 140 | a.set_low(); | ||
| 141 | a.set_as_output(); | ||
| 142 | |||
| 143 | delay(); | ||
| 144 | assert!(b.is_low()); | ||
| 145 | a.set_high(); | ||
| 146 | delay(); | ||
| 147 | assert!(b.is_high()); | ||
| 148 | } | ||
| 149 | |||
| 150 | // Test input pulldown | ||
| 151 | { | ||
| 152 | let mut b = Flex::new(&mut b); | ||
| 153 | b.set_as_input(); | ||
| 154 | b.set_pull(Pull::Down); | ||
| 155 | delay(); | ||
| 156 | assert!(b.is_low()); | ||
| 157 | |||
| 158 | let mut a = Flex::new(&mut a); | ||
| 159 | a.set_low(); | ||
| 160 | a.set_as_output(); | ||
| 161 | delay(); | ||
| 162 | assert!(b.is_low()); | ||
| 163 | a.set_high(); | ||
| 164 | delay(); | ||
| 165 | assert!(b.is_high()); | ||
| 166 | } | ||
| 167 | |||
| 168 | // Test input pullup | ||
| 169 | { | ||
| 170 | let mut b = Flex::new(&mut b); | ||
| 171 | b.set_as_input(); | ||
| 172 | b.set_pull(Pull::Up); | ||
| 173 | delay(); | ||
| 174 | assert!(b.is_high()); | ||
| 175 | |||
| 176 | let mut a = Flex::new(&mut a); | ||
| 177 | a.set_high(); | ||
| 178 | a.set_as_output(); | ||
| 179 | delay(); | ||
| 180 | assert!(b.is_high()); | ||
| 181 | a.set_low(); | ||
| 182 | delay(); | ||
| 183 | assert!(b.is_low()); | ||
| 184 | } | ||
| 185 | |||
| 186 | info!("Test OK"); | ||
| 187 | cortex_m::asm::bkpt(); | ||
| 188 | } | ||
| 189 | |||
| 190 | fn delay() { | ||
| 191 | cortex_m::asm::delay(10000); | ||
| 192 | } | ||
