Asynchronous GPIO support
This commit is contained in:
parent
454635a473
commit
4411d1e65a
@ -8,6 +8,7 @@ cfg-if = "1"
|
|||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
|
|
||||||
rtt-target = "0.6"
|
rtt-target = "0.6"
|
||||||
panic-rtt-target = "0.2"
|
panic-rtt-target = "0.2"
|
||||||
|
181
examples/embassy/src/bin/async-gpio.rs
Normal file
181
examples/embassy/src/bin/async-gpio.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_embassy::embassy;
|
||||||
|
use va108xx_hal::gpio::{handle_interrupt_for_async_gpio, InputPinAsync};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{DynPin, PinsA},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct GpioCmd {
|
||||||
|
cmd_type: GpioCmdType,
|
||||||
|
after_delay: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpioCmd {
|
||||||
|
pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
cmd_type,
|
||||||
|
after_delay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum GpioCmdType {
|
||||||
|
SetHigh,
|
||||||
|
SetLow,
|
||||||
|
RisingEdge,
|
||||||
|
FallingEdge,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a bounded channel of 3 u32s.
|
||||||
|
static CHANNEL: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("-- VA108xx Async GPIO Demo --");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Safety: Only called once here.
|
||||||
|
unsafe {
|
||||||
|
embassy::init(
|
||||||
|
&mut dp.sysconfig,
|
||||||
|
&dp.irqsel,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
|
||||||
|
let led0 = porta.pa10.into_readable_push_pull_output();
|
||||||
|
let out = porta.pa0.into_readable_push_pull_output();
|
||||||
|
let input = porta.pa1.into_floating_input();
|
||||||
|
let mut async_input = InputPinAsync::new(input, pac::Interrupt::OC10);
|
||||||
|
let out_dyn = out.downgrade();
|
||||||
|
let mut led_0_dyn = led0.downgrade();
|
||||||
|
|
||||||
|
spawner.spawn(output_task(out_dyn)).unwrap();
|
||||||
|
rprintln!(
|
||||||
|
"sending SetHigh command ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
CHANNEL.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
|
||||||
|
async_input.wait_for_high().await;
|
||||||
|
rprintln!("Input pin is high now ({} ms)", Instant::now().as_millis());
|
||||||
|
|
||||||
|
rprintln!("sending SetLow command ({} ms)", Instant::now().as_millis());
|
||||||
|
CHANNEL.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
|
||||||
|
async_input.wait_for_low().await;
|
||||||
|
rprintln!("Input pin is low now ({} ms)", Instant::now().as_millis());
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"sending RisingEdge command ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
CHANNEL
|
||||||
|
.send(GpioCmd::new(GpioCmdType::RisingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_rising_edge().await;
|
||||||
|
rprintln!(
|
||||||
|
"input pin had rising edge ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"sending Falling command ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
CHANNEL
|
||||||
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_falling_edge().await;
|
||||||
|
rprintln!(
|
||||||
|
"input pin had a falling edge ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"sending Falling command ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
CHANNEL
|
||||||
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_any_edge().await;
|
||||||
|
rprintln!(
|
||||||
|
"input pin had a falling (any) edge ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!(
|
||||||
|
"sending Falling command ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
CHANNEL
|
||||||
|
.send(GpioCmd::new(GpioCmdType::RisingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_any_edge().await;
|
||||||
|
rprintln!(
|
||||||
|
"input pin had a rising (any) edge ({} ms)",
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
rprintln!("Example done, toggling LED0");
|
||||||
|
loop {
|
||||||
|
led_0_dyn.toggle().unwrap();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn output_task(mut out: DynPin) {
|
||||||
|
loop {
|
||||||
|
let next_cmd = CHANNEL.receive().await;
|
||||||
|
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
|
||||||
|
match next_cmd.cmd_type {
|
||||||
|
GpioCmdType::SetHigh => {
|
||||||
|
rprintln!("Set output high");
|
||||||
|
out.set_high().unwrap();
|
||||||
|
}
|
||||||
|
GpioCmdType::SetLow => {
|
||||||
|
rprintln!("Set output low");
|
||||||
|
out.set_low().unwrap();
|
||||||
|
}
|
||||||
|
GpioCmdType::RisingEdge => {
|
||||||
|
if !out.is_low().unwrap() {
|
||||||
|
out.set_low().unwrap();
|
||||||
|
}
|
||||||
|
rprintln!("Rising edge");
|
||||||
|
out.set_high().unwrap();
|
||||||
|
}
|
||||||
|
GpioCmdType::FallingEdge => {
|
||||||
|
if !out.is_high().unwrap() {
|
||||||
|
out.set_high().unwrap();
|
||||||
|
}
|
||||||
|
rprintln!("Falling edge");
|
||||||
|
out.set_low().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC10() {
|
||||||
|
handle_interrupt_for_async_gpio();
|
||||||
|
}
|
@ -16,6 +16,7 @@ cortex-m-rt = "0.7"
|
|||||||
nb = "1"
|
nb = "1"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
|
embedded-hal-async = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
embedded-io = "0.6"
|
embedded-io = "0.6"
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
@ -26,6 +27,8 @@ thiserror = { version = "2", default-features = false }
|
|||||||
void = { version = "1", default-features = false }
|
void = { version = "1", default-features = false }
|
||||||
once_cell = {version = "1", default-features = false }
|
once_cell = {version = "1", default-features = false }
|
||||||
va108xx = { version = "0.3", default-features = false, features = ["critical-section"]}
|
va108xx = { version = "0.3", default-features = false, features = ["critical-section"]}
|
||||||
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
embassy-sync = "0.6"
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
3
va108xx-hal/docs.sh
Executable file
3
va108xx-hal/docs.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||||
|
cargo +nightly doc --all-features --open
|
449
va108xx-hal/src/gpio/asynch.rs
Normal file
449
va108xx-hal/src/gpio/asynch.rs
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
//! # Async GPIO functionality for the VA108xx 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 one generic
|
||||||
|
//! [handler][handle_interrupt_for_async_gpio] which should be called in ALL user interrupt handlers
|
||||||
|
//! for which handle GPIO interrupts.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/async-gpio/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 va108xx::{self as pac, Irqsel, Sysconfig};
|
||||||
|
|
||||||
|
use crate::IrqCfg;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
|
||||||
|
NUM_GPIO_PINS, NUM_PINS_PORT_A,
|
||||||
|
};
|
||||||
|
|
||||||
|
static WAKERS: [AtomicWaker; NUM_GPIO_PINS] = [const { AtomicWaker::new() }; NUM_GPIO_PINS];
|
||||||
|
static EDGE_DETECTION: [AtomicBool; NUM_GPIO_PINS] =
|
||||||
|
[const { AtomicBool::new(false) }; NUM_GPIO_PINS];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn pin_id_to_offset(dyn_pin_id: DynPinId) -> usize {
|
||||||
|
match dyn_pin_id.group {
|
||||||
|
DynGroup::A => dyn_pin_id.num as usize,
|
||||||
|
DynGroup::B => NUM_PINS_PORT_A + dyn_pin_id.num as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic interrupt handler for GPIO interrupts to support the async functionalities.
|
||||||
|
///
|
||||||
|
/// This handler will wake the correspoding wakers for the pins which triggered an interrupt
|
||||||
|
/// as well as updating the static edge detection structures. This allows the pin future to
|
||||||
|
/// complete async operations. The user should call this function in ALL interrupt handlers
|
||||||
|
/// which handle any GPIO interrupts.
|
||||||
|
#[inline]
|
||||||
|
pub fn handle_interrupt_for_async_gpio() {
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
|
||||||
|
handle_interrupt_for_gpio_and_port(
|
||||||
|
periphs.porta.irq_enb().read().bits(),
|
||||||
|
periphs.porta.edge_status().read().bits(),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
handle_interrupt_for_gpio_and_port(
|
||||||
|
periphs.portb.irq_enb().read().bits(),
|
||||||
|
periphs.portb.edge_status().read().bits(),
|
||||||
|
NUM_PINS_PORT_A,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses the enabled interrupt register and the persistent edge status to capture all GPIO events.
|
||||||
|
#[inline]
|
||||||
|
fn handle_interrupt_for_gpio_and_port(mut irq_enb: u32, edge_status: u32, pin_base_offset: usize) {
|
||||||
|
while irq_enb != 0 {
|
||||||
|
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||||
|
let bit_mask = 1 << bit_pos;
|
||||||
|
|
||||||
|
WAKERS[pin_base_offset + bit_pos].wake();
|
||||||
|
|
||||||
|
if edge_status & bit_mask != 0 {
|
||||||
|
EDGE_DETECTION[pin_base_offset + 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputPinFuture {
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This calls [Self::new] but uses [pac::Peripherals::steal] to get the system configuration
|
||||||
|
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||||
|
/// related to this input pin are not being used elsewhere concurrently.
|
||||||
|
pub unsafe fn new_unchecked_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
let mut periphs = pac::Peripherals::steal();
|
||||||
|
Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_dyn_pin(
|
||||||
|
pin: &mut DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
sys_cfg: &mut Sysconfig,
|
||||||
|
irq_sel: &mut Irqsel,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
EDGE_DETECTION[pin_id_to_offset(pin.id())]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.interrupt_edge(
|
||||||
|
edge,
|
||||||
|
IrqCfg::new(irq, true, true),
|
||||||
|
Some(sys_cfg),
|
||||||
|
Some(irq_sel),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ok(Self { pin_id: pin.id() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This calls [Self::new] but uses [pac::Peripherals::steal] to get the system configuration
|
||||||
|
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
|
||||||
|
/// related to this input pin are not being used elsewhere concurrently.
|
||||||
|
pub unsafe fn new_unchecked_with_pin<I: PinId, C: InputConfig>(
|
||||||
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
) -> Self {
|
||||||
|
let mut periphs = pac::Peripherals::steal();
|
||||||
|
Self::new_with_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_pin<I: PinId, C: InputConfig>(
|
||||||
|
pin: &mut Pin<I, pin::Input<C>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
edge: InterruptEdge,
|
||||||
|
sys_cfg: &mut Sysconfig,
|
||||||
|
irq_sel: &mut Irqsel,
|
||||||
|
) -> Self {
|
||||||
|
EDGE_DETECTION[pin_id_to_offset(pin.id())]
|
||||||
|
.store(false, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
pin.interrupt_edge(
|
||||||
|
edge,
|
||||||
|
IrqCfg::new(irq, true, true),
|
||||||
|
Some(sys_cfg),
|
||||||
|
Some(irq_sel),
|
||||||
|
);
|
||||||
|
Self { pin_id: pin.id() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InputPinFuture {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let periphs = unsafe { pac::Peripherals::steal() };
|
||||||
|
if self.pin_id.group == DynGroup::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 = pin_id_to_offset(self.pin_id);
|
||||||
|
WAKERS[idx].register(cx.waker());
|
||||||
|
if EDGE_DETECTION[idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||||
|
return core::task::Poll::Ready(());
|
||||||
|
}
|
||||||
|
core::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InputDynPinAsync {
|
||||||
|
pin: DynPin,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 [handle_interrupt_for_async_gpio] function must be called inside that function for
|
||||||
|
/// the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
if !pin.is_input_pin() {
|
||||||
|
return Err(InvalidPinTypeError);
|
||||||
|
}
|
||||||
|
Ok(Self { pin, irq })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is high.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_high(&mut self) {
|
||||||
|
let fut = unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
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 = unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
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) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees a rising edge.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
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) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_dyn_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
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>>,
|
||||||
|
irq: pac::Interrupt,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 [handle_interrupt_for_async_gpio] function must be called inside that function for
|
||||||
|
/// the asynchronous functionality to work.
|
||||||
|
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
|
||||||
|
Self { pin, irq }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin is high.
|
||||||
|
///
|
||||||
|
/// This returns immediately if the pin is already high.
|
||||||
|
pub async fn wait_for_high(&mut self) {
|
||||||
|
let fut = unsafe {
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
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 = unsafe {
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
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) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::HighToLow,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees rising edge.
|
||||||
|
pub async fn wait_for_rising_edge(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Unwrap okay, checked pin in constructor.
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::LowToHigh,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||||
|
pub async fn wait_for_any_edge(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
InputPinFuture::new_unchecked_with_pin(
|
||||||
|
&mut self.pin,
|
||||||
|
self.irq,
|
||||||
|
InterruptEdge::BothEdges,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.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(())
|
||||||
|
}
|
||||||
|
}
|
@ -60,7 +60,7 @@ use super::{
|
|||||||
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
pin::{FilterType, InterruptEdge, InterruptLevel, Pin, PinId, PinMode, PinState},
|
||||||
reg::RegisterInterface,
|
reg::RegisterInterface,
|
||||||
};
|
};
|
||||||
use crate::{clock::FilterClkSel, pac, FunSel, IrqCfg};
|
use crate::{clock::FilterClkSel, enable_interrupt, pac, FunSel, IrqCfg};
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// DynPinMode configurations
|
// DynPinMode configurations
|
||||||
@ -173,16 +173,14 @@ pub struct DynPinId {
|
|||||||
///
|
///
|
||||||
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||||
/// access the corresponding regsiters.
|
/// access the corresponding regsiters.
|
||||||
pub(crate) struct DynRegisters {
|
pub(crate) struct DynRegisters(DynPinId);
|
||||||
id: DynPinId,
|
|
||||||
}
|
|
||||||
|
|
||||||
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||||
// guarantees that each pin is a singleton, so this implementation is safe.
|
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||||
unsafe impl RegisterInterface for DynRegisters {
|
unsafe impl RegisterInterface for DynRegisters {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn id(&self) -> DynPinId {
|
fn id(&self) -> DynPinId {
|
||||||
self.id
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +193,7 @@ impl DynRegisters {
|
|||||||
/// the same [`DynPinId`]
|
/// the same [`DynPinId`]
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn new(id: DynPinId) -> Self {
|
unsafe fn new(id: DynPinId) -> Self {
|
||||||
DynRegisters { id }
|
DynRegisters(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +229,7 @@ impl DynPin {
|
|||||||
/// Return a copy of the pin ID
|
/// Return a copy of the pin ID
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> DynPinId {
|
pub fn id(&self) -> DynPinId {
|
||||||
self.regs.id
|
self.regs.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a copy of the pin mode
|
/// Return a copy of the pin mode
|
||||||
@ -250,6 +248,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);
|
||||||
@ -342,6 +345,11 @@ impl DynPin {
|
|||||||
self.regs.write_pin_masked(false)
|
self.regs.write_pin_masked(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn edge_has_occurred(&mut self) -> bool {
|
||||||
|
self.regs.edge_has_occurred()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn irq_enb(
|
pub(crate) fn irq_enb(
|
||||||
&mut self,
|
&mut self,
|
||||||
irq_cfg: crate::IrqCfg,
|
irq_cfg: crate::IrqCfg,
|
||||||
@ -369,6 +377,9 @@ impl DynPin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if irq_cfg.enable {
|
||||||
|
unsafe { enable_interrupt(irq_cfg.irq) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See p.53 of the programmers guide for more information.
|
/// See p.53 of the programmers guide for more information.
|
||||||
@ -512,7 +523,7 @@ impl DynPin {
|
|||||||
/// or refuse to perform it.
|
/// or refuse to perform it.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
||||||
if self.regs.id == I::DYN && self.mode == M::DYN {
|
if self.regs.0 == I::DYN && self.mode == M::DYN {
|
||||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
// corresponding `Pin`
|
// corresponding `Pin`
|
||||||
return Ok(unsafe { Pin::new() });
|
return Ok(unsafe { Pin::new() });
|
||||||
|
@ -27,10 +27,17 @@
|
|||||||
#[error("The pin is masked")]
|
#[error("The pin is masked")]
|
||||||
pub struct IsMaskedError;
|
pub struct IsMaskedError;
|
||||||
|
|
||||||
|
pub const NUM_PINS_PORT_A: usize = 32;
|
||||||
|
pub const NUM_PINS_PORT_B: usize = 24;
|
||||||
|
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
|
||||||
|
|
||||||
pub mod dynpin;
|
pub mod dynpin;
|
||||||
pub use dynpin::*;
|
pub use dynpin::*;
|
||||||
|
|
||||||
pub mod pin;
|
pub mod pin;
|
||||||
pub use pin::*;
|
pub use pin::*;
|
||||||
|
|
||||||
|
pub mod asynch;
|
||||||
|
pub use asynch::*;
|
||||||
|
|
||||||
mod reg;
|
mod reg;
|
||||||
|
@ -342,6 +342,10 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> DynPinId {
|
||||||
|
self.inner.id()
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert the pin to the requested [`PinMode`]
|
/// Convert the pin to the requested [`PinMode`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
||||||
|
@ -327,6 +327,13 @@ pub(super) unsafe trait RegisterInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Persistent bit which specifies whether an edge was detected. Reading will clear the bit.
|
||||||
|
#[inline(always)]
|
||||||
|
fn edge_has_occurred(&mut self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
((portreg.edge_status().read().bits() >> self.id().num) & 0x01) == 1
|
||||||
|
}
|
||||||
|
|
||||||
/// Only useful for output pins
|
/// Only useful for output pins
|
||||||
/// See p.52 of the programmers guide for more information.
|
/// See p.52 of the programmers guide for more information.
|
||||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
@ -213,22 +213,6 @@ impl<Mode> ReducedPwmPin<Mode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim>> for ReducedPwmPin<PwmA> {
|
|
||||||
fn from(pwm_pin: PwmPin<Pin, Tim>) -> Self {
|
|
||||||
ReducedPwmPin {
|
|
||||||
dyn_reg: TimDynRegister {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
// ::from(pwm_pin.reg),
|
|
||||||
common: pwm_pin.pwm_base,
|
|
||||||
pin_id: Pin::DYN,
|
|
||||||
mode: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl<Mode> ReducedPwmPin<Mode> {
|
impl<Mode> ReducedPwmPin<Mode> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -286,7 +286,7 @@ pub type TimRegBlock = 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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user