aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2021-12-17 12:50:48 +0100
committerUlf Lilleengen <[email protected]>2021-12-17 12:54:51 +0100
commit3811c0a401281ece6e7adb24238ebf7ff39a2362 (patch)
tree2c8ac7efe3c39b326c8a5acd9e637bf03717dfa3
parentad2f4694070104f8815563a0008141eec29c06cd (diff)
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.
-rw-r--r--embassy-stm32/Cargo.toml1
-rw-r--r--embassy-stm32/src/usart/mod.rs31
-rw-r--r--embassy-traits/Cargo.toml1
-rw-r--r--embassy-traits/src/adapter.rs166
-rw-r--r--embassy-traits/src/lib.rs1
-rw-r--r--examples/stm32l4/src/bin/i2c_blocking_async.rs29
-rw-r--r--examples/stm32l4/src/bin/spi_blocking_async.rs57
-rw-r--r--examples/stm32l4/src/bin/usart_blocking_async.rs32
8 files changed, 316 insertions, 2 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 135f9a918..264005850 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -27,6 +27,7 @@ atomic-polyfill = "0.1.5"
27stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } 27stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
28vcell = { version = "0.1.3", optional = true } 28vcell = { version = "0.1.3", optional = true }
29bxcan = "0.6.2" 29bxcan = "0.6.2"
30nb = "1.0.0"
30 31
31seq-macro = "0.2.2" 32seq-macro = "0.2.2"
32 33
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index b51a728c0..7e4df619c 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -192,8 +192,35 @@ impl<'d, T: Instance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
192 } 192 }
193} 193}
194 194
195impl<'d, T: Instance, RxDma> embedded_hal::blocking::serial::Write<u8> 195impl<'d, T: Instance, TxDma, RxDma> embedded_hal::serial::Read<u8> for Uart<'d, T, TxDma, RxDma> {
196 for Uart<'d, T, NoDma, RxDma> 196 type Error = Error;
197 fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
198 let r = self.inner.regs();
199 unsafe {
200 let sr = sr(r).read();
201 if sr.pe() {
202 rdr(r).read_volatile();
203 Err(nb::Error::Other(Error::Parity))
204 } else if sr.fe() {
205 rdr(r).read_volatile();
206 Err(nb::Error::Other(Error::Framing))
207 } else if sr.ne() {
208 rdr(r).read_volatile();
209 Err(nb::Error::Other(Error::Noise))
210 } else if sr.ore() {
211 rdr(r).read_volatile();
212 Err(nb::Error::Other(Error::Overrun))
213 } else if sr.rxne() {
214 Ok(rdr(r).read_volatile())
215 } else {
216 Err(nb::Error::WouldBlock)
217 }
218 }
219 }
220}
221
222impl<'d, T: Instance, TxDma, RxDma> embedded_hal::blocking::serial::Write<u8>
223 for Uart<'d, T, TxDma, RxDma>
197{ 224{
198 type Error = Error; 225 type Error = Error;
199 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { 226 fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
diff --git a/embassy-traits/Cargo.toml b/embassy-traits/Cargo.toml
index c98b583e5..a93cb3c54 100644
--- a/embassy-traits/Cargo.toml
+++ b/embassy-traits/Cargo.toml
@@ -10,3 +10,4 @@ std = []
10[dependencies] 10[dependencies]
11defmt = { version = "0.3", optional = true } 11defmt = { version = "0.3", optional = true }
12embedded-hal = { version = "0.2.6", features = ["unproven"] } 12embedded-hal = { version = "0.2.6", features = ["unproven"] }
13nb = "1.0.0"
diff --git a/embassy-traits/src/adapter.rs b/embassy-traits/src/adapter.rs
new file mode 100644
index 000000000..ce7dd411f
--- /dev/null
+++ b/embassy-traits/src/adapter.rs
@@ -0,0 +1,166 @@
1use core::future::Future;
2use embedded_hal::blocking;
3use embedded_hal::serial;
4
5/// BlockingAsync is a wrapper that implements async traits using blocking peripherals. This allows
6/// driver writers to depend on the async traits while still supporting embedded-hal peripheral implementations.
7///
8/// BlockingAsync will implement any async trait that maps to embedded-hal traits implemented for the wrapped driver.
9///
10/// Driver users are then free to choose which implementation that is available to them.
11pub struct BlockingAsync<T> {
12 wrapped: T,
13}
14
15impl<T> BlockingAsync<T> {
16 /// Create a new instance of a wrapper for a given peripheral.
17 pub fn new(wrapped: T) -> Self {
18 Self { wrapped }
19 }
20}
21
22//
23// I2C implementatinos
24//
25
26impl<T, E> crate::i2c::I2c for BlockingAsync<T>
27where
28 E: 'static,
29 T: blocking::i2c::WriteRead<Error = E>
30 + blocking::i2c::Read<Error = E>
31 + blocking::i2c::Write<Error = E>,
32{
33 type Error = E;
34
35 #[rustfmt::skip]
36 type WriteFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
37 #[rustfmt::skip]
38 type ReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
39 #[rustfmt::skip]
40 type WriteReadFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
41
42 fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
43 async move { self.wrapped.read(address, buffer) }
44 }
45
46 fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
47 async move { self.wrapped.write(address, bytes) }
48 }
49
50 fn write_read<'a>(
51 &'a mut self,
52 address: u8,
53 bytes: &'a [u8],
54 buffer: &'a mut [u8],
55 ) -> Self::WriteReadFuture<'a> {
56 async move { self.wrapped.write_read(address, bytes, buffer) }
57 }
58}
59
60//
61// SPI implementatinos
62//
63
64impl<T, E, Word> crate::spi::Spi<Word> for BlockingAsync<T>
65where
66 T: blocking::spi::Write<Word, Error = E>,
67{
68 type Error = E;
69}
70
71impl<T, E, Word> crate::spi::FullDuplex<Word> for BlockingAsync<T>
72where
73 E: 'static,
74 Word: Clone,
75 T: blocking::spi::Transfer<Word, Error = E> + blocking::spi::Write<Word, Error = E>,
76{
77 #[rustfmt::skip]
78 type WriteReadFuture<'a> where Word: 'a, Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
79
80 fn read_write<'a>(
81 &'a mut self,
82 read: &'a mut [Word],
83 write: &'a [Word],
84 ) -> Self::WriteReadFuture<'a> {
85 async move {
86 // Ensure we write the expected bytes
87 for i in 0..core::cmp::min(read.len(), write.len()) {
88 read[i] = write[i].clone();
89 }
90 self.wrapped.transfer(read)?;
91 Ok(())
92 }
93 }
94}
95
96impl<T, E, Word> crate::spi::Write<Word> for BlockingAsync<T>
97where
98 E: 'static,
99 Word: Clone,
100 T: blocking::spi::Write<Word, Error = E>,
101{
102 #[rustfmt::skip]
103 type WriteFuture<'a> where Word: 'a, Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
104
105 fn write<'a>(&'a mut self, data: &'a [Word]) -> Self::WriteFuture<'a> {
106 async move { self.wrapped.write(data) }
107 }
108}
109
110impl<T, E, Word> crate::spi::Read<Word> for BlockingAsync<T>
111where
112 E: 'static,
113 Word: Clone,
114 T: blocking::spi::Transfer<Word, Error = E> + blocking::spi::Write<Word, Error = E>,
115{
116 #[rustfmt::skip]
117 type ReadFuture<'a> where Word: 'a, Self: 'a = impl Future<Output = Result<(), Self::Error>> + 'a;
118
119 fn read<'a>(&'a mut self, data: &'a mut [Word]) -> Self::ReadFuture<'a> {
120 async move {
121 self.wrapped.transfer(data)?;
122 Ok(())
123 }
124 }
125}
126
127// Uart implementatinos
128impl<T> crate::uart::Read for BlockingAsync<T>
129where
130 T: serial::Read<u8>,
131{
132 #[rustfmt::skip]
133 type ReadFuture<'a> where T: 'a = impl Future<Output = Result<(), crate::uart::Error>> + 'a;
134 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
135 async move {
136 let mut pos = 0;
137 while pos < buf.len() {
138 match self.wrapped.read() {
139 Err(nb::Error::WouldBlock) => {}
140 Err(_) => return Err(crate::uart::Error::Other),
141 Ok(b) => {
142 buf[pos] = b;
143 pos += 1;
144 }
145 }
146 }
147 Ok(())
148 }
149 }
150}
151
152impl<T> crate::uart::Write for BlockingAsync<T>
153where
154 T: blocking::serial::Write<u8>,
155{
156 #[rustfmt::skip]
157 type WriteFuture<'a> where T: 'a = impl Future<Output = Result<(), crate::uart::Error>> + 'a;
158 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
159 async move {
160 self.wrapped
161 .bwrite_all(buf)
162 .map_err(|_| crate::uart::Error::Other)?;
163 self.wrapped.bflush().map_err(|_| crate::uart::Error::Other)
164 }
165 }
166}
diff --git a/embassy-traits/src/lib.rs b/embassy-traits/src/lib.rs
index 65fb95bd4..a5342b77e 100644
--- a/embassy-traits/src/lib.rs
+++ b/embassy-traits/src/lib.rs
@@ -2,6 +2,7 @@
2#![feature(generic_associated_types)] 2#![feature(generic_associated_types)]
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4 4
5pub mod adapter;
5pub mod delay; 6pub mod delay;
6pub mod flash; 7pub mod flash;
7pub mod gpio; 8pub mod gpio;
diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs
new file mode 100644
index 000000000..0339ed4d3
--- /dev/null
+++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs
@@ -0,0 +1,29 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7
8use embassy::executor::Spawner;
9use embassy_stm32::dma::NoDma;
10use embassy_stm32::i2c::I2c;
11use embassy_stm32::interrupt;
12use embassy_stm32::time::Hertz;
13use embassy_stm32::Peripherals;
14use embassy_traits::{adapter::BlockingAsync, i2c::I2c as _};
15use example_common::{info, unwrap};
16
17const ADDRESS: u8 = 0x5F;
18const WHOAMI: u8 = 0x0F;
19
20#[embassy::main]
21async fn main(_spawner: Spawner, p: Peripherals) -> ! {
22 let irq = interrupt::take!(I2C2_EV);
23 let i2c = I2c::new(p.I2C2, p.PB10, p.PB11, irq, NoDma, NoDma, Hertz(100_000));
24 let mut i2c = BlockingAsync::new(i2c);
25
26 let mut data = [0u8; 1];
27 unwrap!(i2c.write_read(ADDRESS, &[WHOAMI], &mut data).await);
28 info!("Whoami: {}", data[0]);
29}
diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs
new file mode 100644
index 000000000..f092706d4
--- /dev/null
+++ b/examples/stm32l4/src/bin/spi_blocking_async.rs
@@ -0,0 +1,57 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7
8use embassy::executor::Spawner;
9use embassy_stm32::dma::NoDma;
10use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
11use embassy_stm32::spi::{Config, Spi};
12use embassy_stm32::time::Hertz;
13use embassy_stm32::Peripherals;
14use embassy_traits::{adapter::BlockingAsync, spi::FullDuplex};
15use embedded_hal::digital::v2::{InputPin, OutputPin};
16use example_common::*;
17
18#[embassy::main]
19async fn main(_spawner: Spawner, p: Peripherals) {
20 info!("Hello World!");
21
22 let spi = Spi::new(
23 p.SPI3,
24 p.PC10,
25 p.PC12,
26 p.PC11,
27 NoDma,
28 NoDma,
29 Hertz(1_000_000),
30 Config::default(),
31 );
32
33 let mut spi = BlockingAsync::new(spi);
34
35 // These are the pins for the Inventek eS-Wifi SPI Wifi Adapter.
36
37 let _boot = Output::new(p.PB12, Level::Low, Speed::VeryHigh);
38 let _wake = Output::new(p.PB13, Level::Low, Speed::VeryHigh);
39 let mut reset = Output::new(p.PE8, Level::Low, Speed::VeryHigh);
40 let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
41 let ready = Input::new(p.PE1, Pull::Up);
42
43 cortex_m::asm::delay(100_000);
44 unwrap!(reset.set_high());
45 cortex_m::asm::delay(100_000);
46
47 while unwrap!(ready.is_low()) {
48 info!("waiting for ready");
49 }
50
51 let write = [0x0A; 10];
52 let mut read = [0; 10];
53 unwrap!(cs.set_low());
54 spi.read_write(&mut read, &write).await.ok();
55 unwrap!(cs.set_high());
56 info!("xfer {=[u8]:x}", read);
57}
diff --git a/examples/stm32l4/src/bin/usart_blocking_async.rs b/examples/stm32l4/src/bin/usart_blocking_async.rs
new file mode 100644
index 000000000..679d4e0da
--- /dev/null
+++ b/examples/stm32l4/src/bin/usart_blocking_async.rs
@@ -0,0 +1,32 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7
8use embassy::executor::Spawner;
9use embassy::traits::{
10 adapter::BlockingAsync,
11 uart::{Read, Write},
12};
13use embassy_stm32::dma::NoDma;
14use embassy_stm32::usart::{Config, Uart};
15use embassy_stm32::Peripherals;
16use example_common::*;
17
18#[embassy::main]
19async fn main(_spawner: Spawner, p: Peripherals) {
20 let config = Config::default();
21 let usart = Uart::new(p.UART4, p.PA1, p.PA0, NoDma, NoDma, config);
22 let mut usart = BlockingAsync::new(usart);
23
24 unwrap!(usart.write(b"Hello Embassy World!\r\n").await);
25 info!("wrote Hello, starting echo");
26
27 let mut buf = [0u8; 1];
28 loop {
29 unwrap!(usart.read(&mut buf).await);
30 unwrap!(usart.write(&buf).await);
31 }
32}