Add embedded_hal_async support for embassy-rp
This commit adds support for embedded-hal-async to the Embassy Raspberry PI crate.
This commit is contained in:
@ -1,11 +1,20 @@
|
||||
use core::convert::Infallible;
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomData;
|
||||
use core::pin::Pin as FuturePin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy::waitqueue::AtomicWaker;
|
||||
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
|
||||
use embassy_hal_common::{unborrow, unsafe_impl_unborrow};
|
||||
|
||||
use crate::pac::common::{Reg, RW};
|
||||
use crate::pac::SIO;
|
||||
use crate::{pac, peripherals, Unborrow};
|
||||
use crate::{interrupt, pac, peripherals, Unborrow};
|
||||
|
||||
const PIN_COUNT: usize = 30;
|
||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||
static INTERRUPT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT];
|
||||
|
||||
/// Represents a digital input or output level.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
@ -75,6 +84,204 @@ impl<'d, T: Pin> Input<'d, T> {
|
||||
pub fn get_level(&self) -> Level {
|
||||
self.pin.get_level()
|
||||
}
|
||||
|
||||
pub async fn wait_for_high<'a>(&mut self) {
|
||||
self.pin.wait_for_high().await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_low<'a>(&mut self) {
|
||||
self.pin.wait_for_low().await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_rising_edge<'a>(&mut self) {
|
||||
self.pin.wait_for_rising_edge().await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_falling_edge<'a>(&mut self) {
|
||||
self.pin.wait_for_falling_edge().await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_any_edge<'a>(&mut self) {
|
||||
self.pin.wait_for_any_edge().await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt trigger levels.
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum InterruptTrigger {
|
||||
LevelLow,
|
||||
LevelHigh,
|
||||
EdgeLow,
|
||||
EdgeHigh,
|
||||
AnyEdge,
|
||||
}
|
||||
|
||||
impl InterruptTrigger {
|
||||
fn from_u32(value: u32) -> Option<InterruptTrigger> {
|
||||
match value {
|
||||
1 => Some(InterruptTrigger::LevelLow),
|
||||
2 => Some(InterruptTrigger::LevelHigh),
|
||||
3 => Some(InterruptTrigger::EdgeLow),
|
||||
4 => Some(InterruptTrigger::EdgeHigh),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
unsafe fn IO_IRQ_BANK0() {
|
||||
let cpu = SIO.cpuid().read() as usize;
|
||||
// There are two sets of interrupt registers, one for cpu0 and one for cpu1
|
||||
// and here we are selecting the set that belongs to the currently executing
|
||||
// cpu.
|
||||
let proc_intx: pac::io::Int = pac::IO_BANK0.int_proc(cpu);
|
||||
for pin in 0..PIN_COUNT {
|
||||
// There are 4 raw interrupt status registers, PROCx_INTS0, PROCx_INTS1,
|
||||
// PROCx_INTS2, and PROCx_INTS3, and we are selecting the one that the
|
||||
// current pin belongs to.
|
||||
let intsx = proc_intx.ints(pin / 8);
|
||||
// The status register is divided into groups of four, one group for
|
||||
// each pin. Each group consists of four trigger levels LEVEL_LOW,
|
||||
// LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin.
|
||||
let pin_group = (pin % 8) as usize;
|
||||
let event = (intsx.read().0 >> pin_group * 4) & 0xf as u32;
|
||||
|
||||
if let Some(trigger) = InterruptTrigger::from_u32(event) {
|
||||
proc_intx.inte(pin / 8).write(|w| match trigger {
|
||||
InterruptTrigger::AnyEdge => {
|
||||
w.set_edge_high(pin_group, false);
|
||||
w.set_edge_low(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::LevelHigh => {
|
||||
debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin);
|
||||
w.set_level_high(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
w.set_level_low(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::EdgeHigh => {
|
||||
w.set_edge_high(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::EdgeLow => {
|
||||
w.set_edge_low(pin_group, false);
|
||||
}
|
||||
});
|
||||
INTERRUPT_WAKERS[pin as usize].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InputFuture<'a, T: Pin> {
|
||||
pin: &'a mut T,
|
||||
level: InterruptTrigger,
|
||||
phantom: PhantomData<&'a mut AnyPin>,
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> InputFuture<'d, T> {
|
||||
pub fn new(pin: &'d mut T, level: InterruptTrigger) -> Self {
|
||||
unsafe {
|
||||
let irq = interrupt::IO_IRQ_BANK0::steal();
|
||||
irq.disable();
|
||||
irq.set_priority(interrupt::Priority::P6);
|
||||
|
||||
// Each INTR register is divided into 8 groups, one group for each
|
||||
// pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW,
|
||||
// and EGDE_HIGH.
|
||||
let pin_group = (pin.pin() % 8) as usize;
|
||||
pin.int_proc().inte((pin.pin() / 8) as usize).write(|w| match level {
|
||||
InterruptTrigger::LevelHigh => {
|
||||
debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin());
|
||||
w.set_level_high(pin_group, true);
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
w.set_level_low(pin_group, true);
|
||||
}
|
||||
InterruptTrigger::EdgeHigh => {
|
||||
w.set_edge_high(pin_group, true);
|
||||
}
|
||||
InterruptTrigger::EdgeLow => {
|
||||
w.set_edge_low(pin_group, true);
|
||||
}
|
||||
InterruptTrigger::AnyEdge => {
|
||||
// noop
|
||||
}
|
||||
});
|
||||
|
||||
irq.enable();
|
||||
}
|
||||
|
||||
Self {
|
||||
pin,
|
||||
level,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> Future for InputFuture<'d, T> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// We need to register/re-register the waker for each poll because any
|
||||
// calls to wake will deregister the waker.
|
||||
INTERRUPT_WAKERS[self.pin.pin() as usize].register(cx.waker());
|
||||
|
||||
// self.int_proc() will get the register offset for the current cpu,
|
||||
// then we want to access the interrupt enable register for our
|
||||
// pin (there are 4 of these PROC0_INTE0, PROC0_INTE1, PROC0_INTE2, and
|
||||
// PROC0_INTE3 per cpu).
|
||||
let inte: pac::io::regs::Int = unsafe { self.pin.int_proc().inte((self.pin.pin() / 8) as usize).read() };
|
||||
// The register is divided into groups of four, one group for
|
||||
// each pin. Each group consists of four trigger levels LEVEL_LOW,
|
||||
// LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin.
|
||||
let pin_group = (self.pin.pin() % 8) as usize;
|
||||
|
||||
// This should check the the level of the interrupt trigger level of
|
||||
// the pin and if it has been disabled that means it was done by the
|
||||
// interrupt service routine, so we then know that the event/trigger
|
||||
// happened and Poll::Ready will be returned.
|
||||
debug!("{:?} for pin {}\n", self.level, self.pin.pin());
|
||||
match self.level {
|
||||
InterruptTrigger::AnyEdge => {
|
||||
if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::LevelHigh => {
|
||||
if !inte.level_high(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
if !inte.level_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::EdgeHigh => {
|
||||
if !inte.edge_high(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::EdgeLow => {
|
||||
if !inte.edge_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("InputFuture::poll return Poll::Pending\n");
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Output<'d, T: Pin> {
|
||||
@ -340,6 +547,32 @@ impl<'d, T: Pin> Flex<'d, T> {
|
||||
pub fn toggle(&mut self) {
|
||||
unsafe { self.pin.sio_out().value_xor().write_value(self.bit()) }
|
||||
}
|
||||
|
||||
pub async fn wait_for_high<'a>(&mut self) {
|
||||
InputFuture::new(&mut self.pin, InterruptTrigger::LevelHigh).await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_low<'a>(&mut self) {
|
||||
InputFuture::new(&mut self.pin, InterruptTrigger::LevelLow).await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_rising_edge<'a>(&mut self) {
|
||||
self.wait_for_low().await;
|
||||
self.wait_for_high().await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_falling_edge<'a>(&mut self) {
|
||||
self.wait_for_high().await;
|
||||
self.wait_for_low().await;
|
||||
}
|
||||
|
||||
pub async fn wait_for_any_edge<'a>(&mut self) {
|
||||
if self.is_high() {
|
||||
self.wait_for_low().await;
|
||||
} else {
|
||||
self.wait_for_high().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> Drop for Flex<'d, T> {
|
||||
@ -401,6 +634,15 @@ pub(crate) mod sealed {
|
||||
fn sio_in(&self) -> Reg<u32, RW> {
|
||||
SIO.gpio_in(self.bank() as _)
|
||||
}
|
||||
|
||||
fn int_proc(&self) -> pac::io::Int {
|
||||
let io_block = match self.bank() {
|
||||
Bank::Bank0 => crate::pac::IO_BANK0,
|
||||
Bank::Qspi => crate::pac::IO_QSPI,
|
||||
};
|
||||
let proc = unsafe { SIO.cpuid().read() };
|
||||
io_block.int_proc(proc as _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -478,6 +720,8 @@ impl_pin!(PIN_QSPI_SD3, Bank::Qspi, 5);
|
||||
// ====================
|
||||
|
||||
mod eh02 {
|
||||
use futures::FutureExt;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Input<'d, T> {
|
||||
@ -595,6 +839,62 @@ mod eh02 {
|
||||
Ok(self.toggle())
|
||||
}
|
||||
}
|
||||
|
||||
use core::convert::Infallible;
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_async::digital::Wait for Flex<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_async::digital::Wait for Input<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
|
Reference in New Issue
Block a user