aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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