From 2811ad92090ff23b2f922728b6637033cd5cd615 Mon Sep 17 00:00:00 2001 From: Juraj Sadel Date: Tue, 7 Oct 2025 16:20:51 +0200 Subject: [PATCH] Merge LCD_CAM HIL into single binary (#4282) --- hil-test/Cargo.toml | 8 - hil-test/src/bin/lcd_cam.rs | 561 +++++++++++++++++++----- hil-test/src/bin/lcd_cam_i8080.rs | 300 ------------- hil-test/src/bin/lcd_cam_i8080_async.rs | 71 --- 4 files changed, 441 insertions(+), 499 deletions(-) delete mode 100644 hil-test/src/bin/lcd_cam_i8080.rs delete mode 100644 hil-test/src/bin/lcd_cam_i8080_async.rs diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 71c7eba2f..567134352 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -76,14 +76,6 @@ harness = false name = "lcd_cam" harness = false -[[bin]] -name = "lcd_cam_i8080" -harness = false - -[[bin]] -name = "lcd_cam_i8080_async" -harness = false - [[bin]] name = "misc_simple" harness = false diff --git a/hil-test/src/bin/lcd_cam.rs b/hil-test/src/bin/lcd_cam.rs index 1289139e6..34c76a128 100644 --- a/hil-test/src/bin/lcd_cam.rs +++ b/hil-test/src/bin/lcd_cam.rs @@ -1,5 +1,4 @@ -//! LCD_CAM Camera and DPI tests - +//! lcd_cam_i8080 and camera tests //% CHIPS: esp32s3 //% FEATURES: unstable @@ -7,152 +6,474 @@ #![no_main] use esp_hal::{ + Async, + Blocking, dma::{DmaChannel, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::Level, lcd_cam::{ + BitOrder, LcdCam, cam::{self, Camera, VhdeMode}, lcd::{ ClockMode, Phase, Polarity, - dpi, - dpi::{Dpi, Format, FrameTiming}, + dpi::{self, Dpi, Format, FrameTiming}, + i8080::{Command, Config, I8080}, }, }, - peripherals::Peripherals, + pcnt::{ + Pcnt, + channel::{CtrlMode, EdgeMode}, + }, + peripherals::{DMA_CH0, Peripherals}, time::Rate, }; use hil_test as _; -struct Context { +const DATA_SIZE: usize = 1024 * 10; + +#[allow(non_snake_case)] +struct Pins { + pub GPIO8: esp_hal::peripherals::GPIO8<'static>, + pub GPIO11: esp_hal::peripherals::GPIO11<'static>, + pub GPIO12: esp_hal::peripherals::GPIO12<'static>, + pub GPIO16: esp_hal::peripherals::GPIO16<'static>, + pub GPIO17: esp_hal::peripherals::GPIO17<'static>, +} + +struct AsyncContext<'d> { + lcd_cam: LcdCam<'d, Async>, + dma: DMA_CH0<'d>, + dma_buf: DmaTxBuf, +} + +struct BlockingContext<'d> { + lcd_cam: LcdCam<'d, Blocking>, + pcnt: Pcnt<'d>, + pins: Pins, + dma: DMA_CH0<'d>, + dma_buf: DmaTxBuf, +} + +struct CameraContext { peripherals: Peripherals, dma_tx_buf: DmaTxBuf, dma_rx_buf: DmaRxBuf, } -#[embedded_test::tests] -mod tests { +// lcd_cam_i8080 tests +mod async_tests { use super::*; - #[init] - fn init() -> Context { - let peripherals = esp_hal::init(esp_hal::Config::default()); + #[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] + mod tests { + use super::*; - let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(50 * 50); - let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); - let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + #[init] + async fn init() -> AsyncContext<'static> { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let lcd_cam = LcdCam::new(peripherals.LCD_CAM).into_async(); + let (_, _, tx_buffer, tx_desc) = dma_buffers!(0, DATA_SIZE); + let dma_buf = DmaTxBuf::new(tx_desc, tx_buffer).unwrap(); - Context { - peripherals, - dma_tx_buf, - dma_rx_buf, - } - } - - #[test] - fn test_camera_can_receive_from_rgb(ctx: Context) { - let peripherals = ctx.peripherals; - - let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - - let (rx_channel, tx_channel) = peripherals.DMA_CH2.split(); - - let (vsync_in, vsync_out) = unsafe { peripherals.GPIO6.split() }; - let (hsync_in, hsync_out) = unsafe { peripherals.GPIO7.split() }; - let (de_in, de_out) = unsafe { peripherals.GPIO14.split() }; - let (pclk_in, pclk_out) = unsafe { peripherals.GPIO13.split() }; - let (d0_in, d0_out) = unsafe { peripherals.GPIO11.split() }; - let (d1_in, d1_out) = unsafe { peripherals.GPIO9.split() }; - let (d2_in, d2_out) = unsafe { peripherals.GPIO8.split() }; - let (d3_in, d3_out) = unsafe { peripherals.GPIO47.split() }; - let (d4_in, d4_out) = unsafe { peripherals.GPIO12.split() }; - let (d5_in, d5_out) = unsafe { peripherals.GPIO18.split() }; - let (d6_in, d6_out) = unsafe { peripherals.GPIO17.split() }; - let (d7_in, d7_out) = unsafe { peripherals.GPIO16.split() }; - - let config = dpi::Config::default() - .with_clock_mode(ClockMode { - polarity: Polarity::IdleHigh, - phase: Phase::ShiftLow, - }) - .with_frequency(Rate::from_khz(500)) - .with_format(Format { - enable_2byte_mode: false, - ..Default::default() - }) - // Send a 50x50 video - .with_timing(FrameTiming { - horizontal_total_width: 65, - hsync_width: 5, - horizontal_blank_front_porch: 10, - horizontal_active_width: 50, - - vertical_total_height: 65, - vsync_width: 5, - vertical_blank_front_porch: 10, - vertical_active_height: 50, - - hsync_position: 0, - }) - .with_vsync_idle_level(Level::High) - .with_hsync_idle_level(Level::High) - .with_de_idle_level(Level::Low) - .with_disable_black_region(false); - - let dpi = Dpi::new(lcd_cam.lcd, tx_channel, config) - .unwrap() - .with_vsync(vsync_out) - .with_hsync(hsync_out) - .with_de(de_out) - .with_pclk(pclk_out) - .with_data0(d0_out) - .with_data1(d1_out) - .with_data2(d2_out) - .with_data3(d3_out) - .with_data4(d4_out) - .with_data5(d5_out) - .with_data6(d6_out) - .with_data7(d7_out); - - let camera = Camera::new( - lcd_cam.cam, - rx_channel, - cam::Config::default() - .with_frequency(Rate::from_mhz(1)) - .with_vh_de_mode(VhdeMode::VsyncHsync), - ) - .unwrap() - .with_vsync(vsync_in) - .with_hsync(hsync_in) - .with_h_enable(de_in) - .with_pixel_clock(pclk_in) - .with_data0(d0_in) - .with_data1(d1_in) - .with_data2(d2_in) - .with_data3(d3_in) - .with_data4(d4_in) - .with_data5(d5_in) - .with_data6(d6_in) - .with_data7(d7_in); - - let mut dma_tx_buf = ctx.dma_tx_buf; - let mut dma_rx_buf = ctx.dma_rx_buf; - - for (i, b) in dma_tx_buf.as_mut_slice().iter_mut().enumerate() { - *b = ((i + 0) % 256) as u8; + AsyncContext { + lcd_cam, + dma: peripherals.DMA_CH0, + dma_buf, + } } - let camera_transfer = camera.receive(dma_rx_buf).map_err(|e| e.0).unwrap(); - // Note: next_frame_en is not false because the RGB driver doesn't send a VSYNC - // at the end of the frame, which means the DMA doesn't flush the last - // few bytes it receives. - let dpi_transfer = dpi.send(true, dma_tx_buf).map_err(|e| e.0).unwrap(); + #[test] + async fn test_i8080_8bit(ctx: AsyncContext<'static>) { + let i8080 = I8080::new( + ctx.lcd_cam.lcd, + ctx.dma, + Config::default().with_frequency(Rate::from_mhz(20)), + ) + .unwrap(); - (_, _, dma_rx_buf) = camera_transfer.wait(); - (_, dma_tx_buf) = dpi_transfer.stop(); + core::mem::drop(ctx.lcd_cam.cam); + let mut transfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); - assert_eq!(dma_tx_buf.as_slice(), dma_rx_buf.as_slice()); + transfer.wait_for_done().await; + transfer.wait_for_done().await; + transfer.wait().0.unwrap(); + } + } +} + +// lcd_cam_i8080 tests +mod blocking_tests { + use super::*; + + #[embedded_test::tests(default_timeout = 3)] + mod tests { + use super::*; + + #[init] + fn init() -> BlockingContext<'static> { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let lcd_cam = LcdCam::new(peripherals.LCD_CAM); + let pcnt = Pcnt::new(peripherals.PCNT); + let (_, _, tx_buffer, tx_desc) = dma_buffers!(0, DATA_SIZE); + let dma_buf = DmaTxBuf::new(tx_desc, tx_buffer).unwrap(); + + BlockingContext { + lcd_cam, + dma: peripherals.DMA_CH0, + pcnt, + pins: Pins { + GPIO8: peripherals.GPIO8, + GPIO11: peripherals.GPIO11, + GPIO12: peripherals.GPIO12, + GPIO16: peripherals.GPIO16, + GPIO17: peripherals.GPIO17, + }, + dma_buf, + } + } + + #[test] + fn test_i8080_8bit(ctx: BlockingContext<'static>) { + let i8080 = I8080::new( + ctx.lcd_cam.lcd, + ctx.dma, + Config::default().with_frequency(Rate::from_mhz(20)), + ) + .unwrap(); + + let xfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); + xfer.wait().0.unwrap(); + } + + #[test] + fn test_i8080_8bit_is_seen_by_pcnt(ctx: BlockingContext<'static>) { + let (unit_ctrl, cs_signal) = unsafe { ctx.pins.GPIO8.split() }; + let (unit0_input, unit0_signal) = unsafe { ctx.pins.GPIO11.split() }; + let (unit1_input, unit1_signal) = unsafe { ctx.pins.GPIO12.split() }; + let (unit2_input, unit2_signal) = unsafe { ctx.pins.GPIO16.split() }; + let (unit3_input, unit3_signal) = unsafe { ctx.pins.GPIO17.split() }; + + let pcnt = ctx.pcnt; + pcnt.unit0 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + pcnt.unit1 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + pcnt.unit2 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + pcnt.unit3 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + + pcnt.unit0 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + pcnt.unit1 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + pcnt.unit2 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + pcnt.unit3 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + ctx.dma, + Config::default().with_frequency(Rate::from_mhz(20)), + ) + .unwrap() + .with_cs(cs_signal) + .with_data0(unit0_signal) + .with_data1(unit1_signal) + .with_data2(unit2_signal) + .with_data3(unit3_signal); + + core::mem::drop(ctx.lcd_cam.cam); + i8080.set_bit_order(BitOrder::Inverted); + + pcnt.unit0.channel0.set_edge_signal(unit0_input); + pcnt.unit1.channel0.set_edge_signal(unit1_input); + pcnt.unit2.channel0.set_edge_signal(unit2_input); + pcnt.unit3.channel0.set_edge_signal(unit3_input); + + pcnt.unit0.channel0.set_ctrl_signal(unit_ctrl.clone()); + pcnt.unit1.channel0.set_ctrl_signal(unit_ctrl.clone()); + pcnt.unit2.channel0.set_ctrl_signal(unit_ctrl.clone()); + pcnt.unit3.channel0.set_ctrl_signal(unit_ctrl.clone()); + + pcnt.unit0.resume(); + pcnt.unit1.resume(); + pcnt.unit2.resume(); + pcnt.unit3.resume(); + + let data_to_send = [ + 0b0000_0000, + 0b1010_0000, + 0b0110_0000, + 0b1110_0000, + 0b0000_0000, + 0b1000_0000, + 0b0100_0000, + 0b1010_0000, + 0b0101_0000, + 0b1000_0000, + ]; + + let mut dma_buf = ctx.dma_buf; + dma_buf.as_mut_slice().fill(0); + dma_buf.as_mut_slice()[..data_to_send.len()].copy_from_slice(&data_to_send); + + let xfer = i8080.send(Command::::None, 0, dma_buf).unwrap(); + xfer.wait().0.unwrap(); + + let actual = [ + pcnt.unit0.value(), + pcnt.unit1.value(), + pcnt.unit2.value(), + pcnt.unit3.value(), + ]; + assert_eq!([5, 3, 2, 1], actual); + } + + #[test] + fn test_i8080_16bit_is_seen_by_pcnt(ctx: BlockingContext<'static>) { + let (unit_ctrl, cs_signal) = unsafe { ctx.pins.GPIO8.split() }; + let (unit0_input, unit0_signal) = unsafe { ctx.pins.GPIO11.split() }; + let (unit1_input, unit1_signal) = unsafe { ctx.pins.GPIO12.split() }; + let (unit2_input, unit2_signal) = unsafe { ctx.pins.GPIO16.split() }; + let (unit3_input, unit3_signal) = unsafe { ctx.pins.GPIO17.split() }; + + let pcnt = ctx.pcnt; + pcnt.unit0 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + pcnt.unit1 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + pcnt.unit2 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + pcnt.unit3 + .channel0 + .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); + + pcnt.unit0 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + pcnt.unit1 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + pcnt.unit2 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + pcnt.unit3 + .channel0 + .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); + + let mut i8080 = I8080::new( + ctx.lcd_cam.lcd, + ctx.dma, + Config::default().with_frequency(Rate::from_mhz(20)), + ) + .unwrap() + .with_cs(cs_signal) + .with_data3(unit0_signal) + .with_data7(unit1_signal) + .with_data11(unit2_signal) + .with_data15(unit3_signal); + + i8080.set_bit_order(BitOrder::Inverted); + + pcnt.unit0.channel0.set_edge_signal(unit0_input); + pcnt.unit1.channel0.set_edge_signal(unit1_input); + pcnt.unit2.channel0.set_edge_signal(unit2_input); + pcnt.unit3.channel0.set_edge_signal(unit3_input); + + pcnt.unit0.channel0.set_ctrl_signal(unit_ctrl.clone()); + pcnt.unit1.channel0.set_ctrl_signal(unit_ctrl.clone()); + pcnt.unit2.channel0.set_ctrl_signal(unit_ctrl.clone()); + pcnt.unit3.channel0.set_ctrl_signal(unit_ctrl.clone()); + + pcnt.unit0.resume(); + pcnt.unit1.resume(); + pcnt.unit2.resume(); + pcnt.unit3.resume(); + + let data_to_send = [ + 0b0000_0000_0000_0000u16, + 0b0001_0000_0001_0000, + 0b0000_0001_0001_0000, + 0b0001_0001_0001_0000, + 0b0000_0000_0000_0000, + 0b0001_0000_0000_0000, + 0b0000_0001_0000_0000, + 0b0001_0000_0001_0000, + 0b0000_0001_0000_0001, + 0b0001_0000_0000_0000, + ]; + + let mut dma_buf = ctx.dma_buf; + dma_buf + .as_mut_slice() + .iter_mut() + .zip(data_to_send.iter().flat_map(|&d| d.to_ne_bytes())) + .for_each(|(d, s)| *d = s); + + let xfer = i8080.send(Command::::None, 0, dma_buf).unwrap(); + xfer.wait().0.unwrap(); + + let actual = [ + pcnt.unit0.value(), + pcnt.unit1.value(), + pcnt.unit2.value(), + pcnt.unit3.value(), + ]; + assert_eq!([5, 3, 2, 1], actual); + } + } +} + +// LCD_CAM Camera and DPI tests +mod camera_tests { + use super::*; + + #[embedded_test::tests] + mod tests { + use super::*; + + #[init] + fn init() -> CameraContext { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let (rx_buf, rx_desc, tx_buf, tx_desc) = dma_buffers!(50 * 50); + let dma_rx_buf = DmaRxBuf::new(rx_desc, rx_buf).unwrap(); + let dma_tx_buf = DmaTxBuf::new(tx_desc, tx_buf).unwrap(); + + CameraContext { + peripherals, + dma_tx_buf, + dma_rx_buf, + } + } + + #[test] + fn test_camera_can_receive_from_rgb(ctx: CameraContext) { + let peripherals = ctx.peripherals; + let lcd_cam = LcdCam::new(peripherals.LCD_CAM); + let (rx_channel, tx_channel) = peripherals.DMA_CH2.split(); + + let ( + (vsync_in, vsync_out), + (hsync_in, hsync_out), + (de_in, de_out), + (pclk_in, pclk_out), + (d0_in, d0_out), + (d1_in, d1_out), + (d2_in, d2_out), + (d3_in, d3_out), + (d4_in, d4_out), + (d5_in, d5_out), + (d6_in, d6_out), + (d7_in, d7_out), + ) = unsafe { + ( + peripherals.GPIO6.split(), + peripherals.GPIO7.split(), + peripherals.GPIO14.split(), + peripherals.GPIO13.split(), + peripherals.GPIO11.split(), + peripherals.GPIO9.split(), + peripherals.GPIO8.split(), + peripherals.GPIO47.split(), + peripherals.GPIO12.split(), + peripherals.GPIO18.split(), + peripherals.GPIO17.split(), + peripherals.GPIO16.split(), + ) + }; + + let config = dpi::Config::default() + .with_clock_mode(ClockMode { + polarity: Polarity::IdleHigh, + phase: Phase::ShiftLow, + }) + .with_frequency(Rate::from_khz(500)) + .with_format(Format { + enable_2byte_mode: false, + ..Default::default() + }) + .with_timing(FrameTiming { + horizontal_total_width: 65, + hsync_width: 5, + horizontal_blank_front_porch: 10, + horizontal_active_width: 50, + vertical_total_height: 65, + vsync_width: 5, + vertical_blank_front_porch: 10, + vertical_active_height: 50, + hsync_position: 0, + }) + .with_vsync_idle_level(Level::High) + .with_hsync_idle_level(Level::High) + .with_de_idle_level(Level::Low) + .with_disable_black_region(false); + + let dpi = Dpi::new(lcd_cam.lcd, tx_channel, config) + .unwrap() + .with_vsync(vsync_out) + .with_hsync(hsync_out) + .with_de(de_out) + .with_pclk(pclk_out) + .with_data0(d0_out) + .with_data1(d1_out) + .with_data2(d2_out) + .with_data3(d3_out) + .with_data4(d4_out) + .with_data5(d5_out) + .with_data6(d6_out) + .with_data7(d7_out); + + let camera = Camera::new( + lcd_cam.cam, + rx_channel, + cam::Config::default() + .with_frequency(Rate::from_mhz(1)) + .with_vh_de_mode(VhdeMode::VsyncHsync), + ) + .unwrap() + .with_vsync(vsync_in) + .with_hsync(hsync_in) + .with_h_enable(de_in) + .with_pixel_clock(pclk_in) + .with_data0(d0_in) + .with_data1(d1_in) + .with_data2(d2_in) + .with_data3(d3_in) + .with_data4(d4_in) + .with_data5(d5_in) + .with_data6(d6_in) + .with_data7(d7_in); + + let mut dma_tx_buf = ctx.dma_tx_buf; + let mut dma_rx_buf = ctx.dma_rx_buf; + + for (i, b) in dma_tx_buf.as_mut_slice().iter_mut().enumerate() { + *b = (i % 256) as u8; + } + + let camera_transfer = camera.receive(dma_rx_buf).map_err(|e| e.0).unwrap(); + let dpi_transfer = dpi.send(true, dma_tx_buf).map_err(|e| e.0).unwrap(); + + (_, _, dma_rx_buf) = camera_transfer.wait(); + (_, dma_tx_buf) = dpi_transfer.stop(); + + assert_eq!(dma_tx_buf.as_slice(), dma_rx_buf.as_slice()); + } } } diff --git a/hil-test/src/bin/lcd_cam_i8080.rs b/hil-test/src/bin/lcd_cam_i8080.rs deleted file mode 100644 index 25c890b81..000000000 --- a/hil-test/src/bin/lcd_cam_i8080.rs +++ /dev/null @@ -1,300 +0,0 @@ -//! lcd_cam i8080 tests - -//% CHIPS: esp32s3 -//% FEATURES: unstable - -#![no_std] -#![no_main] - -use esp_hal::{ - Blocking, - dma::DmaTxBuf, - dma_buffers, - lcd_cam::{ - BitOrder, - LcdCam, - lcd::i8080::{Command, Config, I8080}, - }, - pcnt::{ - Pcnt, - channel::{CtrlMode, EdgeMode}, - }, - peripherals::DMA_CH0, - time::Rate, -}; -use hil_test as _; - -const DATA_SIZE: usize = 1024 * 10; - -#[allow(non_snake_case)] -struct Pins { - pub GPIO8: esp_hal::peripherals::GPIO8<'static>, - pub GPIO11: esp_hal::peripherals::GPIO11<'static>, - pub GPIO12: esp_hal::peripherals::GPIO12<'static>, - pub GPIO16: esp_hal::peripherals::GPIO16<'static>, - pub GPIO17: esp_hal::peripherals::GPIO17<'static>, -} - -struct Context<'d> { - lcd_cam: LcdCam<'d, Blocking>, - pcnt: Pcnt<'d>, - pins: Pins, - dma: DMA_CH0<'d>, - dma_buf: DmaTxBuf, -} - -#[embedded_test::tests(default_timeout = 3)] -mod tests { - use super::*; - - #[init] - fn init() -> Context<'static> { - let peripherals = esp_hal::init(esp_hal::Config::default()); - let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - let pcnt = Pcnt::new(peripherals.PCNT); - - let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE); - let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - - Context { - lcd_cam, - dma: peripherals.DMA_CH0, - pcnt, - pins: Pins { - GPIO8: peripherals.GPIO8, - GPIO11: peripherals.GPIO11, - GPIO12: peripherals.GPIO12, - GPIO16: peripherals.GPIO16, - GPIO17: peripherals.GPIO17, - }, - dma_buf, - } - } - - #[test] - fn test_i8080_8bit(ctx: Context<'static>) { - let i8080 = I8080::new( - ctx.lcd_cam.lcd, - ctx.dma, - Config::default().with_frequency(Rate::from_mhz(20)), - ) - .unwrap(); - - let xfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); - xfer.wait().0.unwrap(); - } - - #[test] - fn test_i8080_8bit_is_seen_by_pcnt(ctx: Context<'static>) { - // FIXME: Update this test to exercise all the I8080 output signals once the - // issue with configuring pins as outputs after inputs have been sorted - // out. See https://github.com/esp-rs/esp-hal/pull/2173#issue-2529323702 - - let (unit_ctrl, cs_signal) = unsafe { ctx.pins.GPIO8.split() }; - let (unit0_input, unit0_signal) = unsafe { ctx.pins.GPIO11.split() }; - let (unit1_input, unit1_signal) = unsafe { ctx.pins.GPIO12.split() }; - let (unit2_input, unit2_signal) = unsafe { ctx.pins.GPIO16.split() }; - let (unit3_input, unit3_signal) = unsafe { ctx.pins.GPIO17.split() }; - - let pcnt = ctx.pcnt; - - pcnt.unit0 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - pcnt.unit1 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - pcnt.unit2 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - pcnt.unit3 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - - pcnt.unit0 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - pcnt.unit1 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - pcnt.unit2 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - pcnt.unit3 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - - let mut i8080 = I8080::new( - ctx.lcd_cam.lcd, - ctx.dma, - Config::default().with_frequency(Rate::from_mhz(20)), - ) - .unwrap() - .with_cs(cs_signal) - .with_data0(unit0_signal) - .with_data1(unit1_signal) - .with_data2(unit2_signal) - .with_data3(unit3_signal); - - // explicitly drop the camera half to see if it disables clocks (unexpectedly, - // I8080 should keep it alive) - core::mem::drop(ctx.lcd_cam.cam); - - // This is to make the test values look more intuitive. - i8080.set_bit_order(BitOrder::Inverted); - - pcnt.unit0.channel0.set_edge_signal(unit0_input); - pcnt.unit1.channel0.set_edge_signal(unit1_input); - pcnt.unit2.channel0.set_edge_signal(unit2_input); - pcnt.unit3.channel0.set_edge_signal(unit3_input); - - pcnt.unit0.channel0.set_ctrl_signal(unit_ctrl.clone()); - pcnt.unit1.channel0.set_ctrl_signal(unit_ctrl.clone()); - pcnt.unit2.channel0.set_ctrl_signal(unit_ctrl.clone()); - pcnt.unit3.channel0.set_ctrl_signal(unit_ctrl.clone()); - - pcnt.unit0.resume(); - pcnt.unit1.resume(); - pcnt.unit2.resume(); - pcnt.unit3.resume(); - - let data_to_send = [ - 0b0000_0000, - 0b1010_0000, - 0b0110_0000, - 0b1110_0000, - 0b0000_0000, - 0b1000_0000, - 0b0100_0000, - 0b1010_0000, - 0b0101_0000, - 0b1000_0000, - ]; - - let mut dma_buf = ctx.dma_buf; - dma_buf.as_mut_slice().fill(0); - dma_buf.as_mut_slice()[..data_to_send.len()].copy_from_slice(&data_to_send); - - let xfer = i8080.send(Command::::None, 0, dma_buf).unwrap(); - xfer.wait().0.unwrap(); - - let actual = [ - pcnt.unit0.value(), - pcnt.unit1.value(), - pcnt.unit2.value(), - pcnt.unit3.value(), - ]; - let expected = [5, 3, 2, 1]; - - assert_eq!(expected, actual); - } - - #[test] - fn test_i8080_16bit_is_seen_by_pcnt(ctx: Context<'static>) { - // FIXME: Update this test to exercise all the I8080 output signals once the - // issue with configuring pins as outputs after inputs have been sorted - // out. See https://github.com/esp-rs/esp-hal/pull/2173#issue-2529323702 - - let (unit_ctrl, cs_signal) = unsafe { ctx.pins.GPIO8.split() }; - let (unit0_input, unit0_signal) = unsafe { ctx.pins.GPIO11.split() }; - let (unit1_input, unit1_signal) = unsafe { ctx.pins.GPIO12.split() }; - let (unit2_input, unit2_signal) = unsafe { ctx.pins.GPIO16.split() }; - let (unit3_input, unit3_signal) = unsafe { ctx.pins.GPIO17.split() }; - - let pcnt = ctx.pcnt; - - pcnt.unit0 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - pcnt.unit1 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - pcnt.unit2 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - pcnt.unit3 - .channel0 - .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); - - pcnt.unit0 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - pcnt.unit1 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - pcnt.unit2 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - pcnt.unit3 - .channel0 - .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - - let mut i8080 = I8080::new( - ctx.lcd_cam.lcd, - ctx.dma, - Config::default().with_frequency(Rate::from_mhz(20)), - ) - .unwrap() - .with_cs(cs_signal) - .with_data3(unit0_signal) - .with_data7(unit1_signal) - .with_data11(unit2_signal) - .with_data15(unit3_signal); - - // This is to make the test values look more intuitive. - i8080.set_bit_order(BitOrder::Inverted); - - pcnt.unit0.channel0.set_edge_signal(unit0_input); - pcnt.unit1.channel0.set_edge_signal(unit1_input); - pcnt.unit2.channel0.set_edge_signal(unit2_input); - pcnt.unit3.channel0.set_edge_signal(unit3_input); - - pcnt.unit0.channel0.set_ctrl_signal(unit_ctrl.clone()); - pcnt.unit1.channel0.set_ctrl_signal(unit_ctrl.clone()); - pcnt.unit2.channel0.set_ctrl_signal(unit_ctrl.clone()); - pcnt.unit3.channel0.set_ctrl_signal(unit_ctrl.clone()); - - pcnt.unit0.resume(); - pcnt.unit1.resume(); - pcnt.unit2.resume(); - pcnt.unit3.resume(); - - let data_to_send = [ - 0b0000_0000_0000_0000u16, - 0b0001_0000_0001_0000, - 0b0000_0001_0001_0000, - 0b0001_0001_0001_0000, - 0b0000_0000_0000_0000, - 0b0001_0000_0000_0000, - 0b0000_0001_0000_0000, - 0b0001_0000_0001_0000, - 0b0000_0001_0000_0001, - 0b0001_0000_0000_0000, - ]; - - let mut dma_buf = ctx.dma_buf; - - // FIXME: Replace this 16 -> 8 bit copy once DmaTxBuf takes a generic parameter. - // i.e. DmaTxBuf - - // Copy 16 bit array into 8 bit buffer. - dma_buf - .as_mut_slice() - .iter_mut() - .zip(data_to_send.iter().flat_map(|&d| d.to_ne_bytes())) - .for_each(|(d, s)| *d = s); - - let xfer = i8080.send(Command::::None, 0, dma_buf).unwrap(); - xfer.wait().0.unwrap(); - - let actual = [ - pcnt.unit0.value(), - pcnt.unit1.value(), - pcnt.unit2.value(), - pcnt.unit3.value(), - ]; - let expected = [5, 3, 2, 1]; - - assert_eq!(expected, actual); - } -} diff --git a/hil-test/src/bin/lcd_cam_i8080_async.rs b/hil-test/src/bin/lcd_cam_i8080_async.rs deleted file mode 100644 index 2b5bc2d8d..000000000 --- a/hil-test/src/bin/lcd_cam_i8080_async.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! lcd_cam i8080 tests - -//% CHIPS: esp32s3 -//% FEATURES: unstable - -#![no_std] -#![no_main] - -use esp_hal::{ - Async, - dma::DmaTxBuf, - dma_buffers, - lcd_cam::{ - LcdCam, - lcd::i8080::{Command, Config, I8080}, - }, - peripherals::DMA_CH0, - time::Rate, -}; -use hil_test as _; - -const DATA_SIZE: usize = 1024 * 10; - -struct Context<'d> { - lcd_cam: LcdCam<'d, Async>, - dma: DMA_CH0<'d>, - dma_buf: DmaTxBuf, -} - -#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] -mod tests { - use super::*; - - #[init] - async fn init() -> Context<'static> { - let peripherals = esp_hal::init(esp_hal::Config::default()); - - let lcd_cam = LcdCam::new(peripherals.LCD_CAM).into_async(); - let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE); - let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - - Context { - lcd_cam, - dma: peripherals.DMA_CH0, - dma_buf, - } - } - - #[test] - async fn test_i8080_8bit(ctx: Context<'static>) { - let i8080 = I8080::new( - ctx.lcd_cam.lcd, - ctx.dma, - Config::default().with_frequency(Rate::from_mhz(20)), - ) - .unwrap(); - - // explicitly drop the camera half to see if it disables clocks (unexpectedly, - // I8080 should keep it alive) - core::mem::drop(ctx.lcd_cam.cam); - - let mut transfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); - - transfer.wait_for_done().await; - - // This should not block forever and should immediately return. - transfer.wait_for_done().await; - - transfer.wait().0.unwrap(); - } -}