diff options
| -rw-r--r-- | embassy-rp/src/gpio.rs | 126 |
1 files changed, 43 insertions, 83 deletions
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 98e182868..7a86418aa 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs | |||
| @@ -136,18 +136,6 @@ pub enum InterruptTrigger { | |||
| 136 | AnyEdge, | 136 | AnyEdge, |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | impl InterruptTrigger { | ||
| 140 | fn from_u32(value: u32) -> Option<InterruptTrigger> { | ||
| 141 | match value { | ||
| 142 | 1 => Some(InterruptTrigger::LevelLow), | ||
| 143 | 2 => Some(InterruptTrigger::LevelHigh), | ||
| 144 | 3 => Some(InterruptTrigger::EdgeLow), | ||
| 145 | 4 => Some(InterruptTrigger::EdgeHigh), | ||
| 146 | _ => None, | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | #[interrupt] | 139 | #[interrupt] |
| 152 | unsafe fn IO_IRQ_BANK0() { | 140 | unsafe fn IO_IRQ_BANK0() { |
| 153 | let cpu = SIO.cpuid().read() as usize; | 141 | let cpu = SIO.cpuid().read() as usize; |
| @@ -166,26 +154,16 @@ unsafe fn IO_IRQ_BANK0() { | |||
| 166 | let pin_group = (pin % 8) as usize; | 154 | let pin_group = (pin % 8) as usize; |
| 167 | let event = (intsx.read().0 >> pin_group * 4) & 0xf as u32; | 155 | let event = (intsx.read().0 >> pin_group * 4) & 0xf as u32; |
| 168 | 156 | ||
| 169 | if let Some(trigger) = InterruptTrigger::from_u32(event) { | 157 | // no more than one event can be awaited per pin at any given time, so |
| 158 | // we can just clear all interrupt enables for that pin without having | ||
| 159 | // to check which event was signalled. | ||
| 160 | if event != 0 { | ||
| 170 | critical_section::with(|_| { | 161 | critical_section::with(|_| { |
| 171 | proc_intx.inte(pin / 8).modify(|w| match trigger { | 162 | proc_intx.inte(pin / 8).modify(|w| { |
| 172 | InterruptTrigger::AnyEdge => { | 163 | w.set_edge_high(pin_group, false); |
| 173 | w.set_edge_high(pin_group, false); | 164 | w.set_edge_low(pin_group, false); |
| 174 | w.set_edge_low(pin_group, false); | 165 | w.set_level_high(pin_group, false); |
| 175 | } | 166 | w.set_level_low(pin_group, false); |
| 176 | InterruptTrigger::LevelHigh => { | ||
| 177 | trace!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); | ||
| 178 | w.set_level_high(pin_group, false); | ||
| 179 | } | ||
| 180 | InterruptTrigger::LevelLow => { | ||
| 181 | w.set_level_low(pin_group, false); | ||
| 182 | } | ||
| 183 | InterruptTrigger::EdgeHigh => { | ||
| 184 | w.set_edge_high(pin_group, false); | ||
| 185 | } | ||
| 186 | InterruptTrigger::EdgeLow => { | ||
| 187 | w.set_edge_low(pin_group, false); | ||
| 188 | } | ||
| 189 | }); | 167 | }); |
| 190 | }); | 168 | }); |
| 191 | INTERRUPT_WAKERS[pin as usize].wake(); | 169 | INTERRUPT_WAKERS[pin as usize].wake(); |
| @@ -207,10 +185,23 @@ impl<'d, T: Pin> InputFuture<'d, T> { | |||
| 207 | irq.disable(); | 185 | irq.disable(); |
| 208 | irq.set_priority(interrupt::Priority::P3); | 186 | irq.set_priority(interrupt::Priority::P3); |
| 209 | 187 | ||
| 188 | let pin_group = (pin.pin() % 8) as usize; | ||
| 189 | // first, clear the INTR register bits. without this INTR will still | ||
| 190 | // contain reports of previous edges, causing the IRQ to fire early | ||
| 191 | // on stale state. clearing these means that we can only detect edges | ||
| 192 | // that occur *after* the clear happened, but since both this and the | ||
| 193 | // alternative are fundamentally racy it's probably fine. | ||
| 194 | // (the alternative being checking the current level and waiting for | ||
| 195 | // its inverse, but that requires reading the current level and thus | ||
| 196 | // missing anything that happened before the level was read.) | ||
| 197 | pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { | ||
| 198 | w.set_edge_high(pin_group, true); | ||
| 199 | w.set_edge_low(pin_group, true); | ||
| 200 | }); | ||
| 201 | |||
| 210 | // Each INTR register is divided into 8 groups, one group for each | 202 | // Each INTR register is divided into 8 groups, one group for each |
| 211 | // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, | 203 | // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, |
| 212 | // and EGDE_HIGH. | 204 | // and EGDE_HIGH. |
| 213 | let pin_group = (pin.pin() % 8) as usize; | ||
| 214 | critical_section::with(|_| { | 205 | critical_section::with(|_| { |
| 215 | pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { | 206 | pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { |
| 216 | InterruptTrigger::LevelHigh => { | 207 | InterruptTrigger::LevelHigh => { |
| @@ -227,7 +218,8 @@ impl<'d, T: Pin> InputFuture<'d, T> { | |||
| 227 | w.set_edge_low(pin_group, true); | 218 | w.set_edge_low(pin_group, true); |
| 228 | } | 219 | } |
| 229 | InterruptTrigger::AnyEdge => { | 220 | InterruptTrigger::AnyEdge => { |
| 230 | // noop | 221 | w.set_edge_high(pin_group, true); |
| 222 | w.set_edge_low(pin_group, true); | ||
| 231 | } | 223 | } |
| 232 | }); | 224 | }); |
| 233 | }); | 225 | }); |
| @@ -257,47 +249,21 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { | |||
| 257 | // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin. | 249 | // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin. |
| 258 | let pin_group = (self.pin.pin() % 8) as usize; | 250 | let pin_group = (self.pin.pin() % 8) as usize; |
| 259 | 251 | ||
| 260 | // This should check the the level of the interrupt trigger level of | 252 | // since the interrupt handler clears all INTE flags we'll check that |
| 261 | // the pin and if it has been disabled that means it was done by the | 253 | // all have been cleared and unconditionally return Ready(()) if so. |
| 262 | // interrupt service routine, so we then know that the event/trigger | 254 | // we don't need further handshaking since only a single event wait |
| 263 | // happened and Poll::Ready will be returned. | 255 | // is possible for any given pin at any given time. |
| 264 | trace!("{:?} for pin {}", self.level, self.pin.pin()); | 256 | if !inte.edge_high(pin_group) |
| 265 | match self.level { | 257 | && !inte.edge_low(pin_group) |
| 266 | InterruptTrigger::AnyEdge => { | 258 | && !inte.level_high(pin_group) |
| 267 | if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { | 259 | && !inte.level_low(pin_group) |
| 268 | #[rustfmt::skip] | 260 | { |
| 269 | trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); | 261 | trace!( |
| 270 | return Poll::Ready(()); | 262 | "{:?} for pin {} was cleared, return Poll::Ready", |
| 271 | } | 263 | self.level, |
| 272 | } | 264 | self.pin.pin() |
| 273 | InterruptTrigger::LevelHigh => { | 265 | ); |
| 274 | if !inte.level_high(pin_group) { | 266 | return Poll::Ready(()); |
| 275 | #[rustfmt::skip] | ||
| 276 | trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); | ||
| 277 | return Poll::Ready(()); | ||
| 278 | } | ||
| 279 | } | ||
| 280 | InterruptTrigger::LevelLow => { | ||
| 281 | if !inte.level_low(pin_group) { | ||
| 282 | #[rustfmt::skip] | ||
| 283 | trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); | ||
| 284 | return Poll::Ready(()); | ||
| 285 | } | ||
| 286 | } | ||
| 287 | InterruptTrigger::EdgeHigh => { | ||
| 288 | if !inte.edge_high(pin_group) { | ||
| 289 | #[rustfmt::skip] | ||
| 290 | trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); | ||
| 291 | return Poll::Ready(()); | ||
| 292 | } | ||
| 293 | } | ||
| 294 | InterruptTrigger::EdgeLow => { | ||
| 295 | if !inte.edge_low(pin_group) { | ||
| 296 | #[rustfmt::skip] | ||
| 297 | trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); | ||
| 298 | return Poll::Ready(()); | ||
| 299 | } | ||
| 300 | } | ||
| 301 | } | 267 | } |
| 302 | trace!("InputFuture::poll return Poll::Pending"); | 268 | trace!("InputFuture::poll return Poll::Pending"); |
| 303 | Poll::Pending | 269 | Poll::Pending |
| @@ -644,23 +610,17 @@ impl<'d, T: Pin> Flex<'d, T> { | |||
| 644 | 610 | ||
| 645 | #[inline] | 611 | #[inline] |
| 646 | pub async fn wait_for_rising_edge(&mut self) { | 612 | pub async fn wait_for_rising_edge(&mut self) { |
| 647 | self.wait_for_low().await; | 613 | InputFuture::new(&mut self.pin, InterruptTrigger::EdgeHigh).await; |
| 648 | self.wait_for_high().await; | ||
| 649 | } | 614 | } |
| 650 | 615 | ||
| 651 | #[inline] | 616 | #[inline] |
| 652 | pub async fn wait_for_falling_edge(&mut self) { | 617 | pub async fn wait_for_falling_edge(&mut self) { |
| 653 | self.wait_for_high().await; | 618 | InputFuture::new(&mut self.pin, InterruptTrigger::EdgeLow).await; |
| 654 | self.wait_for_low().await; | ||
| 655 | } | 619 | } |
| 656 | 620 | ||
| 657 | #[inline] | 621 | #[inline] |
| 658 | pub async fn wait_for_any_edge(&mut self) { | 622 | pub async fn wait_for_any_edge(&mut self) { |
| 659 | if self.is_high() { | 623 | InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; |
| 660 | self.wait_for_low().await; | ||
| 661 | } else { | ||
| 662 | self.wait_for_high().await; | ||
| 663 | } | ||
| 664 | } | 624 | } |
| 665 | } | 625 | } |
| 666 | 626 | ||
