Async GPIO implementation
This commit is contained in:
parent
7f6b1a7ba5
commit
d66fb51773
@ -16,21 +16,26 @@ critical-section = "1"
|
|||||||
nb = "1"
|
nb = "1"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
typenum = "1"
|
typenum = "1"
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
bitfield = "0.17"
|
bitfield = "0.17"
|
||||||
defmt = { version = "0.3", optional = true }
|
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
delegate = "0.12"
|
delegate = "0.12"
|
||||||
void = { version = "1", default-features = false }
|
void = { version = "1", default-features = false }
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
portable-atomic = "1"
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
[dependencies.va416xx]
|
[dependencies.va416xx]
|
||||||
default-features = false
|
default-features = false
|
||||||
version = "0.3"
|
path = "../va416xx"
|
||||||
|
version = "0.4"
|
||||||
features = ["critical-section"]
|
features = ["critical-section"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
452
va416xx-hal/src/gpio/asynch.rs
Normal file
452
va416xx-hal/src/gpio/asynch.rs
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
//! # Async GPIO functionality for the VA416xx family.
|
||||||
|
//!
|
||||||
|
//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement
|
||||||
|
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
|
||||||
|
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
|
||||||
|
//! which must be provided for async support to work. However, it provides the
|
||||||
|
//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all
|
||||||
|
//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
|
||||||
|
use core::future::Future;
|
||||||
|
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embedded_hal::digital::InputPin;
|
||||||
|
use embedded_hal_async::digital::Wait;
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
|
use va416xx::{self as pac};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
|
||||||
|
NUM_PINS_PORT_A_TO_F,
|
||||||
|
};
|
||||||
|
|
||||||
|
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
|
||||||
|
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PINS_PORT_A_TO_F] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F];
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("port G does not support async functionality")]
|
||||||
|
pub struct PortGDoesNotSupportAsyncError;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum AsyncDynPinError {
|
||||||
|
#[error("invalid pin type: {0}")]
|
||||||
|
InvalidPinType(#[from] InvalidPinTypeError),
|
||||||
|
#[error("port g does not support async functionality: {0}")]
|
||||||
|
PortGDoesNotSupportAsync(#[from] PortGDoesNotSupportAsyncError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities
|
||||||
|
///
|
||||||
|
/// This function should be called in all interrupt handlers which handle any GPIO interrupts
|
||||||
|
/// matching the [Port] argument.
|
||||||
|
/// The handler will wake the corresponding wakers for the pins that triggered an interrupts
|
||||||
|
/// as well as update the static edge detection structures. This allows the pin future tocomplete
|
||||||
|
/// complete async operations.
|
||||||
|
pub fn on_interrupt_for_async_gpio_for_port(
|
||||||
|
port: Port,
|
||||||
|
) -> Result<(), PortGDoesNotSupportAsyncError> {
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
|
||||||
|
let (irq_enb, edge_status, wakers, edge_detection) = match port {
|
||||||
|
Port::A => (
|
||||||
|
periphs.porta.irq_enb().read().bits(),
|
||||||
|
periphs.porta.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_A,
|
||||||
|
&EDGE_DETECTION_PORT_A,
|
||||||
|
),
|
||||||
|
Port::B => (
|
||||||
|
periphs.portb.irq_enb().read().bits(),
|
||||||
|
periphs.portb.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_B,
|
||||||
|
&EDGE_DETECTION_PORT_B,
|
||||||
|
),
|
||||||
|
Port::C => (
|
||||||
|
periphs.portc.irq_enb().read().bits(),
|
||||||
|
periphs.portc.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_C,
|
||||||
|
&EDGE_DETECTION_PORT_C,
|
||||||
|
),
|
||||||
|
Port::D => (
|
||||||
|
periphs.portd.irq_enb().read().bits(),
|
||||||
|
periphs.portd.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_D,
|
||||||
|
&EDGE_DETECTION_PORT_D,
|
||||||
|
),
|
||||||
|
Port::E => (
|
||||||
|
periphs.porte.irq_enb().read().bits(),
|
||||||
|
periphs.porte.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_E,
|
||||||
|
&EDGE_DETECTION_PORT_E,
|
||||||
|
),
|
||||||
|
Port::F => (
|
||||||
|
periphs.portf.irq_enb().read().bits(),
|
||||||
|
periphs.portf.edge_status().read().bits(),
|
||||||
|
&WAKERS_FOR_PORT_F,
|
||||||
|
&EDGE_DETECTION_PORT_F,
|
||||||
|
),
|
||||||
|
Port::G => return Err(PortGDoesNotSupportAsyncError),
|
||||||
|
};
|
||||||
|
|
||||||
|
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn on_interrupt_for_port(
|
||||||
|
mut irq_enb: u32,
|
||||||
|
edge_status: u32,
|
||||||
|
wakers: &'static [AtomicWaker],
|
||||||
|
edge_detection: &'static [AtomicBool],
|
||||||
|
) {
|
||||||
|
while irq_enb != 0 {
|
||||||
|
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||||
|
let bit_mask = 1 << bit_pos;
|
||||||
|
|
||||||
|
wakers[bit_pos].wake();
|
||||||
|
|
||||||
|
if edge_status & bit_mask != 0 {
|
||||||
|
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Clear the processed bit
|
||||||
|
irq_enb &= !bit_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input pin future which implements the [Future] trait.
|
||||||
|
///
|
||||||
|
/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this
|
||||||
|
/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this
|
||||||
|
/// struture is granted to allow writing custom async structures.
|
||||||
|
pub struct InputPinFuture {
|
||||||
|
pin_id: DynPinId,
|
||||||
|
waker_group: &'static [AtomicWaker],
|
||||||
|
edge_detection_group: &'static [AtomicBool],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputPinFuture {
|
||||||
|
pub fn new_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, AsyncDynPinError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError(pin.mode()).into());
|
||||||
|
}
|
||||||
|
if pin.id().port == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (waker_group, edge_detection_group) =
|
||||||
|
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port);
|
||||||
|
edge_detection_group[pin.id().num as usize]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.configure_edge_interrupt(edge).unwrap();
|
||||||
|
Ok(Self {
|
||||||
|
pin_id: pin.id(),
|
||||||
|
waker_group,
|
||||||
|
edge_detection_group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||||
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, PortGDoesNotSupportAsyncError> {
|
||||||
|
if pin.id().port == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError);
|
||||||
|
}
|
||||||
|
let (waker_group, edge_detection_group) =
|
||||||
|
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port);
|
||||||
|
edge_detection_group[pin.id().num as usize]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.configure_edge_interrupt(edge);
|
||||||
|
Ok(Self {
|
||||||
|
pin_id: pin.id(),
|
||||||
|
waker_group,
|
||||||
|
edge_detection_group,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pin_group_to_waker_and_edge_detection_group(
|
||||||
|
group: Port,
|
||||||
|
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||||
|
match group {
|
||||||
|
Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()),
|
||||||
|
Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()),
|
||||||
|
Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()),
|
||||||
|
Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()),
|
||||||
|
Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()),
|
||||||
|
Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()),
|
||||||
|
_ => panic!("unexpected pin group G"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InputPinFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// TODO: Fix this. Try to use HAL helpers.
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
if self.pin_id.port == Port::A {
|
||||||
|
periphs
|
||||||
|
.porta
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||||
|
} else {
|
||||||
|
periphs
|
||||||
|
.porta
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for InputPinFuture {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(
|
||||||
|
self: core::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut core::task::Context<'_>,
|
||||||
|
) -> core::task::Poll<Self::Output> {
|
||||||
|
let idx = self.pin_id.num as usize;
|
||||||
|
self.waker_group[idx].register(cx.waker());
|
||||||
|
if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
|
return core::task::Poll::Ready(());
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InputDynPinAsync {
|
||||||
|
pin: DynPin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputDynPinAsync {
|
||||||
|
/// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be
|
||||||
|
/// passed as well and is used to route and enable the interrupt.
|
||||||
|
///
|
||||||
|
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||||
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
|
/// for the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: DynPin) -> Result<Self, AsyncDynPinError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError(pin.mode()).into());
|
||||||
|
}
|
||||||
|
if pin.id().port == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError.into());
|
||||||
|
}
|
||||||
|
Ok(Self { pin })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is high.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_high(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
let fut =
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
||||||
|
if self.pin.is_high().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is low.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_low(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
let fut =
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
||||||
|
if self.pin.is_low().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees a falling edge.
|
||||||
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::HighToLow)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees a rising edge.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::LowToHigh)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::BothEdges)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> DynPin {
|
||||||
|
self.pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_hal::digital::ErrorType for InputDynPinAsync {
|
||||||
|
type Error = core::convert::Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Wait for InputDynPinAsync {
|
||||||
|
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_high().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_low().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_rising_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_falling_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_any_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InputPinAsync<I: PinId, C: InputConfig> {
|
||||||
|
pin: Pin<I, pin::Input<C>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
|
||||||
|
/// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be
|
||||||
|
/// passed as well and is used to route and enable the interrupt.
|
||||||
|
///
|
||||||
|
/// Please note that the interrupt handler itself must be provided by the user and the
|
||||||
|
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||||
|
/// for the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: Pin<I, pin::Input<C>>) -> Result<Self, PortGDoesNotSupportAsyncError> {
|
||||||
|
if pin.id().port == Port::G {
|
||||||
|
return Err(PortGDoesNotSupportAsyncError);
|
||||||
|
}
|
||||||
|
Ok(Self { pin })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is high.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_high(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
let fut = InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
||||||
|
if self.pin.is_high().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is low.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_low(&mut self) {
|
||||||
|
let fut = InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
||||||
|
if self.pin.is_low().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fut.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees falling edge.
|
||||||
|
pub async fn wait_for_falling_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::HighToLow)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees rising edge.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::LowToHigh)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::BothEdges)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(self) -> Pin<I, pin::Input<C>> {
|
||||||
|
self.pin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<I: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
|
||||||
|
type Error = core::convert::Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Wait for InputPinAsync<I, C> {
|
||||||
|
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_high().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_low().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_rising_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_falling_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self.wait_for_any_edge().await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -59,7 +59,7 @@ use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId,
|
reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId,
|
||||||
PinMode, PinState,
|
PinMode, PinState, Port,
|
||||||
};
|
};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -155,24 +155,13 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
|
|||||||
// DynGroup & DynPinId
|
// DynGroup & DynPinId
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
/// Value-level `enum` for pin groups
|
pub type DynGroup = Port;
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum DynGroup {
|
|
||||||
A,
|
|
||||||
B,
|
|
||||||
C,
|
|
||||||
D,
|
|
||||||
E,
|
|
||||||
F,
|
|
||||||
G,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Value-level `struct` representing pin IDs
|
/// Value-level `struct` representing pin IDs
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct DynPinId {
|
pub struct DynPinId {
|
||||||
pub group: DynGroup,
|
pub port: Port,
|
||||||
pub num: u8,
|
pub num: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,6 +252,11 @@ impl DynPin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_input_pin(&self) -> bool {
|
||||||
|
matches!(self.mode, DynPinMode::Input(_))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_funsel_1(&mut self) {
|
pub fn into_funsel_1(&mut self) {
|
||||||
self.into_mode(DYN_ALT_FUNC_1);
|
self.into_mode(DYN_ALT_FUNC_1);
|
||||||
|
@ -21,15 +21,62 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Errors, Definitions and Constants
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub const NUM_PINS_PORT_A_TO_F: usize = 16;
|
||||||
|
pub const NUM_PINS_PORT_G: usize = 8;
|
||||||
|
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A_TO_F * 6 + NUM_PINS_PORT_G;
|
||||||
|
pub const NUM_GPIO_PINS_WITH_IRQ: usize = NUM_GPIO_PINS - NUM_PINS_PORT_G;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("pin is masked")]
|
#[error("pin is masked")]
|
||||||
pub struct IsMaskedError;
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Port {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
E,
|
||||||
|
F,
|
||||||
|
G,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InterruptEdge {
|
||||||
|
HighToLow,
|
||||||
|
LowToHigh,
|
||||||
|
BothEdges,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum InterruptLevel {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
pub mod pin;
|
pub mod pin;
|
||||||
pub use pin::*;
|
pub use pin::*;
|
||||||
|
|
||||||
pub mod dynpin;
|
pub mod dynpin;
|
||||||
pub use dynpin::*;
|
pub use dynpin::*;
|
||||||
|
|
||||||
|
pub mod asynch;
|
||||||
|
pub use asynch::*;
|
||||||
|
|
||||||
mod reg;
|
mod reg;
|
||||||
|
@ -78,36 +78,10 @@ use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
|
|||||||
use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPin, DynPinId,
|
reg::RegisterInterface, DynAlternate, DynInput, DynOutput, DynPin, DynPinId, DynPinMode,
|
||||||
DynPinMode,
|
InterruptEdge, InterruptLevel, PinState, Port,
|
||||||
};
|
};
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// Errors and Definitions
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum InterruptEdge {
|
|
||||||
HighToLow,
|
|
||||||
LowToHigh,
|
|
||||||
BothEdges,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum InterruptLevel {
|
|
||||||
Low = 0,
|
|
||||||
High = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum PinState {
|
|
||||||
Low = 0,
|
|
||||||
High = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Input configuration
|
// Input configuration
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
@ -312,7 +286,7 @@ macro_rules! pin_id {
|
|||||||
$(#[$meta])?
|
$(#[$meta])?
|
||||||
impl PinId for $Id {
|
impl PinId for $Id {
|
||||||
const DYN: DynPinId = DynPinId {
|
const DYN: DynPinId = DynPinId {
|
||||||
group: DynGroup::$Group,
|
port: Port::$Group,
|
||||||
num: $NUM,
|
num: $NUM,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use crate::FunSel;
|
use crate::FunSel;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
dynpin::{self, DynGroup, DynPinId},
|
dynpin::{self, DynPinId},
|
||||||
DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState,
|
DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState,
|
||||||
|
Port,
|
||||||
};
|
};
|
||||||
use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
||||||
|
|
||||||
@ -146,28 +147,28 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn port_reg(&self) -> &PortRegisterBlock {
|
fn port_reg(&self) -> &PortRegisterBlock {
|
||||||
match self.id().group {
|
match self.id().port {
|
||||||
DynGroup::A => unsafe { &(*Porta::ptr()) },
|
Port::A => unsafe { &(*Porta::ptr()) },
|
||||||
DynGroup::B => unsafe { &(*Portb::ptr()) },
|
Port::B => unsafe { &(*Portb::ptr()) },
|
||||||
DynGroup::C => unsafe { &(*Portc::ptr()) },
|
Port::C => unsafe { &(*Portc::ptr()) },
|
||||||
DynGroup::D => unsafe { &(*Portd::ptr()) },
|
Port::D => unsafe { &(*Portd::ptr()) },
|
||||||
DynGroup::E => unsafe { &(*Porte::ptr()) },
|
Port::E => unsafe { &(*Porte::ptr()) },
|
||||||
DynGroup::F => unsafe { &(*Portf::ptr()) },
|
Port::F => unsafe { &(*Portf::ptr()) },
|
||||||
DynGroup::G => unsafe { &(*Portg::ptr()) },
|
Port::G => unsafe { &(*Portg::ptr()) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn iocfg_port(&self) -> &PortReg {
|
fn iocfg_port(&self) -> &PortReg {
|
||||||
let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() };
|
let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() };
|
||||||
match self.id().group {
|
match self.id().port {
|
||||||
DynGroup::A => ioconfig.porta(self.id().num as usize),
|
Port::A => ioconfig.porta(self.id().num as usize),
|
||||||
DynGroup::B => ioconfig.portb0(self.id().num as usize),
|
Port::B => ioconfig.portb0(self.id().num as usize),
|
||||||
DynGroup::C => ioconfig.portc0(self.id().num as usize),
|
Port::C => ioconfig.portc0(self.id().num as usize),
|
||||||
DynGroup::D => ioconfig.portd0(self.id().num as usize),
|
Port::D => ioconfig.portd0(self.id().num as usize),
|
||||||
DynGroup::E => ioconfig.porte0(self.id().num as usize),
|
Port::E => ioconfig.porte0(self.id().num as usize),
|
||||||
DynGroup::F => ioconfig.portf0(self.id().num as usize),
|
Port::F => ioconfig.portf0(self.id().num as usize),
|
||||||
DynGroup::G => ioconfig.portg0(self.id().num as usize),
|
Port::G => ioconfig.portg0(self.id().num as usize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ pub type TimRegBlock = pac::tim0::RegisterBlock;
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Users should only implement the [`tim_id`] function. No default function
|
/// Users should only implement the [Self::tim_id] function. No default function
|
||||||
/// implementations should be overridden. The implementing type must also have
|
/// implementations should be overridden. The implementing type must also have
|
||||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
/// pin ID is a singleton.
|
/// pin ID is a singleton.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! # API for the UART peripheral
|
//! # API for the UART peripheral
|
||||||
//!
|
//!
|
||||||
//! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures.
|
//! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures.
|
||||||
//! The RX structure also has a dedicated [RxWithIrq] variant which allows reading the receiver
|
//! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver
|
||||||
//! using interrupts.
|
//! using interrupts.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
|
Loading…
x
Reference in New Issue
Block a user