From a5307504350aa982e26017542dd4e0ad936e05f3 Mon Sep 17 00:00:00 2001 From: liebman Date: Thu, 18 Jul 2024 16:30:00 -0700 Subject: [PATCH 01/14] implement async for lcd_cam i8080 --- esp-hal/src/lcd_cam/lcd/i8080.rs | 76 ++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index f2413ee17a8..4698b1416e9 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -57,7 +57,7 @@ //! # } //! ``` -use core::{fmt::Formatter, mem::size_of}; +use core::{fmt::Formatter, marker::PhantomData, mem::size_of}; use fugit::HertzU32; @@ -86,21 +86,41 @@ 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> I8080<'d, CH, P, crate::Blocking> where CH::P: LcdCamPeripheral, P::Word: Into, { pub fn new( + lcd: Lcd<'d>, + channel: ChannelTx<'d, CH>, + descriptors: &'static mut [DmaDescriptor], + pins: P, + frequency: HertzU32, + config: Config, + clocks: &Clocks, + ) -> Self { + Self::new_internal(lcd, channel, descriptors, pins, frequency, config, clocks) + } +} + +impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> I8080<'d, CH, P, DM> +where + CH::P: LcdCamPeripheral, + P::Word: Into, +{ + fn new_internal( lcd: Lcd<'d>, mut channel: ChannelTx<'d, CH>, descriptors: &'static mut [DmaDescriptor], @@ -249,11 +269,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 +287,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 +299,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 +385,46 @@ 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 fn new_async( + lcd: Lcd<'d>, + channel: ChannelTx<'d, CH>, + descriptors: &'static mut [DmaDescriptor], + pins: P, + frequency: HertzU32, + config: Config, + clocks: &Clocks, + ) -> Self + where + ::P: crate::dma::LcdCamPeripheral, + { + I8080::new_internal(lcd, channel, descriptors, pins, frequency, config, clocks) + } + + 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(); + crate::dma::asynch::DmaTxFuture::new(&mut self.tx_channel).await?; + 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 +536,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() } From 02595ed10fc359b4b8b150fc6d91fe9a9c57df24 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 06:34:00 -0700 Subject: [PATCH 02/14] lcd_cam: implement InterruptConfigurable for blocking and improve async --- esp-hal/src/lcd_cam/lcd/i8080.rs | 20 ++-- esp-hal/src/lcd_cam/lcd/mod.rs | 3 +- esp-hal/src/lcd_cam/mod.rs | 156 ++++++++++++++++++++++++++++++- 3 files changed, 162 insertions(+), 17 deletions(-) diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 4698b1416e9..b9b2c60fc45 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -88,6 +88,8 @@ use crate::{ peripherals::LCD_CAM, Mode, }; +#[cfg(feature = "async")] +use crate::{dma::asynch::DmaTxFuture, lcd_cam::asynch::LcdDoneFuture}; pub struct I8080<'d, CH: DmaChannel, P, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, @@ -97,13 +99,13 @@ pub struct I8080<'d, CH: DmaChannel, P, DM: Mode> { _phantom: PhantomData, } -impl<'d, CH: DmaChannel, P: TxPins> I8080<'d, CH, P, crate::Blocking> +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>, channel: ChannelTx<'d, CH>, descriptors: &'static mut [DmaDescriptor], pins: P, @@ -113,15 +115,9 @@ where ) -> Self { Self::new_internal(lcd, channel, descriptors, pins, frequency, config, clocks) } -} -impl<'d, CH: DmaChannel, P: TxPins, DM: Mode> I8080<'d, CH, P, DM> -where - CH::P: LcdCamPeripheral, - P::Word: Into, -{ fn new_internal( - lcd: Lcd<'d>, + lcd: Lcd<'d, DM>, mut channel: ChannelTx<'d, CH>, descriptors: &'static mut [DmaDescriptor], mut pins: P, @@ -391,7 +387,7 @@ where P::Word: Into, { pub fn new_async( - lcd: Lcd<'d>, + lcd: Lcd<'d, crate::Async>, channel: ChannelTx<'d, CH>, descriptors: &'static mut [DmaDescriptor], pins: P, @@ -419,7 +415,9 @@ where self.setup_send(cmd.into(), dummy); self.start_write_bytes_dma(ptr as _, len * size_of::())?; self.start_send(); - crate::dma::asynch::DmaTxFuture::new(&mut self.tx_channel).await?; + + DmaTxFuture::new(&mut self.tx_channel).await?; + LcdDoneFuture::new().await; Ok(()) } } 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..39a357ecb80 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,42 @@ 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); + + Self { + lcd: Lcd { + lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + _mode: PhantomData, }, cam: Cam { lcd_cam: unsafe { lcd_cam.clone_unchecked() }, @@ -60,7 +99,114 @@ 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 { + Instance::listen_lcd_done(); + 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 { + unsafe { + // TODO: this is a shared interrupt with Camera and here we ignore that! + crate::interrupt::bind_interrupt( + crate::peripherals::Interrupt::LCD_CAM, + interrupt_handler.handler(), + ); + } + crate::interrupt::enable( + crate::peripherals::Interrupt::LCD_CAM, + interrupt_handler.priority(), + ) + .unwrap(); + + TX_WAKER.register(cx.waker()); + if Instance::is_listening_lcd_done() { + Poll::Pending + } else { + Poll::Ready(()) + } + } + } + #[handler] + fn interrupt_handler() { + // TODO: this is a shared interrupt with Camera and here we ignore that! + if Instance::is_lcd_done_set() { + Instance::clear_lcd_done(); + 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, 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_listening_lcd_done() -> bool { + let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_ena() + .read() + .lcd_trans_done_int_ena() + .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 From 8d2464d53dd924259fbd501bb88bf323d898590d Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 07:08:02 -0700 Subject: [PATCH 03/14] lcd_cam: use new async interrupt semantics from #1835 --- esp-hal/src/lcd_cam/mod.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 39a357ecb80..878a084a407 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -141,18 +141,25 @@ pub mod asynch { .unwrap(); TX_WAKER.register(cx.waker()); - if Instance::is_listening_lcd_done() { - Poll::Pending - } else { + if Instance::is_lcd_done_set() { + Instance::clear_lcd_done(); Poll::Ready(()) + } else { + Poll::Pending } } } + + impl Drop for LcdDoneFuture { + fn drop(&mut self) { + Instance::unlisten_lcd_done(); + } + } + #[handler] fn interrupt_handler() { // TODO: this is a shared interrupt with Camera and here we ignore that! if Instance::is_lcd_done_set() { - Instance::clear_lcd_done(); Instance::unlisten_lcd_done(); TX_WAKER.wake() } From b121fd46d98ad1a66db32f3c881a4a74d753f5e3 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 09:23:29 -0700 Subject: [PATCH 04/14] lcd_cam: move interrupt handler binding into `new_async()` --- esp-hal/src/lcd_cam/mod.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 878a084a407..403578a7964 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -65,6 +65,18 @@ impl<'d> LcdCam<'d, crate::Async> { 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() }, @@ -127,19 +139,6 @@ pub mod asynch { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - unsafe { - // TODO: this is a shared interrupt with Camera and here we ignore that! - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::LCD_CAM, - interrupt_handler.handler(), - ); - } - crate::interrupt::enable( - crate::peripherals::Interrupt::LCD_CAM, - interrupt_handler.priority(), - ) - .unwrap(); - TX_WAKER.register(cx.waker()); if Instance::is_lcd_done_set() { Instance::clear_lcd_done(); @@ -157,7 +156,7 @@ pub mod asynch { } #[handler] - fn interrupt_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(); From 6110b35954e5023f3e105561e13da29d50cd1477 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 09:29:16 -0700 Subject: [PATCH 05/14] lcd_cam: Instance::is_listening_lcd_done not used --- esp-hal/src/lcd_cam/mod.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 403578a7964..51e9ebc4997 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -188,15 +188,6 @@ mod private { .modify(|_, w| w.lcd_trans_done_int_ena().clear_bit()); } - pub(crate) fn is_listening_lcd_done() -> bool { - let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; - lcd_cam - .lc_dma_int_ena() - .read() - .lcd_trans_done_int_ena() - .bit() - } - pub(crate) fn is_lcd_done_set() -> bool { let lcd_cam = unsafe { crate::peripherals::LCD_CAM::steal() }; lcd_cam From bc43ed6f1ac220d13dd26bbba8f322e86b76fb61 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 09:45:18 -0700 Subject: [PATCH 06/14] i8080: no need for seperate `new_async()` --- esp-hal/src/lcd_cam/lcd/i8080.rs | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index b9b2c60fc45..252d1a24f5d 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -105,18 +105,6 @@ where P::Word: Into, { pub fn new( - lcd: Lcd<'d, DM>, - channel: ChannelTx<'d, CH>, - descriptors: &'static mut [DmaDescriptor], - pins: P, - frequency: HertzU32, - config: Config, - clocks: &Clocks, - ) -> Self { - Self::new_internal(lcd, channel, descriptors, pins, frequency, config, clocks) - } - - fn new_internal( lcd: Lcd<'d, DM>, mut channel: ChannelTx<'d, CH>, descriptors: &'static mut [DmaDescriptor], @@ -386,21 +374,6 @@ impl<'d, CH: DmaChannel, P: TxPins> I8080<'d, CH, P, crate::Async> where P::Word: Into, { - pub fn new_async( - lcd: Lcd<'d, crate::Async>, - channel: ChannelTx<'d, CH>, - descriptors: &'static mut [DmaDescriptor], - pins: P, - frequency: HertzU32, - config: Config, - clocks: &Clocks, - ) -> Self - where - ::P: crate::dma::LcdCamPeripheral, - { - I8080::new_internal(lcd, channel, descriptors, pins, frequency, config, clocks) - } - pub async fn send_dma_async<'t, TXBUF>( &'t mut self, cmd: impl Into>, From 415c409cc15f41bf7200d73f91425d80919032f2 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 13:01:31 -0700 Subject: [PATCH 07/14] i8080: don't use DmaTxFuture, just test for dma error after complete --- esp-hal/src/lcd_cam/lcd/i8080.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 252d1a24f5d..c8fd60fe6dd 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -61,6 +61,8 @@ 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::{ @@ -88,8 +90,6 @@ use crate::{ peripherals::LCD_CAM, Mode, }; -#[cfg(feature = "async")] -use crate::{dma::asynch::DmaTxFuture, lcd_cam::asynch::LcdDoneFuture}; pub struct I8080<'d, CH: DmaChannel, P, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, @@ -389,8 +389,10 @@ where self.start_write_bytes_dma(ptr as _, len * size_of::())?; self.start_send(); - DmaTxFuture::new(&mut self.tx_channel).await?; LcdDoneFuture::new().await; + if self.tx_channel.has_error() { + return Err(DmaError::DescriptorError); + } Ok(()) } } From de331aba49dd4a282f3a5022a844a37e2ae40aec Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 13:02:41 -0700 Subject: [PATCH 08/14] add HIL tests for LCD_CAM i8080 in blocking and async. --- hil-test/Cargo.toml | 8 +++ hil-test/tests/lcd_cam_i8080.rs | 72 ++++++++++++++++++++++++++ hil-test/tests/lcd_cam_i8080_async.rs | 74 +++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 hil-test/tests/lcd_cam_i8080.rs create mode 100644 hil-test/tests/lcd_cam_i8080_async.rs 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..df2d3f822b9 --- /dev/null +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -0,0 +1,72 @@ +//! lcd_cam i8080 tests + +//% CHIPS: esp32s3 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + dma::{Dma, 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; + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() {} + + #[test] + fn test_i8080_8bit() { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + + let dma = Dma::new(peripherals.DMA); + let channel = 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 lcd_cam = LcdCam::new(peripherals.LCD_CAM); + + let mut i8080 = I8080::new( + lcd_cam.lcd, + channel.tx, + tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &clocks, + ); + + let xfer = i8080.send_dma(Command::::None, 0, &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..44cac304880 --- /dev/null +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -0,0 +1,74 @@ +//! lcd_cam i8080 tests + +//% CHIPS: esp32s3 + +#![no_std] +#![no_main] + +use defmt_rtt as _; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + dma::{Dma, 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; + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use super::*; + + #[init] + async fn init() {} + + #[test] + async fn test_i8080_8bit() { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + + let dma = Dma::new(peripherals.DMA); + let channel = 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 lcd_cam = LcdCam::new_async(peripherals.LCD_CAM); + + let mut i8080 = I8080::new( + lcd_cam.lcd, + channel.tx, + tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &clocks, + ); + + i8080 + .send_dma_async(Command::::None, 0, &tx_buffer) + .await + .unwrap(); + } +} From de3775fd00a5dd0e66f8cd6a1514d5a86fc33feb Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 14:42:16 -0700 Subject: [PATCH 09/14] lcd_cam_i8080: test channels configured for async as well since teh compiler can't prevent it for now --- hil-test/tests/lcd_cam_i8080.rs | 75 ++++++++++++++++++++++----- hil-test/tests/lcd_cam_i8080_async.rs | 73 +++++++++++++++++++++----- 2 files changed, 122 insertions(+), 26 deletions(-) diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs index df2d3f822b9..1bcf5df7327 100644 --- a/hil-test/tests/lcd_cam_i8080.rs +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -23,27 +23,77 @@ use esp_hal::{ prelude::*, system::SystemControl, }; +use esp_hal::clock::Clocks; +use esp_hal::dma::DmaDescriptor; 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() {} + fn init() -> Context<'static> { + Context::init() + } #[test] - fn test_i8080_8bit() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + 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 (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + channel.tx, + ctx.tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &ctx.clocks, + ); - let dma = Dma::new(peripherals.DMA); - let channel = dma.channel0.configure(false, DmaPriority::Priority0); + 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(), @@ -54,19 +104,18 @@ mod tests { DummyPin::new(), DummyPin::new(), ); - let lcd_cam = LcdCam::new(peripherals.LCD_CAM); let mut i8080 = I8080::new( - lcd_cam.lcd, + ctx.lcd_cam.lcd, channel.tx, - tx_descriptors, + ctx.tx_descriptors, pins, 20.MHz(), i8080::Config::default(), - &clocks, + &ctx.clocks, ); - let xfer = i8080.send_dma(Command::::None, 0, &tx_buffer).unwrap(); + 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 index 44cac304880..5f5f360ce14 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -23,27 +23,75 @@ use esp_hal::{ prelude::*, system::SystemControl, }; +use esp_hal::clock::Clocks; +use esp_hal::dma::DmaDescriptor; 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() {} + async fn init() -> Context<'static> { + Context::init() + } #[test] - async fn test_i8080_8bit() { - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + 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 (tx_buffer, tx_descriptors, _, _) = dma_buffers!(DATA_SIZE, 0); + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + channel.tx, + ctx.tx_descriptors, + pins, + 20.MHz(), + i8080::Config::default(), + &ctx.clocks, + ); - let dma = Dma::new(peripherals.DMA); - let channel = dma.channel0.configure(false, DmaPriority::Priority0); + 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(), @@ -54,20 +102,19 @@ mod tests { DummyPin::new(), DummyPin::new(), ); - let lcd_cam = LcdCam::new_async(peripherals.LCD_CAM); let mut i8080 = I8080::new( - lcd_cam.lcd, + ctx.lcd_cam.lcd, channel.tx, - tx_descriptors, + ctx.tx_descriptors, pins, 20.MHz(), i8080::Config::default(), - &clocks, + &ctx.clocks, ); i8080 - .send_dma_async(Command::::None, 0, &tx_buffer) + .send_dma_async(Command::::None, 0, &ctx.tx_buffer) .await .unwrap(); } From 050fbe5176a72cf2bdd1b078be6c23ba5d53cda2 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 14:47:34 -0700 Subject: [PATCH 10/14] fmt :-/ --- hil-test/tests/lcd_cam_i8080.rs | 24 ++++++++++++++++-------- hil-test/tests/lcd_cam_i8080_async.rs | 19 +++++++++++++------ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs index 1bcf5df7327..ea8ab112385 100644 --- a/hil-test/tests/lcd_cam_i8080.rs +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -8,8 +8,8 @@ use defmt_rtt as _; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaPriority}, + clock::{ClockControl, Clocks}, + dma::{Dma, DmaDescriptor, DmaPriority}, dma_buffers, gpio::dummy_pin::DummyPin, lcd_cam::{ @@ -23,8 +23,6 @@ use esp_hal::{ prelude::*, system::SystemControl, }; -use esp_hal::clock::Clocks; -use esp_hal::dma::DmaDescriptor; const DATA_SIZE: usize = 1024 * 10; @@ -45,7 +43,13 @@ impl<'d> Context<'d> { 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 } + Self { + lcd_cam, + clocks, + dma, + tx_buffer, + tx_descriptors, + } } } @@ -62,7 +66,7 @@ mod tests { #[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(), @@ -84,7 +88,9 @@ mod tests { &ctx.clocks, ); - let xfer = i8080.send_dma(Command::::None, 0, &ctx.tx_buffer).unwrap(); + let xfer = i8080 + .send_dma(Command::::None, 0, &ctx.tx_buffer) + .unwrap(); xfer.wait().unwrap(); } @@ -115,7 +121,9 @@ mod tests { &ctx.clocks, ); - let xfer = i8080.send_dma(Command::::None, 0, &ctx.tx_buffer).unwrap(); + 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 index 5f5f360ce14..4d8432660d2 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -8,8 +8,8 @@ use defmt_rtt as _; use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, - dma::{Dma, DmaPriority}, + clock::{ClockControl, Clocks}, + dma::{Dma, DmaDescriptor, DmaPriority}, dma_buffers, gpio::dummy_pin::DummyPin, lcd_cam::{ @@ -23,8 +23,6 @@ use esp_hal::{ prelude::*, system::SystemControl, }; -use esp_hal::clock::Clocks; -use esp_hal::dma::DmaDescriptor; const DATA_SIZE: usize = 1024 * 10; @@ -45,7 +43,13 @@ impl<'d> Context<'d> { 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 } + Self { + lcd_cam, + clocks, + dma, + tx_buffer, + tx_descriptors, + } } } @@ -91,7 +95,10 @@ mod tests { #[test] async fn test_i8080_8bit_async_channel(ctx: Context<'static>) { - let channel = ctx.dma.channel0.configure_for_async(false, DmaPriority::Priority0); + let channel = ctx + .dma + .channel0 + .configure_for_async(false, DmaPriority::Priority0); let pins = TxEightBits::new( DummyPin::new(), DummyPin::new(), From 059c85ff528a23a125ed5c6f07c2bc6aa44d7c68 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 15:15:33 -0700 Subject: [PATCH 11/14] lcd_cam fix comment --- esp-hal/src/lcd_cam/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 51e9ebc4997..ee0609a24ef 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -170,7 +170,7 @@ mod private { 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, the Camera is implemented a + // 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 { From 11278649733271e7e11096163dd4b189a705793f Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 21 Jul 2024 15:32:41 -0700 Subject: [PATCH 12/14] changelog --- esp-hal/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) 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) From e851cfe349bbc9e1b93015c5e1baa6f08f9a6ad2 Mon Sep 17 00:00:00 2001 From: liebman Date: Wed, 24 Jul 2024 10:12:45 -0700 Subject: [PATCH 13/14] lcd_cam async: no need to enable interrupts until polled --- esp-hal/src/lcd_cam/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index ee0609a24ef..ec1b2b3b334 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -127,7 +127,6 @@ pub mod asynch { impl LcdDoneFuture { pub(crate) fn new() -> Self { - Instance::listen_lcd_done(); Self {} } } @@ -144,6 +143,7 @@ pub mod asynch { Instance::clear_lcd_done(); Poll::Ready(()) } else { + Instance::listen_lcd_done(); Poll::Pending } } From 55206035ef63f24706735a606a21008a4337c2ef Mon Sep 17 00:00:00 2001 From: liebman Date: Mon, 29 Jul 2024 06:13:35 -0700 Subject: [PATCH 14/14] lcd_cam: i8080 update for ReadBuffer changes --- 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 c8fd60fe6dd..309bc8d7bd4 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -381,7 +381,7 @@ where data: &'t TXBUF, ) -> Result<(), DmaError> where - TXBUF: ReadBuffer, + TXBUF: ReadBuffer, { let (ptr, len) = unsafe { data.read_buffer() };