diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-07-10 21:22:46 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-07-10 21:22:46 +0000 |
| commit | 93e7d53e393a8f79be1c4a5c0e1fd8497305fc50 (patch) | |
| tree | 11d9be878c0eb04e7b9b9e7deb766ca4489c8935 | |
| parent | 5f43c1d37e9db847c7861fe0bd821db62edba9f6 (diff) | |
| parent | 323b0d1a5c430bfcec3288e229179a5b7c86038a (diff) | |
Merge #851
851: Gpio dynamic flex r=Dirbaio a=AntoineMugnier
Add Flex GPIO type for embassy-stm32 as it is the case for embassy-nrf.
Co-authored-by: [email protected] <[email protected]>
| -rw-r--r-- | embassy-stm32/src/exti.rs | 10 | ||||
| -rw-r--r-- | embassy-stm32/src/gpio.rs | 491 | ||||
| -rw-r--r-- | tests/stm32/src/bin/gpio.rs | 106 |
3 files changed, 419 insertions, 188 deletions
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 378665b77..94e0e941d 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs | |||
| @@ -99,7 +99,7 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> { | |||
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | pub async fn wait_for_high<'a>(&'a mut self) { | 101 | pub async fn wait_for_high<'a>(&'a mut self) { |
| 102 | let fut = ExtiInputFuture::new(self.pin.pin.pin(), self.pin.pin.port(), true, false); | 102 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); |
| 103 | if self.is_high() { | 103 | if self.is_high() { |
| 104 | return; | 104 | return; |
| 105 | } | 105 | } |
| @@ -107,7 +107,7 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> { | |||
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | pub async fn wait_for_low<'a>(&'a mut self) { | 109 | pub async fn wait_for_low<'a>(&'a mut self) { |
| 110 | let fut = ExtiInputFuture::new(self.pin.pin.pin(), self.pin.pin.port(), false, true); | 110 | let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); |
| 111 | if self.is_low() { | 111 | if self.is_low() { |
| 112 | return; | 112 | return; |
| 113 | } | 113 | } |
| @@ -115,15 +115,15 @@ impl<'d, T: GpioPin> ExtiInput<'d, T> { | |||
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | pub async fn wait_for_rising_edge<'a>(&'a mut self) { | 117 | pub async fn wait_for_rising_edge<'a>(&'a mut self) { |
| 118 | ExtiInputFuture::new(self.pin.pin.pin(), self.pin.pin.port(), true, false).await | 118 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | pub async fn wait_for_falling_edge<'a>(&'a mut self) { | 121 | pub async fn wait_for_falling_edge<'a>(&'a mut self) { |
| 122 | ExtiInputFuture::new(self.pin.pin.pin(), self.pin.pin.port(), false, true).await | 122 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | pub async fn wait_for_any_edge<'a>(&'a mut self) { | 125 | pub async fn wait_for_any_edge<'a>(&'a mut self) { |
| 126 | ExtiInputFuture::new(self.pin.pin.pin(), self.pin.pin.port(), true, true).await | 126 | ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await |
| 127 | } | 127 | } |
| 128 | } | 128 | } |
| 129 | 129 | ||
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 6e445f8cd..806b5eb68 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs | |||
| @@ -7,6 +7,197 @@ use embassy_hal_common::{unborrow, unsafe_impl_unborrow}; | |||
| 7 | use crate::pac::gpio::{self, vals}; | 7 | use crate::pac::gpio::{self, vals}; |
| 8 | use crate::{pac, peripherals, Unborrow}; | 8 | use crate::{pac, peripherals, Unborrow}; |
| 9 | 9 | ||
| 10 | /// GPIO flexible pin. | ||
| 11 | /// | ||
| 12 | /// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain | ||
| 13 | /// set while not in output mode, so the pin's level will be 'remembered' when it is not in output | ||
| 14 | /// mode. | ||
| 15 | pub struct Flex<'d, T: Pin> { | ||
| 16 | pub(crate) pin: T, | ||
| 17 | phantom: PhantomData<&'d mut T>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'d, T: Pin> Flex<'d, T> { | ||
| 21 | /// Wrap the pin in a `Flex`. | ||
| 22 | /// | ||
| 23 | /// The pin remains disconnected. The initial output level is unspecified, but can be changed | ||
| 24 | /// before the pin is put into output mode. | ||
| 25 | /// | ||
| 26 | #[inline] | ||
| 27 | pub fn new(pin: impl Unborrow<Target = T> + 'd) -> Self { | ||
| 28 | unborrow!(pin); | ||
| 29 | // Pin will be in disconnected state. | ||
| 30 | Self { | ||
| 31 | pin, | ||
| 32 | phantom: PhantomData, | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | /// Put the pin into input mode. | ||
| 37 | #[inline] | ||
| 38 | pub fn set_as_input(&mut self, pull: Pull) { | ||
| 39 | critical_section::with(|_| unsafe { | ||
| 40 | let r = self.pin.block(); | ||
| 41 | let n = self.pin.pin() as usize; | ||
| 42 | #[cfg(gpio_v1)] | ||
| 43 | { | ||
| 44 | let cnf = match pull { | ||
| 45 | Pull::Up => { | ||
| 46 | r.bsrr().write(|w| w.set_bs(n, true)); | ||
| 47 | vals::CnfIn::PULL | ||
| 48 | } | ||
| 49 | Pull::Down => { | ||
| 50 | r.bsrr().write(|w| w.set_br(n, true)); | ||
| 51 | vals::CnfIn::PULL | ||
| 52 | } | ||
| 53 | Pull::None => vals::CnfIn::FLOATING, | ||
| 54 | }; | ||
| 55 | |||
| 56 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 57 | r.cr(crlh).modify(|w| { | ||
| 58 | w.set_mode(n % 8, vals::Mode::INPUT); | ||
| 59 | w.set_cnf_in(n % 8, cnf); | ||
| 60 | }); | ||
| 61 | } | ||
| 62 | #[cfg(gpio_v2)] | ||
| 63 | { | ||
| 64 | r.pupdr().modify(|w| w.set_pupdr(n, pull.into())); | ||
| 65 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL)); | ||
| 66 | r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT)); | ||
| 67 | } | ||
| 68 | }); | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Put the pin into output mode. | ||
| 72 | /// | ||
| 73 | /// The pin level will be whatever was set before (or low by default). If you want it to begin | ||
| 74 | /// at a specific level, call `set_high`/`set_low` on the pin first. | ||
| 75 | #[inline] | ||
| 76 | pub fn set_as_output(&mut self, speed: Speed) { | ||
| 77 | critical_section::with(|_| unsafe { | ||
| 78 | let r = self.pin.block(); | ||
| 79 | let n = self.pin.pin() as usize; | ||
| 80 | #[cfg(gpio_v1)] | ||
| 81 | { | ||
| 82 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 83 | r.cr(crlh).modify(|w| { | ||
| 84 | w.set_mode(n % 8, speed.into()); | ||
| 85 | w.set_cnf_out(n % 8, vals::CnfOut::PUSHPULL); | ||
| 86 | }); | ||
| 87 | } | ||
| 88 | #[cfg(gpio_v2)] | ||
| 89 | { | ||
| 90 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); | ||
| 91 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL)); | ||
| 92 | self.pin.set_speed(speed); | ||
| 93 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); | ||
| 94 | } | ||
| 95 | }); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Put the pin into input + output mode. | ||
| 99 | /// | ||
| 100 | /// This is commonly used for "open drain" mode. | ||
| 101 | /// the hardware will drive the line low if you set it to low, and will leave it floating if you set | ||
| 102 | /// it to high, in which case you can read the input to figure out whether another device | ||
| 103 | /// is driving the line low. | ||
| 104 | /// | ||
| 105 | /// The pin level will be whatever was set before (or low by default). If you want it to begin | ||
| 106 | /// at a specific level, call `set_high`/`set_low` on the pin first. | ||
| 107 | #[inline] | ||
| 108 | pub fn set_as_input_output(&mut self, speed: Speed, pull: Pull) { | ||
| 109 | critical_section::with(|_| unsafe { | ||
| 110 | let r = self.pin.block(); | ||
| 111 | let n = self.pin.pin() as usize; | ||
| 112 | #[cfg(gpio_v1)] | ||
| 113 | { | ||
| 114 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 115 | match pull { | ||
| 116 | Pull::Up => r.bsrr().write(|w| w.set_bs(n, true)), | ||
| 117 | Pull::Down => r.bsrr().write(|w| w.set_br(n, true)), | ||
| 118 | Pull::None => {} | ||
| 119 | } | ||
| 120 | r.cr(crlh).modify(|w| w.set_mode(n % 8, speed.into())); | ||
| 121 | r.cr(crlh).modify(|w| w.set_cnf_out(n % 8, vals::CnfOut::OPENDRAIN)); | ||
| 122 | } | ||
| 123 | #[cfg(gpio_v2)] | ||
| 124 | { | ||
| 125 | r.pupdr().modify(|w| w.set_pupdr(n, pull.into())); | ||
| 126 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::OPENDRAIN)); | ||
| 127 | self.pin.set_speed(speed); | ||
| 128 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); | ||
| 129 | } | ||
| 130 | }); | ||
| 131 | } | ||
| 132 | |||
| 133 | #[inline] | ||
| 134 | pub fn is_high(&self) -> bool { | ||
| 135 | !self.is_low() | ||
| 136 | } | ||
| 137 | |||
| 138 | #[inline] | ||
| 139 | pub fn is_low(&self) -> bool { | ||
| 140 | let state = unsafe { self.pin.block().idr().read().idr(self.pin.pin() as _) }; | ||
| 141 | state == vals::Idr::LOW | ||
| 142 | } | ||
| 143 | |||
| 144 | #[inline] | ||
| 145 | pub fn is_set_high(&self) -> bool { | ||
| 146 | !self.is_set_low() | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Is the output pin set as low? | ||
| 150 | #[inline] | ||
| 151 | pub fn is_set_low(&self) -> bool { | ||
| 152 | let state = unsafe { self.pin.block().odr().read().odr(self.pin.pin() as _) }; | ||
| 153 | state == vals::Odr::LOW | ||
| 154 | } | ||
| 155 | |||
| 156 | #[inline] | ||
| 157 | pub fn set_high(&mut self) { | ||
| 158 | self.pin.set_high(); | ||
| 159 | } | ||
| 160 | |||
| 161 | /// Set the output as low. | ||
| 162 | #[inline] | ||
| 163 | pub fn set_low(&mut self) { | ||
| 164 | self.pin.set_low(); | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Toggle pin output | ||
| 168 | #[inline] | ||
| 169 | pub fn toggle(&mut self) { | ||
| 170 | if self.is_set_low() { | ||
| 171 | self.set_high() | ||
| 172 | } else { | ||
| 173 | self.set_low() | ||
| 174 | } | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | impl<'d, T: Pin> Drop for Flex<'d, T> { | ||
| 179 | #[inline] | ||
| 180 | fn drop(&mut self) { | ||
| 181 | critical_section::with(|_| unsafe { | ||
| 182 | let r = self.pin.block(); | ||
| 183 | let n = self.pin.pin() as usize; | ||
| 184 | #[cfg(gpio_v1)] | ||
| 185 | { | ||
| 186 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 187 | r.cr(crlh).modify(|w| { | ||
| 188 | w.set_mode(n % 8, vals::Mode::INPUT); | ||
| 189 | w.set_cnf_in(n % 8, vals::CnfIn::FLOATING); | ||
| 190 | }); | ||
| 191 | } | ||
| 192 | #[cfg(gpio_v2)] | ||
| 193 | { | ||
| 194 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); | ||
| 195 | r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT)); | ||
| 196 | } | ||
| 197 | }); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 10 | /// Pull setting for an input. | 201 | /// Pull setting for an input. |
| 11 | #[derive(Debug, Eq, PartialEq)] | 202 | #[derive(Debug, Eq, PartialEq)] |
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 203 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| @@ -70,78 +261,25 @@ impl From<Speed> for vals::Ospeedr { | |||
| 70 | 261 | ||
| 71 | /// GPIO input driver. | 262 | /// GPIO input driver. |
| 72 | pub struct Input<'d, T: Pin> { | 263 | pub struct Input<'d, T: Pin> { |
| 73 | pub(crate) pin: T, | 264 | pub(crate) pin: Flex<'d, T>, |
| 74 | phantom: PhantomData<&'d mut T>, | ||
| 75 | } | 265 | } |
| 76 | 266 | ||
| 77 | impl<'d, T: Pin> Input<'d, T> { | 267 | impl<'d, T: Pin> Input<'d, T> { |
| 78 | #[inline] | 268 | #[inline] |
| 79 | pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { | 269 | pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { |
| 80 | unborrow!(pin); | 270 | let mut pin = Flex::new(pin); |
| 81 | 271 | pin.set_as_input(pull); | |
| 82 | critical_section::with(|_| unsafe { | 272 | Self { pin } |
| 83 | let r = pin.block(); | ||
| 84 | let n = pin.pin() as usize; | ||
| 85 | #[cfg(gpio_v1)] | ||
| 86 | { | ||
| 87 | let cnf = match pull { | ||
| 88 | Pull::Up => { | ||
| 89 | r.bsrr().write(|w| w.set_bs(n, true)); | ||
| 90 | vals::CnfIn::PULL | ||
| 91 | } | ||
| 92 | Pull::Down => { | ||
| 93 | r.bsrr().write(|w| w.set_br(n, true)); | ||
| 94 | vals::CnfIn::PULL | ||
| 95 | } | ||
| 96 | Pull::None => vals::CnfIn::FLOATING, | ||
| 97 | }; | ||
| 98 | |||
| 99 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 100 | r.cr(crlh).modify(|w| { | ||
| 101 | w.set_mode(n % 8, vals::Mode::INPUT); | ||
| 102 | w.set_cnf_in(n % 8, cnf); | ||
| 103 | }); | ||
| 104 | } | ||
| 105 | #[cfg(gpio_v2)] | ||
| 106 | { | ||
| 107 | r.pupdr().modify(|w| w.set_pupdr(n, pull.into())); | ||
| 108 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL)); | ||
| 109 | r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT)); | ||
| 110 | } | ||
| 111 | }); | ||
| 112 | |||
| 113 | Self { | ||
| 114 | pin, | ||
| 115 | phantom: PhantomData, | ||
| 116 | } | ||
| 117 | } | 273 | } |
| 118 | 274 | ||
| 119 | #[inline] | 275 | #[inline] |
| 120 | pub fn is_high(&self) -> bool { | 276 | pub fn is_high(&self) -> bool { |
| 121 | !self.is_low() | 277 | self.pin.is_high() |
| 122 | } | 278 | } |
| 123 | 279 | ||
| 124 | #[inline] | 280 | #[inline] |
| 125 | pub fn is_low(&self) -> bool { | 281 | pub fn is_low(&self) -> bool { |
| 126 | let state = unsafe { self.pin.block().idr().read().idr(self.pin.pin() as _) }; | 282 | self.pin.is_low() |
| 127 | state == vals::Idr::LOW | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | impl<'d, T: Pin> Drop for Input<'d, T> { | ||
| 132 | #[inline] | ||
| 133 | fn drop(&mut self) { | ||
| 134 | critical_section::with(|_| unsafe { | ||
| 135 | let r = self.pin.block(); | ||
| 136 | let n = self.pin.pin() as usize; | ||
| 137 | #[cfg(gpio_v1)] | ||
| 138 | { | ||
| 139 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 140 | r.cr(crlh).modify(|w| w.set_cnf_in(n % 8, vals::CnfIn::FLOATING)); | ||
| 141 | } | ||
| 142 | #[cfg(gpio_v2)] | ||
| 143 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); | ||
| 144 | }); | ||
| 145 | } | 283 | } |
| 146 | } | 284 | } |
| 147 | 285 | ||
| @@ -155,44 +293,19 @@ pub enum Level { | |||
| 155 | 293 | ||
| 156 | /// GPIO output driver. | 294 | /// GPIO output driver. |
| 157 | pub struct Output<'d, T: Pin> { | 295 | pub struct Output<'d, T: Pin> { |
| 158 | pub(crate) pin: T, | 296 | pub(crate) pin: Flex<'d, T>, |
| 159 | phantom: PhantomData<&'d mut T>, | ||
| 160 | } | 297 | } |
| 161 | 298 | ||
| 162 | impl<'d, T: Pin> Output<'d, T> { | 299 | impl<'d, T: Pin> Output<'d, T> { |
| 163 | #[inline] | 300 | #[inline] |
| 164 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level, speed: Speed) -> Self { | 301 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level, speed: Speed) -> Self { |
| 165 | unborrow!(pin); | 302 | let mut pin = Flex::new(pin); |
| 166 | |||
| 167 | match initial_output { | 303 | match initial_output { |
| 168 | Level::High => pin.set_high(), | 304 | Level::High => pin.set_high(), |
| 169 | Level::Low => pin.set_low(), | 305 | Level::Low => pin.set_low(), |
| 170 | } | 306 | } |
| 171 | 307 | pin.set_as_output(speed); | |
| 172 | critical_section::with(|_| unsafe { | 308 | Self { pin } |
| 173 | let r = pin.block(); | ||
| 174 | let n = pin.pin() as usize; | ||
| 175 | #[cfg(gpio_v1)] | ||
| 176 | { | ||
| 177 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 178 | r.cr(crlh).modify(|w| { | ||
| 179 | w.set_mode(n % 8, speed.into()); | ||
| 180 | w.set_cnf_out(n % 8, vals::CnfOut::PUSHPULL); | ||
| 181 | }); | ||
| 182 | } | ||
| 183 | #[cfg(gpio_v2)] | ||
| 184 | { | ||
| 185 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); | ||
| 186 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL)); | ||
| 187 | pin.set_speed(speed); | ||
| 188 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); | ||
| 189 | } | ||
| 190 | }); | ||
| 191 | |||
| 192 | Self { | ||
| 193 | pin, | ||
| 194 | phantom: PhantomData, | ||
| 195 | } | ||
| 196 | } | 309 | } |
| 197 | 310 | ||
| 198 | /// Set the output as high. | 311 | /// Set the output as high. |
| @@ -210,104 +323,49 @@ impl<'d, T: Pin> Output<'d, T> { | |||
| 210 | /// Is the output pin set as high? | 323 | /// Is the output pin set as high? |
| 211 | #[inline] | 324 | #[inline] |
| 212 | pub fn is_set_high(&self) -> bool { | 325 | pub fn is_set_high(&self) -> bool { |
| 213 | !self.is_set_low() | 326 | self.pin.is_set_high() |
| 214 | } | 327 | } |
| 215 | 328 | ||
| 216 | /// Is the output pin set as low? | 329 | /// Is the output pin set as low? |
| 217 | #[inline] | 330 | #[inline] |
| 218 | pub fn is_set_low(&self) -> bool { | 331 | pub fn is_set_low(&self) -> bool { |
| 219 | let state = unsafe { self.pin.block().odr().read().odr(self.pin.pin() as _) }; | 332 | self.pin.is_set_low() |
| 220 | state == vals::Odr::LOW | ||
| 221 | } | 333 | } |
| 222 | 334 | ||
| 223 | /// Toggle pin output | 335 | /// Toggle pin output |
| 224 | #[inline] | 336 | #[inline] |
| 225 | pub fn toggle(&mut self) { | 337 | pub fn toggle(&mut self) { |
| 226 | if self.is_set_low() { | 338 | self.pin.toggle(); |
| 227 | self.set_high() | ||
| 228 | } else { | ||
| 229 | self.set_low() | ||
| 230 | } | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | impl<'d, T: Pin> Drop for Output<'d, T> { | ||
| 235 | #[inline] | ||
| 236 | fn drop(&mut self) { | ||
| 237 | critical_section::with(|_| unsafe { | ||
| 238 | let r = self.pin.block(); | ||
| 239 | let n = self.pin.pin() as usize; | ||
| 240 | #[cfg(gpio_v1)] | ||
| 241 | { | ||
| 242 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 243 | r.cr(crlh).modify(|w| { | ||
| 244 | w.set_mode(n % 8, vals::Mode::INPUT); | ||
| 245 | w.set_cnf_in(n % 8, vals::CnfIn::FLOATING); | ||
| 246 | }); | ||
| 247 | } | ||
| 248 | #[cfg(gpio_v2)] | ||
| 249 | { | ||
| 250 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); | ||
| 251 | r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT)); | ||
| 252 | } | ||
| 253 | }); | ||
| 254 | } | 339 | } |
| 255 | } | 340 | } |
| 256 | 341 | ||
| 257 | /// GPIO output open-drain driver. | 342 | /// GPIO output open-drain driver. |
| 258 | pub struct OutputOpenDrain<'d, T: Pin> { | 343 | pub struct OutputOpenDrain<'d, T: Pin> { |
| 259 | pub(crate) pin: T, | 344 | pub(crate) pin: Flex<'d, T>, |
| 260 | phantom: PhantomData<&'d mut T>, | ||
| 261 | } | 345 | } |
| 262 | 346 | ||
| 263 | impl<'d, T: Pin> OutputOpenDrain<'d, T> { | 347 | impl<'d, T: Pin> OutputOpenDrain<'d, T> { |
| 264 | #[inline] | 348 | #[inline] |
| 265 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self { | 349 | pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self { |
| 266 | unborrow!(pin); | 350 | let mut pin = Flex::new(pin); |
| 267 | 351 | ||
| 268 | match initial_output { | 352 | match initial_output { |
| 269 | Level::High => pin.set_high(), | 353 | Level::High => pin.set_high(), |
| 270 | Level::Low => pin.set_low(), | 354 | Level::Low => pin.set_low(), |
| 271 | } | 355 | } |
| 272 | 356 | ||
| 273 | critical_section::with(|_| unsafe { | 357 | pin.set_as_input_output(speed, pull); |
| 274 | let r = pin.block(); | 358 | Self { pin } |
| 275 | let n = pin.pin() as usize; | ||
| 276 | #[cfg(gpio_v1)] | ||
| 277 | { | ||
| 278 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 279 | match pull { | ||
| 280 | Pull::Up => r.bsrr().write(|w| w.set_bs(n, true)), | ||
| 281 | Pull::Down => r.bsrr().write(|w| w.set_br(n, true)), | ||
| 282 | Pull::None => {} | ||
| 283 | } | ||
| 284 | r.cr(crlh).modify(|w| w.set_mode(n % 8, speed.into())); | ||
| 285 | r.cr(crlh).modify(|w| w.set_cnf_out(n % 8, vals::CnfOut::OPENDRAIN)); | ||
| 286 | } | ||
| 287 | #[cfg(gpio_v2)] | ||
| 288 | { | ||
| 289 | r.pupdr().modify(|w| w.set_pupdr(n, pull.into())); | ||
| 290 | r.otyper().modify(|w| w.set_ot(n, vals::Ot::OPENDRAIN)); | ||
| 291 | pin.set_speed(speed); | ||
| 292 | r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT)); | ||
| 293 | } | ||
| 294 | }); | ||
| 295 | |||
| 296 | Self { | ||
| 297 | pin, | ||
| 298 | phantom: PhantomData, | ||
| 299 | } | ||
| 300 | } | 359 | } |
| 301 | 360 | ||
| 302 | #[inline] | 361 | #[inline] |
| 303 | pub fn is_high(&self) -> bool { | 362 | pub fn is_high(&self) -> bool { |
| 304 | !self.is_low() | 363 | !self.pin.is_low() |
| 305 | } | 364 | } |
| 306 | 365 | ||
| 307 | #[inline] | 366 | #[inline] |
| 308 | pub fn is_low(&self) -> bool { | 367 | pub fn is_low(&self) -> bool { |
| 309 | let state = unsafe { self.pin.block().idr().read().idr(self.pin.pin() as _) }; | 368 | self.pin.is_low() |
| 310 | state == vals::Idr::LOW | ||
| 311 | } | 369 | } |
| 312 | 370 | ||
| 313 | /// Set the output as high. | 371 | /// Set the output as high. |
| @@ -331,41 +389,13 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { | |||
| 331 | /// Is the output pin set as low? | 389 | /// Is the output pin set as low? |
| 332 | #[inline] | 390 | #[inline] |
| 333 | pub fn is_set_low(&self) -> bool { | 391 | pub fn is_set_low(&self) -> bool { |
| 334 | let state = unsafe { self.pin.block().odr().read().odr(self.pin.pin() as _) }; | 392 | self.pin.is_set_low() |
| 335 | state == vals::Odr::LOW | ||
| 336 | } | 393 | } |
| 337 | 394 | ||
| 338 | /// Toggle pin output | 395 | /// Toggle pin output |
| 339 | #[inline] | 396 | #[inline] |
| 340 | pub fn toggle(&mut self) { | 397 | pub fn toggle(&mut self) { |
| 341 | if self.is_set_low() { | 398 | self.pin.toggle() |
| 342 | self.set_high() | ||
| 343 | } else { | ||
| 344 | self.set_low() | ||
| 345 | } | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | impl<'d, T: Pin> Drop for OutputOpenDrain<'d, T> { | ||
| 350 | #[inline] | ||
| 351 | fn drop(&mut self) { | ||
| 352 | critical_section::with(|_| unsafe { | ||
| 353 | let r = self.pin.block(); | ||
| 354 | let n = self.pin.pin() as usize; | ||
| 355 | #[cfg(gpio_v1)] | ||
| 356 | { | ||
| 357 | let crlh = if n < 8 { 0 } else { 1 }; | ||
| 358 | r.cr(crlh).modify(|w| { | ||
| 359 | w.set_mode(n % 8, vals::Mode::INPUT); | ||
| 360 | w.set_cnf_in(n % 8, vals::CnfIn::FLOATING); | ||
| 361 | }); | ||
| 362 | } | ||
| 363 | #[cfg(gpio_v2)] | ||
| 364 | { | ||
| 365 | r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING)); | ||
| 366 | r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT)); | ||
| 367 | } | ||
| 368 | }); | ||
| 369 | } | 399 | } |
| 370 | } | 400 | } |
| 371 | 401 | ||
| @@ -692,6 +722,55 @@ mod eh02 { | |||
| 692 | Ok(self.toggle()) | 722 | Ok(self.toggle()) |
| 693 | } | 723 | } |
| 694 | } | 724 | } |
| 725 | |||
| 726 | impl<'d, T: Pin> InputPin for Flex<'d, T> { | ||
| 727 | type Error = Infallible; | ||
| 728 | |||
| 729 | #[inline] | ||
| 730 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 731 | Ok(self.is_high()) | ||
| 732 | } | ||
| 733 | |||
| 734 | #[inline] | ||
| 735 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 736 | Ok(self.is_low()) | ||
| 737 | } | ||
| 738 | } | ||
| 739 | |||
| 740 | impl<'d, T: Pin> OutputPin for Flex<'d, T> { | ||
| 741 | type Error = Infallible; | ||
| 742 | |||
| 743 | #[inline] | ||
| 744 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 745 | Ok(self.set_high()) | ||
| 746 | } | ||
| 747 | |||
| 748 | #[inline] | ||
| 749 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 750 | Ok(self.set_low()) | ||
| 751 | } | ||
| 752 | } | ||
| 753 | |||
| 754 | impl<'d, T: Pin> StatefulOutputPin for Flex<'d, T> { | ||
| 755 | #[inline] | ||
| 756 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 757 | Ok(self.is_set_high()) | ||
| 758 | } | ||
| 759 | |||
| 760 | /// Is the output pin set as low? | ||
| 761 | #[inline] | ||
| 762 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 763 | Ok(self.is_set_low()) | ||
| 764 | } | ||
| 765 | } | ||
| 766 | |||
| 767 | impl<'d, T: Pin> ToggleableOutputPin for Flex<'d, T> { | ||
| 768 | type Error = Infallible; | ||
| 769 | #[inline] | ||
| 770 | fn toggle(&mut self) -> Result<(), Self::Error> { | ||
| 771 | Ok(self.toggle()) | ||
| 772 | } | ||
| 773 | } | ||
| 695 | } | 774 | } |
| 696 | 775 | ||
| 697 | #[cfg(feature = "unstable-traits")] | 776 | #[cfg(feature = "unstable-traits")] |
| @@ -788,6 +867,54 @@ mod eh1 { | |||
| 788 | Ok(self.toggle()) | 867 | Ok(self.toggle()) |
| 789 | } | 868 | } |
| 790 | } | 869 | } |
| 870 | |||
| 871 | impl<'d, T: Pin> InputPin for Flex<'d, T> { | ||
| 872 | #[inline] | ||
| 873 | fn is_high(&self) -> Result<bool, Self::Error> { | ||
| 874 | Ok(self.is_high()) | ||
| 875 | } | ||
| 876 | |||
| 877 | #[inline] | ||
| 878 | fn is_low(&self) -> Result<bool, Self::Error> { | ||
| 879 | Ok(self.is_low()) | ||
| 880 | } | ||
| 881 | } | ||
| 882 | |||
| 883 | impl<'d, T: Pin> OutputPin for Flex<'d, T> { | ||
| 884 | #[inline] | ||
| 885 | fn set_high(&mut self) -> Result<(), Self::Error> { | ||
| 886 | Ok(self.set_high()) | ||
| 887 | } | ||
| 888 | |||
| 889 | #[inline] | ||
| 890 | fn set_low(&mut self) -> Result<(), Self::Error> { | ||
| 891 | Ok(self.set_low()) | ||
| 892 | } | ||
| 893 | } | ||
| 894 | |||
| 895 | impl<'d, T: Pin> ToggleableOutputPin for Flex<'d, T> { | ||
| 896 | #[inline] | ||
| 897 | fn toggle(&mut self) -> Result<(), Self::Error> { | ||
| 898 | Ok(self.toggle()) | ||
| 899 | } | ||
| 900 | } | ||
| 901 | |||
| 902 | impl<'d, T: Pin> ErrorType for Flex<'d, T> { | ||
| 903 | type Error = Infallible; | ||
| 904 | } | ||
| 905 | |||
| 906 | impl<'d, T: Pin> StatefulOutputPin for Flex<'d, T> { | ||
| 907 | #[inline] | ||
| 908 | fn is_set_high(&self) -> Result<bool, Self::Error> { | ||
| 909 | Ok(self.is_set_high()) | ||
| 910 | } | ||
| 911 | |||
| 912 | /// Is the output pin set as low? | ||
| 913 | #[inline] | ||
| 914 | fn is_set_low(&self) -> Result<bool, Self::Error> { | ||
| 915 | Ok(self.is_set_low()) | ||
| 916 | } | ||
| 917 | } | ||
| 791 | } | 918 | } |
| 792 | 919 | ||
| 793 | #[cfg(feature = "unstable-pac")] | 920 | #[cfg(feature = "unstable-pac")] |
diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index c7991953f..37cfe7cf4 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | mod example_common; | 6 | mod example_common; |
| 7 | use defmt::assert; | 7 | use defmt::assert; |
| 8 | use embassy::executor::Spawner; | 8 | use embassy::executor::Spawner; |
| 9 | use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; | 9 | use embassy_stm32::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull, Speed}; |
| 10 | use embassy_stm32::Peripherals; | 10 | use embassy_stm32::Peripherals; |
| 11 | use example_common::*; | 11 | use example_common::*; |
| 12 | 12 | ||
| @@ -88,6 +88,110 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 88 | assert!(b.is_high()); | 88 | assert!(b.is_high()); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | // Test output open drain | ||
| 92 | { | ||
| 93 | let b = Input::new(&mut b, Pull::Down); | ||
| 94 | // no pull, the status is undefined | ||
| 95 | |||
| 96 | let mut a = OutputOpenDrain::new(&mut a, Level::Low, Speed::Low, Pull::None); | ||
| 97 | delay(); | ||
| 98 | assert!(b.is_low()); | ||
| 99 | a.set_high(); // High-Z output | ||
| 100 | delay(); | ||
| 101 | assert!(b.is_low()); | ||
| 102 | } | ||
| 103 | |||
| 104 | // FLEX | ||
| 105 | // Test initial output | ||
| 106 | { | ||
| 107 | //Flex pin configured as input | ||
| 108 | let mut b = Flex::new(&mut b); | ||
| 109 | b.set_as_input(Pull::None); | ||
| 110 | |||
| 111 | { | ||
| 112 | //Flex pin configured as output | ||
| 113 | let mut a = Flex::new(&mut a); //Flex pin configured as output | ||
| 114 | a.set_low(); // Pin state must be set before configuring the pin, thus we avoid unknown state | ||
| 115 | a.set_as_output(Speed::Low); | ||
| 116 | delay(); | ||
| 117 | assert!(b.is_low()); | ||
| 118 | } | ||
| 119 | { | ||
| 120 | //Flex pin configured as output | ||
| 121 | let mut a = Flex::new(&mut a); | ||
| 122 | a.set_high(); | ||
| 123 | a.set_as_output(Speed::Low); | ||
| 124 | |||
| 125 | delay(); | ||
| 126 | assert!(b.is_high()); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | // Test input no pull | ||
| 131 | { | ||
| 132 | let mut b = Flex::new(&mut b); | ||
| 133 | b.set_as_input(Pull::None); // no pull, the status is undefined | ||
| 134 | |||
| 135 | let mut a = Flex::new(&mut a); | ||
| 136 | a.set_low(); | ||
| 137 | a.set_as_output(Speed::Low); | ||
| 138 | |||
| 139 | delay(); | ||
| 140 | assert!(b.is_low()); | ||
| 141 | a.set_high(); | ||
| 142 | delay(); | ||
| 143 | assert!(b.is_high()); | ||
| 144 | } | ||
| 145 | |||
| 146 | // Test input pulldown | ||
| 147 | { | ||
| 148 | let mut b = Flex::new(&mut b); | ||
| 149 | b.set_as_input(Pull::Down); | ||
| 150 | delay(); | ||
| 151 | assert!(b.is_low()); | ||
| 152 | |||
| 153 | let mut a = Flex::new(&mut a); | ||
| 154 | a.set_low(); | ||
| 155 | a.set_as_output(Speed::Low); | ||
| 156 | delay(); | ||
| 157 | assert!(b.is_low()); | ||
| 158 | a.set_high(); | ||
| 159 | delay(); | ||
| 160 | assert!(b.is_high()); | ||
| 161 | } | ||
| 162 | |||
| 163 | // Test input pullup | ||
| 164 | { | ||
| 165 | let mut b = Flex::new(&mut b); | ||
| 166 | b.set_as_input(Pull::Up); | ||
| 167 | delay(); | ||
| 168 | assert!(b.is_high()); | ||
| 169 | |||
| 170 | let mut a = Flex::new(&mut a); | ||
| 171 | a.set_high(); | ||
| 172 | a.set_as_output(Speed::Low); | ||
| 173 | delay(); | ||
| 174 | assert!(b.is_high()); | ||
| 175 | a.set_low(); | ||
| 176 | delay(); | ||
| 177 | assert!(b.is_low()); | ||
| 178 | } | ||
| 179 | |||
| 180 | // Test output open drain | ||
| 181 | { | ||
| 182 | let mut b = Flex::new(&mut b); | ||
| 183 | b.set_as_input(Pull::Down); | ||
| 184 | |||
| 185 | let mut a = Flex::new(&mut a); | ||
| 186 | a.set_low(); | ||
| 187 | a.set_as_input_output(Speed::Low, Pull::None); | ||
| 188 | delay(); | ||
| 189 | assert!(b.is_low()); | ||
| 190 | a.set_high(); // High-Z output | ||
| 191 | delay(); | ||
| 192 | assert!(b.is_low()); | ||
| 193 | } | ||
| 194 | |||
| 91 | info!("Test OK"); | 195 | info!("Test OK"); |
| 92 | cortex_m::asm::bkpt(); | 196 | cortex_m::asm::bkpt(); |
| 93 | } | 197 | } |
