aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpennae <[email protected]>2023-05-02 08:29:32 +0200
committerpennae <[email protected]>2023-05-02 08:43:04 +0200
commitc6424fdc112776b5ceeef4a01c56b1479c2901c5 (patch)
tree56fc370244d41b69ccb320d6ccc7e35708a34c47
parent05c36e05f9f6b1a0a36982239b2e7c697f0d3734 (diff)
gp/gpio: fix InputFuture edge waits
InputFuture did not use and check edge interrupts correctly. InterruptTrigger should've checked for not 1,2,3,4 but 1,2,4,8 since the inte fields are bitmasks, and not clearing INTR would have repeatedly triggered edge interrupts early.
-rw-r--r--embassy-rp/src/gpio.rs126
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
139impl 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]
152unsafe fn IO_IRQ_BANK0() { 140unsafe 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