Skip to content

Commit 491585c

Browse files
committed
Bugfix and improvements for async GPIO
1 parent d9e1994 commit 491585c

File tree

4 files changed

+99
-39
lines changed

4 files changed

+99
-39
lines changed

examples/embassy/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ embassy-executor = { version = "0.7", features = [
2828
]}
2929

3030
va108xx-hal = { version = "0.9", path = "../../va108xx-hal" }
31-
va108xx-embassy = "0.1"
31+
va108xx-embassy = { version = "0.1", path = "../../va108xx-embassy" }
3232

3333
[features]
3434
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]

examples/embassy/src/bin/async-gpio.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ use embedded_hal_async::digital::Wait;
1414
use panic_rtt_target as _;
1515
use rtt_target::{rprintln, rtt_init_print};
1616
use va108xx_embassy::embassy;
17-
use va108xx_hal::gpio::{on_interrupt_for_asynch_gpio, InputDynPinAsync, InputPinAsync, PinsB};
17+
use va108xx_hal::gpio::{
18+
on_interrupt_for_async_gpio_port_a, on_interrupt_for_async_gpio_port_b, InputDynPinAsync,
19+
InputPinAsync, PinsB,
20+
};
1821
use va108xx_hal::{
1922
gpio::{DynPin, PinsA},
2023
pac::{self, interrupt},
@@ -244,15 +247,16 @@ async fn output_task(
244247
}
245248

246249
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
247-
248250
#[interrupt]
249251
#[allow(non_snake_case)]
250252
fn OC10() {
251-
on_interrupt_for_asynch_gpio();
253+
on_interrupt_for_async_gpio_port_a();
254+
on_interrupt_for_async_gpio_port_b();
252255
}
253256

257+
// This interrupt only handles PORT B interrupts.
254258
#[interrupt]
255259
#[allow(non_snake_case)]
256260
fn OC11() {
257-
on_interrupt_for_asynch_gpio();
261+
on_interrupt_for_async_gpio_port_b();
258262
}

va108xx-hal/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1717
## Changed
1818

1919
- Missing GPIO API replacements from `x` to `configure_x`
20+
- Split up generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio`
21+
into port specific variants.
22+
23+
## Fixed
24+
25+
- Bug in async GPIO interrupt handler where all enabled interrupts, even the ones which might
26+
be unrelated to the pin, were disabled.
2027

2128
## [v0.9.0]
2229

va108xx-hal/src/gpio/asynch.rs

Lines changed: 83 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
44
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
55
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
6-
//! which must be provided for async support to work. However, it provides one generic
7-
//! [handler][on_interrupt_for_asynch_gpio] which should be called in ALL user interrupt handlers
8-
//! which handle GPIO interrupts.
6+
//! which must be provided for async support to work. However, it provides two generic interrupt
7+
//! handlers:
8+
//!
9+
//! - [on_interrupt_for_async_gpio_port_a]
10+
//! - [on_interrupt_for_async_gpio_port_b]
11+
//!
12+
//! Those should be called in the interrupt handlers which handle GPIO interrupts for port A
13+
//! and/or port B.
914
//!
1015
//! # Example
1116
//!
@@ -22,55 +27,76 @@ use crate::InterruptConfig;
2227

2328
use super::{
2429
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
25-
NUM_GPIO_PINS, NUM_PINS_PORT_A,
30+
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
2631
};
2732

28-
static WAKERS: [AtomicWaker; NUM_GPIO_PINS] = [const { AtomicWaker::new() }; NUM_GPIO_PINS];
29-
static EDGE_DETECTION: [AtomicBool; NUM_GPIO_PINS] =
30-
[const { AtomicBool::new(false) }; NUM_GPIO_PINS];
31-
32-
#[inline]
33-
fn pin_id_to_offset(dyn_pin_id: DynPinId) -> usize {
34-
match dyn_pin_id.group {
35-
DynGroup::A => dyn_pin_id.num as usize,
36-
DynGroup::B => NUM_PINS_PORT_A + dyn_pin_id.num as usize,
37-
}
38-
}
33+
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
34+
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
35+
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
36+
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
37+
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
38+
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
39+
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
40+
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
3941

40-
/// Generic interrupt handler for GPIO interrupts to support the async functionalities.
42+
/// Generic interrupt handler for GPIO interrupts on PORT A to support the async functionalities.
43+
///
44+
/// This function should be called in all interrupt hanflers which handle any PORT A GPIO
45+
/// interrupts.
4146
///
4247
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
4348
/// as well as updating the static edge detection structures. This allows the pin future to
4449
/// complete async operations. The user should call this function in ALL interrupt handlers
4550
/// which handle any GPIO interrupts.
4651
#[inline]
47-
pub fn on_interrupt_for_asynch_gpio() {
52+
pub fn on_interrupt_for_async_gpio_port_a() {
4853
let periphs = unsafe { pac::Peripherals::steal() };
4954

5055
handle_interrupt_for_gpio_and_port(
5156
periphs.porta.irq_enb().read().bits(),
5257
periphs.porta.edge_status().read().bits(),
53-
0,
58+
&WAKERS_FOR_PORT_A,
59+
&EDGE_DETECTION_PORT_A,
5460
);
61+
}
62+
63+
/// Generic interrupt handler for GPIO interrupts on PORT B to support the async functionalities.
64+
///
65+
/// This function should be called in all interrupt hanflers which handle any PORT B GPIO
66+
/// interrupts.
67+
///
68+
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
69+
/// as well as updating the static edge detection structures. This allows the pin future to
70+
/// complete async operations. The user should call this function in ALL interrupt handlers
71+
/// which handle any GPIO interrupts.
72+
#[inline]
73+
pub fn on_interrupt_for_async_gpio_port_b() {
74+
let periphs = unsafe { pac::Peripherals::steal() };
75+
5576
handle_interrupt_for_gpio_and_port(
5677
periphs.portb.irq_enb().read().bits(),
5778
periphs.portb.edge_status().read().bits(),
58-
NUM_PINS_PORT_A,
79+
&WAKERS_FOR_PORT_B,
80+
&EDGE_DETECTION_PORT_B,
5981
);
6082
}
6183

6284
// Uses the enabled interrupt register and the persistent edge status to capture all GPIO events.
6385
#[inline]
64-
fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_base_offset: usize) {
86+
fn handle_interrupt_for_gpio_and_port(
87+
mut irq_enb: u32,
88+
edge_status: u32,
89+
wakers: &'static [AtomicWaker],
90+
edge_detection: &'static [AtomicBool],
91+
) {
6592
while irq_enb != 0 {
6693
let bit_pos = irq_enb.trailing_zeros() as usize;
6794
let bit_mask = 1 << bit_pos;
6895

69-
WAKERS[pin_base_offset + bit_pos].wake();
96+
wakers[bit_pos].wake();
7097

7198
if edge_status & bit_mask != 0 {
72-
EDGE_DETECTION[pin_base_offset + bit_pos]
73-
.store(true, core::sync::atomic::Ordering::Relaxed);
99+
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
74100

75101
// Clear the processed bit
76102
irq_enb &= !bit_mask;
@@ -85,6 +111,8 @@ fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_ba
85111
/// struture is granted to allow writing custom async structures.
86112
pub struct InputPinFuture {
87113
pin_id: DynPinId,
114+
waker_group: &'static [AtomicWaker],
115+
edge_detection_group: &'static [AtomicBool],
88116
}
89117

90118
impl InputPinFuture {
@@ -102,6 +130,15 @@ impl InputPinFuture {
102130
Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
103131
}
104132

133+
pub fn pin_group_to_waker_and_edge_detection_group(
134+
group: DynGroup,
135+
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
136+
match group {
137+
DynGroup::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
138+
DynGroup::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
139+
}
140+
}
141+
105142
pub fn new_with_dyn_pin(
106143
pin: &mut DynPin,
107144
irq: pac::Interrupt,
@@ -113,7 +150,9 @@ impl InputPinFuture {
113150
return Err(InvalidPinTypeError(pin.mode()));
114151
}
115152

116-
EDGE_DETECTION[pin_id_to_offset(pin.id())]
153+
let (waker_group, edge_detection_group) =
154+
Self::pin_group_to_waker_and_edge_detection_group(pin.id().group);
155+
edge_detection_group[pin.id().num as usize]
117156
.store(false, core::sync::atomic::Ordering::Relaxed);
118157
pin.configure_edge_interrupt(
119158
edge,
@@ -122,7 +161,11 @@ impl InputPinFuture {
122161
Some(irq_sel),
123162
)
124163
.unwrap();
125-
Ok(Self { pin_id: pin.id() })
164+
Ok(Self {
165+
pin_id: pin.id(),
166+
waker_group,
167+
edge_detection_group,
168+
})
126169
}
127170

128171
/// # Safety
@@ -146,15 +189,21 @@ impl InputPinFuture {
146189
sys_cfg: &mut Sysconfig,
147190
irq_sel: &mut Irqsel,
148191
) -> Self {
149-
EDGE_DETECTION[pin_id_to_offset(pin.id())]
192+
let (waker_group, edge_detection_group) =
193+
Self::pin_group_to_waker_and_edge_detection_group(pin.id().group);
194+
edge_detection_group[pin.id().num as usize]
150195
.store(false, core::sync::atomic::Ordering::Relaxed);
151196
pin.configure_edge_interrupt(
152197
edge,
153198
InterruptConfig::new(irq, true, true),
154199
Some(sys_cfg),
155200
Some(irq_sel),
156201
);
157-
Self { pin_id: pin.id() }
202+
Self {
203+
pin_id: pin.id(),
204+
edge_detection_group,
205+
waker_group,
206+
}
158207
}
159208
}
160209

@@ -181,9 +230,9 @@ impl Future for InputPinFuture {
181230
self: core::pin::Pin<&mut Self>,
182231
cx: &mut core::task::Context<'_>,
183232
) -> core::task::Poll<Self::Output> {
184-
let idx = pin_id_to_offset(self.pin_id);
185-
WAKERS[idx].register(cx.waker());
186-
if EDGE_DETECTION[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
233+
let idx = self.pin_id.num as usize;
234+
self.waker_group[idx].register(cx.waker());
235+
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
187236
return core::task::Poll::Ready(());
188237
}
189238
core::task::Poll::Pending
@@ -200,8 +249,8 @@ impl InputDynPinAsync {
200249
/// passed as well and is used to route and enable the interrupt.
201250
///
202251
/// Please note that the interrupt handler itself must be provided by the user and the
203-
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
204-
/// the asynchronous functionality to work.
252+
/// generic [on_interrupt_for_async_gpio_port_a] or [on_interrupt_for_async_gpio_port_b]
253+
/// function must be called inside that function for the asynchronous functionality to work.
205254
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
206255
if !pin.is_input_pin() {
207256
return Err(InvalidPinTypeError(pin.mode()));
@@ -335,8 +384,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
335384
/// passed as well and is used to route and enable the interrupt.
336385
///
337386
/// Please note that the interrupt handler itself must be provided by the user and the
338-
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
339-
/// the asynchronous functionality to work.
387+
/// generic [on_interrupt_for_async_gpio_port_a] or [on_interrupt_for_async_gpio_port_b]
388+
/// function must be called inside that function for the asynchronous functionality to work.
340389
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
341390
Self { pin, irq }
342391
}

0 commit comments

Comments
 (0)