esp-hal/qa-test/src/bin/lcd_dpi.rs
Dániel Buga ef98e2b24f
[DMA 7/8]: Remove Dma, move channels to Peripherals (#2545)
* Move DMA channels into Peripherals

* Initialize DMA in the critical section needed for clock management

* Update esp-hal/MIGRATING-0.22.md

Co-authored-by: Jesse Braham <jessebraham@users.noreply.github.com>

---------

Co-authored-by: Jesse Braham <jessebraham@users.noreply.github.com>
2024-11-25 09:31:18 +00:00

387 lines
12 KiB
Rust

//! Drives the 16-bit parallel RGB display on ESP32-S3-LCD-EV-Board v1.5
//!
//! This example fills the screen with every color.
//!
//! The following wiring is assumed:
//! - LCD_VSYNC => GPIO3
//! - LCD_HSYNC => GPIO46
//! - LCD_DE => GPIO17
//! - LCD_PCLK => GPIO9
//! - LCD_DATA0 => GPIO10
//! - LCD_DATA1 => GPIO11
//! - LCD_DATA2 => GPIO12
//! - LCD_DATA3 => GPIO13
//! - LCD_DATA4 => GPIO14
//! - LCD_DATA5 => GPIO21
//! - LCD_DATA6 => GPIO8
//! - LCD_DATA7 => GPIO18
//! - LCD_DATA8 => GPIO45
//! - LCD_DATA9 => GPIO38
//! - LCD_DATA10 => GPIO39
//! - LCD_DATA11 => GPIO40
//! - LCD_DATA12 => GPIO41
//! - LCD_DATA13 => GPIO42
//! - LCD_DATA14 => GPIO2
//! - LCD_DATA15 => GPIO1
//% CHIPS: esp32s3
#![no_std]
#![no_main]
use core::iter::{empty, once};
use esp_backtrace as _;
use esp_hal::{
delay::Delay,
dma_loop_buffer,
gpio::{Level, Output},
i2c,
i2c::master::I2c,
lcd_cam::{
lcd::{
dpi::{Config, Dpi, Format, FrameTiming},
ClockMode,
Phase,
Polarity,
},
LcdCam,
},
peripherals::Peripherals,
prelude::*,
Blocking,
};
use esp_println::println;
#[entry]
fn main() -> ! {
esp_println::logger::init_logger_from_env();
let peripherals: Peripherals = esp_hal::init(esp_hal::Config::default());
let i2c = I2c::new(
peripherals.I2C0,
i2c::master::Config {
frequency: 400.kHz(),
..Default::default()
},
)
.with_sda(peripherals.GPIO47)
.with_scl(peripherals.GPIO48);
let tx_channel = peripherals.DMA_CH2;
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let mut expander = Tca9554::new(i2c);
expander.write_output_reg(0b1111_0011).unwrap();
expander.write_direction_reg(0b1111_0001).unwrap();
let delay = Delay::new();
println!("Initialising");
let mut write_byte = |b: u8, is_cmd: bool| {
const SCS_BIT: u8 = 0b0000_0010;
const SCL_BIT: u8 = 0b0000_0100;
const SDA_BIT: u8 = 0b0000_1000;
let mut output = 0b1111_0001 & !SCS_BIT;
expander.write_output_reg(output).unwrap();
for bit in once(!is_cmd).chain((0..8).map(|i| (b >> i) & 0b1 != 0).rev()) {
let prev = output;
if bit {
output |= SDA_BIT;
} else {
output &= !SDA_BIT;
}
if prev != output {
expander.write_output_reg(output).unwrap();
}
output &= !SCL_BIT;
expander.write_output_reg(output).unwrap();
output |= SCL_BIT;
expander.write_output_reg(output).unwrap();
}
output &= !SCL_BIT;
expander.write_output_reg(output).unwrap();
output &= !SDA_BIT;
expander.write_output_reg(output).unwrap();
output |= SCS_BIT;
expander.write_output_reg(output).unwrap();
};
let mut vsync_pin = peripherals.GPIO3;
let vsync_must_be_high_during_setup = Output::new(&mut vsync_pin, Level::High);
for &init in INIT_CMDS.iter() {
match init {
InitCmd::Cmd(cmd, args) => {
write_byte(cmd, true);
for &arg in args {
write_byte(arg, false);
}
}
InitCmd::Delay(ms) => {
delay.delay_millis(ms as _);
}
}
}
drop(vsync_must_be_high_during_setup);
let mut dma_buf = dma_loop_buffer!(2 * 16);
let mut config = Config::default();
config.clock_mode = ClockMode {
polarity: Polarity::IdleLow,
phase: Phase::ShiftLow,
};
config.format = Format {
enable_2byte_mode: true,
..Default::default()
};
config.timing = FrameTiming {
horizontal_active_width: 480,
horizontal_total_width: 520,
horizontal_blank_front_porch: 10,
vertical_active_height: 480,
vertical_total_height: 510,
vertical_blank_front_porch: 10,
hsync_width: 10,
vsync_width: 10,
hsync_position: 0,
};
config.vsync_idle_level = Level::High;
config.hsync_idle_level = Level::High;
config.de_idle_level = Level::Low;
config.disable_black_region = false;
let mut dpi = Dpi::new(lcd_cam.lcd, tx_channel, 16.MHz(), config)
.with_vsync(vsync_pin)
.with_hsync(peripherals.GPIO46)
.with_de(peripherals.GPIO17)
.with_pclk(peripherals.GPIO9)
// Blue
.with_data0(peripherals.GPIO10)
.with_data1(peripherals.GPIO11)
.with_data2(peripherals.GPIO12)
.with_data3(peripherals.GPIO13)
.with_data4(peripherals.GPIO14)
// Green
.with_data5(peripherals.GPIO21)
.with_data6(peripherals.GPIO8)
.with_data7(peripherals.GPIO18)
.with_data8(peripherals.GPIO45)
.with_data9(peripherals.GPIO38)
.with_data10(peripherals.GPIO39)
// Red
.with_data11(peripherals.GPIO40)
.with_data12(peripherals.GPIO41)
.with_data13(peripherals.GPIO42)
.with_data14(peripherals.GPIO2)
.with_data15(peripherals.GPIO1);
const MAX_RED: u16 = (1 << 5) - 1;
const MAX_GREEN: u16 = (1 << 6) - 1;
const MAX_BLUE: u16 = (1 << 5) - 1;
fn rgb(r: u16, g: u16, b: u16) -> u16 {
(r << 11) | (g << 5) | (b << 0)
}
let mut colors = empty()
// Start with red and gradually add green
.chain((0..=MAX_GREEN).map(|g| rgb(MAX_RED, g, 0)))
// Then remove the red
.chain((0..=MAX_RED).rev().map(|r| rgb(r, MAX_GREEN, 0)))
// Then add blue
.chain((0..=MAX_BLUE).map(|b| rgb(0, MAX_GREEN, b)))
// Then remove green
.chain((0..=MAX_GREEN).rev().map(|g| rgb(0, g, MAX_BLUE)))
// Then add red
.chain((0..=MAX_RED).map(|r| rgb(r, 0, MAX_BLUE)))
// Then remove blue
.chain((0..=MAX_BLUE).rev().map(|b| rgb(MAX_RED, 0, b)))
// Once we get we have red, and we can start again.
.cycle();
println!("Rendering");
loop {
let transfer = dpi.send(false, dma_buf).map_err(|e| e.0).unwrap();
(_, dpi, dma_buf) = transfer.wait();
if let Some(color) = colors.next() {
for chunk in dma_buf.chunks_mut(2) {
chunk.copy_from_slice(&color.to_le_bytes());
}
}
}
}
struct Tca9554 {
i2c: I2c<'static, Blocking>,
address: u8,
}
impl Tca9554 {
pub fn new(i2c: I2c<'static, Blocking>) -> Self {
Self { i2c, address: 0x20 }
}
pub fn write_direction_reg(&mut self, value: u8) -> Result<(), i2c::master::Error> {
self.i2c.write(self.address, &[0x03, value])
}
pub fn write_output_reg(&mut self, value: u8) -> Result<(), i2c::master::Error> {
self.i2c.write(self.address, &[0x01, value])
}
}
#[derive(Copy, Clone)]
enum InitCmd {
Cmd(u8, &'static [u8]),
Delay(u8),
}
const INIT_CMDS: &[InitCmd] = &[
InitCmd::Cmd(0xf0, &[0x55, 0xaa, 0x52, 0x08, 0x00]),
InitCmd::Cmd(0xf6, &[0x5a, 0x87]),
InitCmd::Cmd(0xc1, &[0x3f]),
InitCmd::Cmd(0xc2, &[0x0e]),
InitCmd::Cmd(0xc6, &[0xf8]),
InitCmd::Cmd(0xc9, &[0x10]),
InitCmd::Cmd(0xcd, &[0x25]),
InitCmd::Cmd(0xf8, &[0x8a]),
InitCmd::Cmd(0xac, &[0x45]),
InitCmd::Cmd(0xa0, &[0xdd]),
InitCmd::Cmd(0xa7, &[0x47]),
InitCmd::Cmd(0xfa, &[0x00, 0x00, 0x00, 0x04]),
InitCmd::Cmd(0x86, &[0x99, 0xa3, 0xa3, 0x51]),
InitCmd::Cmd(0xa3, &[0xee]),
InitCmd::Cmd(0xfd, &[0x3c, 0x3]),
InitCmd::Cmd(0x71, &[0x48]),
InitCmd::Cmd(0x72, &[0x48]),
InitCmd::Cmd(0x73, &[0x00, 0x44]),
InitCmd::Cmd(0x97, &[0xee]),
InitCmd::Cmd(0x83, &[0x93]),
InitCmd::Cmd(0x9a, &[0x72]),
InitCmd::Cmd(0x9b, &[0x5a]),
InitCmd::Cmd(0x82, &[0x2c, 0x2c]),
InitCmd::Cmd(0xB1, &[0x10]),
InitCmd::Cmd(
0x6d,
&[
0x00, 0x1f, 0x19, 0x1a, 0x10, 0x0e, 0x0c, 0x0a, 0x02, 0x07, 0x1e, 0x1e, 0x1e, 0x1e,
0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x08, 0x01, 0x09, 0x0b, 0x0d, 0x0f,
0x1a, 0x19, 0x1f, 0x00,
],
),
InitCmd::Cmd(
0x64,
&[
0x38, 0x05, 0x01, 0xdb, 0x03, 0x03, 0x38, 0x04, 0x01, 0xdc, 0x03, 0x03, 0x7a, 0x7a,
0x7a, 0x7a,
],
),
InitCmd::Cmd(
0x65,
&[
0x38, 0x03, 0x01, 0xdd, 0x03, 0x03, 0x38, 0x02, 0x01, 0xde, 0x03, 0x03, 0x7a, 0x7a,
0x7a, 0x7a,
],
),
InitCmd::Cmd(
0x66,
&[
0x38, 0x01, 0x01, 0xdf, 0x03, 0x03, 0x38, 0x00, 0x01, 0xe0, 0x03, 0x03, 0x7a, 0x7a,
0x7a, 0x7a,
],
),
InitCmd::Cmd(
0x67,
&[
0x30, 0x01, 0x01, 0xe1, 0x03, 0x03, 0x30, 0x02, 0x01, 0xe2, 0x03, 0x03, 0x7a, 0x7a,
0x7a, 0x7a,
],
),
InitCmd::Cmd(
0x68,
&[
0x00, 0x08, 0x15, 0x08, 0x15, 0x7a, 0x7a, 0x08, 0x15, 0x08, 0x15, 0x7a, 0x7a,
],
),
InitCmd::Cmd(0x60, &[0x38, 0x08, 0x7a, 0x7a, 0x38, 0x09, 0x7a, 0x7a]),
InitCmd::Cmd(0x63, &[0x31, 0xe4, 0x7a, 0x7a, 0x31, 0xe5, 0x7a, 0x7a]),
InitCmd::Cmd(0x69, &[0x04, 0x22, 0x14, 0x22, 0x14, 0x22, 0x08]),
InitCmd::Cmd(0x6b, &[0x07]),
InitCmd::Cmd(0x7a, &[0x08, 0x13]),
InitCmd::Cmd(0x7b, &[0x08, 0x13]),
InitCmd::Cmd(
0xd1,
&[
0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35,
0x00, 0x47, 0x00, 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7,
0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5,
0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, 0xff,
],
),
InitCmd::Cmd(
0xd2,
&[
0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35,
0x00, 0x47, 0x00, 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7,
0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5,
0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, 0xff,
],
),
InitCmd::Cmd(
0xd3,
&[
0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35,
0x00, 0x47, 0x00, 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7,
0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5,
0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, 0xff,
],
),
InitCmd::Cmd(
0xd4,
&[
0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35,
0x00, 0x47, 0x00, 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7,
0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5,
0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, 0xff,
],
),
InitCmd::Cmd(
0xd5,
&[
0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35,
0x00, 0x47, 0x00, 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7,
0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5,
0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, 0xff,
],
),
InitCmd::Cmd(
0xd6,
&[
0x00, 0x00, 0x00, 0x04, 0x00, 0x12, 0x00, 0x18, 0x00, 0x21, 0x00, 0x2a, 0x00, 0x35,
0x00, 0x47, 0x00, 0x56, 0x00, 0x90, 0x00, 0xe5, 0x01, 0x68, 0x01, 0xd5, 0x01, 0xd7,
0x02, 0x36, 0x02, 0xa6, 0x02, 0xee, 0x03, 0x48, 0x03, 0xa0, 0x03, 0xba, 0x03, 0xc5,
0x03, 0xd0, 0x03, 0xe0, 0x03, 0xea, 0x03, 0xfa, 0x03, 0xff,
],
),
InitCmd::Cmd(0x3A, &[0x66]),
InitCmd::Cmd(0x11, &[]),
InitCmd::Delay(120),
InitCmd::Cmd(0x29, &[]),
InitCmd::Delay(20),
];