From bed45e741ef09bde8a5a2ef9f84412f4ac54c5dc Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Wed, 6 Sep 2023 09:36:48 +0300 Subject: [PATCH] Upgrade to e-hal 1.0-rc1 (#295) * Upgrade to e-hal 1.0-rc1 * e-hal is no longer alpha * Fix for ESP IDF < 5 * Unite all delay impls in a single module * Model delays between transactions * SPI: Make queueing it a bit more readable * SPI: Plug delays * Shorten the threshold for the Delay provider * Clippy * Fix the examples * SPI: Detect last transaction in the presence of delays * SPI: Introduce CsPin * SPI: Introduce CsPin * SPI: Mark delays with TODO * Clippy * SPI: Rename CsPin to CsCtl * Transfer_transaction not necessary --- CHANGELOG.md | 1 + Cargo.toml | 10 +- README.md | 2 +- examples/button.rs | 4 +- examples/timer_notify.rs | 2 +- src/adc.rs | 52 +++ src/{delay/mod.rs => delay.rs} | 70 ++- src/delay/general_purpose.rs | 58 --- src/i2s.rs | 53 +++ src/io.rs | 31 ++ src/lib.rs | 2 + src/spi.rs | 794 +++++++++++++++++++-------------- src/uart.rs | 93 +++- 13 files changed, 738 insertions(+), 434 deletions(-) rename src/{delay/mod.rs => delay.rs} (77%) delete mode 100644 src/delay/general_purpose.rs create mode 100644 src/io.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 543a4fcd5..a14c8d0ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Upgraded to `embedded-hal` 1.0.0-rc.1 and `embedded-hal-async` 1.0.0-rc.1 * OTA: `EspOtaUpdate` now parametric over time and returned by value * Dependency `esp-idf-sys` now re-exported as `esp_idf_hal::sys` +* Breaking change: `delay::Delay` struct extended with configurable threshold ## [0.41.2] - 2023-06-21 diff --git a/Cargo.toml b/Cargo.toml index fa682b4d1..e1ac23cc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ esp-idf-sys = { git = "https://github.com/esp-rs/esp-idf-sys" } default = ["std", "native", "binstart"] std = ["alloc", "esp-idf-sys/std", "edge-executor?/std"] alloc = [] -nightly = ["embedded-hal-async"] +nightly = ["embedded-hal-async", "embedded-io-async"] esp-idf-sys = ["dep:esp-idf-sys", "atomic-waker"] riscv-ulp-hal = [] embassy-sync = [] # Only for backwards compatibility @@ -38,10 +38,12 @@ libstart = ["esp-idf-sys/libstart"] [dependencies] nb = "1.0.0" embedded-can = "0.4.1" -embedded-hal = "=1.0.0-alpha.10" +embedded-hal = "=1.0.0-rc.1" embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } -embedded-hal-nb = "=1.0.0-alpha.2" -embedded-hal-async = { version = "0.2.0-alpha.1", optional = true } +embedded-hal-nb = "=1.0.0-rc.1" +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true } +embedded-io = "0.5" +embedded-io-async = { version = "0.5", optional = true } esp-idf-sys = { version = "0.33", optional = true, default-features = false, features = ["native"] } critical-section = { version = "1.1.1", optional = true } heapless = "0.7" diff --git a/README.md b/README.md index 58f9dc3e2..5b903af7f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ## Highlights -* Implements the traits of [embedded-hal](https://github.com/rust-embedded/embedded-hal) `V0.2` as well as those of `V1.0.alpha` +* Implements the traits of [embedded-hal](https://github.com/rust-embedded/embedded-hal) `V0.2` as well as those of `V1.0` - both blocking and async * Supports almost all ESP IDF drivers: GPIO, SPI, I2C, TIMER, PWM, I2S, UART, etc. * Blocking and `async` mode for each driver (`async` support in progress) * Re-exports `esp-idf-sys` as `esp_idf_hal::sys` diff --git a/examples/button.rs b/examples/button.rs index 39866d4c6..34a8689eb 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -7,7 +7,7 @@ //! Depending on your target and the board you are using you should change the pins. //! If your board doesn't have on-board LEDs don't forget to add an appropriate resistor. -use esp_idf_hal::delay::Delay; +use esp_idf_hal::delay::FreeRtos; use esp_idf_hal::gpio::*; use esp_idf_hal::peripherals::Peripherals; @@ -22,7 +22,7 @@ fn main() -> anyhow::Result<()> { loop { // we are using thread::sleep here to make sure the watchdog isn't triggered - Delay::delay_ms(10); + FreeRtos::delay_ms(10); if button.is_high() { led.set_low()?; diff --git a/examples/timer_notify.rs b/examples/timer_notify.rs index e7bff42db..db7893f91 100644 --- a/examples/timer_notify.rs +++ b/examples/timer_notify.rs @@ -1,4 +1,4 @@ -use esp_idf_hal::sys::{self as _, EspError, TaskHandle_t}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported +use esp_idf_hal::sys::{EspError, TaskHandle_t}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported fn main() -> Result<(), EspError> { // It is necessary to call this function once. Otherwise some patches to the runtime diff --git a/src/adc.rs b/src/adc.rs index ed6d9275f..6967fb67c 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -459,6 +459,7 @@ pub mod continuous { use crate::delay::{self, TickType}; use crate::gpio::{sealed::ADCPin as _, ADCPin}; + use crate::io::EspIOError; use crate::peripheral::Peripheral; use crate::private::notification::Notification; @@ -903,6 +904,26 @@ pub mod continuous { Ok(read as usize / core::mem::size_of::()) } + pub fn read_bytes( + &mut self, + buf: &mut [u8], + timeout: TickType_t, + ) -> Result { + let mut read: u32 = 0; + + esp!(unsafe { + adc_continuous_read( + self.handle, + buf.as_mut_ptr() as *mut _, + core::mem::size_of_val(buf) as _, + &mut read, + TickType(timeout).as_millis_u32(), + ) + })?; + + Ok(read as usize) + } + #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))] pub async fn read_async(&mut self, buf: &mut [AdcMeasurement]) -> Result { loop { @@ -916,6 +937,19 @@ pub mod continuous { } } + #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))] + pub async fn read_bytes_async(&mut self, buf: &mut [u8]) -> Result { + loop { + match self.read_bytes(buf, delay::NON_BLOCK) { + Ok(len) if len > 0 => return Ok(len), + Err(e) if e.code() != ESP_ERR_TIMEOUT => return Err(e), + _ => { + NOTIFIER[self.adc as usize].wait().await; + } + } + } + } + #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))] extern "C" fn handle_isr( _handle: adc_continuous_handle_t, @@ -951,6 +985,24 @@ pub mod continuous { unsafe impl<'d> Send for AdcDriver<'d> {} + impl<'d> embedded_io::ErrorType for AdcDriver<'d> { + type Error = EspIOError; + } + + impl<'d> embedded_io::Read for AdcDriver<'d> { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_bytes(buf, delay::BLOCK).map_err(EspIOError) + } + } + + #[cfg(feature = "nightly")] + #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))] + impl<'d> embedded_io_async::Read for AdcDriver<'d> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_bytes_async(buf).await.map_err(EspIOError) + } + } + #[cfg(not(esp_idf_adc_continuous_isr_iram_safe))] #[cfg(any(esp32c2, esp32h2, esp32c5, esp32c6, esp32p4))] // TODO: Check for esp32c5 and esp32p4 static NOTIFIER: [Notification; 1] = [Notification::new()]; diff --git a/src/delay/mod.rs b/src/delay.rs similarity index 77% rename from src/delay/mod.rs rename to src/delay.rs index c6b207c8c..de8406291 100644 --- a/src/delay/mod.rs +++ b/src/delay.rs @@ -8,10 +8,6 @@ use core::{cmp::min, time::Duration}; use esp_idf_sys::*; -mod general_purpose; - -pub use general_purpose::Delay; - #[allow(non_upper_case_globals)] pub const BLOCK: TickType_t = TickType_t::MAX; @@ -231,3 +227,69 @@ impl embedded_hal::delay::DelayUs for FreeRtos { FreeRtos::delay_ms(ms) } } + +/// A delay provider that uses [`Ets`] for delays below a certain threshold +/// and [`FreeRtos`] for delays equal or above the threshold. +#[derive(Copy, Clone)] +pub struct Delay(u32); + +impl Delay { + /// Create a delay with a default threshold of 1ms + pub const fn new_default() -> Self { + Self::new(1000) + } + + pub const fn new(threshold: u32) -> Self { + Self(threshold) + } + + pub fn delay_us(&self, us: u32) { + if us < self.0 { + Ets::delay_us(us); + } else { + FreeRtos::delay_us(us); + } + } + + pub fn delay_ms(&self, ms: u32) { + if ms * 1000 < self.0 { + Ets::delay_ms(ms); + } else { + FreeRtos::delay_ms(ms); + } + } +} + +impl embedded_hal::delay::DelayUs for Delay { + fn delay_us(&mut self, us: u32) { + Delay::delay_us(self, us) + } + + fn delay_ms(&mut self, ms: u32) { + Delay::delay_ms(self, ms) + } +} + +impl embedded_hal_0_2::blocking::delay::DelayUs for Delay { + fn delay_us(&mut self, us: u16) { + Delay::delay_us(self, us as _); + } +} + +impl embedded_hal_0_2::blocking::delay::DelayUs for Delay { + fn delay_us(&mut self, us: u32) { + Delay::delay_us(self, us); + } +} + +impl embedded_hal_0_2::blocking::delay::DelayMs for Delay { + fn delay_ms(&mut self, ms: u16) { + Delay::delay_ms(self, ms as _) + } +} + +impl embedded_hal_0_2::blocking::delay::DelayMs for Delay { + fn delay_ms(&mut self, ms: u32) { + Delay::delay_ms(self, ms) + } +} diff --git a/src/delay/general_purpose.rs b/src/delay/general_purpose.rs deleted file mode 100644 index b3bd56eae..000000000 --- a/src/delay/general_purpose.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::delay::{Ets, FreeRtos}; - -/// A delay provider that uses [`Ets`] for delays <10ms, and [`FreeRtos`] for -/// delays >=10 ms -#[derive(Copy, Clone)] -pub struct Delay; - -impl Delay { - pub fn delay_us(us: u32) { - if us < 10_000 { - Ets::delay_us(us); - } else { - FreeRtos::delay_us(us); - } - } - - pub fn delay_ms(ms: u32) { - if ms < 10 { - Ets::delay_ms(ms); - } else { - FreeRtos::delay_ms(ms); - } - } -} - -impl embedded_hal::delay::DelayUs for Delay { - fn delay_us(&mut self, us: u32) { - Delay::delay_us(us) - } - - fn delay_ms(&mut self, ms: u32) { - Delay::delay_ms(ms) - } -} - -impl embedded_hal_0_2::blocking::delay::DelayUs for Delay { - fn delay_us(&mut self, us: u16) { - Delay::delay_us(us as _); - } -} - -impl embedded_hal_0_2::blocking::delay::DelayUs for Delay { - fn delay_us(&mut self, us: u32) { - Delay::delay_us(us); - } -} - -impl embedded_hal_0_2::blocking::delay::DelayMs for Delay { - fn delay_ms(&mut self, ms: u16) { - Delay::delay_ms(ms as _) - } -} - -impl embedded_hal_0_2::blocking::delay::DelayMs for Delay { - fn delay_ms(&mut self, ms: u32) { - Delay::delay_ms(ms) - } -} diff --git a/src/i2s.rs b/src/i2s.rs index 11b76ec86..7c079b368 100644 --- a/src/i2s.rs +++ b/src/i2s.rs @@ -22,6 +22,7 @@ use esp_idf_sys::{ #[cfg(not(esp_idf_version_major = "4"))] use crate::private::notification::Notification; +use crate::{delay, io::EspIOError}; // For v5+, we rely configuration options for PDM/TDM support. // For v4, we have to examine the chip type. @@ -1184,6 +1185,58 @@ impl<'d, Dir> I2sPort for I2sDriver<'d, Dir> { } } +impl<'d, Dir> embedded_io::ErrorType for I2sDriver<'d, Dir> { + type Error = EspIOError; +} + +impl<'d, Dir> embedded_io::Read for I2sDriver<'d, Dir> +where + Dir: I2sRxSupported, +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf, delay::BLOCK).map_err(EspIOError) + } +} + +impl<'d, Dir> embedded_io::Write for I2sDriver<'d, Dir> +where + Dir: I2sTxSupported, +{ + fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf, delay::BLOCK).map_err(EspIOError) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + +#[cfg(feature = "nightly")] +#[cfg(not(esp_idf_version_major = "4"))] +impl<'d, Dir> embedded_io_async::Read for I2sDriver<'d, Dir> +where + Dir: I2sRxSupported, +{ + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_async(buf).await.map_err(EspIOError) + } +} + +#[cfg(feature = "nightly")] +#[cfg(not(esp_idf_version_major = "4"))] +impl<'d, Dir> embedded_io_async::Write for I2sDriver<'d, Dir> +where + Dir: I2sTxSupported, +{ + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_async(buf).await.map_err(EspIOError) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} + /// C-facing ISR dispatcher for on_send_* callbacks. #[cfg(not(esp_idf_version_major = "4"))] unsafe extern "C" fn dispatch_send( diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 000000000..98597f272 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,31 @@ +//! Error types + +use core::fmt::{self, Display, Formatter}; + +use embedded_io::{Error, ErrorKind}; + +use crate::sys::EspError; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct EspIOError(pub EspError); + +impl Error for EspIOError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} + +impl From for EspIOError { + fn from(e: EspError) -> Self { + EspIOError(e) + } +} + +impl Display for EspIOError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for EspIOError {} diff --git a/src/lib.rs b/src/lib.rs index 9003942d5..8aa00210e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,8 @@ pub mod i2s; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod interrupt; #[cfg(not(feature = "riscv-ulp-hal"))] +pub mod io; +#[cfg(not(feature = "riscv-ulp-hal"))] pub mod ledc; #[cfg(all( any(all(esp32, esp_idf_eth_use_esp32_emac), esp_idf_eth_use_openeth), diff --git a/src/spi.rs b/src/spi.rs index 260c5fac8..12bec2dff 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -38,17 +38,14 @@ use core::cell::Cell; use core::cell::UnsafeCell; use core::cmp::{max, min, Ordering}; use core::iter::once; -use core::iter::Peekable; use core::marker::PhantomData; use core::{ptr, u8}; use embassy_sync::mutex::Mutex; -use embedded_hal::spi::{ - Operation, SpiBus, SpiBusFlush, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, - SpiDeviceWrite, -}; +use embedded_hal::spi::{Operation, SpiBus, SpiDevice}; use esp_idf_sys::*; +use heapless::Deque; use crate::delay::{self, Ets, BLOCK}; use crate::gpio::{AnyOutputPin, InputPin, Level, Output, OutputMode, OutputPin, PinDriver}; @@ -245,6 +242,7 @@ pub mod config { pub cs_active_high: bool, pub input_delay_ns: i32, pub polling: bool, + pub allow_pre_post_delays: bool, pub queue_size: usize, } @@ -301,6 +299,12 @@ pub mod config { self } + #[must_use] + pub fn allow_pre_post_delays(mut self, allow_pre_post_delays: bool) -> Self { + self.allow_pre_post_delays = allow_pre_post_delays; + self + } + #[must_use] pub fn queue_size(mut self, queue_size: usize) -> Self { self.queue_size = queue_size; @@ -319,6 +323,7 @@ pub mod config { bit_order: BitOrder::MsbFirst, input_delay_ns: 0, polling: true, + allow_pre_post_delays: false, queue_size: 1, } } @@ -611,37 +616,22 @@ where type Error = SpiError; } -impl<'d, T> SpiBusFlush for SpiBusDriver<'d, T> -where - T: BorrowMut>, -{ - fn flush(&mut self) -> Result<(), Self::Error> { - SpiBusDriver::flush(self).map_err(to_spi_err) - } -} - -impl<'d, T> SpiBusRead for SpiBusDriver<'d, T> +impl<'d, T> SpiBus for SpiBusDriver<'d, T> where T: BorrowMut>, { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { SpiBusDriver::read(self, words).map_err(to_spi_err) } -} -impl<'d, T> SpiBusWrite for SpiBusDriver<'d, T> -where - T: BorrowMut>, -{ fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { SpiBusDriver::write(self, words).map_err(to_spi_err) } -} -impl<'d, T> SpiBus for SpiBusDriver<'d, T> -where - T: BorrowMut>, -{ + fn flush(&mut self) -> Result<(), Self::Error> { + SpiBusDriver::flush(self).map_err(to_spi_err) + } + fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { SpiBusDriver::transfer(self, read, write).map_err(to_spi_err) } @@ -651,6 +641,55 @@ where } } +#[cfg(feature = "nightly")] +impl<'d, T> embedded_hal_async::spi::SpiBus for SpiBusDriver<'d, T> +where + T: BorrowMut>, +{ + async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + SpiBusDriver::read_async(self, buf) + .await + .map_err(to_spi_err) + } + + async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + SpiBusDriver::write_async(self, buf) + .await + .map_err(to_spi_err) + } + + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + SpiBusDriver::transfer_async(self, read, write) + .await + .map_err(to_spi_err) + } + + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + SpiBusDriver::transfer_in_place_async(self, words) + .await + .map_err(to_spi_err) + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + SpiBusDriver::flush(self).map_err(to_spi_err) + } +} + +enum SpiOperation { + Transaction(spi_transaction_t), + Delay(u32), +} + +impl SpiOperation { + pub fn transaction(self) -> Option { + if let Self::Transaction(transaction) = self { + Some(transaction) + } else { + None + } + } +} + pub type SpiSingleDeviceDriver<'d> = SpiDeviceDriver<'d, SpiDriver<'d>>; pub struct SpiDeviceDriver<'d, T> @@ -661,6 +700,7 @@ where driver: T, cs_pin_configured: bool, polling: bool, + allow_pre_post_delays: bool, queue_size: usize, _d: PhantomData<&'d ()>, } @@ -735,6 +775,7 @@ where driver, cs_pin_configured: cs >= 0, polling: config.polling, + allow_pre_post_delays: config.allow_pre_post_delays, queue_size: config.queue_size, _d: PhantomData, }) @@ -746,8 +787,8 @@ where pub fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), EspError> { self.run( - &mut operations.iter_mut().map(copy_operation), - SoftCsPin::none(), + self.hardware_cs_ctl(operations.iter_mut().map(copy_operation))?, + operations.iter_mut().map(copy_operation), ) } @@ -755,8 +796,11 @@ where &mut self, operations: &mut [Operation<'_, u8>], ) -> Result<(), EspError> { - self.run_async(operations.iter_mut().map(copy_operation), SoftCsPin::none()) - .await + self.run_async( + self.hardware_cs_ctl(operations.iter_mut().map(copy_operation))?, + operations.iter_mut().map(copy_operation), + ) + .await } pub fn read(&mut self, read: &mut [u8]) -> Result<(), EspError> { @@ -769,8 +813,8 @@ where pub fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), EspError> { self.run( - &mut operations.iter_mut().map(|slice| Operation::Read(slice)), - SoftCsPin::none(), + self.hardware_cs_ctl(operations.iter_mut().map(|slice| Operation::Read(slice)))?, + operations.iter_mut().map(|slice| Operation::Read(slice)), ) } @@ -779,8 +823,8 @@ where operations: &mut [&mut [u8]], ) -> Result<(), EspError> { self.run_async( - &mut operations.iter_mut().map(|slice| Operation::Read(slice)), - SoftCsPin::none(), + self.hardware_cs_ctl(operations.iter_mut().map(|slice| Operation::Read(slice)))?, + operations.iter_mut().map(|slice| Operation::Read(slice)), ) .await } @@ -795,15 +839,15 @@ where pub fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), EspError> { self.run( - &mut operations.iter().map(|slice| Operation::Write(slice)), - SoftCsPin::none(), + self.hardware_cs_ctl(operations.iter().map(|slice| Operation::Write(slice)))?, + operations.iter().map(|slice| Operation::Write(slice)), ) } pub async fn write_transaction_async(&mut self, operations: &[&[u8]]) -> Result<(), EspError> { self.run_async( - &mut operations.iter().map(|slice| Operation::Write(slice)), - SoftCsPin::none(), + self.hardware_cs_ctl(operations.iter().map(|slice| Operation::Write(slice)))?, + operations.iter().map(|slice| Operation::Write(slice)), ) .await } @@ -822,10 +866,14 @@ where operations: &mut [&mut [u8]], ) -> Result<(), EspError> { self.run( - &mut operations + self.hardware_cs_ctl( + operations + .iter_mut() + .map(|slice| Operation::TransferInPlace(slice)), + )?, + operations .iter_mut() .map(|slice| Operation::TransferInPlace(slice)), - SoftCsPin::none(), ) } @@ -834,10 +882,14 @@ where operations: &mut [&mut [u8]], ) -> Result<(), EspError> { self.run_async( - &mut operations + self.hardware_cs_ctl( + operations + .iter_mut() + .map(|slice| Operation::TransferInPlace(slice)), + )?, + operations .iter_mut() .map(|slice| Operation::TransferInPlace(slice)), - SoftCsPin::none(), ) .await } @@ -851,132 +903,190 @@ where .await } - pub fn transfer_transaction( - &mut self, - operations: &mut [(&mut [u8], &[u8])], - ) -> Result<(), EspError> { - self.run( - &mut operations - .iter_mut() - .map(|(read_slice, write_slice)| Operation::Transfer(read_slice, write_slice)), - SoftCsPin::none(), - ) - } - - pub async fn transfer_transaction_async( - &mut self, - operations: &mut [(&mut [u8], &[u8])], - ) -> Result<(), EspError> { - self.run_async( - &mut operations - .iter_mut() - .map(|(read_slice, write_slice)| Operation::Transfer(read_slice, write_slice)), - SoftCsPin::none(), - ) - .await - } - fn run<'a, 'c, 'p, P, M>( - &self, + &mut self, + mut cs_pin: CsCtl<'c, 'p, P, M>, operations: impl Iterator> + 'a, - mut soft_cs_pin: Option>, ) -> Result<(), EspError> where P: OutputPin, M: OutputMode, { - let mut transactions = mark_last(self.spi_transactions(operations)).peekable(); - - let needs_bus_lock = - soft_cs_pin.is_some() || !transactions.peek().map(|(_, last)| *last).unwrap_or(true); - - let _lock = if needs_bus_lock { + let _lock = if cs_pin.needs_bus_lock() { Some(BusLock::new(self.device())?) } else { None }; - if let Some(soft_cs_pin) = soft_cs_pin.as_mut() { - soft_cs_pin.raise()?; + cs_pin.raise_cs()?; + + let mut spi_operations = self + .spi_operations(operations) + .enumerate() + .map(|(index, mut operation)| { + cs_pin.configure(&mut operation, index); + operation + }) + .peekable(); + + let delay_impl = crate::delay::Delay::new_default(); + let mut result = Ok(()); + + while spi_operations.peek().is_some() { + if let Some(SpiOperation::Delay(delay)) = spi_operations.peek() { + delay_impl.delay_us(*delay); + spi_operations.next(); + } else { + let transactions = core::iter::from_fn(|| { + spi_operations + .next_if(|operation| matches!(operation, SpiOperation::Transaction(_))) + }) + .fuse() + .filter_map(|operation| operation.transaction()); + + result = spi_transmit(self.handle, transactions, self.polling, self.queue_size); + + if result.is_err() { + break; + } + } } - let has_hardware_cs = soft_cs_pin.is_none() && self.cs_pin_configured; - let transactions = transactions.map(|(mut t, last)| { - set_keep_cs_active(&mut t, has_hardware_cs && !last); - t - }); - spi_transmit(self.handle, transactions, self.polling, self.queue_size)?; + cs_pin.lower_cs()?; - if let Some(mut soft_cs_pin) = soft_cs_pin { - soft_cs_pin.lower()?; - } - - Ok(()) + result } async fn run_async<'a, 'c, 'p, P, M>( &self, + mut cs_pin: CsCtl<'c, 'p, P, M>, operations: impl Iterator> + 'a, - mut soft_cs_pin: Option>, ) -> Result<(), EspError> where P: OutputPin, M: OutputMode, { - let mut transactions = mark_last(self.spi_transactions(operations)).peekable(); - - let needs_bus_lock = - soft_cs_pin.is_some() || !transactions.peek().map(|(_, last)| *last).unwrap_or(true); - - let _async_bus_lock = if needs_bus_lock { + let _async_bus_lock = if cs_pin.needs_bus_lock() { Some(self.driver.borrow().bus_async_lock.lock().await) } else { None }; - let _lock = if needs_bus_lock { + let _lock = if cs_pin.needs_bus_lock() { Some(BusLock::new(self.device())?) } else { None }; - if let Some(soft_cs_pin) = soft_cs_pin.as_mut() { - soft_cs_pin.raise()?; + cs_pin.raise_cs()?; + + let delay_impl = crate::delay::Delay::new_default(); // TODO: Need to wait asnchronously if in async mode + let mut result = Ok(()); + + let mut spi_operations = self + .spi_operations(operations) + .enumerate() + .map(|(index, mut operation)| { + cs_pin.configure(&mut operation, index); + operation + }) + .peekable(); + + while spi_operations.peek().is_some() { + if let Some(SpiOperation::Delay(delay)) = spi_operations.peek() { + delay_impl.delay_us(*delay); + spi_operations.next(); + } else { + let transactions = core::iter::from_fn(|| { + spi_operations + .next_if(|operation| matches!(operation, SpiOperation::Transaction(_))) + }) + .fuse() + .filter_map(|operation| operation.transaction()); + + result = spi_transmit_async(self.handle, transactions, self.queue_size).await; + + if result.is_err() { + break; + } + } } - let has_hardware_cs = soft_cs_pin.is_none() && self.cs_pin_configured; - let transactions = transactions.map(|(mut t, last)| { - set_keep_cs_active(&mut t, has_hardware_cs && !last); - t - }); - spi_transmit_async(self.handle, transactions, self.queue_size).await?; + cs_pin.lower_cs()?; - if let Some(mut soft_cs_pin) = soft_cs_pin { - soft_cs_pin.lower()?; - } - - Ok(()) + result } - fn spi_transactions<'a>( + fn hardware_cs_ctl<'a, 'c, 'p>( &self, operations: impl Iterator> + 'a, - ) -> impl Iterator + 'a { - enum OperationsIter { + ) -> Result, EspError> { + let (total_count, transactions_count, first_transaction, last_transaction) = + self.spi_operations_stats(operations); + + if !self.allow_pre_post_delays + && self.cs_pin_configured + && transactions_count > 0 + && (first_transaction != Some(0) || last_transaction != Some(total_count - 1)) + { + Err(EspError::from_infallible::())?; + } + + Ok(CsCtl::Hardware { + enabled: self.cs_pin_configured, + transactions_count, + last_transaction, + }) + } + + fn spi_operations_stats<'a>( + &self, + operations: impl Iterator> + 'a, + ) -> (usize, usize, Option, Option) { + self.spi_operations(operations).enumerate().fold( + (0, 0, None, None), + |(total_count, transactions_count, first_transaction, last_transaction), + (index, operation)| { + if matches!(operation, SpiOperation::Transaction(_)) { + ( + total_count + 1, + transactions_count + 1, + Some(first_transaction.unwrap_or(index)), + Some(index), + ) + } else { + ( + total_count + 1, + transactions_count, + first_transaction, + last_transaction, + ) + } + }, + ) + } + + fn spi_operations<'a>( + &self, + operations: impl Iterator> + 'a, + ) -> impl Iterator + 'a { + enum OperationsIter { Read(R), Write(W), Transfer(T), TransferInPlace(I), + Delay(D), } - impl Iterator for OperationsIter + impl Iterator for OperationsIter where - R: Iterator, - W: Iterator, - T: Iterator, - I: Iterator, + R: Iterator, + W: Iterator, + T: Iterator, + I: Iterator, + D: Iterator, { - type Item = spi_transaction_t; + type Item = SpiOperation; fn next(&mut self) -> Option { match self { @@ -984,6 +1094,7 @@ where Self::Write(iter) => iter.next(), Self::Transfer(iter) => iter.next(), Self::TransferInPlace(iter) => iter.next(), + Self::Delay(iter) => iter.next(), } } } @@ -991,18 +1102,22 @@ where let chunk_size = self.driver.borrow().max_transfer_size; operations.flat_map(move |op| match op { - Operation::Read(words) => { - OperationsIter::Read(spi_read_transactions(words, chunk_size)) - } - Operation::Write(words) => { - OperationsIter::Write(spi_write_transactions(words, chunk_size)) - } - Operation::Transfer(read, write) => { - OperationsIter::Transfer(spi_transfer_transactions(read, write, chunk_size)) - } - Operation::TransferInPlace(words) => OperationsIter::TransferInPlace( - spi_transfer_in_place_transactions(words, chunk_size), + Operation::Read(words) => OperationsIter::Read( + spi_read_transactions(words, chunk_size).map(SpiOperation::Transaction), ), + Operation::Write(words) => OperationsIter::Write( + spi_write_transactions(words, chunk_size).map(SpiOperation::Transaction), + ), + Operation::Transfer(read, write) => OperationsIter::Transfer( + spi_transfer_transactions(read, write, chunk_size).map(SpiOperation::Transaction), + ), + Operation::TransferInPlace(words) => OperationsIter::TransferInPlace( + spi_transfer_in_place_transactions(words, chunk_size) + .map(SpiOperation::Transaction), + ), + Operation::DelayUs(delay) => { + OperationsIter::Delay(core::iter::once(SpiOperation::Delay(delay))) + } }) } } @@ -1025,36 +1140,18 @@ where type Error = SpiError; } -impl<'d, T> SpiDeviceRead for SpiDeviceDriver<'d, T> +impl<'d, T> SpiDevice for SpiDeviceDriver<'d, T> where T: Borrow> + 'd, { - fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { - Self::read_transaction(self, operations).map_err(to_spi_err) - } - fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { Self::read(self, buf).map_err(to_spi_err) } -} - -impl<'d, T> SpiDeviceWrite for SpiDeviceDriver<'d, T> -where - T: Borrow> + 'd, -{ - fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { - Self::write_transaction(self, operations).map_err(to_spi_err) - } fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { Self::write(self, buf).map_err(to_spi_err) } -} -impl<'d, T> SpiDevice for SpiDeviceDriver<'d, T> -where - T: Borrow> + 'd, -{ fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { Self::transaction(self, operations).map_err(to_spi_err) } @@ -1127,6 +1224,7 @@ where &mut transaction, self.cs_pin_configured && words.peek().is_some(), ); + spi_transmit( self.handle, once(transaction), @@ -1150,18 +1248,46 @@ where operations: &mut [embedded_hal_0_2::blocking::spi::Operation<'_, u8>], ) -> Result<(), Self::Error> { self.run( + self.hardware_cs_ctl(operations.iter_mut().map(|op| match op { + embedded_hal_0_2::blocking::spi::Operation::Write(words) => Operation::Write(words), + embedded_hal_0_2::blocking::spi::Operation::Transfer(words) => { + Operation::TransferInPlace(words) + } + }))?, operations.iter_mut().map(|op| match op { embedded_hal_0_2::blocking::spi::Operation::Write(words) => Operation::Write(words), embedded_hal_0_2::blocking::spi::Operation::Transfer(words) => { Operation::TransferInPlace(words) } }), - SoftCsPin::none(), ) .map_err(to_spi_err) } } +#[cfg(feature = "nightly")] +impl<'d, T> embedded_hal_async::spi::SpiDevice for SpiDeviceDriver<'d, T> +where + T: Borrow> + 'd, +{ + async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + Self::read_async(self, buf).await.map_err(to_spi_err) + } + + async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + Self::write_async(self, buf).await.map_err(to_spi_err) + } + + async fn transaction( + &mut self, + operations: &mut [Operation<'_, u8>], + ) -> Result<(), Self::Error> { + Self::transaction_async(self, operations) + .await + .map_err(to_spi_err) + } +} + pub struct SpiSharedDeviceDriver<'d, T> where T: Borrow> + 'd, @@ -1279,14 +1405,14 @@ where } pub fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), EspError> { - self.run(&mut operations.iter_mut().map(|slice| Operation::Read(slice))) + self.run(operations.iter_mut().map(|slice| Operation::Read(slice))) } pub async fn read_transaction_async( &mut self, operations: &mut [&mut [u8]], ) -> Result<(), EspError> { - self.run_async(&mut operations.iter_mut().map(|slice| Operation::Read(slice))) + self.run_async(operations.iter_mut().map(|slice| Operation::Read(slice))) .await } @@ -1299,11 +1425,11 @@ where } pub fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), EspError> { - self.run(&mut operations.iter().map(|slice| Operation::Write(slice))) + self.run(operations.iter().map(|slice| Operation::Write(slice))) } pub async fn write_transaction_async(&mut self, operations: &[&[u8]]) -> Result<(), EspError> { - self.run_async(&mut operations.iter().map(|slice| Operation::Write(slice))) + self.run_async(operations.iter().map(|slice| Operation::Write(slice))) .await } @@ -1321,7 +1447,7 @@ where operations: &mut [&mut [u8]], ) -> Result<(), EspError> { self.run( - &mut operations + operations .iter_mut() .map(|slice| Operation::TransferInPlace(slice)), ) @@ -1332,7 +1458,7 @@ where operations: &mut [&mut [u8]], ) -> Result<(), EspError> { self.run_async( - &mut operations + operations .iter_mut() .map(|slice| Operation::TransferInPlace(slice)), ) @@ -1348,34 +1474,11 @@ where .await } - pub fn transfer_transaction( - &mut self, - operations: &mut [(&mut [u8], &[u8])], - ) -> Result<(), EspError> { - self.run( - &mut operations - .iter_mut() - .map(|(read_slice, write_slice)| Operation::Transfer(read_slice, write_slice)), - ) - } - - pub async fn transfer_transaction_async( - &mut self, - operations: &mut [(&mut [u8], &[u8])], - ) -> Result<(), EspError> { - self.run_async( - &mut operations - .iter_mut() - .map(|(read_slice, write_slice)| Operation::Transfer(read_slice, write_slice)), - ) - .await - } - fn run<'a>( &mut self, operations: impl Iterator> + 'a, ) -> Result<(), EspError> { - let soft_cs_pin = SoftCsPin { + let cs_pin = CsCtl::Software { cs: &mut self.cs_pin, pre_delay: self.pre_delay_us, post_delay: self.post_delay_us, @@ -1383,14 +1486,14 @@ where self.shared_device .borrow() - .lock(move |device| device.run(operations, Some(soft_cs_pin))) + .lock(move |device| device.run(cs_pin, operations)) } async fn run_async<'a>( &mut self, operations: impl Iterator> + 'a, ) -> Result<(), EspError> { - let soft_cs_pin = SoftCsPin { + let cs_pin = CsCtl::Software { cs: &mut self.cs_pin, pre_delay: self.pre_delay_us, post_delay: self.post_delay_us, @@ -1401,7 +1504,9 @@ where let _async_guard = device.async_lock.lock().await; let _guard = device.lock.enter(); - unsafe { device.driver_mut() }.run(operations, Some(soft_cs_pin)) + let driver = unsafe { device.driver_mut() }; + + driver.run_async(cs_pin, operations).await } } @@ -1413,44 +1518,49 @@ where type Error = SpiError; } -impl<'d, DEVICE, DRIVER> SpiDeviceRead for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER> -where - DEVICE: Borrow> + 'd, - DRIVER: Borrow> + 'd, -{ - fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> { - Self::read_transaction(self, operations).map_err(to_spi_err) - } - - fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { - Self::read(self, buf).map_err(to_spi_err) - } -} - -impl<'d, DEVICE, DRIVER> SpiDeviceWrite for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER> -where - DEVICE: Borrow> + 'd, - DRIVER: Borrow> + 'd, -{ - fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> { - Self::write_transaction(self, operations).map_err(to_spi_err) - } - - fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { - Self::write(self, buf).map_err(to_spi_err) - } -} - impl<'d, DEVICE, DRIVER> SpiDevice for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER> where DEVICE: Borrow> + 'd, DRIVER: Borrow> + 'd, { + fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + Self::read(self, buf).map_err(to_spi_err) + } + + fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + Self::write(self, buf).map_err(to_spi_err) + } + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { Self::transaction(self, operations).map_err(to_spi_err) } } +#[cfg(feature = "nightly")] +impl<'d, DEVICE, DRIVER> embedded_hal_async::spi::SpiDevice + for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER> +where + DEVICE: Borrow> + 'd, + DRIVER: Borrow> + 'd, +{ + async fn read(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + Self::read_async(self, buf).await.map_err(to_spi_err) + } + + async fn write(&mut self, buf: &[u8]) -> Result<(), Self::Error> { + Self::write_async(self, buf).await.map_err(to_spi_err) + } + + async fn transaction( + &mut self, + operations: &mut [Operation<'_, u8>], + ) -> Result<(), Self::Error> { + Self::transaction_async(self, operations) + .await + .map_err(to_spi_err) + } +} + fn to_spi_err(err: EspError) -> SpiError { SpiError::other(err) } @@ -1488,43 +1598,78 @@ impl Drop for BusLock { } } -struct SoftCsPin<'c, 'p, P, M> +enum CsCtl<'c, 'p, P, M> where P: OutputPin, M: OutputMode, { - cs: &'c mut PinDriver<'p, P, M>, - pre_delay: Option, - post_delay: Option, + Hardware { + enabled: bool, + transactions_count: usize, + last_transaction: Option, + }, + Software { + cs: &'c mut PinDriver<'p, P, M>, + pre_delay: Option, + post_delay: Option, + }, } -impl<'c, 'p, P, M> SoftCsPin<'c, 'p, P, M> +impl<'c, 'p, P, M> CsCtl<'c, 'p, P, M> where P: OutputPin, M: OutputMode, { - fn raise(&mut self) -> Result<(), EspError> { - self.cs.toggle()?; + fn needs_bus_lock(&self) -> bool { + match self { + Self::Hardware { + transactions_count, .. + } => *transactions_count > 1, + Self::Software { .. } => true, + } + } - if let Some(delay) = self.pre_delay { - Ets::delay_us(delay); + fn raise_cs(&mut self) -> Result<(), EspError> { + if let CsCtl::Software { cs, pre_delay, .. } = self { + cs.toggle()?; + + // TODO: Need to wait asnchronously if in async mode + if let Some(delay) = pre_delay { + Ets::delay_us(*delay); + } } Ok(()) } - fn lower(&mut self) -> Result<(), EspError> { - if let Some(delay) = self.post_delay { - Ets::delay_us(delay); + fn lower_cs(&mut self) -> Result<(), EspError> { + if let CsCtl::Software { cs, post_delay, .. } = self { + cs.toggle()?; + + // TODO: Need to wait asnchronously if in async mode + if let Some(delay) = post_delay { + Ets::delay_us(*delay); + } } - self.cs.toggle() + Ok(()) } -} -impl SoftCsPin<'static, 'static, AnyOutputPin, Output> { - fn none() -> Option { - None + fn configure(&self, operation: &mut SpiOperation, index: usize) { + if let SpiOperation::Transaction(transaction) = operation { + self.configure_transaction(transaction, index) + } + } + + fn configure_transaction(&self, transaction: &mut spi_transaction_t, index: usize) { + if let Self::Hardware { + enabled, + last_transaction, + .. + } = self + { + set_keep_cs_active(transaction, *enabled && Some(index) != *last_transaction); + } } } @@ -1674,36 +1819,49 @@ fn spi_transmit( esp!(unsafe { spi_device_polling_transmit(handle, &mut transaction as *mut _) })?; } } else { - let mut transaction_queue = [spi_transaction_t::default(); MAX_QUEUED_TRANSACTIONS]; - let mut queue_iter = transaction_queue.iter_mut().take(queue_size); + pub type Queue = Deque; + + let mut queue = Queue::new(); + let queue_size = min(MAX_QUEUED_TRANSACTIONS, queue_size); + + let push = |queue: &mut Queue, transaction| { + let _ = queue.push_back(transaction); + esp!(unsafe { spi_device_queue_trans(handle, queue.back_mut().unwrap(), delay::BLOCK) }) + }; + + let pop = |queue: &mut Queue| { + let mut rtrans = ptr::null_mut(); + esp!(unsafe { spi_device_get_trans_result(handle, &mut rtrans, delay::BLOCK) })?; + + if rtrans != queue.front_mut().unwrap() { + unreachable!(); + } + queue.pop_front().unwrap(); + + Ok(()) + }; + + let pop_all = |queue: &mut Queue| { + while !queue.is_empty() { + pop(queue)?; + } + + Ok(()) + }; for transaction in transactions { - let slot = queue_iter.next(); - let trans = if let Some(slot) = slot { - slot - } else { - // If the queue is full, we wait for the first transaction in the queue and use it - // for the next one. - let mut ret_trans: *mut spi_transaction_t = ptr::null_mut(); - esp!(unsafe { - spi_device_get_trans_result(handle, &mut ret_trans as *mut _, delay::BLOCK) - })?; - unsafe { &mut *ret_trans } - }; + if queue.len() == queue_size { + // If the queue is full, we wait for the first transaction in the queue + pop(&mut queue)?; + } - // Write transaction to stable memory location - *trans = transaction; - esp!(unsafe { spi_device_queue_trans(handle, trans as *mut _, delay::BLOCK) })?; + // Write transaction to a stable memory location + push(&mut queue, transaction)?; } - let queued_transactions = min(MAX_QUEUED_TRANSACTIONS, queue_size) - queue_iter.len(); - for _ in 0..queued_transactions { - let mut ret_trans: *mut spi_transaction_t = ptr::null_mut(); - esp!(unsafe { - spi_device_get_trans_result(handle, &mut ret_trans as *mut _, delay::BLOCK) - })?; - } + pop_all(&mut queue)?; } + Ok(()) } @@ -1712,89 +1870,70 @@ async fn spi_transmit_async( transactions: impl Iterator, queue_size: usize, ) -> Result<(), EspError> { - #[allow(clippy::declare_interior_mutable_const)] // OK because this is only used as an array initializer - const SPI_NOTIF_INIT: Notification = Notification::new(); - - let notifications: [Notification; MAX_QUEUED_TRANSACTIONS] = - [SPI_NOTIF_INIT; MAX_QUEUED_TRANSACTIONS]; - - let mut transaction_queue = [spi_transaction_t::default(); MAX_QUEUED_TRANSACTIONS]; - let mut queue_iter = transaction_queue.iter_mut().take(queue_size); - - let queued_transactions = Cell::new(0); // Total successful spi_device_queue_trans - let completed_transactions = Cell::new(0); // Total successful spi_device_get_trans_result + let queued = Cell::new(0_usize); with_completion( async { - for (index, transaction) in transactions.enumerate() { - let slot = queue_iter.next(); - let trans = if let Some(slot) = slot { - slot - } else { - // If the queue is full, we wait for the first transaction in the queue and use it - // for the next one. + pub type Queue = Deque<(spi_transaction_t, Notification), MAX_QUEUED_TRANSACTIONS>; - notifications[completed_transactions.get() % MAX_QUEUED_TRANSACTIONS] - .wait() - .await; + let mut queue = Queue::new(); + let queue_size = min(MAX_QUEUED_TRANSACTIONS, queue_size); - let mut ret_trans: *mut spi_transaction_t = ptr::null_mut(); - match esp!(unsafe { - spi_device_get_trans_result( - handle, - &mut ret_trans as *mut _, - delay::NON_BLOCK, - ) - }) { - Err(e) if e.code() == ESP_ERR_TIMEOUT => unreachable!(), - other => other, - }?; - completed_transactions.set(completed_transactions.get() + 1); - unsafe { &mut *ret_trans } - }; + let push = |queue: &mut Queue, transaction| { + let _ = queue.push_back((transaction, Notification::new())); + queued.set(queue.len()); - *trans = transaction; - trans.user = - ptr::addr_of!(notifications[index % MAX_QUEUED_TRANSACTIONS]) as *mut _; + let last = queue.back_mut().unwrap(); + last.0.user = &last.1 as *const _ as *mut _; + match esp!(unsafe { spi_device_queue_trans(handle, &mut last.0, delay::NON_BLOCK) }) + { + Err(e) if e.code() == ESP_ERR_TIMEOUT => unreachable!(), + other => other, + } + }; + let pop = |queue: &mut Queue| { + let mut rtrans = ptr::null_mut(); match esp!(unsafe { - spi_device_queue_trans(handle, trans as *mut _, delay::NON_BLOCK) + spi_device_get_trans_result(handle, &mut rtrans, delay::NON_BLOCK) }) { Err(e) if e.code() == ESP_ERR_TIMEOUT => unreachable!(), other => other, }?; - queued_transactions.set(queued_transactions.get() + 1); + + if rtrans != &mut queue.front_mut().unwrap().0 { + unreachable!(); + } + queue.pop_front().unwrap(); + queued.set(queue.len()); + + Ok(()) + }; + + for transaction in transactions { + if queue.len() == queue_size { + // If the queue is full, we wait for the first transaction in the queue + queue.front_mut().unwrap().1.wait().await; + pop(&mut queue)?; + } + + // Write transaction to a stable memory location + push(&mut queue, transaction)?; } - while completed_transactions.get() < queued_transactions.get() { - notifications[completed_transactions.get() % MAX_QUEUED_TRANSACTIONS] - .wait() - .await; - - let mut ret_trans: *mut spi_transaction_t = ptr::null_mut(); - match esp!(unsafe { - spi_device_get_trans_result(handle, &mut ret_trans as *mut _, delay::NON_BLOCK) - }) { - Err(e) if e.code() == ESP_ERR_TIMEOUT => unreachable!(), - other => other, - }?; - completed_transactions.set(completed_transactions.get() + 1); + while !queue.is_empty() { + queue.front_mut().unwrap().1.wait().await; + pop(&mut queue)?; } Ok(()) }, |completed| { if !completed { - while completed_transactions.get() < queued_transactions.get() { - let mut ret_trans: *mut spi_transaction_t = ptr::null_mut(); - match esp!(unsafe { - spi_device_get_trans_result(handle, &mut ret_trans as *mut _, delay::BLOCK) - }) { - Err(e) if e.code() == ESP_ERR_TIMEOUT => unreachable!(), - other => other, - } - .unwrap(); - completed_transactions.set(completed_transactions.get() + 1); + for _ in 0..queued.get() { + let mut rtrans = ptr::null_mut(); + esp!(unsafe { spi_device_get_trans_result(handle, &mut rtrans, delay::BLOCK) }) + .unwrap(); } } }, @@ -1813,40 +1952,13 @@ extern "C" fn spi_notify(transaction: *mut spi_transaction_t) { } } -fn mark_last(iter: I) -> impl Iterator -where - I: Iterator, -{ - let iter = iter.peekable(); - - struct Marked(Peekable) - where - I: Iterator; - - impl Iterator for Marked - where - I: Iterator, - { - type Item = (I::Item, bool); - - fn next(&mut self) -> Option { - if let Some(next) = self.0.next() { - Some((next, self.0.peek().is_none())) - } else { - None - } - } - } - - Marked(iter) -} - fn copy_operation<'b>(operation: &'b mut Operation<'_, u8>) -> Operation<'b, u8> { match operation { Operation::Read(read) => Operation::Read(read), Operation::Write(write) => Operation::Write(write), Operation::Transfer(read, write) => Operation::Transfer(read, write), Operation::TransferInPlace(write) => Operation::TransferInPlace(write), + Operation::DelayUs(delay) => Operation::DelayUs(*delay), } } diff --git a/src/uart.rs b/src/uart.rs index a8f43fe6c..90994058a 100644 --- a/src/uart.rs +++ b/src/uart.rs @@ -42,11 +42,13 @@ use core::mem::ManuallyDrop; use core::ptr; use core::sync::atomic::{AtomicU8, Ordering}; -use crate::delay::NON_BLOCK; +use crate::delay::{self, NON_BLOCK}; use crate::gpio::*; +use crate::io::EspIOError; use crate::units::*; +use embedded_hal_nb::serial::ErrorKind; use esp_idf_sys::*; use crate::peripheral::Peripheral; @@ -422,8 +424,8 @@ pub trait Uart { crate::embedded_hal_error!( SerialError, - embedded_hal::serial::Error, - embedded_hal::serial::ErrorKind + embedded_hal_nb::serial::Error, + embedded_hal_nb::serial::ErrorKind ); /// Serial abstraction @@ -609,8 +611,25 @@ impl<'d> Drop for UartDriver<'d> { } } -impl<'d> embedded_hal::serial::ErrorType for UartDriver<'d> { - type Error = SerialError; +impl<'d> embedded_io::ErrorType for UartDriver<'d> { + type Error = EspIOError; +} + +impl<'d> embedded_io::Read for UartDriver<'d> { + fn read(&mut self, buf: &mut [u8]) -> Result { + UartDriver::read(self, buf, delay::BLOCK).map_err(EspIOError) + } +} + +impl<'d> embedded_io::Write for UartDriver<'d> { + fn write(&mut self, buf: &[u8]) -> Result { + UartDriver::write(self, buf).map_err(EspIOError) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + UartDriver::flush_read(self).map_err(EspIOError)?; + UartDriver::flush_write(self).map_err(EspIOError) + } } impl<'d> embedded_hal_0_2::serial::Read for UartDriver<'d> { @@ -621,12 +640,6 @@ impl<'d> embedded_hal_0_2::serial::Read for UartDriver<'d> { } } -impl<'d> embedded_hal_nb::serial::Read for UartDriver<'d> { - fn read(&mut self) -> nb::Result { - embedded_hal_nb::serial::Read::read(&mut *self.rx()) - } -} - impl<'d> embedded_hal_0_2::serial::Write for UartDriver<'d> { type Error = SerialError; @@ -639,14 +652,24 @@ impl<'d> embedded_hal_0_2::serial::Write for UartDriver<'d> { } } -impl<'d> embedded_hal_nb::serial::Write for UartDriver<'d> { - fn flush(&mut self) -> nb::Result<(), Self::Error> { - embedded_hal_nb::serial::Write::flush(&mut *self.tx()) - } +impl<'d> embedded_hal_nb::serial::ErrorType for UartDriver<'d> { + type Error = SerialError; +} +impl<'d> embedded_hal_nb::serial::Read for UartDriver<'d> { + fn read(&mut self) -> nb::Result { + embedded_hal_nb::serial::Read::read(&mut *self.rx()) + } +} + +impl<'d> embedded_hal_nb::serial::Write for UartDriver<'d> { fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { embedded_hal_nb::serial::Write::write(&mut *self.tx(), byte) } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + embedded_hal_nb::serial::Write::flush(&mut *self.tx()) + } } impl<'d> core::fmt::Write for UartDriver<'d> { @@ -655,10 +678,6 @@ impl<'d> core::fmt::Write for UartDriver<'d> { } } -impl<'d> embedded_hal::serial::ErrorType for UartRxDriver<'d> { - type Error = SerialError; -} - impl<'d> UartRxDriver<'d> { /// Create a new serial receiver pub fn new( @@ -765,6 +784,16 @@ impl<'d> Drop for UartRxDriver<'d> { } } +impl<'d> embedded_io::ErrorType for UartRxDriver<'d> { + type Error = EspIOError; +} + +impl<'d> embedded_io::Read for UartRxDriver<'d> { + fn read(&mut self, buf: &mut [u8]) -> Result { + UartRxDriver::read(self, buf, delay::BLOCK).map_err(EspIOError) + } +} + impl<'d> embedded_hal_0_2::serial::Read for UartRxDriver<'d> { type Error = SerialError; @@ -777,6 +806,10 @@ impl<'d> embedded_hal_0_2::serial::Read for UartRxDriver<'d> { } } +impl<'d> embedded_hal_nb::serial::ErrorType for UartRxDriver<'d> { + type Error = SerialError; +} + impl<'d> embedded_hal_nb::serial::Read for UartRxDriver<'d> { fn read(&mut self) -> nb::Result { let mut buf = [0_u8]; @@ -892,8 +925,18 @@ impl<'d> Drop for UartTxDriver<'d> { } } -impl<'d> embedded_hal::serial::ErrorType for UartTxDriver<'d> { - type Error = SerialError; +impl<'d> embedded_io::Write for UartTxDriver<'d> { + fn write(&mut self, buf: &[u8]) -> Result { + UartTxDriver::write(self, buf).map_err(EspIOError) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + UartTxDriver::flush(self).map_err(EspIOError) + } +} + +impl<'d> embedded_io::ErrorType for UartTxDriver<'d> { + type Error = EspIOError; } impl<'d> embedded_hal_0_2::serial::Write for UartTxDriver<'d> { @@ -908,6 +951,10 @@ impl<'d> embedded_hal_0_2::serial::Write for UartTxDriver<'d> { } } +impl<'d> embedded_hal_nb::serial::ErrorType for UartTxDriver<'d> { + type Error = SerialError; +} + impl<'d> embedded_hal_nb::serial::Write for UartTxDriver<'d> { fn flush(&mut self) -> nb::Result<(), Self::Error> { UartTxDriver::flush(self).map_err(to_nb_err) @@ -1108,7 +1155,7 @@ fn to_nb_err(err: EspError) -> nb::Error { if err.code() == ESP_ERR_TIMEOUT { nb::Error::WouldBlock } else { - nb::Error::Other(SerialError::from(err)) + nb::Error::Other(SerialError::new(ErrorKind::Other, err)) } } @@ -1117,7 +1164,7 @@ fn check_nb(result: Result, value: T) -> nb::Result Ok(value), Ok(0) => Err(nb::Error::WouldBlock), Ok(_) => unreachable!(), - Err(err) => Err(nb::Error::Other(SerialError::other(err))), + Err(err) => Err(nb::Error::Other(SerialError::new(ErrorKind::Other, err))), } }