Add adapter for implementing async traits for blocking types
This allows writing drivers relying on async traits, while still functioning with implementations that already implement the embedded-hal traits. Add examples to stm32l4 for using this feature.
This commit is contained in:
parent
ad2f469407
commit
3811c0a401
@ -27,6 +27,7 @@ atomic-polyfill = "0.1.5"
|
|||||||
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
|
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
|
||||||
vcell = { version = "0.1.3", optional = true }
|
vcell = { version = "0.1.3", optional = true }
|
||||||
bxcan = "0.6.2"
|
bxcan = "0.6.2"
|
||||||
|
nb = "1.0.0"
|
||||||
|
|
||||||
seq-macro = "0.2.2"
|
seq-macro = "0.2.2"
|
||||||
|
|
||||||
|
@ -192,8 +192,35 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, RxDma> embedded_hal::blocking::serial::Write<u8>
|
impl<'d, T: Instance, TxDma, RxDma> embedded_hal::serial::Read<u8> for Uart<'d, T, TxDma, RxDma> {
|
||||||
for Uart<'d, T, NoDma, RxDma>
|
type Error = Error;
|
||||||
|
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||||
|
let r = self.inner.regs();
|
||||||
|
unsafe {
|
||||||
|
let sr = sr(r).read();
|
||||||
|
if sr.pe() {
|
||||||
|
rdr(r).read_volatile();
|
||||||
|
Err(nb::Error::Other(Error::Parity))
|
||||||
|
} else if sr.fe() {
|
||||||
|
rdr(r).read_volatile();
|
||||||
|
Err(nb::Error::Other(Error::Framing))
|
||||||
|
} else if sr.ne() {
|
||||||
|
rdr(r).read_volatile();
|
||||||
|
Err(nb::Error::Other(Error::Noise))
|
||||||
|
} else if sr.ore() {
|
||||||
|
rdr(r).read_volatile();
|
||||||
|
Err(nb::Error::Other(Error::Overrun))
|
||||||
|
} else if sr.rxne() {
|
||||||
|
Ok(rdr(r).read_volatile())
|
||||||
|
} else {
|
||||||
|
Err(nb::Error::WouldBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, TxDma, RxDma> embedded_hal::blocking::serial::Write<u8>
|
||||||
|
for Uart<'d, T, TxDma, RxDma>
|
||||||
{
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
@ -10,3 +10,4 @@ std = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
embedded-hal = { version = "0.2.6", features = ["unproven"] }
|
embedded-hal = { version = "0.2.6", features = ["unproven"] }
|
||||||
|
nb = "1.0.0"
|
||||||
|
166
embassy-traits/src/adapter.rs
Normal file
166
embassy-traits/src/adapter.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
use core::future::Future;
|
||||||
|
use embedded_hal::blocking;
|
||||||
|
use embedded_hal::serial;
|
||||||
|
|
||||||
|
/// BlockingAsync is a wrapper that implements async traits using blocking peripherals. This allows
|
||||||
|
/// driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations.
|
||||||
|
///
|
||||||
|
/// BlockingAsync will implement any async trait that maps to embedded-hal traits implemented for the wrapped driver.
|
||||||
|
///
|
||||||
|
/// Driver users are then free to choose which implementation that is available to them.
|
||||||
|
pub struct BlockingAsync<T> {
|
||||||
|
wrapped: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BlockingAsync<T> {
|
||||||
|
/// Create a new instance of a wrapper for a given peripheral.
|
||||||
|
pub fn new(wrapped: T) -> Self {
|
||||||
|
Self { wrapped }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// I2C implementatinos
|
||||||
|
//
|
||||||
|
|
||||||
|
impl<T, E> crate::i2c::I2c for BlockingAsync<T>
|
||||||
|
where
|
||||||
|
E: 'static,
|
||||||
|
T: blocking::i2c::WriteRead<Error = E>
|
||||||
|
+ blocking::i2c::Read<Error = E>
|
||||||
|
+ blocking::i2c::Write<Error = E>,
|
||||||
|
{
|
||||||
|
type Error = E;
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type WriteReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||||
|
|
||||||
|
fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
|
async move { self.wrapped.read(address, buffer) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
|
async move { self.wrapped.write(address, bytes) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_read<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
address: u8,
|
||||||
|
bytes: &'a [u8],
|
||||||
|
buffer: &'a mut [u8],
|
||||||
|
) -> Self::WriteReadFuture<'a> {
|
||||||
|
async move { self.wrapped.write_read(address, bytes, buffer) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SPI implementatinos
|
||||||
|
//
|
||||||
|
|
||||||
|
impl<T, E, Word> crate::spi::Spi<Word> for BlockingAsync<T>
|
||||||
|
where
|
||||||
|
T: blocking::spi::Write<Word, Error = E>,
|
||||||
|
{
|
||||||
|
type Error = E;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, Word> crate::spi::FullDuplex<Word> for BlockingAsync<T>
|
||||||
|
where
|
||||||
|
E: 'static,
|
||||||
|
Word: Clone,
|
||||||
|
T: blocking::spi::Transfer<Word, Error = E> + blocking::spi::Write<Word, Error = E>,
|
||||||
|
{
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type WriteReadFuture<'a> where Word: 'a, Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||||
|
|
||||||
|
fn read_write<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
read: &'a mut [Word],
|
||||||
|
write: &'a [Word],
|
||||||
|
) -> Self::WriteReadFuture<'a> {
|
||||||
|
async move {
|
||||||
|
// Ensure we write the expected bytes
|
||||||
|
for i in 0..core::cmp::min(read.len(), write.len()) {
|
||||||
|
read[i] = write[i].clone();
|
||||||
|
}
|
||||||
|
self.wrapped.transfer(read)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, Word> crate::spi::Write<Word> for BlockingAsync<T>
|
||||||
|
where
|
||||||
|
E: 'static,
|
||||||
|
Word: Clone,
|
||||||
|
T: blocking::spi::Write<Word, Error = E>,
|
||||||
|
{
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type WriteFuture<'a> where Word: 'a, Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||||
|
|
||||||
|
fn write<'a>(&'a mut self, data: &'a [Word]) -> Self::WriteFuture<'a> {
|
||||||
|
async move { self.wrapped.write(data) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, Word> crate::spi::Read<Word> for BlockingAsync<T>
|
||||||
|
where
|
||||||
|
E: 'static,
|
||||||
|
Word: Clone,
|
||||||
|
T: blocking::spi::Transfer<Word, Error = E> + blocking::spi::Write<Word, Error = E>,
|
||||||
|
{
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type ReadFuture<'a> where Word: 'a, Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||||
|
|
||||||
|
fn read<'a>(&'a mut self, data: &'a mut [Word]) -> Self::ReadFuture<'a> {
|
||||||
|
async move {
|
||||||
|
self.wrapped.transfer(data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uart implementatinos
|
||||||
|
impl<T> crate::uart::Read for BlockingAsync<T>
|
||||||
|
where
|
||||||
|
T: serial::Read<u8>,
|
||||||
|
{
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type ReadFuture<'a> where T: 'a = impl Future<Output = Result<(), crate::uart::Error>> + 'a;
|
||||||
|
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
|
async move {
|
||||||
|
let mut pos = 0;
|
||||||
|
while pos < buf.len() {
|
||||||
|
match self.wrapped.read() {
|
||||||
|
Err(nb::Error::WouldBlock) => {}
|
||||||
|
Err(_) => return Err(crate::uart::Error::Other),
|
||||||
|
Ok(b) => {
|
||||||
|
buf[pos] = b;
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> crate::uart::Write for BlockingAsync<T>
|
||||||
|
where
|
||||||
|
T: blocking::serial::Write<u8>,
|
||||||
|
{
|
||||||
|
#[rustfmt::skip]
|
||||||
|
type WriteFuture<'a> where T: 'a = impl Future<Output = Result<(), crate::uart::Error>> + 'a;
|
||||||
|
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
|
async move {
|
||||||
|
self.wrapped
|
||||||
|
.bwrite_all(buf)
|
||||||
|
.map_err(|_| crate::uart::Error::Other)?;
|
||||||
|
self.wrapped.bflush().map_err(|_| crate::uart::Error::Other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
#![feature(generic_associated_types)]
|
#![feature(generic_associated_types)]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
pub mod adapter;
|
||||||
pub mod delay;
|
pub mod delay;
|
||||||
pub mod flash;
|
pub mod flash;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
|
29
examples/stm32l4/src/bin/i2c_blocking_async.rs
Normal file
29
examples/stm32l4/src/bin/i2c_blocking_async.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_stm32::dma::NoDma;
|
||||||
|
use embassy_stm32::i2c::I2c;
|
||||||
|
use embassy_stm32::interrupt;
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::{adapter::BlockingAsync, i2c::I2c as _};
|
||||||
|
use example_common::{info, unwrap};
|
||||||
|
|
||||||
|
const ADDRESS: u8 = 0x5F;
|
||||||
|
const WHOAMI: u8 = 0x0F;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) -> ! {
|
||||||
|
let irq = interrupt::take!(I2C2_EV);
|
||||||
|
let i2c = I2c::new(p.I2C2, p.PB10, p.PB11, irq, NoDma, NoDma, Hertz(100_000));
|
||||||
|
let mut i2c = BlockingAsync::new(i2c);
|
||||||
|
|
||||||
|
let mut data = [0u8; 1];
|
||||||
|
unwrap!(i2c.write_read(ADDRESS, &[WHOAMI], &mut data).await);
|
||||||
|
info!("Whoami: {}", data[0]);
|
||||||
|
}
|
57
examples/stm32l4/src/bin/spi_blocking_async.rs
Normal file
57
examples/stm32l4/src/bin/spi_blocking_async.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy_stm32::dma::NoDma;
|
||||||
|
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||||
|
use embassy_stm32::spi::{Config, Spi};
|
||||||
|
use embassy_stm32::time::Hertz;
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use embassy_traits::{adapter::BlockingAsync, spi::FullDuplex};
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin};
|
||||||
|
use example_common::*;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
info!("Hello World!");
|
||||||
|
|
||||||
|
let spi = Spi::new(
|
||||||
|
p.SPI3,
|
||||||
|
p.PC10,
|
||||||
|
p.PC12,
|
||||||
|
p.PC11,
|
||||||
|
NoDma,
|
||||||
|
NoDma,
|
||||||
|
Hertz(1_000_000),
|
||||||
|
Config::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut spi = BlockingAsync::new(spi);
|
||||||
|
|
||||||
|
// These are the pins for the Inventek eS-Wifi SPI Wifi Adapter.
|
||||||
|
|
||||||
|
let _boot = Output::new(p.PB12, Level::Low, Speed::VeryHigh);
|
||||||
|
let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);
|
||||||
|
let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);
|
||||||
|
let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
|
||||||
|
let ready = Input::new(p.PE1, Pull::Up);
|
||||||
|
|
||||||
|
cortex_m::asm::delay(100_000);
|
||||||
|
unwrap!(reset.set_high());
|
||||||
|
cortex_m::asm::delay(100_000);
|
||||||
|
|
||||||
|
while unwrap!(ready.is_low()) {
|
||||||
|
info!("waiting for ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
let write = [0x0A; 10];
|
||||||
|
let mut read = [0; 10];
|
||||||
|
unwrap!(cs.set_low());
|
||||||
|
spi.read_write(&mut read, &write).await.ok();
|
||||||
|
unwrap!(cs.set_high());
|
||||||
|
info!("xfer {=[u8]:x}", read);
|
||||||
|
}
|
32
examples/stm32l4/src/bin/usart_blocking_async.rs
Normal file
32
examples/stm32l4/src/bin/usart_blocking_async.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
#[path = "../example_common.rs"]
|
||||||
|
mod example_common;
|
||||||
|
|
||||||
|
use embassy::executor::Spawner;
|
||||||
|
use embassy::traits::{
|
||||||
|
adapter::BlockingAsync,
|
||||||
|
uart::{Read, Write},
|
||||||
|
};
|
||||||
|
use embassy_stm32::dma::NoDma;
|
||||||
|
use embassy_stm32::usart::{Config, Uart};
|
||||||
|
use embassy_stm32::Peripherals;
|
||||||
|
use example_common::*;
|
||||||
|
|
||||||
|
#[embassy::main]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
|
let config = Config::default();
|
||||||
|
let usart = Uart::new(p.UART4, p.PA1, p.PA0, NoDma, NoDma, config);
|
||||||
|
let mut usart = BlockingAsync::new(usart);
|
||||||
|
|
||||||
|
unwrap!(usart.write(b"Hello Embassy World!\r\n").await);
|
||||||
|
info!("wrote Hello, starting echo");
|
||||||
|
|
||||||
|
let mut buf = [0u8; 1];
|
||||||
|
loop {
|
||||||
|
unwrap!(usart.read(&mut buf).await);
|
||||||
|
unwrap!(usart.write(&buf).await);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user