Merge LCD_CAM HIL into single binary (#4282)

This commit is contained in:
Juraj Sadel 2025-10-07 16:20:51 +02:00 committed by GitHub
parent 6869eedc92
commit 2811ad9209
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 441 additions and 499 deletions

View File

@ -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

View File

@ -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::<u8>::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::<u8>::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::<u8>::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::<u16>::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());
}
}
}

View File

@ -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::<u8>::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::<u8>::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<u16>
// 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::<u16>::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);
}
}

View File

@ -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::<u8>::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();
}
}