Skip to content

Commit 31b25b0

Browse files
committed
Merge pull request 'Adaption Async GPIO' (#59) from adaption-gpio-asynch into main
Reviewed-on: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/pulls/59
2 parents a65f403 + 8b55d09 commit 31b25b0

File tree

9 files changed

+153
-111
lines changed

9 files changed

+153
-111
lines changed

examples/embassy/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ embassy-executor = { version = "0.7", features = [
2727
"executor-interrupt"
2828
]}
2929

30-
va108xx-hal = "0.9"
31-
va108xx-embassy = "0.1"
30+
va108xx-hal = { version = "0.9", path = "../../va108xx-hal" }
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: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ 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_for_port, InputDynPinAsync, InputPinAsync, PinsB, Port,
19+
};
1820
use va108xx_hal::{
1921
gpio::{DynPin, PinsA},
2022
pac::{self, interrupt},
@@ -244,15 +246,16 @@ async fn output_task(
244246
}
245247

246248
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
247-
248249
#[interrupt]
249250
#[allow(non_snake_case)]
250251
fn OC10() {
251-
on_interrupt_for_asynch_gpio();
252+
on_interrupt_for_async_gpio_for_port(Port::A);
253+
on_interrupt_for_async_gpio_for_port(Port::B);
252254
}
253255

256+
// This interrupt only handles PORT B interrupts.
254257
#[interrupt]
255258
#[allow(non_snake_case)]
256259
fn OC11() {
257-
on_interrupt_for_asynch_gpio();
260+
on_interrupt_for_async_gpio_for_port(Port::B);
258261
}

va108xx-embassy/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ embassy-time-queue-utils = "0.1"
2020

2121
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
2222

23-
va108xx-hal = "0.9"
23+
va108xx-hal = { version = "0.9", path = "../va108xx-hal" }
2424

2525
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
2626
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }

va108xx-hal/CHANGELOG.md

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

