Skip to content

Commit b6583ea

Browse files
authored
Merge pull request #46 from dmunizu/gpio-async
Async GPIO implementation
2 parents cafe827 + d0f8331 commit b6583ea

File tree

22 files changed

+516
-11
lines changed

22 files changed

+516
-11
lines changed

.github/workflows/e310x-hal.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ jobs:
1111
build-riscv:
1212
strategy:
1313
matrix:
14-
# All generated code should be running on stable now, MRSV is 1.76.0
15-
toolchain: [ stable, nightly, 1.76.0 ]
14+
# All generated code should be running on stable now, MRSV is 1.79.0
15+
toolchain: [ stable, nightly, 1.79.0 ]
1616
include:
1717
# Nightly is only for reference and allowed to fail
1818
- toolchain: nightly
@@ -47,4 +47,4 @@ jobs:
4747
run: cargo test --package e310x-hal
4848
- name: Build (all features)
4949
run: cargo test --package e310x-hal --all-features
50-
50+

.github/workflows/hifive1.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ jobs:
1111
build-riscv:
1212
strategy:
1313
matrix:
14-
# All generated code should be running on stable now, MRSV is 1.76.0
15-
toolchain: [nightly, stable, 1.76.0]
14+
# All generated code should be running on stable now, MRSV is 1.79.0
15+
toolchain: [nightly, stable, 1.79.0]
1616
board: [hifive1, hifive1-revb, redv, lofive, lofive-r1]
1717
include:
1818
# Nightly is only for reference and allowed to fail
@@ -49,4 +49,4 @@ jobs:
4949
run: cargo test --package hifive1 --features board-${{ matrix.board }}
5050
- name: Build (vectored)
5151
run: cargo test --package hifive1 --features board-${{ matrix.board }},v-trap
52-
52+

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"e310x-hal",
66
"hifive1",
77
"hifive1-examples",
8+
"hifive1-async-examples",
89
]
910
default-members = [
1011
"e310x",

e310x-hal/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Changed
1111
- Update `e310x` dependency and adapt code
1212
- Add interrupt managing methods to `e310x-hal::gpio` module
13+
- Add embedded-hal-async digital module support to `e310x-hal::gpio` module
1314

1415
## [v0.12.0] - 2024-12-10
1516

e310x-hal/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ description = "HAL for the E310x family of microcontrollers."
88
keywords = ["riscv", "e310", "hal"]
99
license = "ISC"
1010
edition = "2021"
11-
rust-version = "1.76"
11+
rust-version = "1.79"
1212

1313
[dependencies]
1414
embedded-hal = "1.0.0"
@@ -19,9 +19,15 @@ nb = "1.0.0"
1919
portable-atomic = { version = "1.9", default-features = false }
2020
riscv = { workspace = true, features = ["critical-section-single-hart"] }
2121

22+
# Async HAL dependencies
23+
riscv-rt = { workspace = true, optional = true }
24+
embedded-hal-async = { version = "1.0.0", optional = true }
25+
critical-section = { workspace = true, optional = true }
26+
2227
[features]
2328
g002 = ["e310x/g002"]
2429
v-trap = ["e310x/v-trap"]
30+
async = ["riscv-rt", "embedded-hal-async", "critical-section"]
2531

2632
[package.metadata.docs.rs]
2733
features = ["g002"]

e310x-hal/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This project is developed and maintained by the [RISC-V team][team].
1212

1313
## Minimum Supported Rust Version (MSRV)
1414

15-
This crate is guaranteed to compile on stable Rust 1.72.0 and up. It *might*
15+
This crate is guaranteed to compile on stable Rust 1.79.0 and up. It *might*
1616
compile with older versions but that may change in any new patch release.
1717

1818
## License

e310x-hal/src/asynch.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Asynchronous HAL for the E310x family of microcontrollers
2+
//!
3+
//! This is an implementation of the [`embedded-hal-async`] traits for the E310x
4+
//! family of microcontrollers.
5+
6+
#![deny(missing_docs)]
7+
8+
pub mod digital;
9+
pub mod prelude;

e310x-hal/src/asynch/digital.rs

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
//! # Digital I/O
2+
//! # Note
3+
//!
4+
//! Implementation of the Async Embedded HAL I/O functionality.
5+
//!
6+
7+
macro_rules! gpio_async {
8+
($GPIOX:ident, [
9+
$($PXi:ident: ($pxi:ident, $i:expr, $handle:ident),)+
10+
]) => {
11+
use core::cell::RefCell;
12+
use core::task::{Poll, Waker};
13+
use core::future::poll_fn;
14+
use critical_section::Mutex;
15+
use crate::gpio::*;
16+
use crate::gpio::gpio0::*;
17+
use e310x::$GPIOX;
18+
use e310x::interrupt::ExternalInterrupt;
19+
use embedded_hal::digital::{Error, ErrorKind, ErrorType, InputPin};
20+
use embedded_hal_async::digital::Wait;
21+
22+
/// Error type for wait trait.
23+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24+
pub enum DigitalError {
25+
/// Error indicating that a wait operation was already in progress.
26+
AlreadyWaiting,
27+
/// Other errors.
28+
Other,
29+
}
30+
31+
const N_PINS: usize = 32;
32+
static PIN_WAKERS: Mutex<RefCell<[Option<Waker>; N_PINS]>> =
33+
Mutex::new(RefCell::new([const{None}; N_PINS]));
34+
35+
impl Error for DigitalError {
36+
fn kind(&self) -> ErrorKind {
37+
ErrorKind::Other
38+
}
39+
}
40+
41+
/// Interrupt handler for GPIO pins.
42+
#[inline]
43+
fn on_irq(pin_n: usize) {
44+
let gpio_block = unsafe { $GPIOX::steal() };
45+
let pin_mask = 1 << pin_n;
46+
47+
// Disable the interrupt for the pin
48+
unsafe{
49+
gpio_block.high_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
50+
gpio_block.low_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
51+
gpio_block.rise_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
52+
gpio_block.fall_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
53+
}
54+
55+
// Wake the pin if possible
56+
critical_section::with(|cs| {
57+
let mut pin_wakers = PIN_WAKERS.borrow_ref_mut(cs);
58+
if let Some(pinwaker) = pin_wakers[pin_n].take() {
59+
pinwaker.wake();
60+
}
61+
});
62+
63+
// Clear pending pin interrupts
64+
unsafe{
65+
gpio_block.high_ip().write(|w| w.bits(pin_mask));
66+
gpio_block.low_ip().write(|w| w.bits(pin_mask));
67+
gpio_block.rise_ip().write(|w| w.bits(pin_mask));
68+
gpio_block.fall_ip().write(|w| w.bits(pin_mask));
69+
}
70+
}
71+
72+
/// GPIO
73+
$(
74+
impl<MODE> ErrorType for $PXi<Input<MODE>> {
75+
type Error = DigitalError;
76+
}
77+
/// Wait trait implementation
78+
impl<MODE> Wait for $PXi<Input<MODE>> {
79+
#[inline]
80+
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
81+
// If the pin is already high, no need to wait.
82+
if self.is_high().unwrap() {
83+
return Ok(());
84+
}
85+
86+
// Prevent concurrent waiters.
87+
if critical_section::with(|cs| {
88+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
89+
}){
90+
return Err(DigitalError::AlreadyWaiting);
91+
}
92+
93+
// Clear previous high interrupts for the pin.
94+
self.clear_interrupt(EventType::High);
95+
96+
// Enable the high interrupt for the pin.
97+
self.enable_interrupt(EventType::High);
98+
99+
// Await until an interrupt indicates that the pin has transitioned high.
100+
poll_fn(|cx| {
101+
if !self.is_interrupt_enabled(EventType::High) {
102+
Poll::Ready(Ok(()))
103+
} else {
104+
critical_section::with(|cs| {
105+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
106+
pinwaker[$i] = Some(cx.waker().clone());
107+
});
108+
Poll::Pending
109+
}
110+
}).await
111+
}
112+
113+
#[inline]
114+
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
115+
// If the pin is already low, no need to wait.
116+
if self.is_low().unwrap() {
117+
return Ok(());
118+
}
119+
120+
// Prevent concurrent waiters.
121+
if critical_section::with(|cs| {
122+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
123+
}){
124+
return Err(DigitalError::AlreadyWaiting);
125+
}
126+
127+
// Clear previous low interrupts for the pin.
128+
self.clear_interrupt(EventType::Low);
129+
130+
// Enable the low interrupt for the pin.
131+
self.enable_interrupt(EventType::Low);
132+
133+
// Await until an interrupt indicates that the pin has transitioned high.
134+
poll_fn(|cx| {
135+
if !self.is_interrupt_enabled(EventType::Low) {
136+
Poll::Ready(Ok(()))
137+
} else {
138+
critical_section::with(|cs| {
139+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
140+
pinwaker[$i] = Some(cx.waker().clone());
141+
});
142+
Poll::Pending
143+
}
144+
}).await
145+
}
146+
147+
#[inline]
148+
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
149+
// Prevent concurrent waiters.
150+
if critical_section::with(|cs| {
151+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
152+
}){
153+
return Err(DigitalError::AlreadyWaiting);
154+
}
155+
156+
// Clear previous rising edge interrupts for the pin.
157+
self.clear_interrupt(EventType::Rise);
158+
159+
// Enable the rising edge interrupt for the pin.
160+
self.enable_interrupt(EventType::Rise);
161+
162+
// Await until an interrupt indicates that the pin has transitioned high.
163+
poll_fn(|cx| {
164+
if !self.is_interrupt_enabled(EventType::Rise) {
165+
Poll::Ready(Ok(()))
166+
} else {
167+
critical_section::with(|cs| {
168+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
169+
pinwaker[$i] = Some(cx.waker().clone());
170+
});
171+
Poll::Pending
172+
}
173+
}).await
174+
}
175+
176+
#[inline]
177+
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
178+
// Prevent concurrent waiters.
179+
if critical_section::with(|cs| {
180+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
181+
}){
182+
return Err(DigitalError::AlreadyWaiting);
183+
}
184+
185+
// Clear previous falling edge interrupts for the pin.
186+
self.clear_interrupt(EventType::Fall);
187+
188+
// Enable the falling edge interrupt for the pin.
189+
self.enable_interrupt(EventType::Fall);
190+
191+
// Await until an interrupt indicates that the pin has transitioned high.
192+
poll_fn(|cx| {
193+
if !self.is_interrupt_enabled(EventType::Fall) {
194+
Poll::Ready(Ok(()))
195+
} else {
196+
critical_section::with(|cs| {
197+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
198+
pinwaker[$i] = Some(cx.waker().clone());
199+
});
200+
Poll::Pending
201+
}
202+
}).await
203+
}
204+
205+
#[inline]
206+
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
207+
// Prevent concurrent waiters.
208+
if critical_section::with(|cs| {
209+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
210+
}){
211+
return Err(DigitalError::AlreadyWaiting);
212+
}
213+
214+
// Clear previous rising and falling edge interrupts for the pin.
215+
self.clear_interrupt(EventType::BothEdges);
216+
217+
// Enable the rising and falling edge interrupts for the pin.
218+
self.enable_interrupt(EventType::BothEdges);
219+
220+
// Await until an interrupt indicates that the pin has transitioned high.
221+
poll_fn(|cx| {
222+
if !self.is_interrupt_enabled(EventType::BothEdges) {
223+
Poll::Ready(Ok(()))
224+
} else {
225+
critical_section::with(|cs| {
226+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
227+
pinwaker[$i] = Some(cx.waker().clone());
228+
});
229+
Poll::Pending
230+
}
231+
}).await
232+
}
233+
}
234+
235+
/// Pin Interrupt Handler
236+
#[riscv_rt::external_interrupt(ExternalInterrupt::$handle)]
237+
fn $pxi() {
238+
on_irq($i);
239+
}
240+
)+
241+
}
242+
}
243+
244+
gpio_async!(Gpio0, [
245+
Pin0: (pin0, 0, GPIO0),
246+
Pin1: (pin1, 1, GPIO1),
247+
Pin2: (pin2, 2, GPIO2),
248+
Pin3: (pin3, 3, GPIO3),
249+
Pin4: (pin4, 4, GPIO4),
250+
Pin5: (pin5, 5, GPIO5),
251+
Pin6: (pin6, 6, GPIO6),
252+
Pin7: (pin7, 7, GPIO7),
253+
Pin8: (pin8, 8, GPIO8),
254+
Pin9: (pin9, 9, GPIO9),
255+
Pin10: (pin10, 10, GPIO10),
256+
Pin11: (pin11, 11, GPIO11),
257+
Pin12: (pin12, 12, GPIO12),
258+
Pin13: (pin13, 13, GPIO13),
259+
Pin14: (pin14, 14, GPIO14),
260+
Pin15: (pin15, 15, GPIO15),
261+
Pin16: (pin16, 16, GPIO16),
262+
Pin17: (pin17, 17, GPIO17),
263+
Pin18: (pin18, 18, GPIO18),
264+
Pin19: (pin19, 19, GPIO19),
265+
Pin20: (pin20, 20, GPIO20),
266+
Pin21: (pin21, 21, GPIO21),
267+
Pin22: (pin22, 22, GPIO22),
268+
Pin23: (pin23, 23, GPIO23),
269+
Pin24: (pin24, 24, GPIO24),
270+
Pin25: (pin25, 25, GPIO25),
271+
Pin26: (pin26, 26, GPIO26),
272+
Pin27: (pin27, 27, GPIO27),
273+
Pin28: (pin28, 28, GPIO28),
274+
Pin29: (pin29, 29, GPIO29),
275+
Pin30: (pin30, 30, GPIO30),
276+
Pin31: (pin31, 31, GPIO31),
277+
]);

e310x-hal/src/asynch/prelude.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
//! Prelude
2+
pub use embedded_hal_async::digital::Wait as _eha_Wait;

e310x-hal/src/gpio.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ macro_rules! gpio {
558558
}
559559
}
560560

561+
#[cfg(not(feature = "async"))]
561562
impl<MODE> ErrorType for $PXi<Input<MODE>> {
562563
type Error = Infallible;
563564
}

0 commit comments

Comments
 (0)