From 36ed7bdf1419fee593444202c76867e728862e8f Mon Sep 17 00:00:00 2001 From: Alexandra Clifford Date: Mon, 29 Jul 2024 03:36:39 -0400 Subject: [PATCH 1/8] Adds temporary fix for rcv frame panic issue (#1862) --- esp-ieee802154/CHANGELOG.md | 2 ++ esp-ieee802154/src/raw.rs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/esp-ieee802154/CHANGELOG.md b/esp-ieee802154/CHANGELOG.md index 479319034db..390b351dd12 100644 --- a/esp-ieee802154/CHANGELOG.md +++ b/esp-ieee802154/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added range check to avoid panic when indexing into RX_BUFFER slice + ### Changed ### Fixed diff --git a/esp-ieee802154/src/raw.rs b/esp-ieee802154/src/raw.rs index cc3da0fc48a..93d7dedbcaa 100644 --- a/esp-ieee802154/src/raw.rs +++ b/esp-ieee802154/src/raw.rs @@ -391,7 +391,12 @@ fn ZB_MAC() { log::warn!("Receive queue full"); } - let frm = &RX_BUFFER[1..][..RX_BUFFER[0] as usize]; + let frm = if RX_BUFFER[0] > FRAME_SIZE as u8 { + log::warn!("RX_BUFFER[0] {:} is larger than frame size", RX_BUFFER[0]); + &RX_BUFFER[1..][..FRAME_SIZE] + } else { + &RX_BUFFER[1..][..RX_BUFFER[0] as usize] + }; if will_auto_send_ack(frm) { *STATE.borrow_ref_mut(cs) = Ieee802154State::TxAck; } else if should_send_enhanced_ack(frm) { From 56806313bd119496b8b58324f79019fc582899c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Mon, 29 Jul 2024 15:48:11 +0200 Subject: [PATCH 2/8] Fix time_driver (#1875) * Fix time_driver * CHANGELOG.md --- esp-hal-embassy/CHANGELOG.md | 2 ++ esp-hal-embassy/src/time_driver.rs | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/esp-hal-embassy/CHANGELOG.md b/esp-hal-embassy/CHANGELOG.md index 2726d794f89..63019c77cfe 100644 --- a/esp-hal-embassy/CHANGELOG.md +++ b/esp-hal-embassy/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed a bug where the timeout was huge whenever the timestamp at the time of scheduling was already in the past (#1875) + ### Removed ## 0.2.0 - 2024-07-15 diff --git a/esp-hal-embassy/src/time_driver.rs b/esp-hal-embassy/src/time_driver.rs index d3363cbcb6a..365cfe3933e 100644 --- a/esp-hal-embassy/src/time_driver.rs +++ b/esp-hal-embassy/src/time_driver.rs @@ -57,12 +57,13 @@ impl EmbassyTimer { handler0, handler1, handler2, handler3, handler4, handler5, handler6, ]; - timers - .iter_mut() - .enumerate() - .for_each(|(n, timer)| timer.set_interrupt_handler(HANDLERS[n])); - critical_section::with(|cs| { + timers.iter_mut().enumerate().for_each(|(n, timer)| { + timer.enable_interrupt(false); + timer.stop(); + timer.set_interrupt_handler(HANDLERS[n]); + }); + TIMERS.replace(cs, Some(timers)); }); @@ -99,7 +100,7 @@ impl EmbassyTimer { fn on_interrupt(&self, id: usize) { let cb = critical_section::with(|cs| { let mut timers = TIMERS.borrow_ref_mut(cs); - let timers = timers.as_mut().unwrap(); + let timers = timers.as_mut().expect("Time driver not initialized"); let timer = &mut timers[id]; timer.clear_interrupt(); @@ -121,7 +122,8 @@ impl EmbassyTimer { fn arm(timer: &mut Timer, timestamp: u64) { let now = current_time().duration_since_epoch(); let ts = timestamp.micros(); - let timeout = if ts > now { ts - now } else { now }; + // if the TS is already in the past make the timer fire immediately + let timeout = if ts > now { ts - now } else { 0.micros() }; timer.schedule(timeout).unwrap(); timer.enable_interrupt(true); } @@ -169,7 +171,7 @@ impl Driver for EmbassyTimer { // soon as possible, but not synchronously.) critical_section::with(|cs| { let mut timers = TIMERS.borrow_ref_mut(cs); - let timers = timers.as_mut().unwrap(); + let timers = timers.as_mut().expect("Time driver not initialized"); let timer = &mut timers[alarm.id() as usize]; Self::arm(timer, timestamp); From 67bb6c54bb5803d4b7166119c3db7c536f258ef0 Mon Sep 17 00:00:00 2001 From: liebman Date: Mon, 29 Jul 2024 06:59:52 -0700 Subject: [PATCH 3/8] Implement async for lcd_cam i8080 (#1834) * implement async for lcd_cam i8080 * lcd_cam: implement InterruptConfigurable for blocking and improve async * lcd_cam: use new async interrupt semantics from #1835 * lcd_cam: move interrupt handler binding into `new_async()` * lcd_cam: Instance::is_listening_lcd_done not used * i8080: no need for seperate `new_async()` * i8080: don't use DmaTxFuture, just test for dma error after complete * add HIL tests for LCD_CAM i8080 in blocking and async. * lcd_cam_i8080: test channels configured for async as well since teh compiler can't prevent it for now * fmt :-/ * lcd_cam fix comment * changelog * lcd_cam async: no need to enable interrupts until polled * lcd_cam: i8080 update for ReadBuffer changes --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/lcd_cam/lcd/i8080.rs | 51 +++++++-- esp-hal/src/lcd_cam/lcd/mod.rs | 3 +- esp-hal/src/lcd_cam/mod.rs | 153 +++++++++++++++++++++++++- hil-test/Cargo.toml | 8 ++ hil-test/tests/lcd_cam_i8080.rs | 129 ++++++++++++++++++++++ hil-test/tests/lcd_cam_i8080_async.rs | 128 +++++++++++++++++++++ 7 files changed, 458 insertions(+), 15 deletions(-) create mode 100644 hil-test/tests/lcd_cam_i8080.rs create mode 100644 hil-test/tests/lcd_cam_i8080_async.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 593a808ff83..65fd89f3abe 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add DmaTransactionTxOwned, DmaTransactionRxOwned, DmaTransactionTxRxOwned, functions to do owning transfers added to SPI half-duplex (#1672) - uart: Implement `embedded_io::ReadReady` for `Uart` and `UartRx` (#1702) - ESP32-S3: Expose optional HSYNC input in LCD_CAM (#1707) +- ESP32-S3: Add async support to the LCD_CAM I8080 driver (#1834) - ESP32-C6: Support lp-core as wake-up source (#1723) - Add support for GPIO wake-up source (#1724) - gpio: add DummyPin (#1769) diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index f2413ee17a8..309bc8d7bd4 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -57,10 +57,12 @@ //! # } //! ``` -use core::{fmt::Formatter, mem::size_of}; +use core::{fmt::Formatter, marker::PhantomData, mem::size_of}; use fugit::HertzU32; +#[cfg(feature = "async")] +use crate::lcd_cam::asynch::LcdDoneFuture; use crate::{ clock::Clocks, dma::{ @@ -86,22 +88,24 @@ use crate::{ }, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, + Mode, }; -pub struct I8080<'d, CH: DmaChannel, P> { +pub struct I8080<'d, CH: DmaChannel, P, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, tx_channel: ChannelTx<'d, CH>, tx_chain: DescriptorChain, _pins: P, + _phantom: PhantomData, } -impl<'d, CH: DmaChannel, P: TxPins> I8080<'d, CH, P> +impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> I8080<'d, CH, P, DM> where CH::P: LcdCamPeripheral, P::Word: Into, { pub fn new( - lcd: Lcd<'d>, + lcd: Lcd<'d, DM>, mut channel: ChannelTx<'d, CH>, descriptors: &'static mut [DmaDescriptor], mut pins: P, @@ -249,11 +253,12 @@ where tx_channel: channel, tx_chain: DescriptorChain::new(descriptors), _pins: pins, + _phantom: PhantomData, } } } -impl<'d, CH: DmaChannel, P: TxPins> DmaSupport for I8080<'d, CH, P> { +impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> DmaSupport for I8080<'d, CH, P, DM> { fn peripheral_wait_dma(&mut self, _is_tx: bool, _is_rx: bool) { let lcd_user = self.lcd_cam.lcd_user(); // Wait until LCD_START is cleared by hardware. @@ -266,7 +271,7 @@ impl<'d, CH: DmaChannel, P: TxPins> DmaSupport for I8080<'d, CH, P> { } } -impl<'d, CH: DmaChannel, P: TxPins> DmaSupportTx for I8080<'d, CH, P> { +impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> DmaSupportTx for I8080<'d, CH, P, DM> { type TX = ChannelTx<'d, CH>; fn tx(&mut self) -> &mut Self::TX { @@ -278,7 +283,7 @@ impl<'d, CH: DmaChannel, P: TxPins> DmaSupportTx for I8080<'d, CH, P> { } } -impl<'d, CH: DmaChannel, P: TxPins> I8080<'d, CH, P> +impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> I8080<'d, CH, P, DM> where P::Word: Into, { @@ -364,7 +369,35 @@ where } } -impl<'d, CH: DmaChannel, P> I8080<'d, CH, P> { +#[cfg(feature = "async")] +impl<'d, CH: DmaChannel, P: TxPins> I8080<'d, CH, P, crate::Async> +where + P::Word: Into, +{ + pub async fn send_dma_async<'t, TXBUF>( + &'t mut self, + cmd: impl Into>, + dummy: u8, + data: &'t TXBUF, + ) -> Result<(), DmaError> + where + TXBUF: ReadBuffer, + { + let (ptr, len) = unsafe { data.read_buffer() }; + + self.setup_send(cmd.into(), dummy); + self.start_write_bytes_dma(ptr as _, len * size_of::())?; + self.start_send(); + + LcdDoneFuture::new().await; + if self.tx_channel.has_error() { + return Err(DmaError::DescriptorError); + } + Ok(()) + } +} + +impl<'d, CH: DmaChannel, P, DM: Mode> I8080<'d, CH, P, DM> { fn setup_send>(&mut self, cmd: Command, dummy: u8) { // Reset LCD control unit and Async Tx FIFO self.lcd_cam @@ -476,7 +509,7 @@ impl<'d, CH: DmaChannel, P> I8080<'d, CH, P> { } } -impl<'d, CH: DmaChannel, P> core::fmt::Debug for I8080<'d, CH, P> { +impl<'d, CH: DmaChannel, P, DM: Mode> core::fmt::Debug for I8080<'d, CH, P, DM> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { f.debug_struct("I8080").finish() } diff --git a/esp-hal/src/lcd_cam/lcd/mod.rs b/esp-hal/src/lcd_cam/lcd/mod.rs index 8b980b9d5fd..a1e5dc85153 100644 --- a/esp-hal/src/lcd_cam/lcd/mod.rs +++ b/esp-hal/src/lcd_cam/lcd/mod.rs @@ -14,8 +14,9 @@ use crate::{peripheral::PeripheralRef, peripherals::LCD_CAM}; pub mod i8080; -pub struct Lcd<'d> { +pub struct Lcd<'d, DM: crate::Mode> { pub(crate) lcd_cam: PeripheralRef<'d, LCD_CAM>, + pub(crate) _mode: core::marker::PhantomData, } #[derive(Debug, Clone, Copy, PartialEq, Default)] diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index a106f0a797b..ec1b2b3b334 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -8,20 +8,23 @@ pub mod cam; pub mod lcd; +use core::marker::PhantomData; + use crate::{ + interrupt::InterruptHandler, lcd_cam::{cam::Cam, lcd::Lcd}, peripheral::Peripheral, peripherals::LCD_CAM, - system, - system::PeripheralClockControl, + system::{self, PeripheralClockControl}, + InterruptConfigurable, }; -pub struct LcdCam<'d> { - pub lcd: Lcd<'d>, +pub struct LcdCam<'d, DM: crate::Mode> { + pub lcd: Lcd<'d, DM>, pub cam: Cam<'d>, } -impl<'d> LcdCam<'d> { +impl<'d> LcdCam<'d, crate::Blocking> { pub fn new(lcd_cam: impl Peripheral

+ 'd) -> Self { crate::into_ref!(lcd_cam); @@ -30,6 +33,54 @@ impl<'d> LcdCam<'d> { Self { lcd: Lcd { lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + _mode: PhantomData, + }, + cam: Cam { + lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + }, + } + } +} + +impl<'d> crate::private::Sealed for LcdCam<'d, crate::Blocking> {} +// TODO: This interrupt is shared with the Camera module, we should handle this +// in a similar way to the gpio::IO +impl<'d> InterruptConfigurable for LcdCam<'d, crate::Blocking> { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + unsafe { + crate::interrupt::bind_interrupt( + crate::peripherals::Interrupt::LCD_CAM, + handler.handler(), + ); + crate::interrupt::enable(crate::peripherals::Interrupt::LCD_CAM, handler.priority()) + .unwrap(); + } + } +} + +#[cfg(feature = "async")] +impl<'d> LcdCam<'d, crate::Async> { + pub fn new_async(lcd_cam: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(lcd_cam); + + PeripheralClockControl::enable(system::Peripheral::LcdCam); + + unsafe { + crate::interrupt::bind_interrupt( + crate::peripherals::Interrupt::LCD_CAM, + asynch::interrupt_handler.handler(), + ); + } + crate::interrupt::enable( + crate::peripherals::Interrupt::LCD_CAM, + asynch::interrupt_handler.priority(), + ) + .unwrap(); + + Self { + lcd: Lcd { + lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + _mode: PhantomData, }, cam: Cam { lcd_cam: unsafe { lcd_cam.clone_unchecked() }, @@ -60,7 +111,99 @@ pub enum ByteOrder { Inverted = 1, } +#[doc(hidden)] +#[cfg(feature = "async")] +pub mod asynch { + use core::task::Poll; + + use embassy_sync::waitqueue::AtomicWaker; + use procmacros::handler; + + use super::private::Instance; + + static TX_WAKER: AtomicWaker = AtomicWaker::new(); + + pub(crate) struct LcdDoneFuture {} + + impl LcdDoneFuture { + pub(crate) fn new() -> Self { + Self {} + } + } + + impl core::future::Future for LcdDoneFuture { + type Output = (); + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + TX_WAKER.register(cx.waker()); + if Instance::is_lcd_done_set() { + Instance::clear_lcd_done(); + Poll::Ready(()) + } else { + Instance::listen_lcd_done(); + Poll::Pending + } + } + } + + impl Drop for LcdDoneFuture { + fn drop(&mut self) { + Instance::unlisten_lcd_done(); + } + } + + #[handler] + pub(crate) fn interrupt_handler() { + // TODO: this is a shared interrupt with Camera and here we ignore that! + if Instance::is_lcd_done_set() { + Instance::unlisten_lcd_done(); + TX_WAKER.wake() + } + } +} + mod private { + #[cfg(feature = "async")] + pub(crate) struct Instance; + + // NOTE: the LCD_CAM interrupt registers are shared between LCD and Camera and + // this is only implemented for the LCD side, when the Camera is implemented a + // CriticalSection will be needed to protect these shared registers. + #[cfg(feature = "async")] + impl Instance { + pub(crate) fn listen_lcd_done() { + let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_ena() + .modify(|_, w| w.lcd_trans_done_int_ena().set_bit()); + } + + pub(crate) fn unlisten_lcd_done() { + let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_ena() + .modify(|_, w| w.lcd_trans_done_int_ena().clear_bit()); + } + + pub(crate) fn is_lcd_done_set() -> bool { + let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_raw() + .read() + .lcd_trans_done_int_raw() + .bit() + } + + pub(crate) fn clear_lcd_done() { + let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_clr() + .write(|w| w.lcd_trans_done_int_clr().set_bit()); + } + } pub struct ClockDivider { // Integral LCD clock divider value. (8 bits) // Value 0 is treated as 256 diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 02c1a7c76d3..c7047cfbbcf 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -56,6 +56,14 @@ harness = false name = "i2s_async" harness = false +[[test]] +name = "lcd_cam_i8080" +harness = false + +[[test]] +name = "lcd_cam_i8080_async" +harness = false + [[test]] name = "spi_full_duplex" harness = false diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs new file mode 100644 index 00000000000..ea8ab112385 --- /dev/null +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -0,0 +1,129 @@ +//! lcd_cam i8080 tests + +//% CHIPS: esp32s3 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use esp_backtrace as _; +use esp_hal::{ + clock::{ClockControl, Clocks}, + dma::{Dma, DmaDescriptor, DmaPriority}, + dma_buffers, + gpio::dummy_pin::DummyPin, + lcd_cam::{ + lcd::{ + i8080, + i8080::{Command, TxEightBits, I8080}, + }, + LcdCam, + }, + peripherals::Peripherals, + prelude::*, + system::SystemControl, +}; + +const DATA_SIZE: usize = 1024 * 10; + +struct Context<'d> { + lcd_cam: LcdCam<'d, esp_hal::Blocking>, + clocks: Clocks<'d>, + dma: Dma<'d>, + tx_buffer: &'static [u8], + tx_descriptors: &'static mut [DmaDescriptor], +} + +impl<'d> Context<'d> { + pub fn init() -> Self { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let dma = Dma::new(peripherals.DMA); + let lcd_cam = LcdCam::new(peripherals.LCD_CAM); + let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + + Self { + lcd_cam, + clocks, + dma, + tx_buffer, + tx_descriptors, + } + } +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context<'static> { + Context::init() + } + + #[test] + fn test_i8080_8bit(ctx: Context<'static>) { + let channel = ctx.dma.channel0.configure(false, DmaPriority::Priority0); + + let pins = TxEightBits::new( + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + ); + + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + channel.tx, + ctx.tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &ctx.clocks, + ); + + let xfer = i8080 + .send_dma(Command::::None, 0, &ctx.tx_buffer) + .unwrap(); + xfer.wait().unwrap(); + } + + #[test] + fn test_i8080_8bit_async_channel(ctx: Context<'static>) { + let channel = ctx + .dma + .channel0 + .configure_for_async(false, DmaPriority::Priority0); + let pins = TxEightBits::new( + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + ); + + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + channel.tx, + ctx.tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &ctx.clocks, + ); + + let xfer = i8080 + .send_dma(Command::::None, 0, &ctx.tx_buffer) + .unwrap(); + xfer.wait().unwrap(); + } +} diff --git a/hil-test/tests/lcd_cam_i8080_async.rs b/hil-test/tests/lcd_cam_i8080_async.rs new file mode 100644 index 00000000000..4d8432660d2 --- /dev/null +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -0,0 +1,128 @@ +//! lcd_cam i8080 tests + +//% CHIPS: esp32s3 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use esp_backtrace as _; +use esp_hal::{ + clock::{ClockControl, Clocks}, + dma::{Dma, DmaDescriptor, DmaPriority}, + dma_buffers, + gpio::dummy_pin::DummyPin, + lcd_cam::{ + lcd::{ + i8080, + i8080::{Command, TxEightBits, I8080}, + }, + LcdCam, + }, + peripherals::Peripherals, + prelude::*, + system::SystemControl, +}; + +const DATA_SIZE: usize = 1024 * 10; + +struct Context<'d> { + lcd_cam: LcdCam<'d, esp_hal::Async>, + clocks: Clocks<'d>, + dma: Dma<'d>, + tx_buffer: &'static [u8], + tx_descriptors: &'static mut [DmaDescriptor], +} + +impl<'d> Context<'d> { + pub fn init() -> Self { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let dma = Dma::new(peripherals.DMA); + let lcd_cam = LcdCam::new_async(peripherals.LCD_CAM); + let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + + Self { + lcd_cam, + clocks, + dma, + tx_buffer, + tx_descriptors, + } + } +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use super::*; + + #[init] + async fn init() -> Context<'static> { + Context::init() + } + + #[test] + async fn test_i8080_8bit(ctx: Context<'static>) { + let channel = ctx.dma.channel0.configure(false, DmaPriority::Priority0); + let pins = TxEightBits::new( + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + ); + + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + channel.tx, + ctx.tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &ctx.clocks, + ); + + i8080 + .send_dma_async(Command::::None, 0, &ctx.tx_buffer) + .await + .unwrap(); + } + + #[test] + async fn test_i8080_8bit_async_channel(ctx: Context<'static>) { + let channel = ctx + .dma + .channel0 + .configure_for_async(false, DmaPriority::Priority0); + let pins = TxEightBits::new( + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + DummyPin::new(), + ); + + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + channel.tx, + ctx.tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &ctx.clocks, + ); + + i8080 + .send_dma_async(Command::::None, 0, &ctx.tx_buffer) + .await + .unwrap(); + } +} From dd2e475f00e4df9a602697003702db0e60a1f047 Mon Sep 17 00:00:00 2001 From: liebman Date: Mon, 29 Jul 2024 08:13:59 -0700 Subject: [PATCH 4/8] lcd_cam: i8080 fix bad len calculation (missed from ReadBuffer update) (#1876) --- esp-hal/src/lcd_cam/lcd/i8080.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 309bc8d7bd4..fad47926b1c 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -386,7 +386,7 @@ where let (ptr, len) = unsafe { data.read_buffer() }; self.setup_send(cmd.into(), dummy); - self.start_write_bytes_dma(ptr as _, len * size_of::())?; + self.start_write_bytes_dma(ptr as _, len)?; self.start_send(); LcdDoneFuture::new().await; From 81f3776aa79d5b6d0098ecf499abaf03e0a6ef56 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Tue, 30 Jul 2024 07:38:57 +0000 Subject: [PATCH 5/8] Deny warnings in CI (#1877) * Deny warnings when building in CI * Build RISC-V using `stable` release channel again --- .github/workflows/ci.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb7812c8d86..a4e3199245d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,7 @@ env: CARGO_TERM_COLOR: always GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} MSRV: "1.76.0" + RUSTDOCFLAGS: -Dwarnings # Cancel any currently running workflows from the same PR, branch, or # tag when a new workflow is triggered. @@ -62,6 +63,13 @@ jobs: ] steps: + - name: Set up cargo environment + run: | + # Convert the target triple from kebab-case to SCREAMING_SNAKE_CASE: + big_target=$(echo "${{ matrix.device.target }}" | tr [:lower:] [:upper:] | tr '-' '_') + # Set the *target specific* RUSTFLAGS for the current device: + echo "CARGO_TARGET_${big_target}_RUSTFLAGS=-Dwarnings" >> $GITHUB_ENV + - uses: actions/checkout@v4 # Install the Rust toolchain for Xtensa devices: @@ -73,12 +81,12 @@ jobs: - uses: dtolnay/rust-toolchain@v1 with: target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf - toolchain: stable + toolchain: nightly components: rust-src - uses: dtolnay/rust-toolchain@v1 with: target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf - toolchain: nightly + toolchain: stable components: rust-src - uses: Swatinem/rust-cache@v2 @@ -89,7 +97,7 @@ jobs: run: cargo xtask build-examples esp-lp-hal ${{ matrix.device.soc }} - if: contains(fromJson('["esp32c6", "esp32s2", "esp32s3"]'), matrix.device.soc) name: Check esp-lp-hal documentation - run: RUSTDOCFLAGS="-D warnings" cargo xtask build-documentation --packages esp-lp-hal --chips ${{ matrix.device.soc }} + run: cargo xtask build-documentation --packages esp-lp-hal --chips ${{ matrix.device.soc }} # Make sure we're able to build the HAL without the default features # enabled: @@ -107,7 +115,7 @@ jobs: - name: Check doc-tests run: cargo +esp xtask run-doc-test esp-hal ${{ matrix.device.soc }} - name: Check documentation - run: RUSTDOCFLAGS="-D warnings" cargo xtask build-documentation --packages esp-hal --chips ${{ matrix.device.soc }} + run: cargo xtask build-documentation --packages esp-hal --chips ${{ matrix.device.soc }} # Run clippy - name: Clippy # We use the 'esp' toolchain for *all* targets, in order to get a @@ -224,8 +232,8 @@ jobs: steps: - uses: actions/checkout@v4 - # Install the Rust toolchain for RISC-V devices: - + + # Install the Rust toolchain for RISC-V devices: - if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.target.soc) }} uses: dtolnay/rust-toolchain@v1 with: From 311f0127de3adc209d670aaa3b952e10a18ce470 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Wed, 31 Jul 2024 07:29:10 +0000 Subject: [PATCH 6/8] Implement `embedded_io::{ReadReady, WriteReady}` traits for `WifiStack` (#1882) * implement `ReadReady` and `WriteReady` traits from `embedded-io` for Wi-Fi * Allow non-static lifetime for `WifiStack` * Update `CHANGELOG.md` * clippy --- esp-wifi/CHANGELOG.md | 6 ++-- esp-wifi/src/wifi_interface.rs | 55 ++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/esp-wifi/CHANGELOG.md b/esp-wifi/CHANGELOG.md index 0ae57dbb337..e0ca0055c46 100644 --- a/esp-wifi/CHANGELOG.md +++ b/esp-wifi/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Implement `embedded_io::{ReadReady, WriteReady}` traits for `WifiStack` (#1882) + ### Changed ### Fixed @@ -17,8 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.7.1 - 2024-07-17 -### Added - ### Changed - Check no password is set when using `AuthMethod::None`(#1806) @@ -27,8 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Downgrade `embedded-svc` to 0.27.1 (#1820) -### Removed - ## 0.7.0 - 2024-07-15 ### Added diff --git a/esp-wifi/src/wifi_interface.rs b/esp-wifi/src/wifi_interface.rs index 9c2910b77e5..7f8d33d0f7e 100644 --- a/esp-wifi/src/wifi_interface.rs +++ b/esp-wifi/src/wifi_interface.rs @@ -2,10 +2,6 @@ use core::{borrow::BorrowMut, cell::RefCell, fmt::Display}; -#[cfg(feature = "tcp")] -use embedded_io::ErrorType; -#[cfg(feature = "tcp")] -use embedded_io::{Read, Write}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::Socket as Dhcpv4Socket; #[cfg(feature = "tcp")] @@ -34,7 +30,7 @@ const LOCAL_PORT_MAX: u16 = 65535; /// /// Mostly a convenience wrapper for `smoltcp` pub struct WifiStack<'a, MODE: WifiDeviceMode> { - device: RefCell>, // TODO allow non static lifetime + device: RefCell>, network_interface: RefCell, sockets: RefCell>, current_millis_fn: fn() -> u64, @@ -53,7 +49,7 @@ pub struct WifiStack<'a, MODE: WifiDeviceMode> { impl<'a, MODE: WifiDeviceMode> WifiStack<'a, MODE> { pub fn new( network_interface: Interface, - device: WifiDevice<'static, MODE>, // TODO relax this lifetime requirement + device: WifiDevice<'a, MODE>, #[allow(unused_mut)] mut sockets: SocketSet<'a>, current_millis_fn: fn() -> u64, ) -> WifiStack<'a, MODE> { @@ -709,12 +705,12 @@ impl embedded_io::Error for IoError { } #[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> ErrorType for Socket<'s, 'n, MODE> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::ErrorType for Socket<'s, 'n, MODE> { type Error = IoError; } #[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> Read for Socket<'s, 'n, MODE> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::Read for Socket<'s, 'n, MODE> { fn read(&mut self, buf: &mut [u8]) -> Result { self.network.with_mut(|interface, device, sockets| { use smoltcp::socket::tcp::RecvError; @@ -735,7 +731,25 @@ impl<'s, 'n: 's, MODE: WifiDeviceMode> Read for Socket<'s, 'n, MODE> { } #[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> Write for Socket<'s, 'n, MODE> { +impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::ReadReady for Socket<'s, 'n, MODE> { + fn read_ready(&mut self) -> Result { + self.network.with_mut(|interface, device, sockets| { + use smoltcp::socket::tcp::RecvError; + + interface.poll(timestamp(), device, sockets); + let socket = sockets.get_mut::(self.socket_handle); + + match socket.peek(1) { + Ok(s) => Ok(!s.is_empty()), + Err(RecvError::Finished) => Err(IoError::SocketClosed), + Err(RecvError::InvalidState) => Err(IoError::TcpRecvError), + } + }) + } +} + +#[cfg(feature = "tcp")] +impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::Write for Socket<'s, 'n, MODE> { fn write(&mut self, buf: &[u8]) -> Result { loop { let (may_send, is_open, can_send) = @@ -799,6 +813,29 @@ impl<'s, 'n: 's, MODE: WifiDeviceMode> Write for Socket<'s, 'n, MODE> { } } +#[cfg(feature = "tcp")] +impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::WriteReady for Socket<'s, 'n, MODE> { + fn write_ready(&mut self) -> Result { + let (may_send, is_open, can_send) = self.network.with_mut(|interface, device, sockets| { + interface.poll( + Instant::from_millis((self.network.current_millis_fn)() as i64), + device, + sockets, + ); + + let socket = sockets.get_mut::(self.socket_handle); + + (socket.may_send(), socket.is_open(), socket.can_send()) + }); + + if !is_open || !can_send { + return Err(IoError::SocketClosed); + } + + Ok(may_send) + } +} + /// A UDP socket #[cfg(feature = "udp")] pub struct UdpSocket<'s, 'n: 's, MODE: WifiDeviceMode> { From 40d5481eddd70580eeb79b6908f6fec0e9b06d61 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Arcos Date: Wed, 31 Jul 2024 17:47:44 +0200 Subject: [PATCH 7/8] ci: Disable test_asymmetric_dma_transfer for S3 (#1888) --- hil-test/tests/spi_full_duplex_dma.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hil-test/tests/spi_full_duplex_dma.rs b/hil-test/tests/spi_full_duplex_dma.rs index f57f377fa37..a07438ef8e3 100644 --- a/hil-test/tests/spi_full_duplex_dma.rs +++ b/hil-test/tests/spi_full_duplex_dma.rs @@ -81,6 +81,8 @@ mod tests { #[test] #[timeout(3)] + // S3 is disabled due to https://github.com/esp-rs/esp-hal/issues/1524#issuecomment-2255306292 + #[cfg(not(feature = "esp32s3"))] fn test_asymmetric_dma_transfer() { let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); From 0c29c4381c1519e566e559bd904c9d87cc98c546 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Arcos Date: Thu, 1 Aug 2024 19:42:49 +0200 Subject: [PATCH 8/8] ci: Use stable channel for RISCV devices (#1896) --- .github/workflows/hil.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/hil.yml b/.github/workflows/hil.yml index 6a805c87004..77170623bde 100644 --- a/.github/workflows/hil.yml +++ b/.github/workflows/hil.yml @@ -55,7 +55,7 @@ jobs: uses: dtolnay/rust-toolchain@v1 with: target: ${{ matrix.target.rust-target }} - toolchain: nightly + toolchain: stable components: rust-src # Install the Rust toolchain for Xtensa devices: - if: contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.target.soc) @@ -103,7 +103,7 @@ jobs: # RISC-V devices: - soc: esp32c2 runner: esp32c2-jtag - usb: ACM0 + usb: USB2 - soc: esp32c3 runner: esp32c3-usb usb: ACM0 @@ -112,7 +112,7 @@ jobs: usb: ACM0 - soc: esp32h2 runner: esp32h2-usb - usb: ACM0 + usb: USB0 # Xtensa devices: - soc: esp32s2 runner: esp32s2-jtag