esp-hal/esp32s3-hal/examples/lcd_i8080.rs
Jesse Braham c42a96c987
Refactor DMA to make API more consistent across devices (#1125)
* Make the `DMA` peripheral API more consistent

* Update examples for devices with `PDMA`

* Update examples for devices with `GDMA`

* Update `CHANGELOG.md`

* Update example after rebasing
2024-01-30 14:58:48 +00:00

246 lines
7.6 KiB
Rust

//! Drives the 8-bit parallel display on a WT32-SC01 Plus
//!
//! This example clears the screen with red and then blue every second.
//!
//! Pins used
//! Backlight GPIO45
//! Reset GPIO4
//! CD GPIO0
//! WR GPIO47
//! D0 GPIO9
//! D1 GPIO46
//! D2 GPIO3
//! D3 GPIO8
//! D4 GPIO18
//! D5 GPIO17
//! D6 GPIO16
//! D7 GPIO15
#![no_std]
#![no_main]
use esp32s3_hal::{
clock::ClockControl,
dma::{Dma, DmaPriority},
dma_buffers,
gpio::IO,
lcd_cam::{
lcd::i8080::{Config, TxEightBits, I8080},
LcdCam,
},
peripherals::Peripherals,
prelude::*,
Delay,
};
use esp_backtrace as _;
use esp_println::println;
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take();
let system = peripherals.SYSTEM.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let lcd_backlight = io.pins.gpio45;
let lcd_reset = io.pins.gpio4;
let lcd_rs = io.pins.gpio0; // Command/Data selection
let lcd_wr = io.pins.gpio47; // Write clock
let _lcd_te = io.pins.gpio48; // Frame sync
let dma = Dma::new(peripherals.DMA);
let channel = dma.channel0;
let (tx_buffer, mut tx_descriptors, _, mut rx_descriptors) = dma_buffers!(32678, 0);
let channel = channel.configure(
false,
&mut tx_descriptors,
&mut rx_descriptors,
DmaPriority::Priority0,
);
let mut delay = Delay::new(&clocks);
let mut backlight = lcd_backlight.into_push_pull_output();
let mut reset = lcd_reset.into_push_pull_output();
let tx_pins = TxEightBits::new(
io.pins.gpio9,
io.pins.gpio46,
io.pins.gpio3,
io.pins.gpio8,
io.pins.gpio18,
io.pins.gpio17,
io.pins.gpio16,
io.pins.gpio15,
);
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let mut i8080 = I8080::new(
lcd_cam.lcd,
channel.tx,
tx_pins,
20u32.MHz(),
Config::default(),
&clocks,
)
.with_ctrl_pins(lcd_rs, lcd_wr);
{
// https://gist.github.com/sukesh-ak/610508bc84779a26efdcf969bf51a2d1
// https://github.com/lovyan03/LovyanGFX/blob/302169a6f23e9a2a6451f03311c366d182193831/src/lgfx/v1/panel/Panel_ST7796.hpp#L28
reset.set_low().unwrap();
delay.delay_us(8_000u32);
reset.set_high().unwrap();
delay.delay_us(64_000u32);
// const CMD_FRMCTR1: u8 = 0xB1;
// const CMD_FRMCTR2: u8 = 0xB2;
// const CMD_FRMCTR3: u8 = 0xB3;
const CMD_INVCTR: u8 = 0xB4;
const CMD_DFUNCTR: u8 = 0xB6;
// const CMD_ETMOD: u8 = 0xB7;
// const CMD_PWCTR1: u8 = 0xC0;
const CMD_PWCTR2: u8 = 0xC1;
const CMD_PWCTR3: u8 = 0xC2;
// const CMD_PWCTR4: u8 = 0xC3;
// const CMD_PWCTR5: u8 = 0xC4;
const CMD_VMCTR: u8 = 0xC5;
const CMD_GMCTRP1: u8 = 0xE0; // Positive Gamma Correction
const CMD_GMCTRN1: u8 = 0xE1; // Negative Gamma Correction
const CMD_DOCA: u8 = 0xE8; // Display Output Ctrl Adjust
const CMD_CSCON: u8 = 0xF0; // Command Set Control
i8080.send(CMD_CSCON, 0, &[0xC3]).unwrap(); // Enable extension command 2 part I
i8080.send(CMD_CSCON, 0, &[0x96]).unwrap(); // Enable extension command 2 part II
i8080.send(CMD_INVCTR, 0, &[0x01]).unwrap(); // 1-dot inversion
i8080
.send(
CMD_DFUNCTR,
0,
&[
0x80, // Display Function Control //Bypass
0x22, /* Source Output Scan from S1 to S960, Gate Output scan from G1 to
* G480, scan cycle=2 */
0x3B,
],
)
.unwrap(); // LCD Drive Line=8*(59+1)
i8080
.send(
CMD_DOCA,
0,
&[
0x40, 0x8A, 0x00, 0x00, 0x29, // Source eqaulizing period time= 22.5 us
0x19, // Timing for "Gate start"=25 (Tclk)
0xA5, // Timing for "Gate End"=37 (Tclk), Gate driver EQ function ON
0x33,
],
)
.unwrap();
i8080.send(CMD_PWCTR2, 0, &[0x06]).unwrap(); // Power control2 //VAP(GVDD)=3.85+( vcom+vcom offset), VAN(GVCL)=-3.85+(
// vcom+vcom offset)
i8080.send(CMD_PWCTR3, 0, &[0xA7]).unwrap(); // Power control 3 //Source driving current level=low, Gamma driving current
// level=High
i8080.send(CMD_VMCTR, 0, &[0x18]).unwrap(); // VCOM Control //VCOM=0.9
delay.delay_us(120_000u32);
i8080
.send(
CMD_GMCTRP1,
0,
&[
0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F, 0x54, 0x42, 0x3C, 0x17, 0x14, 0x18,
0x1B,
],
)
.unwrap();
i8080
.send(
CMD_GMCTRN1,
0,
&[
0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B, 0x43, 0x42, 0x3B, 0x16, 0x14, 0x17,
0x1B,
],
)
.unwrap();
delay.delay_us(120_000u32);
i8080.send(CMD_CSCON, 0, &[0x3C]).unwrap(); // Command Set control // Disable extension command 2 partI
i8080.send(CMD_CSCON, 0, &[0x69]).unwrap(); // Command Set control // Disable
// extension command 2 partII
i8080.send(0x11, 0, &[]).unwrap(); // ExitSleepMode
delay.delay_us(130_000u32);
i8080.send(0x38, 0, &[]).unwrap(); // ExitIdleMode
i8080.send(0x29, 0, &[]).unwrap(); // SetDisplayOn
i8080.send(0x21, 0, &[]).unwrap(); // SetInvertMode(ColorInversion::Inverted)
// let madctl = SetAddressMode::from(options);
// i8080.send(madctl)?;
i8080.send(0x3A, 0, &[0x55]).unwrap(); // RGB565
}
let width = 320u16;
let height = 480u16;
{
println!("Set addresses");
let width_b = width.to_be_bytes();
let height_b = height.to_be_bytes();
i8080
.send(0x2A, 0, &[0, 0, width_b[0], width_b[1]])
.unwrap(); // CASET
i8080
.send(0x2B, 0, &[0, 0, height_b[0], height_b[1]])
.unwrap(); // PASET
}
println!("Drawing");
const RED: u16 = 0b00000_000000_11111;
const BLUE: u16 = 0b11111_000000_00000;
backlight.set_high().unwrap();
let total_pixels = width as usize * height as usize;
let total_bytes = total_pixels * 2;
let mut buffer = tx_buffer;
for color in [RED, BLUE].iter().cycle() {
let color = color.to_be_bytes();
for chunk in buffer.chunks_mut(2) {
chunk.copy_from_slice(&color);
}
let mut bytes_left_to_write = total_bytes;
let transfer = i8080.send_dma(0x2C, 0, buffer).unwrap();
(buffer, i8080) = transfer.wait().unwrap();
bytes_left_to_write -= buffer.len();
while bytes_left_to_write >= buffer.len() {
let transfer = i8080.send_dma(0x3C, 0, buffer).unwrap();
(buffer, i8080) = transfer.wait().unwrap();
bytes_left_to_write -= buffer.len();
}
if bytes_left_to_write > 0 {
let transfer = i8080.send_dma(0x3C, 0, buffer).unwrap();
(buffer, i8080) = transfer.wait().unwrap();
}
delay.delay_ms(1_000u32);
}
loop {
delay.delay_ms(1_000u32);
}
}