From c3f58178fc432812020ba8c3db1424431f7345c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Hlusi=C4=8Dka?= Date: Sat, 28 Feb 2026 03:15:04 +0100 Subject: [PATCH] Code cleanup --- firmware/Cargo.lock | 1 - firmware/acid-firmware/Cargo.toml | 2 +- firmware/acid-firmware/src/logging.rs | 1 + .../src/peripherals/st7701s/commands.rs | 1220 ++++++++++++++++ .../st7701s/{lcd.rs => init_sequence.rs} | 114 +- .../src/peripherals/st7701s/mod.rs | 1235 +---------------- .../src/peripherals/st7701s/spi.rs | 102 ++ 7 files changed, 1334 insertions(+), 1341 deletions(-) create mode 100644 firmware/acid-firmware/src/peripherals/st7701s/commands.rs rename firmware/acid-firmware/src/peripherals/st7701s/{lcd.rs => init_sequence.rs} (82%) create mode 100644 firmware/acid-firmware/src/peripherals/st7701s/spi.rs diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 7efafc2..7c2c12b 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -2151,7 +2151,6 @@ dependencies = [ "document-features", "embassy-sync 0.7.2", "esp-hal", - "log", "ouroboros", ] diff --git a/firmware/acid-firmware/Cargo.toml b/firmware/acid-firmware/Cargo.toml index 426252c..02e1447 100644 --- a/firmware/acid-firmware/Cargo.toml +++ b/firmware/acid-firmware/Cargo.toml @@ -89,7 +89,7 @@ esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } indoc = "2.0.7" ouroboros = "0.18.5" -esp-hal-bounce-buffers = { git = "https://forgejo.limeth.cz/limeth/esp-hal-bounce-buffers", rev = "8d3763a190368f476aed6d98777264c959bfdc2d", features = ["esp32s3", "log"] } +esp-hal-bounce-buffers = { git = "https://forgejo.limeth.cz/limeth/esp-hal-bounce-buffers", rev = "8d3763a190368f476aed6d98777264c959bfdc2d", features = ["esp32s3"] } # A fork of slint with patches for `allocator_api` support. # Don't forget to change `slint-build` in build dependencies, if this is changed. diff --git a/firmware/acid-firmware/src/logging.rs b/firmware/acid-firmware/src/logging.rs index 028816b..bba4387 100644 --- a/firmware/acid-firmware/src/logging.rs +++ b/firmware/acid-firmware/src/logging.rs @@ -2,6 +2,7 @@ use core::fmt::Arguments; use log::LevelFilter; +// TODO: Replace with `log`'s `STATIC_MAX_LEVEL` set via crate features. pub const LOG_LEVEL_FILTER: LevelFilter = { if let Some(string) = option_env!("ESP_LOG") { if string.eq_ignore_ascii_case("OFF") { diff --git a/firmware/acid-firmware/src/peripherals/st7701s/commands.rs b/firmware/acid-firmware/src/peripherals/st7701s/commands.rs new file mode 100644 index 0000000..4e3a5ee --- /dev/null +++ b/firmware/acid-firmware/src/peripherals/st7701s/commands.rs @@ -0,0 +1,1220 @@ +use paste::paste; +// use tinyvec::ArrayVec; + +pub trait Command { + fn address(&self) -> u8; + fn args_iter(&'_ self) -> core::slice::Iter<'_, u8>; +} + +// pub struct ArrayCommand { +// pub address: u8, +// pub args: ArrayVec<[u8; N]>, +// } + +// impl const ArrayCommand { +// const fn from(command: impl Command) -> Self { +// Self { +// address: command.address(), +// args: command.args_iter().copied().collect(), +// } +// } +// } + +// impl Command for ArrayCommand { +// fn address(&self) -> u8 { +// self.address +// } + +// fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> { +// self.args.iter() +// } +// } + +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; } + } +} diff --git a/firmware/acid-firmware/src/peripherals/st7701s/lcd.rs b/firmware/acid-firmware/src/peripherals/st7701s/init_sequence.rs similarity index 82% rename from firmware/acid-firmware/src/peripherals/st7701s/lcd.rs rename to firmware/acid-firmware/src/peripherals/st7701s/init_sequence.rs index 03969db..9cef7d5 100644 --- a/firmware/acid-firmware/src/peripherals/st7701s/lcd.rs +++ b/firmware/acid-firmware/src/peripherals/st7701s/init_sequence.rs @@ -1,125 +1,13 @@ use alloc::{boxed::Box, vec, vec::Vec}; -use core::iter::once; -use embassy_time::{Duration, Timer}; -use esp_hal::gpio::{Flex, Level, Output}; use lazy_static::lazy_static; -use crate::peripherals::st7701s::Command; - -async fn spi_delay() { - Timer::after(Duration::from_micros(1)).await; -} - -async fn spi_dummy_bit(sck: &mut Output<'_>) { - sck.set_low(); - spi_delay().await; - sck.set_high(); - spi_delay().await; -} - -async fn spi_write_bit(bit: bool, mosi: &mut Flex<'_>, sck: &mut Output<'_>) { - mosi.set_level(if bit { Level::High } else { Level::Low }); - sck.set_low(); - spi_delay().await; - sck.set_high(); - spi_delay().await; -} - -async fn spi_read_bit(mosi: &mut Flex<'_>, sck: &mut Output<'_>) -> bool { - sck.set_low(); - spi_delay().await; - sck.set_high(); - spi_delay().await; - mosi.is_high() -} - -async fn spi_write_bits( - bits: impl Iterator, - mosi: &mut Flex<'_>, - sck: &mut Output<'_>, -) { - for bit in bits { - spi_write_bit(bit, mosi, sck).await; - } -} - -async fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex<'_>, sck: &mut Output<'_>) { - assert!(sck.is_set_high()); - spi_write_bits( - once(is_param).chain((0..8).map(|i| (data >> i) & 1 != 0).rev()), - mosi, - sck, - ) - .await; -} - -pub async 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).await; - - for arg in args { - spi_write_word(true, arg, mosi, sck).await; - } - - cs.set_high(); -} - -#[expect(unused)] -pub async fn spi_read( - command: u8, - dummy_cycle: bool, - mosi: &mut Flex<'_>, - sck: &mut Output<'_>, - cs: &mut Output<'_>, - output_buffer: &mut [u8], -) { - output_buffer.fill(0); - - cs.set_low(); - spi_write_word(false, command, mosi, sck).await; - - mosi.set_output_enable(false); - mosi.set_input_enable(true); - - if dummy_cycle { - spi_dummy_bit(sck).await; - } - - for output_byte in output_buffer { - for i in (0..8).rev() { - if spi_read_bit(mosi, sck).await { - *output_byte |= 1 << i; - } - } - } - - mosi.set_input_enable(false); - mosi.set_output_enable(true); - - cs.set_high(); -} - -use crate::peripherals::st7701s::*; +use crate::peripherals::st7701s::commands::*; // struct InitSequenceAction { // command: ArrayCommand<8>, // sleep: u64, // } -// pub const INIT_SEQUENCE: [InitSequenceAction; _] = [CmdCn2bkxsel( -// CmdCn2bkxselArg0::new(), -// CmdCn2bkxselArg1::new(), -// CmdCn2bkxselArg2::new(), -// CmdCn2bkxselArg3::new(), -// CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true), -// )]; - lazy_static! { pub static ref INIT_SEQUENCE_COMMANDS: Vec<(Vec>, u64)> = vec![ (vec![ diff --git a/firmware/acid-firmware/src/peripherals/st7701s/mod.rs b/firmware/acid-firmware/src/peripherals/st7701s/mod.rs index 3af869d..12a46f3 100644 --- a/firmware/acid-firmware/src/peripherals/st7701s/mod.rs +++ b/firmware/acid-firmware/src/peripherals/st7701s/mod.rs @@ -9,1231 +9,14 @@ use esp_hal::{ ledc::{self, LowSpeed, channel::ChannelIFace, timer::TimerIFace}, time::Rate, }; -use lcd::spi_write; use log::debug; use ouroboros::self_referencing; -use paste::paste; -// use tinyvec::ArrayVec; -mod lcd; +use crate::peripherals::st7701s::commands::Command; -pub trait Command { - fn address(&self) -> u8; - fn args_iter(&'_ self) -> core::slice::Iter<'_, u8>; -} - -// pub struct ArrayCommand { -// pub address: u8, -// pub args: ArrayVec<[u8; N]>, -// } - -// impl const ArrayCommand { -// const fn from(command: impl Command) -> Self { -// Self { -// address: command.address(), -// args: command.args_iter().copied().collect(), -// } -// } -// } - -// impl Command for ArrayCommand { -// fn address(&self) -> u8 { -// self.address -// } - -// fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> { -// self.args.iter() -// } -// } - -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; } - } -} +mod commands; +mod init_sequence; +mod spi; #[self_referencing] pub struct Backlight<'d> { @@ -1260,9 +43,9 @@ impl<'d> St7701sController<'d> { pub async fn send_init_sequence(&mut self) { debug!("Writing ST7701S init sequence."); - for (subsequence, delay_ms) in &*lcd::INIT_SEQUENCE_COMMANDS { + for (subsequence, delay_ms) in &*init_sequence::INIT_SEQUENCE_COMMANDS { for command in subsequence { - spi_write( + spi::spi_write( command.address(), command.args_iter().copied(), &mut self.mosi, @@ -1277,7 +60,7 @@ impl<'d> St7701sController<'d> { } pub async fn send(&mut self, command: impl Command) { - spi_write( + spi::spi_write( command.address(), command.args_iter().copied(), &mut self.mosi, @@ -1290,12 +73,12 @@ impl<'d> St7701sController<'d> { /// Puts the display into sleep mode and disables the backlight. pub async fn sleep_on(&mut self) { self.backlight.set_duty(0).unwrap(); - self.send(CmdSlpin()).await; + self.send(commands::CmdSlpin()).await; } /// Brings the display out of sleep mode and enables the backlight. pub async fn sleep_off(&mut self) { - self.send(CmdSlpout()).await; + self.send(commands::CmdSlpout()).await; self.backlight.set_duty(100).unwrap(); } } diff --git a/firmware/acid-firmware/src/peripherals/st7701s/spi.rs b/firmware/acid-firmware/src/peripherals/st7701s/spi.rs new file mode 100644 index 0000000..36c469b --- /dev/null +++ b/firmware/acid-firmware/src/peripherals/st7701s/spi.rs @@ -0,0 +1,102 @@ +use core::iter::once; +use embassy_time::{Duration, Timer}; +use esp_hal::gpio::{Flex, Level, Output}; + +async fn spi_delay() { + Timer::after(Duration::from_micros(1)).await; +} + +async fn spi_dummy_bit(sck: &mut Output<'_>) { + sck.set_low(); + spi_delay().await; + sck.set_high(); + spi_delay().await; +} + +async fn spi_write_bit(bit: bool, mosi: &mut Flex<'_>, sck: &mut Output<'_>) { + mosi.set_level(if bit { Level::High } else { Level::Low }); + sck.set_low(); + spi_delay().await; + sck.set_high(); + spi_delay().await; +} + +async fn spi_read_bit(mosi: &mut Flex<'_>, sck: &mut Output<'_>) -> bool { + sck.set_low(); + spi_delay().await; + sck.set_high(); + spi_delay().await; + mosi.is_high() +} + +async fn spi_write_bits( + bits: impl Iterator, + mosi: &mut Flex<'_>, + sck: &mut Output<'_>, +) { + for bit in bits { + spi_write_bit(bit, mosi, sck).await; + } +} + +async fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex<'_>, sck: &mut Output<'_>) { + assert!(sck.is_set_high()); + spi_write_bits( + once(is_param).chain((0..8).map(|i| (data >> i) & 1 != 0).rev()), + mosi, + sck, + ) + .await; +} + +pub async 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).await; + + for arg in args { + spi_write_word(true, arg, mosi, sck).await; + } + + cs.set_high(); +} + +#[expect(unused)] +pub async fn spi_read( + command: u8, + dummy_cycle: bool, + mosi: &mut Flex<'_>, + sck: &mut Output<'_>, + cs: &mut Output<'_>, + output_buffer: &mut [u8], +) { + output_buffer.fill(0); + + cs.set_low(); + spi_write_word(false, command, mosi, sck).await; + + mosi.set_output_enable(false); + mosi.set_input_enable(true); + + if dummy_cycle { + spi_dummy_bit(sck).await; + } + + for output_byte in output_buffer { + for i in (0..8).rev() { + if spi_read_bit(mosi, sck).await { + *output_byte |= 1 << i; + } + } + } + + mosi.set_input_enable(false); + mosi.set_output_enable(true); + + cs.set_high(); +}