1919
- Missing GPIO API replacements from `x` to `configure_x`
20+
- Renamed GPIO `DynGroup` to `Port`
21+
- Rename generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio`
22+
into `on_interrupt_for_async_gpio_for_port` which expects a Port argument
23+
24+
## Fixed
25+
26+
- Bug in async GPIO interrupt handler where all enabled interrupts, even the ones which might
27+
be unrelated to the pin, were disabled.
2028

2129
## [v0.9.0]
2230

va108xx-hal/src/gpio/asynch.rs

Lines changed: 83 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
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 the
7+
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
8+
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
99
//!
1010
//! # Example
1111
//!
@@ -21,60 +21,66 @@ use va108xx::{self as pac, Irqsel, Sysconfig};
2121
use crate::InterruptConfig;
2222

2323
use super::{
24-
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
25-
NUM_GPIO_PINS, NUM_PINS_PORT_A,
24+
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
25+
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
2626
};
2727

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];
28+
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
29+
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
30+
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
31+
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
32+
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
33+
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
34+
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
35+
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
3136

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-
}
39-
40-
/// Generic interrupt handler for GPIO interrupts to support the async functionalities.
37+
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
4138
///
42-
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
43-
/// as well as updating the static edge detection structures. This allows the pin future to
44-
/// complete async operations. The user should call this function in ALL interrupt handlers
45-
/// which handle any GPIO interrupts.
46-
#[inline]
47-
pub fn on_interrupt_for_asynch_gpio() {
39+
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
40+
/// matching the [Port] argument.
41+
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
42+
/// as well as update the static edge detection structures. This allows the pin future tocomplete
43+
/// complete async operations.
44+
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
4845
let periphs = unsafe { pac::Peripherals::steal() };
4946

50-
handle_interrupt_for_gpio_and_port(
51-
periphs.porta.irq_enb().read().bits(),
52-
periphs.porta.edge_status().read().bits(),
53-
0,
54-
);
55-
handle_interrupt_for_gpio_and_port(
56-
periphs.portb.irq_enb().read().bits(),
57-
periphs.portb.edge_status().read().bits(),
58-
NUM_PINS_PORT_A,
59-
);
47+
let (irq_enb, edge_status, wakers, edge_detection) = match port {
48+
Port::A => (
49+
periphs.porta.irq_enb().read().bits(),
50+
periphs.porta.edge_status().read().bits(),
51+
WAKERS_FOR_PORT_A.as_ref(),
52+
EDGE_DETECTION_PORT_A.as_ref(),
53+
),
54+
Port::B => (
55+
periphs.portb.irq_enb().read().bits(),
56+
periphs.portb.edge_status().read().bits(),
57+
WAKERS_FOR_PORT_B.as_ref(),
58+
EDGE_DETECTION_PORT_B.as_ref(),
59+
),
60+
};
61+
62+
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
6063
}
6164

62-
// Uses the enabled interrupt register and the persistent edge status to capture all GPIO events.
6365
#[inline]
64-
fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_base_offset: usize) {
66+
fn on_interrupt_for_port(
67+
mut irq_enb: u32,
68+
edge_status: u32,
69+
wakers: &'static [AtomicWaker],
70+
edge_detection: &'static [AtomicBool],
71+
) {
6572
while irq_enb != 0 {
6673
let bit_pos = irq_enb.trailing_zeros() as usize;
6774
let bit_mask = 1 << bit_pos;
6875

69-
WAKERS[pin_base_offset + bit_pos].wake();
76+
wakers[bit_pos].wake();
7077

7178
if edge_status & bit_mask != 0 {
72-
EDGE_DETECTION[pin_base_offset + bit_pos]
73-
.store(true, core::sync::atomic::Ordering::Relaxed);
74-
}
79+
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
7580

76-
// Clear the processed bit
77-
irq_enb &= !bit_mask;
81+
// Clear the processed bit
82+
irq_enb &= !bit_mask;
83+
}
7884
}
7985
}
8086

@@ -85,6 +91,8 @@ fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_ba
8591
/// struture is granted to allow writing custom async structures.
8692
pub struct InputPinFuture {
8793
pin_id: DynPinId,
94+
waker_group: &'static [AtomicWaker],
95+
edge_detection_group: &'static [AtomicBool],
8896
}
8997

9098
impl InputPinFuture {
@@ -102,6 +110,16 @@ impl InputPinFuture {
102110
Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
103111
}
104112

113+
#[inline]
114+
pub fn pin_group_to_waker_and_edge_detection_group(
115+
group: Port,
116+
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
117+
match group {
118+
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
119+
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
120+
}
121+
}
122+
105123
pub fn new_with_dyn_pin(
106124
pin: &mut DynPin,
107125
irq: pac::Interrupt,
@@ -113,7 +131,9 @@ impl InputPinFuture {
113131
return Err(InvalidPinTypeError(pin.mode()));
114132
}
115133

116-
EDGE_DETECTION[pin_id_to_offset(pin.id())]
134+
let (waker_group, edge_detection_group) =
135+
Self::pin_group_to_waker_and_edge_detection_group(pin.id().group);
136+
edge_detection_group[pin.id().num as usize]
117137
.store(false, core::sync::atomic::Ordering::Relaxed);
118138
pin.configure_edge_interrupt(
119139
edge,
@@ -122,7 +142,11 @@ impl InputPinFuture {
122142
Some(irq_sel),
123143
)
124144
.unwrap();
125-
Ok(Self { pin_id: pin.id() })
145+
Ok(Self {
146+
pin_id: pin.id(),
147+
waker_group,
148+
edge_detection_group,
149+
})
126150
}
127151

128152
/// # Safety
@@ -146,22 +170,28 @@ impl InputPinFuture {
146170
sys_cfg: &mut Sysconfig,
147171
irq_sel: &mut Irqsel,
148172
) -> Self {
149-
EDGE_DETECTION[pin_id_to_offset(pin.id())]
173+
let (waker_group, edge_detection_group) =
174+
Self::pin_group_to_waker_and_edge_detection_group(pin.id().group);
175+
edge_detection_group[pin.id().num as usize]
150176
.store(false, core::sync::atomic::Ordering::Relaxed);
151177
pin.configure_edge_interrupt(
152178
edge,
153179
InterruptConfig::new(irq, true, true),
154180
Some(sys_cfg),
155181
Some(irq_sel),
156182
);
157-
Self { pin_id: pin.id() }
183+
Self {
184+
pin_id: pin.id(),
185+
edge_detection_group,
186+
waker_group,
187+
}
158188
}
159189
}
160190

161191
impl Drop for InputPinFuture {
162192
fn drop(&mut self) {
163193
let periphs = unsafe { pac::Peripherals::steal() };
164-
if self.pin_id.group == DynGroup::A {
194+
if self.pin_id.group == Port::A {
165195
periphs
166196
.porta
167197
.irq_enb()
@@ -181,9 +211,9 @@ impl Future for InputPinFuture {
181211
self: core::pin::Pin<&mut Self>,
182212
cx: &mut core::task::Context<'_>,
183213
) -> 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) {
214+
let idx = self.pin_id.num as usize;
215+
self.waker_group[idx].register(cx.waker());
216+
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
187217
return core::task::Poll::Ready(());
188218
}
189219
core::task::Poll::Pending
@@ -200,8 +230,8 @@ impl InputDynPinAsync {
200230
/// passed as well and is used to route and enable the interrupt.
201231
///
202232
/// 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.
233+
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
234+
/// for the asynchronous functionality to work.
205235
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
206236
if !pin.is_input_pin() {
207237
return Err(InvalidPinTypeError(pin.mode()));
@@ -335,8 +365,8 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
335365
/// passed as well and is used to route and enable the interrupt.
336366
///
337367
/// 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.
368+
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
369+
/// for the asynchronous functionality to work.
340370
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
341371
Self { pin, irq }
342372
}

va108xx-hal/src/gpio/dynpin.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@
5757
//! [InvalidPinTypeError].
5858
5959
use super::{
60-
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
60+
pin::{FilterType, Pin, PinId, PinMode},
6161
reg::RegisterInterface,
62-
InputDynPinAsync,
62+
InputDynPinAsync, InterruptEdge, InterruptLevel, PinState,
6363
};
6464
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
6565

@@ -156,19 +156,13 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
156156
// DynGroup & DynPinId
157157
//==================================================================================================
158158

159-
/// Value-level `enum` for pin groups
160-
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
161-
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
162-
pub enum DynGroup {
163-
A,
164-
B,
165-
}
159+
pub type DynGroup = super::Port;
166160

167161
/// Value-level `struct` representing pin IDs
168162
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
169163
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
170164
pub struct DynPinId {
171-
pub group: DynGroup,
165+
pub group: super::Port,
172166
pub num: u8,
173167
}
174168

@@ -369,12 +363,12 @@ impl DynPin {
369363
if irq_cfg.route {
370364
match self.regs.id().group {
371365
// Set the correct interrupt number in the IRQSEL register
372-
DynGroup::A => {
366+
super::Port::A => {
373367
irqsel
374368
.porta0(self.regs.id().num as usize)
375369
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
376370
}
377-
DynGroup::B => {
371+
super::Port::B => {
378372
irqsel
379373
.portb0(self.regs.id().num as usize)
380374
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });

0 commit comments

Comments
 (0)