From 632e246d25261864ad9b31f82c5ad803d8797b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Hlusi=C4=8Dka?= Date: Thu, 18 Dec 2025 23:50:59 +0100 Subject: [PATCH] IO --- firmware/Cargo.lock | 38 +- firmware/Cargo.toml | 4 + firmware/src/bin/main.rs | 1102 +++++++++++++++++++++------------- firmware/src/lib.rs | 1 + firmware/src/st7701s.rs | 1203 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1926 insertions(+), 422 deletions(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 760673e..5c3846f 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -6,6 +6,7 @@ version = 4 name = "acid-firmware" version = "0.1.0" dependencies = [ + "bitflags", "critical-section", "embassy-executor", "embassy-time", @@ -15,7 +16,10 @@ dependencies = [ "esp-hal", "esp-hal-embassy", "esp-println", + "itertools", + "lazy_static", "log", + "paste", "static_cell", ] @@ -74,9 +78,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.3" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "bytemuck" @@ -216,6 +220,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "embassy-embedded-hal" version = "0.5.0" @@ -875,6 +885,15 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -905,6 +924,15 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + [[package]] name = "libc" version = "0.2.176" @@ -1181,6 +1209,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9ecb2c142aac72bff4d0b35b4907c6625c82d171c7e2f3602f31b614467d88" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index 1eb4c2f..4b570e1 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -35,6 +35,10 @@ esp-backtrace = { version = "0.17.0", git = "https://github.com/esp-rs/esp-hal", esp-hal-embassy = { version = "0.9.0", git = "https://github.com/esp-rs/esp-hal", features = ["esp32s3", "log-04"] } esp-println = { version = "0.15.0", git = "https://github.com/esp-rs/esp-hal", features = ["esp32s3", "log-04"] } static_cell = "2.1.1" +itertools = { version = "0.14.0", default-features = false } +bitflags = "2.9.4" +paste = "1.0.15" +lazy_static = { version = "1.5.0", features = ["spin_no_std"], default-features = false } [profile.dev] diff --git a/firmware/src/bin/main.rs b/firmware/src/bin/main.rs index 0e095b0..31c0ce6 100644 --- a/firmware/src/bin/main.rs +++ b/firmware/src/bin/main.rs @@ -6,34 +6,63 @@ holding buffers for the duration of a data transfer." )] #![feature(iter_intersperse)] +#![feature(array_windows)] #![feature(allocator_api)] use core::alloc::{GlobalAlloc, Layout}; use core::future::IntoFuture; use core::iter::once; +use acid_firmware::st7701s::{ + CmdCn2bkxsel, CmdCn2bkxselArg0, CmdCn2bkxselArg1, CmdCn2bkxselArg2, CmdCn2bkxselArg3, + CmdCn2bkxselArg4, CmdColmod, CmdColmodArg0, CmdDispon, CmdInvsel, CmdInvselArg0, CmdInvselArg1, + CmdLneset, CmdLnesetArg0, CmdLnesetArg1, CmdMadctl, CmdMadctlArg0, CmdMipiset1, + CmdMipiset1Arg0, CmdNvgamctrl, CmdNvgamctrlArg0, CmdNvgamctrlArg1, CmdNvgamctrlArg10, + CmdNvgamctrlArg11, CmdNvgamctrlArg12, CmdNvgamctrlArg13, CmdNvgamctrlArg14, CmdNvgamctrlArg15, + CmdNvgamctrlArg2, CmdNvgamctrlArg3, CmdNvgamctrlArg4, CmdNvgamctrlArg5, CmdNvgamctrlArg6, + CmdNvgamctrlArg7, CmdNvgamctrlArg8, CmdNvgamctrlArg9, CmdPclks2, CmdPclks2Arg0, CmdPdr1, + CmdPdr1Arg0, CmdPdr2, CmdPdr2Arg0, CmdPorctrl, CmdPorctrlArg0, CmdPorctrlArg1, CmdPvgamctrl, + CmdPvgamctrlArg0, CmdPvgamctrlArg1, CmdPvgamctrlArg10, CmdPvgamctrlArg11, CmdPvgamctrlArg12, + CmdPvgamctrlArg13, CmdPvgamctrlArg14, CmdPvgamctrlArg15, CmdPvgamctrlArg2, CmdPvgamctrlArg3, + CmdPvgamctrlArg4, CmdPvgamctrlArg5, CmdPvgamctrlArg6, CmdPvgamctrlArg7, CmdPvgamctrlArg8, + CmdPvgamctrlArg9, CmdPwctrl1, CmdPwctrl1Arg0, CmdPwctrl2, CmdPwctrl2Arg0, CmdPwctrl3, + CmdPwctrl3Arg0, CmdRgbctrl, CmdRgbctrlArg0, CmdRgbctrlArg1, CmdRgbctrlArg2, CmdSlpout, + CmdTescmd, CmdTescmdArg0, CmdVcoms, CmdVcomsArg0, CmdVghss, CmdVghssArg0, CmdVgls, CmdVglsArg0, + CmdVrhs, CmdVrhsArg0, Command, CustomCommand, +}; +use alloc::boxed::Box; +use alloc::sync::Arc; +use alloc::vec; use alloc::vec::Vec; use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; -use esp_alloc::{HeapRegion, MemoryCapability}; +use esp_alloc::{EspHeap, HeapRegion, MemoryCapability}; use esp_backtrace as _; use esp_hal::clock::CpuClock; use esp_hal::delay::Delay; use esp_hal::dma::{ - Channel, DmaChannel, DmaDescriptor, DmaDescriptorFlags, DmaLoopBuf, DmaRxTxBuf, DmaTxBuf, + BurstConfig, Channel, DmaChannel, DmaDescriptor, DmaDescriptorFlags, DmaLoopBuf, DmaRxTxBuf, + DmaTxBuf, ExternalBurstConfig, }; -use esp_hal::gpio::{self, Flex, Level, Output, OutputConfig, OutputPin, Pin}; +use esp_hal::gpio::{ + self, Flex, Input, InputConfig, Level, Output, OutputConfig, OutputPin, Pin, Pull, +}; +use esp_hal::i2c::master::{I2c, I2cAddress}; use esp_hal::lcd_cam::lcd::dpi::{self, Dpi, Format, FrameTiming}; +use esp_hal::lcd_cam::lcd::i8080::I8080; use esp_hal::lcd_cam::lcd::{self, ClockMode, DelayMode, Lcd, Phase, Polarity}; use esp_hal::lcd_cam::{BitOrder, LcdCam}; use esp_hal::mcpwm::operator::PwmPin; use esp_hal::mcpwm::{McPwm, PeripheralClockConfig}; use esp_hal::peripherals::PSRAM; +use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock}; use esp_hal::rtc_cntl::sleep; -use esp_hal::spi::master::{Address, Command, DataMode, Spi}; +use esp_hal::spi::master::{Address, Command as SpiCommand, DataMode, Spi}; use esp_hal::time::Rate; use esp_hal::timer::systimer::SystemTimer; use esp_hal::{dma_buffers, dma_loop_buffer, dma_tx_buffer, Async}; +use itertools::chain; +use lazy_static::lazy_static; use log::info; extern crate alloc; @@ -49,9 +78,26 @@ async fn main(spawner: Spawner) { // generator version: 0.5.0 esp_println::logger::init_logger_from_env(); + info!("Logger initialized!"); - let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let config = esp_hal::Config::default() + .with_cpu_clock(CpuClock::max()) + .with_psram(PsramConfig { + size: PsramSize::AutoDetect, + core_clock: Some(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m), + flash_frequency: FlashFreq::default(), + ram_frequency: SpiRamFreq::Freq80m, + }); let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config); + info!("System initialized!"); + + // Use the internal DRAM as the heap. + esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64 * 1024); + info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats()); + + let timer0 = SystemTimer::new(peripherals.SYSTIMER); + esp_hal_embassy::init(timer0.alarm0); + info!("Embassy initialized!"); // Initialize the PSRAM allocator. { @@ -64,30 +110,30 @@ async fn main(spawner: Spawner) { )); } } - - let dma_buf_desc = DmaDescriptor { - buffer: unsafe { - PSRAM_ALLOCATOR.alloc(Layout::from_size_align(2 * 360 * 960, 32).unwrap()) - }, - next: core::ptr::null_mut(), - flags: { - let mut flags = DmaDescriptorFlags(0); - flags.set_length(2 * 360 * 960); - flags.set_owner(false); - flags.set_size(2 * 360 * 960); - flags.set_suc_eof(false); - flags - }, + const BUFFER_LEN: usize = core::mem::size_of::() + * (360 + /* TODO: Figure out why more bytes are needed: */ 8) + * 960; + // Note: We just leak this buffer. + let buffer_ptr = unsafe { + // ⚠️ Note: For chips that support DMA to/from PSRAM (ESP32-S3) DMA transfers to/from PSRAM + // have extra alignment requirements. The address and size of the buffer pointed to by each + // descriptor must be a multiple of the cache line (block) size. This is 32 bytes on ESP32-S3. + PSRAM_ALLOCATOR.alloc(Layout::from_size_align(BUFFER_LEN, 32).unwrap()) }; - // let dma_buf = DmaTxBuf::new(&[dma_buf_desc], todo!()).unwrap(); + let buffer = unsafe { core::slice::from_raw_parts_mut(buffer_ptr, BUFFER_LEN) }; + let burst_config: BurstConfig = ExternalBurstConfig::Size16.into(); - // Use the internal DRAM as the heap. - esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64 * 1024); + info!( + "PSRAM SPI burst config: max_compatible_chunk_size={}", + burst_config.max_compatible_chunk_size() + ); - let timer0 = SystemTimer::new(peripherals.SYSTIMER); - esp_hal_embassy::init(timer0.alarm0); - - info!("Embassy initialized!"); + let dma_buf_descs_len = + esp_hal::dma::descriptor_count(BUFFER_LEN, burst_config.max_compatible_chunk_size(), false); + // Descriptors are initialized by `DmaTxBuf::new`. + let dma_buf_descs = Box::leak(vec![DmaDescriptor::EMPTY; dma_buf_descs_len].into_boxed_slice()); + let mut dma_buf = DmaTxBuf::new(dma_buf_descs, buffer).unwrap(); + // let mut dma_buf = dma_tx_buffer!(BUFFER_LEN).unwrap(); // TODO: Spawn some tasks let _ = spawner; @@ -95,17 +141,6 @@ async fn main(spawner: Spawner) { // Enable LDO2 let _ = Output::new(peripherals.GPIO17, Level::High, OutputConfig::default()); - // let mut o = Output::new(peripherals.GPIO35, Level::Low, OutputConfig::default()); - - // loop { - // o.set_high(); - // Timer::after(Duration::from_secs(1)).await; - // o.set_low(); - // Timer::after(Duration::from_secs(1)).await; - // info!("yeet"); - // Timer::after(Duration::from_secs(1)).await; - // } - // let mut x = Spi::new( // peripherals.SPI2, // esp_hal::spi::master::Config::default().with_frequency(Rate::from_mhz(1)), // For debugging @@ -114,66 +149,32 @@ async fn main(spawner: Spawner) { // .with_mosi(peripherals.GPIO35) // .with_sck(peripherals.GPIO36); - // let init = [ - // &b"\xff\x05w\x01\x00\x00\x13"[..], - // b"\xef\x01\x08", - // b"\xff\x05w\x01\x00\x00\x10", - // b"\xc0\x02w\x00", - // b"\xc1\x02\x11\x0c", - // b"\xc2\x02\x07\x02", - // b"\xcc\x010", - // b"\xb0\x10\x06\xcf\x14\x0c\x0f\x03\x00\n\x07\x1b\x03\x12\x10%6\x1e", - // b"\xb1\x10\x0c\xd4\x18\x0c\x0e\x06\x03\x06\x08#\x06\x12\x100/\x1f", - // b"\xff\x05w\x01\x00\x00\x11", - // b"\xb0\x01s", - // b"\xb1\x01|", - // b"\xb2\x01\x83", - // b"\xb3\x01\x80", - // b"\xb5\x01I", - // b"\xb7\x01\x87", - // b"\xb8\x013", - // b"\xb9\x02\x10\x1f", - // b"\xbb\x01\x03", - // b"\xc1\x01\x08", - // b"\xc2\x01\x08", - // b"\xd0\x01\x88", - // b"\xe0\x06\x00\x00\x02\x00\x00\x0c", - // b"\xe1\x0b\x05\x96\x07\x96\x06\x96\x08\x96\x00DD", - // b"\xe2\x0c\x00\x00\x03\x03\x00\x00\x02\x00\x00\x00\x02\x00", - // b"\xe3\x04\x00\x0033", - // b"\xe4\x02DD", - // b"\xe5\x10\r\xd4(\x8c\x0f\xd6(\x8c\t\xd0(\x8c\x0b\xd2(\x8c", - // b"\xe6\x04\x00\x0033", - // b"\xe7\x02DD", - // b"\xe8\x10\x0e\xd5(\x8c\x10\xd7(\x8c\n\xd1(\x8c\x0c\xd3(\x8c", - // b"\xeb\x06\x00\x01\xe4\xe4D\x00", - // b"\xed\x10\xf3\xc1\xba\x0ffwDUUDwf\xf0\xab\x1c?", - // b"\xef\x06\x10\r\x04\x08?\x1f", - // b"\xff\x05w\x01\x00\x00\x13", - // b"\xe8\x02\x00\x0e", - // b"\x11\x80x", - // b"\xe8\x82\x00\x0c\n", - // b"\xe8\x02@\x00", - // b"\xff\x05w\x01\x00\x00\x00", - // b"6\x01\x00", - // b":\x01f", - // b")\x80\x14", - // b"\xff\x05w\x01\x00\x00\x10", - // b"\xe5\x02\x00\x00", - // ] - // .into_iter() - // .flatten() - // .copied() - // .collect::>(); - - // info!("init sequence writing"); - // x.write(&init).unwrap(); - // info!("init sequence written"); - // TODO: Use PWM to control the pwm_pin. let mut _pwm = McPwm::new(peripherals.MCPWM0, PeripheralClockConfig::with_prescaler(1)); let mut _pwm_pin = Output::new(peripherals.GPIO21, Level::High, OutputConfig::default()); + let mut matrix_interrupt = Input::new( + peripherals.GPIO7, + InputConfig::default().with_pull(Pull::Up), + ); + // let mut sda = Flex::new(peripherals.GPIO8); + // let mut scl = Output::new(peripherals.GPIO9, Level::High, OutputConfig::default()); + let mut i2c = I2c::new( + peripherals.I2C0, + esp_hal::i2c::master::Config::default().with_frequency(Rate::from_khz(400)), + ) + .unwrap() + .with_sda(peripherals.GPIO8) + .with_scl(peripherals.GPIO9); + + const I2C_ADDR_MATRIX_LEFT: I2cAddress = I2cAddress::SevenBit(0b0100000); + const I2C_ADDR_MATRIX_RIGHT: I2cAddress = I2cAddress::SevenBit(0b0100001); + + for addr in [I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT] { + i2c.write(addr, &[0b10010000, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) + .unwrap(); + } + let mut sck = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default()); let mut mosi = Flex::new(peripherals.GPIO35); let mut cs = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default()); @@ -183,16 +184,32 @@ async fn main(spawner: Spawner) { info!("init sequence writing"); - cs.set_low(); - for op in INIT_SEQUENCE { - match *op { - InitOp::WriteComm(data) => spi_write_word(false, data, &mut mosi, &mut sck), - InitOp::WriteData(data) => spi_write_word(true, data, &mut mosi, &mut sck), - InitOp::SleepMs(ms) => Timer::after_millis(ms).await, - _ => panic!(), + // cs.set_low(); + // for op in INIT_SEQUENCE { + // match *op { + // InitOp::WriteComm(data) => spi_write_word(false, data, &mut mosi, &mut sck), + // InitOp::WriteData(data) => spi_write_word(true, data, &mut mosi, &mut sck), + // InitOp::SleepMs(ms) => Timer::after_millis(ms).await, + // _ => panic!(), + // } + // } + // cs.set_high(); + for (subsequence, delay_ms) in &*INIT_SEQUENCE_COMMANDS { + for command in subsequence { + spi_write( + command.address(), + command.args_iter().copied(), + &mut mosi, + &mut sck, + &mut cs, + ); + info!("COMM 0x{:02X}", command.address()); + for arg in command.args_iter() { + info!("DATA 0x{arg:02X}"); + } } + Timer::after_millis(*delay_ms).await } - cs.set_high(); info!("init sequence written"); @@ -211,7 +228,17 @@ async fn main(spawner: Spawner) { let mut lcd = LcdCam::new(peripherals.LCD_CAM).into_async().lcd; let lcd_config = lcd::dpi::Config::default() - .with_frequency(Rate::from_mhz(16)) // From Adafruit + // Internal memory can use the full 16 MHz, but when external PSRAM is used, it cannot keep up with the display. + // For that reason, we choose the highest value for which it doesn't glitch by showing black + // stripes on the screen. + // + // There are three knobs you can turn to improve the bandwidth situation. + // - increase psram frequency + // - decrease the peripheral's frequency + // - prevent flash from being used whilst your program is running. (There's a PR to make + // this easy to do) + // https://github.com/esp-rs/esp-hal/pull/3024 + .with_frequency(Rate::from_mhz(11)) // From Adafruit .with_clock_mode(ClockMode { polarity: Polarity::IdleLow, // From Adafruit phase: Phase::ShiftHigh, // From Adafruit @@ -226,6 +253,12 @@ async fn main(spawner: Spawner) { // https://github.com/adafruit/Adafruit_CircuitPython_Qualia/blob/742d336e05e6a4d8bdaa46e15bbf60c9f30d2eba/adafruit_qualia/displays/__init__.py#L59-L62 // CircuitPython code handling Adafruit's config // https://github.com/adafruit/circuitpython/blob/97c6617817e95b1f6aa2ce458778aaa8371de39b/ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c#L63 + // ESP-IDF peripheral configuration code: + // https://github.com/espressif/esp-idf/blob/800f141f94c0f880c162de476512e183df671307/components/esp_lcd/rgb/esp_lcd_panel_rgb.c#L556 + // Espressif's docs: + // https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32s3/api-reference/peripherals/lcd/rgb_lcd.html#structures + + // TODO: Investigate PORCTRL instruction in datasheet of ST7701 let horizontal_resolution: usize = 240; let vertical_resolution = 960; let overscan_left = 120; @@ -239,16 +272,16 @@ async fn main(spawner: Spawner) { let horizontal_active_width = (horizontal_resolution + overscan_left).div_ceil(16) * 16; // Round up to a multiple of 16. let vertical_active_height = vertical_resolution; FrameTiming { - horizontal_total_width: horizontal_active_width - + hsync_width + horizontal_total_width: hsync_width + horizontal_blank_back_porch - + horizontal_blank_back_porch, - vertical_total_height: vertical_active_height - + vsync_width + + horizontal_active_width + + horizontal_blank_front_porch, + vertical_total_height: vsync_width + vertical_blank_back_porch + + vertical_active_height + vertical_blank_front_porch, - horizontal_blank_front_porch, - vertical_blank_front_porch, + horizontal_blank_front_porch: horizontal_blank_front_porch + hsync_width, + vertical_blank_front_porch: vertical_blank_front_porch + vsync_width, horizontal_active_width, vertical_active_height, vsync_width, @@ -261,7 +294,7 @@ async fn main(spawner: Spawner) { .with_de_idle_level(Level::Low); // let mut dma_buf = dma_loop_buffer!(2 * 32); - let mut dma_buf = dma_tx_buffer!(2 * 960 * 320 / 4).unwrap(); + // let mut dma_buf = dma_tx_buffer!(2 * 960 * 320 / 4).unwrap(); // let tx_buf = dma_tx_buffer!(core::mem::size_of::() * 280 * 1000).unwrap(); // let (c_rx, c_tx) = peripherals.DMA_CH0.split(); @@ -300,21 +333,54 @@ async fn main(spawner: Spawner) { (((r & MAX_RED) as u16) << 11) | (((g & MAX_GREEN) as u16) << 5) | ((b & MAX_BLUE) as u16) } - let mut colors = core::iter::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(); + // let mut colors = core::iter::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(); + // let mut colors = colors + // .take(len - 10) + // .chain(core::iter::repeat_n(0, 10)) + // .cycle(); + + // This is wrong, only a left padding of 120 should be needed. + // const padding_left: usize = 120; + // const padding_right: usize = 0; + const padding_left: usize = 121; + const padding_right: usize = 7; + + const colors_width: usize = 240; + const colors_height: usize = 960; + + fn row(edge: u16) -> impl Iterator + Clone { + chain![ + core::iter::repeat_n(rgb(0, 0, 0xFF), padding_left), + core::iter::once(rgb(0xFF, 0xFF, 0xFF)), + core::iter::repeat_n(edge, colors_width - 2), + core::iter::once(rgb(0xFF, 0xFF, 0xFF)), + core::iter::repeat_n(rgb(0, 0, 0xFF), padding_right), + ] + }; + + let mut colors = chain![ + row(rgb(0xFF, 0, 0)), + core::iter::repeat_n(row(0), colors_height - 2).flatten(), + row(rgb(0xFF, 0xFF, 0)), + ]; + + // for _ in 0..120 { + // colors.next(); + // } // let write = |command: u8, args: &[u8]| { // // TODO: 3-wire SPI requires 9-bit words, where each 8-bit word is prefixed by 1 bit @@ -330,53 +396,88 @@ async fn main(spawner: Spawner) { // .unwrap(); // }; - loop { - // let mut buf = [0u8; 3]; - // x.half_duplex_read( - // DataMode::Single, - // Command::_9Bit(0x04, DataMode::Single), - // Address::None, - // 1, - // &mut buf, - // ) - // .unwrap(); - let mut buf = [0_u8, 0, 0]; - spi_read( - 0xDA, - false, - &mut mosi, - &mut sck, - &mut cs, - &mut buf[0..][..1], - ); - spi_read( - 0xDB, - false, - &mut mosi, - &mut sck, - &mut cs, - &mut buf[1..][..1], - ); - spi_read( - 0xDC, - false, - &mut mosi, - &mut sck, - &mut cs, - &mut buf[2..][..1], - ); - let mut buf2 = [0_u8, 0, 0]; - spi_read(0x04, true, &mut mosi, &mut sck, &mut cs, &mut buf2); - // info!("Hello world! {buf:02x?} {buf2:02x?}"); + let mut i = 0; - for (chunk, color) in dma_buf.as_mut_slice().chunks_mut(2).zip(&mut colors) { - chunk.copy_from_slice(&color.to_le_bytes()); + loop { + // LCD stuff + { + // let mut buf = [0u8; 3]; + // x.half_duplex_read( + // DataMode::Single, + // Command::_9Bit(0x04, DataMode::Single), + // Address::None, + // 1, + // &mut buf, + // ) + // .unwrap(); + let mut buf = [0_u8, 0, 0]; + spi_read( + 0xDA, + false, + &mut mosi, + &mut sck, + &mut cs, + &mut buf[0..][..1], + ); + spi_read( + 0xDB, + false, + &mut mosi, + &mut sck, + &mut cs, + &mut buf[1..][..1], + ); + spi_read( + 0xDC, + false, + &mut mosi, + &mut sck, + &mut cs, + &mut buf[2..][..1], + ); + let mut buf2 = [0_u8, 0, 0]; + spi_read(0x04, true, &mut mosi, &mut sck, &mut cs, &mut buf2); + // info!("Hello world! {buf:02x?} {buf2:02x?}"); + + if i == 0 { + for (chunk, color) in dma_buf.as_mut_slice().chunks_mut(2).zip(&mut colors) { + chunk.copy_from_slice(&color.to_le_bytes()); + } + + info!("chunk addr: {}", dma_buf.as_slice().as_ptr() as usize); + // colors.next(); // Shift colors + } + + let transfer = dpi.send(false, dma_buf).map_err(|e| e.0).unwrap(); + (_, dpi, dma_buf) = transfer.wait(); } - colors.next(); // Shift colors + // IO stuff + { + let mut input_register_banks = [0_u8; 10]; + i2c.write_read( + I2C_ADDR_MATRIX_LEFT, + &[0x80], + &mut input_register_banks[..5], + ) + .unwrap(); + i2c.write_read( + I2C_ADDR_MATRIX_RIGHT, + &[0x80], + &mut input_register_banks[5..], + ) + .unwrap(); + let mut rows = [0_u16; 5]; + for (row, (left, right)) in rows.iter_mut().zip(core::iter::zip( + &input_register_banks[..5], + &input_register_banks[5..], + )) { + *row = *left as u16 | ((*right as u16) << 6); + } + info!("rows: {rows:x?}") + } - let transfer = dpi.send(false, dma_buf).map_err(|e| e.0).unwrap(); - (_, dpi, dma_buf) = transfer.wait(); + i += 1; // x.half_duplex_write( // DataMode::Single, @@ -434,11 +535,17 @@ fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex, sck: &mut Output) { ); } -fn spi_write(command: u8, args: &[u8], mosi: &mut Flex, sck: &mut Output, cs: &mut Output) { +fn spi_write( + command: u8, + args: impl IntoIterator, + mosi: &mut Flex, + sck: &mut Output, + cs: &mut Output, +) { cs.set_low(); spi_write_word(false, command, mosi, sck); - for &arg in args { + for arg in args { spi_write_word(true, arg, mosi, sck); } @@ -499,6 +606,374 @@ enum InitOp { use InitOp::*; +lazy_static! { + static ref INIT_SEQUENCE_COMMANDS: Vec<(Vec>, u64)> = vec![ + (vec![ + Box::new(CmdCn2bkxsel( + CmdCn2bkxselArg0::new(), + CmdCn2bkxselArg1::new(), + CmdCn2bkxselArg2::new(), + CmdCn2bkxselArg3::new(), + CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true), + )), + Box::new(CustomCommand { + address: 0xEF, + args: &[0x08], + }), + Box::new(CmdCn2bkxsel( + CmdCn2bkxselArg0::new(), + CmdCn2bkxselArg1::new(), + CmdCn2bkxselArg2::new(), + CmdCn2bkxselArg3::new(), + CmdCn2bkxselArg4::new().with_bksel(0x0).with_cn2(true), + )), + Box::new(CmdLneset( + CmdLnesetArg0::new().with_bar(119).with_lde_en(false), + CmdLnesetArg1::new().with_line_delta(0), + )), + Box::new(CmdPorctrl( + CmdPorctrlArg0::new().with_vbp(17), + CmdPorctrlArg1::new().with_vfp(12), + )), + Box::new(CmdInvsel( + CmdInvselArg0::new().with_nlinv(0b111), + CmdInvselArg1::new().with_rtni(2), + )), + Box::new(CustomCommand { + address: 0xCC, + args: &[0x30], + }), + Box::new(CmdPvgamctrl( + CmdPvgamctrlArg0::new().with_vc0p(6).with_aj0p(0), + CmdPvgamctrlArg1::new().with_vc4p(15).with_aj1p(3), + CmdPvgamctrlArg2::new().with_vc8p(14).with_aj2p(0), + CmdPvgamctrlArg3::new().with_vc16p(12), + CmdPvgamctrlArg4::new().with_vc24p(15).with_aj3p(0), + CmdPvgamctrlArg5::new().with_vc52p(3), + CmdPvgamctrlArg6::new().with_vc80p(0), + CmdPvgamctrlArg7::new().with_vc108p(10), + CmdPvgamctrlArg8::new().with_vc147p(7), + CmdPvgamctrlArg9::new().with_vc175p(27), + CmdPvgamctrlArg10::new().with_vc203p(3), + CmdPvgamctrlArg11::new().with_vc231p(18).with_aj4p(0), + CmdPvgamctrlArg12::new().with_vc239p(16), + CmdPvgamctrlArg13::new().with_vc247p(37).with_aj5p(0), + CmdPvgamctrlArg14::new().with_vc251p(54).with_aj6p(0), + CmdPvgamctrlArg15::new().with_vc255p(30).with_aj7p(0), + )), + Box::new(CmdNvgamctrl( + CmdNvgamctrlArg0::new().with_vc0n(12).with_aj0n(0), + CmdNvgamctrlArg1::new().with_vc4n(14).with_aj1n(3), + CmdNvgamctrlArg2::new().with_vc8n(18).with_aj2n(0), + CmdNvgamctrlArg3::new().with_vc16n(12), + CmdNvgamctrlArg4::new().with_vc24n(14).with_aj3n(0), + CmdNvgamctrlArg5::new().with_vc52n(6), + CmdNvgamctrlArg6::new().with_vc80n(3), + CmdNvgamctrlArg7::new().with_vc108n(6), + CmdNvgamctrlArg8::new().with_vc147n(8), + CmdNvgamctrlArg9::new().with_vc175n(35), + CmdNvgamctrlArg10::new().with_vc203n(6), + CmdNvgamctrlArg11::new().with_vc231n(18).with_aj4n(0), + CmdNvgamctrlArg12::new().with_vc239n(16), + CmdNvgamctrlArg13::new().with_vc247n(48).with_aj5n(0), + CmdNvgamctrlArg14::new().with_vc251n(47).with_aj6n(0), + CmdNvgamctrlArg15::new().with_vc255n(31).with_aj7n(0), + )), + Box::new(CmdCn2bkxsel( + CmdCn2bkxselArg0::new(), + CmdCn2bkxselArg1::new(), + CmdCn2bkxselArg2::new(), + CmdCn2bkxselArg3::new(), + CmdCn2bkxselArg4::new().with_bksel(0x1).with_cn2(true), + )), + Box::new(CmdVrhs( + CmdVrhsArg0::new().with_vrha(115), + )), + Box::new(CmdVcoms( + CmdVcomsArg0::new().with_vcom(124), + )), + Box::new(CmdVghss( + // The first bit is set to 1 in the original init code, but not here. + CmdVghssArg0::new().with_vghss(0x3), // 13 V + )), + Box::new(CmdTescmd( + CmdTescmdArg0::new(), + )), + Box::new(CmdVgls( + CmdVglsArg0::new().with_vgls(0x9) // -10.17 V + )), + Box::new(CmdPwctrl1( + CmdPwctrl1Arg0::new() + .with_apos(0x3) // Max + .with_apis(0x1) // Min + .with_ap(0x2) // Middle + )), + Box::new(CmdPwctrl2( + CmdPwctrl2Arg0::new() + .with_avcl(0x3) // -5 V + .with_avdd(0x3) // 6.8 V + )), + Box::new(CmdPwctrl3( + CmdPwctrl3Arg0::new() + .with_svno_pum(0) // Cell setting 4 + .with_svpo_pum(0x1) // Cell setting 5 + )), + Box::new(CmdPclks2( + CmdPclks2Arg0::new().with_sbstcks(0x3) + )), + Box::new(CmdPdr1( + CmdPdr1Arg0::new().with_t2d(8) // 1.6 us + )), + Box::new(CmdPdr2( + CmdPdr2Arg0::new().with_t3d(8) // 6.4 us + )), + Box::new(CmdMipiset1( + CmdMipiset1Arg0::new().with_err_sel(0).with_eotp_en(true), + )), + Box::new(CustomCommand { + address: 0xE0, + args: &[ + 0x00, + 0x00, + 0x02, + 0x00, + 0x00, + 0x0C, + ] + }), + Box::new(CustomCommand { + address: 0xE1, + args: &[ + 0x05, + 0x96, + 0x07, + 0x96, + 0x06, + 0x96, + 0x08, + 0x96, + 0x00, + 0x44, + 0x44, + ] + }), + Box::new(CustomCommand { + address: 0xE2, + args: &[ + 0x00, + 0x00, + 0x03, + 0x03, + 0x00, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x02, + 0x00, + ] + }), + Box::new(CustomCommand { + address: 0xE3, + args: &[ + 0x00, + 0x00, + 0x33, + 0x33, + ] + }), + Box::new(CustomCommand { + address: 0xE4, + args: &[ + 0x44, + 0x44, + ] + }), + Box::new(CustomCommand { + address: 0xE5, + args: &[ + 0x0D, + 0xD4, + 0x28, + 0x8C, + 0x0F, + 0xD6, + 0x28, + 0x8C, + 0x09, + 0xD0, + 0x28, + 0x8C, + 0x0B, + 0xD2, + 0x28, + 0x8C, + ] + }), + Box::new(CustomCommand { + address: 0xE6, + args: &[ + 0x00, + 0x00, + 0x33, + 0x33, + ] + }), + Box::new(CustomCommand { + address: 0xE7, + args: &[ + 0x44, + 0x44, + ] + }), + Box::new(CustomCommand { + address: 0xE8, + args: &[ + 0x0E, + 0xD5, + 0x28, + 0x8C, + 0x10, + 0xD7, + 0x28, + 0x8C, + 0x0A, + 0xD1, + 0x28, + 0x8C, + 0x0C, + 0xD3, + 0x28, + 0x8C, + ] + }), + Box::new(CustomCommand { + address: 0xEB, + args: &[ + 0x00, + 0x01, + 0xE4, + 0xE4, + 0x44, + 0x00, + ] + }), + Box::new(CustomCommand { + address: 0xED, + args: &[ + 0xF3, + 0xC1, + 0xBA, + 0x0F, + 0x66, + 0x77, + 0x44, + 0x55, + 0x55, + 0x44, + 0x77, + 0x66, + 0xF0, + 0xAB, + 0x1C, + 0x3F, + ] + }), + Box::new(CustomCommand { + address: 0xEF, + args: &[ + 0x10, + 0x0D, + 0x04, + 0x08, + 0x3F, + 0x1F, + ] + }), + Box::new(CmdCn2bkxsel( + CmdCn2bkxselArg0::new(), + CmdCn2bkxselArg1::new(), + CmdCn2bkxselArg2::new(), + CmdCn2bkxselArg3::new(), + CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true), + )), + Box::new(CustomCommand { + address: 0xE8, + args: &[ + 0x00, + 0x0E, + ], + }), + Box::new(CmdSlpout()), + ], 120), + (vec![ + Box::new(CustomCommand { + address: 0xE8, + args: &[ + 0x00, + 0x0C, + ], + }), + ], 10), + (vec![ + Box::new(CustomCommand { + address: 0xE8, + args: &[ + 0x40, + 0x00, + ], + }), + Box::new(CmdCn2bkxsel( + CmdCn2bkxselArg0::new(), + CmdCn2bkxselArg1::new(), + CmdCn2bkxselArg2::new(), + CmdCn2bkxselArg3::new(), + CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false), + )), + Box::new(CmdMadctl( + CmdMadctlArg0::new().with_bgr(false).with_ml(false), + )), + Box::new(CmdColmod( + CmdColmodArg0::new().with_vipf(6) // 18-bit pixel + )), + Box::new(CmdDispon()), + ], 20), + // (vec![ + // Box::new(CmdCn2bkxsel( + // CmdCn2bkxselArg0::new(), + // CmdCn2bkxselArg1::new(), + // CmdCn2bkxselArg2::new(), + // CmdCn2bkxselArg3::new(), + // CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(true), + // )), + // Box::new(CmdPorctrl( + // CmdPorctrlArg0::new().with_vbp(0xFF), + // CmdPorctrlArg1::new().with_vfp(0x0), + // )), + // // Box::new(CmdRgbctrl( + // // CmdRgbctrlArg0::new() + // // .with_ep(false) + // // .with_dp(false) + // // .with_hsp(false) + // // .with_vsp(false) + // // .with_de_hv(true), + // // CmdRgbctrlArg1::new() + // // .with_hbp_hvrgb(16), + // // CmdRgbctrlArg2::new() + // // .with_vbp_hvrgb(8), + // // )), + // Box::new(CmdCn2bkxsel( + // CmdCn2bkxselArg0::new(), + // CmdCn2bkxselArg1::new(), + // CmdCn2bkxselArg2::new(), + // CmdCn2bkxselArg3::new(), + // CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false), + // )), + // ], 20), + ]; +} + const INIT_SEQUENCE: &[InitOp] = &[ WriteComm(0xff), WriteData(0x77), @@ -525,6 +1000,7 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0x02), WriteComm(0xcc), WriteData(0x30), + // WriteComm(0xB0), WriteData(0x06), WriteData(0xCF), @@ -542,6 +1018,7 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0x25), WriteData(0x36), WriteData(0x1E), + // WriteComm(0xB1), WriteData(0x0C), WriteData(0xD4), @@ -559,37 +1036,51 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0x30), WriteData(0x2F), WriteData(0x1F), + // WriteComm(0xff), WriteData(0x77), WriteData(0x01), WriteData(0x00), WriteData(0x00), WriteData(0x11), + // WriteComm(0xb0), WriteData(0x73), + // WriteComm(0xb1), WriteData(0x7C), + // WriteComm(0xb2), WriteData(0x83), + // WriteComm(0xb3), WriteData(0x80), + // WriteComm(0xb5), WriteData(0x49), + // WriteComm(0xb7), WriteData(0x87), + // WriteComm(0xb8), WriteData(0x33), + // WriteComm(0xb9), WriteData(0x10), - WriteData(0x1f), + WriteData(0x1f), // ??? + // WriteComm(0xbb), WriteData(0x03), + // WriteComm(0xc1), WriteData(0x08), + // WriteComm(0xc2), WriteData(0x08), + // WriteComm(0xd0), WriteData(0x88), + // TODO: Unknown command WriteComm(0xe0), WriteData(0x00), WriteData(0x00), @@ -597,6 +1088,7 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0x00), WriteData(0x00), WriteData(0x0c), + // TODO: Unknown command WriteComm(0xe1), WriteData(0x05), WriteData(0x96), @@ -609,6 +1101,7 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0x00), WriteData(0x44), WriteData(0x44), + // TODO: Unknown command WriteComm(0xe2), WriteData(0x00), WriteData(0x00), @@ -622,14 +1115,17 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0x00), WriteData(0x02), WriteData(0x00), + // TODO: Unknown command WriteComm(0xe3), WriteData(0x00), WriteData(0x00), WriteData(0x33), WriteData(0x33), + // TODO: Unknown command WriteComm(0xe4), WriteData(0x44), WriteData(0x44), + // TODO: Unknown command WriteComm(0xe5), WriteData(0x0d), WriteData(0xd4), @@ -647,14 +1143,17 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0xd2), WriteData(0x28), WriteData(0x8c), + // TODO: Unknown command WriteComm(0xe6), WriteData(0x00), WriteData(0x00), WriteData(0x33), WriteData(0x33), + // TODO: Unknown command WriteComm(0xe7), WriteData(0x44), WriteData(0x44), + // TODO: Unknown command WriteComm(0xe8), WriteData(0x0e), WriteData(0xd5), @@ -672,6 +1171,7 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0xd3), WriteData(0x28), WriteData(0x8c), + // TODO: Unknown command WriteComm(0xeb), WriteData(0x00), WriteData(0x01), @@ -679,6 +1179,7 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0xe4), WriteData(0x44), WriteData(0x00), + // TODO: Unknown command WriteComm(0xed), WriteData(0xf3), WriteData(0xc1), @@ -696,6 +1197,7 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0xab), WriteData(0x1c), WriteData(0x3f), + // TODO: Unknown command WriteComm(0xef), WriteData(0x10), WriteData(0x0d), @@ -703,34 +1205,43 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData(0x08), WriteData(0x3f), WriteData(0x1f), + // WriteComm(0xff), WriteData(0x77), WriteData(0x01), WriteData(0x00), WriteData(0x00), WriteData(0x13), + // TODO: Unknown command WriteComm(0xe8), WriteData(0x00), WriteData(0x0e), + // WriteComm(0x11), SleepMs(120), + // TODO: Unknown command WriteComm(0xe8), WriteData(0x00), WriteData(0x0c), SleepMs(10), + // TODO: Unknown command WriteComm(0xe8), WriteData(0x40), WriteData(0x00), + // WriteComm(0xff), WriteData(0x77), WriteData(0x01), WriteData(0x00), WriteData(0x00), WriteData(0x00), + // WriteComm(0x36), WriteData(0x00), + // WriteComm(0x3A), WriteData(0x66), + // WriteComm(0x29), SleepMs(20), /* @@ -745,252 +1256,3 @@ const INIT_SEQUENCE: &[InitOp] = &[ WriteData (0x00); */ ]; - -/* -TODO: Use this init sequence - -WriteComm (0xff); -WriteData (0x77); -WriteData (0x01); -WriteData (0x00); -WriteData (0x00); -WriteData (0x13); -WriteComm (0xef); -WriteData (0x08); -WriteComm (0xff); -WriteData (0x77); -WriteData (0x01); -WriteData (0x00); -WriteData (0x00); -WriteData (0x10); -WriteComm (0xc0); -WriteData (0x77); -WriteData (0x00); -WriteComm (0xc1); -WriteData (0x11); -WriteData (0x0c); -WriteComm (0xc2); -WriteData (0x07); -WriteData (0x02); -WriteComm (0xcc); -WriteData (0x30); -WriteComm (0xB0); -WriteData (0x06); -WriteData (0xCF); -WriteData (0x14); -WriteData (0x0C); -WriteData (0x0F); -WriteData (0x03); -WriteData (0x00); -WriteData (0x0A); -WriteData (0x07); -WriteData (0x1B); -WriteData (0x03); -WriteData (0x12); -WriteData (0x10); -WriteData (0x25); -WriteData (0x36); -WriteData (0x1E); -WriteComm (0xB1); -WriteData (0x0C); -WriteData (0xD4); -WriteData (0x18); -WriteData (0x0C); -WriteData (0x0E); -WriteData (0x06); -WriteData (0x03); -WriteData (0x06); -WriteData (0x08); -WriteData (0x23); -WriteData (0x06); -WriteData (0x12); -WriteData (0x10); -WriteData (0x30); -WriteData (0x2F); -WriteData (0x1F); -WriteComm (0xff); -WriteData (0x77); -WriteData (0x01); -WriteData (0x00); -WriteData (0x00); -WriteData (0x11); -WriteComm (0xb0); -WriteData (0x73); -WriteComm (0xb1); -WriteData (0x7C); -WriteComm (0xb2); -WriteData (0x83); -WriteComm (0xb3); -WriteData (0x80); -WriteComm (0xb5); -WriteData (0x49); -WriteComm (0xb7); -WriteData (0x87); -WriteComm (0xb8); -WriteData (0x33); -WriteComm (0xb9); -WriteData (0x10); -WriteData (0x1f); -WriteComm (0xbb); -WriteData (0x03); -WriteComm (0xc1); -WriteData (0x08); -WriteComm (0xc2); -WriteData (0x08); -WriteComm (0xd0); -WriteData (0x88); -WriteComm (0xe0); -WriteData (0x00); -WriteData (0x00); -WriteData (0x02); -WriteData (0x00); -WriteData (0x00); -WriteData (0x0c); -WriteComm (0xe1); -WriteData (0x05); -WriteData (0x96); -WriteData (0x07); -WriteData (0x96); -WriteData (0x06); -WriteData (0x96); -WriteData (0x08); -WriteData (0x96); -WriteData (0x00); -WriteData (0x44); -WriteData (0x44); -WriteComm (0xe2); -WriteData (0x00); -WriteData (0x00); -WriteData (0x03); -WriteData (0x03); -WriteData (0x00); -WriteData (0x00); -WriteData (0x02); -WriteData (0x00); -WriteData (0x00); -WriteData (0x00); -WriteData (0x02); -WriteData (0x00); -WriteComm (0xe3); -WriteData (0x00); -WriteData (0x00); -WriteData (0x33); -WriteData (0x33); -WriteComm (0xe4); -WriteData (0x44); -WriteData (0x44); -WriteComm (0xe5); -WriteData (0x0d); -WriteData (0xd4); -WriteData (0x28); -WriteData (0x8c); -WriteData (0x0f); -WriteData (0xd6); -WriteData (0x28); -WriteData (0x8c); -WriteData (0x09); -WriteData (0xd0); -WriteData (0x28); -WriteData (0x8c); -WriteData (0x0b); -WriteData (0xd2); -WriteData (0x28); -WriteData (0x8c); -WriteComm (0xe6); -WriteData (0x00); -WriteData (0x00); -WriteData (0x33); -WriteData (0x33); -WriteComm (0xe7); -WriteData (0x44); -WriteData (0x44); -WriteComm (0xe8); -WriteData (0x0e); -WriteData (0xd5); -WriteData (0x28); -WriteData (0x8c); -WriteData (0x10); -WriteData (0xd7); -WriteData (0x28); -WriteData (0x8c); -WriteData (0x0a); -WriteData (0xd1); -WriteData (0x28); -WriteData (0x8c); -WriteData (0x0c); -WriteData (0xd3); -WriteData (0x28); -WriteData (0x8c); -WriteComm (0xeb); -WriteData (0x00); -WriteData (0x01); -WriteData (0xe4); -WriteData (0xe4); -WriteData (0x44); -WriteData (0x00); -WriteComm (0xed); -WriteData (0xf3); -WriteData (0xc1); -WriteData (0xba); -WriteData (0x0f); -WriteData (0x66); -WriteData (0x77); -WriteData (0x44); -WriteData (0x55); -WriteData (0x55); -WriteData (0x44); -WriteData (0x77); -WriteData (0x66); -WriteData (0xf0); -WriteData (0xab); -WriteData (0x1c); -WriteData (0x3f); -WriteComm (0xef); -WriteData (0x10); -WriteData (0x0d); -WriteData (0x04); -WriteData (0x08); -WriteData (0x3f); -WriteData (0x1f); -WriteComm (0xff); -WriteData (0x77); -WriteData (0x01); -WriteData (0x00); -WriteData (0x00); -WriteData (0x13); -WriteComm (0xe8); -WriteData (0x00); -WriteData (0x0e); -WriteComm (0x11); -Delay_ms(120); -WriteComm (0xe8); -WriteData (0x00); -WriteData (0x0c); -Delay_ms(10); -WriteComm (0xe8); -WriteData (0x40); -WriteData (0x00); -WriteComm (0xff); -WriteData (0x77); -WriteData (0x01); -WriteData (0x00); -WriteData (0x00); -WriteData (0x00); -WriteComm (0x36); -WriteData (0x00); -WriteComm (0x3A); -WriteData (0x66); -WriteComm (0x29); -Delay_ms(20); -/* -WriteComm (0xff); -WriteData (0x77); -WriteData (0x01); -WriteData (0x00); -WriteData (0x00); -WriteData (0x10); -WriteComm (0xe5); -WriteData (0x00); -WriteData (0x00); -*/ -*/ diff --git a/firmware/src/lib.rs b/firmware/src/lib.rs index a4fb256..4b08017 100644 --- a/firmware/src/lib.rs +++ b/firmware/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] #![deny(clippy::mem_forget)] +#![feature(macro_metavar_expr)] pub mod st7701s; diff --git a/firmware/src/st7701s.rs b/firmware/src/st7701s.rs index 3fb43cc..587c2cf 100644 --- a/firmware/src/st7701s.rs +++ b/firmware/src/st7701s.rs @@ -1,9 +1,1212 @@ +use bitflags::bitflags; use esp_hal::gpio::OutputPin; +use paste::paste; + +pub trait Command { + fn address(&self) -> u8; + fn args_iter(&'_ self) -> core::slice::Iter<'_, u8>; +} + +pub struct CustomCommand { + pub address: u8, + pub args: &'static [u8], +} + +impl Command for CustomCommand { + fn address(&self) -> u8 { + self.address + } + + fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> { + self.args.iter() + } +} + +macro_rules! define_arg { + (@field ($field:ident: $min:literal..=$max:literal)) => { + paste! { + #[allow(unused, clippy::identity_op)] + pub const fn [](mut self, val: u8) -> Self { + const MASK: u8 = ((1 << ($max - $min + 1)) - 1); + self.0 &= !(MASK << $min); // Clear bits + self.0 |= (val & MASK) << $min; // Assign bits + self + } + } + }; + + (@field ($field:ident: $index:literal)) => { + paste! { + #[allow(unused)] + pub const fn [](mut self, val: bool) -> Self { + self.0 &= !(1 << $index); // Clear bit + self.0 |= (val as u8) << $index; // Assign bit + self + } + } + }; + + (@field ($field:ident)) => { + paste! { + #[allow(unused)] + pub const fn [](mut self, val: u8) -> Self { + self.0 = val; + self + } + } + }; + + ($visibility:vis struct $type:ident { + const INIT = $init:literal; + $($field:ident $(: $min:literal$(..=$max:literal)?)?),*$(,)? + }) => { + #[derive(Clone, Copy)] + $visibility struct $type(u8); + + impl Default for $type { + fn default() -> Self { + Self::new() + } + } + + #[allow(unused)] + impl $type { + pub const fn new() -> Self { + Self($init) + } + + pub const fn into_byte(self) -> u8 { + self.0 + } + + $( + define_arg!(@field ($field $(: $min$(..=$max)?)?)); + )* + } + }; + + ($visibility:vis struct $type:ident { + $field:ident $(,)? + }) => { + #[derive(Clone, Copy)] + $visibility struct $type(u8); + + impl Default for $type { + fn default() -> Self { + Self::new() + } + } + + #[allow(unused)] + impl $type { + pub const fn new() -> Self { + Self(0) + } + + pub const fn into_byte(self) -> u8 { + self.0 + } + + define_arg!(@field ($field)); + } + }; +} + +macro_rules! define_commands { + ($( + $(#[$docs:meta])* + $visibility:vis command $command:ident { + const ADDRESS = $address:literal; + $( + arg { + $($body:tt)* + } + )* + } + )*) => { + paste! { + $( + $( + define_arg!($visibility struct [< Cmd $command Arg ${index()}>] { $($body)* }); + )* + + #[repr(C)] + #[derive(Default)] + $(#[$docs])* + $visibility struct [< Cmd $command >]($(pub [< Cmd $command Arg ${index()} >], $(${ignore($body)})*)*); + + impl Command for [< Cmd $command >] { + fn address(&self) -> u8 { + $address + } + + fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> { + self.into_iter() + } + } + + impl<'a> IntoIterator for &'a [< Cmd $command >] { + type Item = &'a u8; + type IntoIter = core::slice::Iter<'a, u8>; + + fn into_iter(self) -> Self::IntoIter { + unsafe { + core::slice::from_raw_parts( + self as *const [< Cmd $command >] as *const u8, + 0 $(+ 1 $(${ignore($body)})*)*, + ) + }.iter() + } + } + )* + } + }; +} + +define_commands! { + // System function command table 1 + + #[doc="No operation"] + pub command Nop { + const ADDRESS = 0x00; + } + + #[doc="Software reset"] + pub command Swreset { + const ADDRESS = 0x01; + } + + #[doc="ID read"] + pub command Rddid { + const ADDRESS = 0x04; + arg { id1 } + arg { id2 } + arg { id3 } + } + + #[doc="ID read"] + pub command Rdnumed { + const ADDRESS = 0x05; + arg { + const INIT = 0b0000_0000; + err: 0..=6, + err_over: 7, + } + } + + #[doc="Read the first pixel of color R"] + pub command Rdred { + const ADDRESS = 0x06; + arg { r_1st } + } + + #[doc="Read the first pixel of color G"] + pub command Rdgreen { + const ADDRESS = 0x07; + arg { g_1st } + } + + #[doc="Read the first pixel of color B"] + pub command Rdblue { + const ADDRESS = 0x08; + arg { b_1st } + } + + #[doc="Read display power mode"] + pub command Rddpm { + const ADDRESS = 0x0A; + arg { + const INIT = 0b0000_1000; + dison: 2, + slpout: 4, + bston: 7, + } + } + + #[doc="Read display MADCTR"] + pub command Rddmadctl { + const ADDRESS = 0x0B; + arg { + const INIT = 0b0000_1000; + bgr: 3, + ml: 4, + } + } + + #[doc="Read display pixel format"] + pub command Rddcolmod { + const ADDRESS = 0x0C; + arg { + const INIT = 0b0000_0000; + vipf: 5..=6, + } + } + + #[doc="Read display image mode"] + pub command Rddim { + const ADDRESS = 0x0D; + arg { + const INIT = 0b0000_0000; + gcs: 0..=2, + alpxloff: 3, + alpxlon: 4, + invon: 5, + } + } + + #[doc="Read display signal mode"] + pub command Rddsm { + const ADDRESS = 0x0E; + arg { + const INIT = 0b0000_0000; + telmd: 6, + teon: 7, + } + } + + #[doc="Read display self-diagnostic result"] + pub command Rddsdr { + const ADDRESS = 0x0F; + arg { + const INIT = 0b0000_0000; + fund: 6, + rld: 7, + } + } + + #[doc="Sleep in"] + pub command Slpin { + const ADDRESS = 0x10; + } + + #[doc="Sleep out"] + pub command Slpout { + const ADDRESS = 0x11; + } + + #[doc="Partial mode on"] + pub command Ptlon { + const ADDRESS = 0x12; + } + + #[doc="Normal display mode on"] + pub command Noron { + const ADDRESS = 0x13; + } + + #[doc="Display inversion off (normal)"] + pub command Invoff { + const ADDRESS = 0x20; + } + + #[doc="Display inversion on"] + pub command Invon { + const ADDRESS = 0x21; + } + + #[doc="All pixel off (black)"] + pub command Allpoff { + const ADDRESS = 0x22; + } + + #[doc="All pixel on (white)"] + pub command Allpon { + const ADDRESS = 0x23; + } + + #[doc="Gamma curve select"] + pub command Gamset { + const ADDRESS = 0x26; + arg { + const INIT = 0b0000_0000; + gc: 0..=3, + } + } + + #[doc="Display off"] + pub command Dispoff { + const ADDRESS = 0x28; + } + + #[doc="Display on"] + pub command Dispon { + const ADDRESS = 0x29; + } + + #[doc="Tearing effect line off"] + pub command Teoff { + const ADDRESS = 0x34; + } + + #[doc="Tearing effect line on"] + pub command Teon { + const ADDRESS = 0x35; + } + + #[doc="Display data access control"] + pub command Madctl { + const ADDRESS = 0x36; + arg { + const INIT = 0b0000_0000; + bgr: 3, + ml: 4, + } + } + + #[doc="Idle mode off"] + pub command Idmoff { + const ADDRESS = 0x38; + } + + #[doc="Idle mode on"] + pub command Idmon { + const ADDRESS = 0x39; + } + + #[doc="Interface pixel format"] + pub command Colmod { + const ADDRESS = 0x3A; + arg { + const INIT = 0b0000_0000; + vipf: 4..=6, + } + } + + #[doc="Read tear line"] + pub command Gsl { + const ADDRESS = 0x45; + arg { tesl_msb } + arg { tesl_lsb } + } + + #[doc="Write display brightness value"] + pub command Wrdibv { + const ADDRESS = 0x51; + arg { dbv } + } + + #[doc="Read display brightness value"] + pub command Rddisbv { + const ADDRESS = 0x52; + arg { dbv } + } + + #[doc="Write control display value"] + pub command Wrctrld { + const ADDRESS = 0x53; + arg { + const INIT = 0b0000_0000; + bl: 2, + dd: 3, + bctrl: 5, + } + } + + #[doc="Read control display value"] + pub command Rrctrld { + const ADDRESS = 0x54; + arg { + const INIT = 0b0000_0000; + bl: 2, + dd: 3, + bctrl: 5, + } + } + + #[doc="Write CABC mode"] + pub command Wrcabc { + const ADDRESS = 0x55; + arg { + const INIT = 0b0000_0000; + cabc_md: 0..=1, + ce_md: 4..=5, + ce_on: 7, + } + } + + #[doc="Read CABC mode"] + pub command Rrcabc { + const ADDRESS = 0x56; + arg { + const INIT = 0b0000_0000; + cabc_md: 0..=1, + ce_md: 4..=5, + ce_on: 7, + } + } + + #[doc="Write CABC minimum brightness"] + pub command Wrcabcmb { + const ADDRESS = 0x5E; + arg { cmb } + } + + #[doc="Read CABC minimum brightness"] + pub command Rrcabcmb { + const ADDRESS = 0x5F; + arg { cmb } + } + + #[doc="Read automatic brightness control self-diagnostic result"] + pub command Rdabcsd { + const ADDRESS = 0x68; + arg { + const INIT = 0b0000_0000; + fund: 6, + rld: 7, + } + } + + #[doc="Read black/white low bits"] + pub command Rdbwlb { + const ADDRESS = 0x70; + arg { + const INIT = 0b0000_0000; + wy: 0..=1, + wx: 2..=3, + bky: 4..=5, + bkx: 6..=7, + } + } + + #[doc="Read Bkx"] + pub command Rdbkx { + const ADDRESS = 0x71; + arg { bkx } + } + + #[doc="Read Bky"] + pub command Rdbky { + const ADDRESS = 0x72; + arg { bky } + } + + #[doc="Read Wx"] + pub command Rdwx { + const ADDRESS = 0x73; + arg { wx } + } + + #[doc="Read Wy"] + pub command Rdwy { + const ADDRESS = 0x74; + arg { wy } + } + + #[doc="Read red/green low bits"] + pub command Rdrglb { + const ADDRESS = 0x75; + arg { + const INIT = 0b0000_0000; + gy: 0..=1, + gx: 2..=3, + ry: 4..=5, + rx: 6..=7, + } + } + + #[doc="Read Rx"] + pub command Rdrx { + const ADDRESS = 0x76; + arg { rx } + } + + #[doc="Read Ry"] + pub command Rdry { + const ADDRESS = 0x77; + arg { ry } + } + + #[doc="Read Gx"] + pub command Rdgx { + const ADDRESS = 0x78; + arg { gx } + } + + #[doc="Read Gy"] + pub command Rdgy { + const ADDRESS = 0x79; + arg { gy } + } + + #[doc="Read blue/a-color low bits"] + pub command Rdbalb { + const ADDRESS = 0x7A; + arg { + const INIT = 0b0000_0000; + ay: 0..=1, + ax: 2..=3, + by: 4..=5, + bx: 6..=7, + } + } + + #[doc="Read Bx"] + pub command Rdbx { + const ADDRESS = 0x7B; + arg { bx } + } + + #[doc="Read By"] + pub command Rdby { + const ADDRESS = 0x7C; + arg { by } + } + + #[doc="Read Ax"] + pub command Rdax { + const ADDRESS = 0x7D; + arg { ax } + } + + #[doc="Read Ay"] + pub command Rday { + const ADDRESS = 0x7E; + arg { ay } + } + + #[doc="Read tthe DDB from the provided location"] + pub command Rdddbs { + const ADDRESS = 0xA1; + arg { const INIT = 0x88; } + arg { const INIT = 0x02; } + arg { mid_msb } + arg { mid_lsb } + arg { const INIT = 0xFF; } + } + + #[doc="Continue reading the DDB from the last read location"] + pub command Rdddbc { + const ADDRESS = 0xA8; + arg { sid_msb } + arg { sid_lsb } + arg { mid_msb } + arg { mid_lsb } + arg { const INIT = 0xFF; } + } + + #[doc="Read first checksum"] + pub command Rdfcs { + const ADDRESS = 0xAA; + arg { fcs } + } + + #[doc="Read continue checksum"] + pub command Rdccs { + const ADDRESS = 0xAF; + arg { ccs } + } + + #[doc="Read ID1"] + pub command Rdid1 { + const ADDRESS = 0xDA; + arg { id1 } + } + + #[doc="Read ID2"] + pub command Rdid2 { + const ADDRESS = 0xDB; + arg { id2 } + } + + #[doc="Read ID3"] + pub command Rdid3 { + const ADDRESS = 0xDC; + arg { id3 } + } + + // System function command table 2 + + #[doc="Command2_BKx function selection"] + pub command Cn2bkxsel { + const ADDRESS = 0xFF; + arg { const INIT = 0b0111_0111; } + arg { const INIT = 0b0000_0001; } + arg { const INIT = 0b0000_0000; } + arg { const INIT = 0b0000_0000; } + arg { + const INIT = 0b0000_0000; + // The spec is ambiguous on the size of this field. + bksel: 0..=1, + cn2: 4, + } + } + + // Command2_BK0 + + #[doc="Positive voltage gamma control"] + pub command Pvgamctrl { + const ADDRESS = 0xB0; + arg { + const INIT = 0b0000_0000; + vc0p: 0..=3, + aj0p: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc4p: 0..=5, + aj1p: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc8p: 0..=5, + aj2p: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc16p: 0..=4, + } + arg { + const INIT = 0b0000_0000; + vc24p: 0..=4, + aj3p: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc52p: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc80p: 0..=5, + } + arg { + const INIT = 0b0000_0000; + vc108p: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc147p: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc175p: 0..=5, + } + arg { + const INIT = 0b0000_0000; + vc203p: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc231p: 0..=4, + aj4p: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc239p: 0..=4, + } + arg { + const INIT = 0b0000_0000; + vc247p: 0..=5, + aj5p: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc251p: 0..=5, + aj6p: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc255p: 0..=4, + aj7p: 6..=7, + } + } + + #[doc="Negative voltage gamma control"] + pub command Nvgamctrl { + const ADDRESS = 0xB1; + arg { + const INIT = 0b0000_0000; + vc0n: 0..=3, + aj0n: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc4n: 0..=5, + aj1n: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc8n: 0..=5, + aj2n: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc16n: 0..=4, + } + arg { + const INIT = 0b0000_0000; + vc24n: 0..=4, + // TODO: This field is not documented in the spec, + // but it seems like it should be here? + aj3n: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc52n: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc80n: 0..=5, + } + arg { + const INIT = 0b0000_0000; + vc108n: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc147n: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc175n: 0..=5, + } + arg { + const INIT = 0b0000_0000; + vc203n: 0..=3, + } + arg { + const INIT = 0b0000_0000; + vc231n: 0..=4, + aj4n: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc239n: 0..=4, + } + arg { + const INIT = 0b0000_0000; + vc247n: 0..=5, + aj5n: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc251n: 0..=5, + aj6n: 6..=7, + } + arg { + const INIT = 0b0000_0000; + vc255n: 0..=4, + aj7n: 6..=7, + } + } + + #[doc="Digital gamma enable"] + pub command Dgmen { + const ADDRESS = 0xB8; + arg { + const INIT = 0b0000_0000; + dgm_on: 4, + } + } + + // DGMLUTR, DGMLUTB not currently supported, because of its high number of parameters. + // It could be supported with a manual implementation of `Command`. + + #[doc="PWM CLK select"] + pub command Pwmclk { + const ADDRESS = 0xBC; + arg { + const INIT = 0b0001_1000; + pwm_clk_sel: 0..=2, + } + } + + #[doc="Display line setting"] + pub command Lneset { + const ADDRESS = 0xC0; + arg { + const INIT = 0b0000_0000; + bar: 0..=6, + lde_en: 7, + } + arg { + const INIT = 0b0000_0000; + line_delta: 0..=1, + } + } + + #[doc="Porch control"] + pub command Porctrl { + const ADDRESS = 0xC1; + arg { vbp } + arg { vfp } + } + + #[doc="Inversion selection & frame rate control"] + pub command Invsel { + const ADDRESS = 0xC2; + arg { + const INIT = 0b0011_0000; + nlinv: 0..=2, + } + arg { + const INIT = 0b0000_0000; + rtni: 0..=4, + } + } + + #[doc="RGB control"] + pub command Rgbctrl { + const ADDRESS = 0xC3; + arg { + const INIT = 0b0000_0000; + ep: 0, + dp: 1, + hsp: 2, + vsp: 3, + de_hv: 7, + } + arg { hbp_hvrgb } + arg { vbp_hvrgb } + } + + #[doc="Partial mode control"] + pub command Parctrl { + const ADDRESS = 0xC5; + arg { ptsa_lsb } + arg { + const INIT = 0b0000_0000; + ptsa_hsb: 0..=1, + } + arg { ptea_lsb } + arg { + const INIT = 0b0000_0000; + ptea_hsb: 0..=1, + } + } + + #[doc="Source direction control"] + pub command Sdir { + const ADDRESS = 0xC7; + arg { + const INIT = 0b0000_0000; + ss: 2, + } + } + + #[doc="Pseudo-dot inversion driving setting"] + pub command Pdotset { + const ADDRESS = 0xC8; + arg { + const INIT = 0b0000_0000; + z_gltor: 5, + z_sdm1s: 6, + z_en: 7, + } + } + + #[doc="Color control"] + pub command Colctrl { + const ADDRESS = 0xCD; + arg { + const INIT = 0b0000_0000; + epf: 0..=2, + mdt: 3, + inv_led_on: 4, + inv_led_pwm: 5, + } + } + + #[doc="Spread spectrum control"] + pub command Ssctrl { + const ADDRESS = 0xCE; + arg { + const INIT = 0b0000_0100; + dssrg: 4..=5, + dsse: 7, + } + } + + #[doc="Sunlight readable enhancement"] + pub command Sectrl { + const ADDRESS = 0xE0; + arg { + const INIT = 0b0000_0000; + sre_alpha: 0..=3, + sre: 4, + } + } + + #[doc="Noise reduce control"] + pub command Nrctrl { + const ADDRESS = 0xE1; + arg { + const INIT = 0b0000_0000; + nr_md: 0..=1, + nre: 4, + } + } + + #[doc="Sharpness control (originally has a conflicting name SECTRL)"] + pub command Shctrl { + const ADDRESS = 0xE2; + arg { + const INIT = 0b0000_0000; + y_gain: 0..=3, + se: 4, + } + } + + #[doc="Color calibration control"] + pub command Ccctrl { + const ADDRESS = 0xE3; + arg { + const INIT = 0b0000_0000; + cce: 0, + } + } + + #[doc="Skin tone preservation control"] + pub command Skctrl { + const ADDRESS = 0xE4; + arg { + const INIT = 0b0000_0000; + skin_ce_mid: 0..=1, + ske: 4, + } + } + + #[doc="NVM address setting enable"] + pub command Nvmsete { + const ADDRESS = 0xEA; + arg { + const INIT = 0b0000_0000; + aden: 0, + } + } + + #[doc="CABC control"] + pub command Cabcctrl { + const ADDRESS = 0xEE; + arg { + const INIT = 0b0000_0000; + led_en: 0, + ledpwr_sel: 4, + } + } + + // Command2_BK1 + + #[doc="Vop amplitude setting"] + pub command Vrhs { + const ADDRESS = 0xB0; + arg { vrha } + } + + #[doc="VCOM amplitude setting"] + pub command Vcoms { + const ADDRESS = 0xB1; + arg { vcom } + } + + #[doc="VGH voltage setting"] + pub command Vghss { + const ADDRESS = 0xB2; + arg { + const INIT = 0b0000_0000; + vghss: 0..=3, + } + } + + #[doc="TEST command setting"] + pub command Tescmd { + const ADDRESS = 0xB3; + arg { const INIT = 0b1000_0000; } + } + + #[doc="VGL voltage setting"] + pub command Vgls { + const ADDRESS = 0xB5; + arg { + const INIT = 0b0100_0000; + vgls: 0..=3, + } + } + + #[doc="VRH_DV voltage setting"] + pub command Vrhdv { + const ADDRESS = 0xB6; + arg { + const INIT = 0b0000_0000; + vrh_dv: 0..=6, + } + } + + #[doc="Power control 1"] + pub command Pwctrl1 { + const ADDRESS = 0xB7; + arg { + const INIT = 0b0000_0000; + apos: 0..=1, + apis: 2..=3, + ap: 6..=7, + } + } + + #[doc="Power control 2"] + pub command Pwctrl2 { + const ADDRESS = 0xB8; + arg { + const INIT = 0b0000_0000; + avcl: 0..=1, + avdd: 4..=5, + } + } + + #[doc="Power control 3"] + pub command Pwctrl3 { + const ADDRESS = 0xB9; + arg { + const INIT = 0b0000_0000; + svno_pum: 0..=1, + svpo_pum: 4..=5, + } + } + + #[doc="Power pumping clk selection 1"] + pub command Pclks1 { + const ADDRESS = 0xBA; + arg { + const INIT = 0b0000_0000; + stp1cks: 0..=1, + stp4cks: 4..=5, + } + } + + #[doc="Power pumping clk selection 2"] + pub command Pclks2 { + const ADDRESS = 0xBB; + arg { + const INIT = 0b0000_0000; + sbstcks: 0..=1, + } + } + + #[doc="Power pumping clk selection 3"] + pub command Pclks3 { + const ADDRESS = 0xBC; + arg { + const INIT = 0b0000_0000; + stp2scks: 0..=1, + stp2pcks: 0..=1, + stp3cks: 4..=5, + } + } + + #[doc="Source pre_drive timing set1 (also known as SPD1)"] + pub command Pdr1 { + const ADDRESS = 0xC1; + arg { + const INIT = 0b0111_0000; + t2d: 0..=3, + } + } + + #[doc="Source pre_drive timing set2 (also known as SPD2)"] + pub command Pdr2 { + const ADDRESS = 0xC2; + arg { + const INIT = 0b0111_0000; + t3d: 0..=3, + } + } + + #[doc="MIPI setting 1"] + pub command Mipiset1 { + const ADDRESS = 0xD0; + arg { + const INIT = 0b1000_0000; + err_sel: 0..=1, + eotp_en: 3, + } + } + + #[doc="MIPI setting 2"] + pub command Mipiset2 { + const ADDRESS = 0xD1; + arg { + const INIT = 0b0000_0000; + mpc_tlpx0: 0..=3, + mpc_tlpx1: 4..=7, + } + arg { + const INIT = 0b0000_0000; + mpc_tlpx2: 0..=3, + mpc_txtimeadj: 4..=7, + } + arg { + const INIT = 0b0000_0000; + mpc_ttago: 0..=3, + } + arg { + const INIT = 0b0000_0000; + mpc_ttaget: 0..=3, + } + } + + #[doc="MIPI setting 3"] + pub command Mipiset3 { + const ADDRESS = 0xD2; + arg { + const INIT = 0b0011_0000; + phy_ttasure: 0..=3, + } + } + + #[doc="MIPI setting 4"] + pub command Mipiset4 { + const ADDRESS = 0xD3; + arg { + const INIT = 0b0000_0000; + phy_csk: 0..=2, + } + arg { + const INIT = 0b0000_0000; + phy_dsk0: 0..=2, + phy_dsk1: 4..=6, + } + } + + // Command2_BK3 + + #[doc="NVM enable"] + pub command Nvmen { + const ADDRESS = 0xC8; + arg { const INIT = 0b0111_0111; } + arg { const INIT = 0b0000_0001; } + arg { const INIT = 0b1110_1110; } + arg { const INIT = 0b0000_0100; } + } + + #[doc="NVM manual control setting"] + pub command Nvmset { + const ADDRESS = 0xCA; + arg { + const INIT = 0b0000_0000; + pa_msb: 0..=1, + } + arg { pa_lsb } + arg { pdin } + } + + #[doc="NVM program active"] + pub command Promact { + const ADDRESS = 0xCC; + arg { const INIT = 0b1010_1010; } + } +} pub struct St7701s {} impl St7701s { pub fn new() -> Self { + let _ = CmdNop::default(); + let x = CmdGsl( + CmdGslArg0::new().with_tesl_msb(1), + CmdGslArg1::new().with_tesl_lsb(0), + ); + x.address(); + x.args_iter(); todo!() } }