Store display init sequence in read-only memory

This commit is contained in:
Jakub Hlusička 2026-02-28 04:25:32 +01:00
parent c3f58178fc
commit 8a99905a84
4 changed files with 457 additions and 415 deletions

View file

@ -84,7 +84,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] }
# serde_with = { version = "3.16", default-features = false, features = ["alloc", "macros"] } # serde_with = { version = "3.16", default-features = false, features = ["alloc", "macros"] }
serde_bytes = { version = "0.11.19", default-features = false, features = ["alloc"] } serde_bytes = { version = "0.11.19", default-features = false, features = ["alloc"] }
chrono = { version = "0.4.43", default-features = false, features = ["alloc", "serde"] } # TODO: defmt chrono = { version = "0.4.43", default-features = false, features = ["alloc", "serde"] } # TODO: defmt
tinyvec = { version = "1.10.0", default-features = false, features = ["alloc"] } tinyvec = { version = "1.10.0", default-features = false, features = ["alloc", "latest_stable_rust"] }
esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] } esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] }
hex = { version = "0.4.3", default-features = false, features = ["alloc"] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
indoc = "2.0.7" indoc = "2.0.7"

View file

@ -1,40 +1,41 @@
use paste::paste; use paste::paste;
// use tinyvec::ArrayVec; use tinyvec::ArrayVec;
pub trait Command { pub trait Command {
fn address(&self) -> u8; fn address(&self) -> u8;
fn args_iter(&'_ self) -> core::slice::Iter<'_, u8>; fn args_iter(&'_ self) -> core::slice::Iter<'_, u8>;
} }
// pub struct ArrayCommand<const N: usize> { pub struct ArrayCommand<const N: usize> {
// pub address: u8, pub address: u8,
// pub args: ArrayVec<[u8; N]>, pub args: ArrayVec<[u8; N]>,
// } }
// impl<const N: usize> const ArrayCommand<N> { impl<const N: usize> Command for ArrayCommand<N> {
// const fn from(command: impl Command) -> Self { fn address(&self) -> u8 {
// Self { self.address
// address: command.address(), }
// args: command.args_iter().copied().collect(),
// }
// }
// }
// impl<const N: usize> Command for ArrayCommand<N> { fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> {
// fn address(&self) -> u8 { self.args.iter()
// self.address }
// } }
// fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> {
// self.args.iter()
// }
// }
pub struct CustomCommand { pub struct CustomCommand {
pub address: u8, pub address: u8,
pub args: &'static [u8], pub args: &'static [u8],
} }
impl CustomCommand {
pub const fn address_const(&self) -> u8 {
self.address
}
pub const fn args_slice(&self) -> &[u8] {
self.args
}
}
impl Command for CustomCommand { impl Command for CustomCommand {
fn address(&self) -> u8 { fn address(&self) -> u8 {
self.address self.address
@ -158,6 +159,21 @@ macro_rules! define_commands {
$(#[$docs])* $(#[$docs])*
$visibility struct [< Cmd $command >]($(pub [< Cmd $command Arg ${index()} >], $(${ignore($body)})*)*); $visibility struct [< Cmd $command >]($(pub [< Cmd $command Arg ${index()} >], $(${ignore($body)})*)*);
impl [< Cmd $command >] {
pub const fn address_const(&self) -> u8 {
$address
}
pub const fn args_slice(&self) -> &[u8] {
unsafe {
core::slice::from_raw_parts(
self as *const [< Cmd $command >] as *const u8,
0 $(+ 1 $(${ignore($body)})*)*,
)
}
}
}
impl Command for [< Cmd $command >] { impl Command for [< Cmd $command >] {
fn address(&self) -> u8 { fn address(&self) -> u8 {
$address $address
@ -173,12 +189,7 @@ macro_rules! define_commands {
type IntoIter = core::slice::Iter<'a, u8>; type IntoIter = core::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
unsafe { self.args_slice().iter()
core::slice::from_raw_parts(
self as *const [< Cmd $command >] as *const u8,
0 $(+ 1 $(${ignore($body)})*)*,
)
}.iter()
} }
} }
)* )*

View file

@ -1,51 +1,83 @@
use alloc::{boxed::Box, vec, vec::Vec}; use tinyvec::ArrayVec;
use lazy_static::lazy_static;
use crate::peripherals::st7701s::commands::*; use crate::peripherals::st7701s::commands::*;
// struct InitSequenceAction { pub enum InitSequenceAction {
// command: ArrayCommand<8>, // TODO: Each command takes up 17 bytes. Consider storing compressed.
// sleep: u64, Command(ArrayCommand<16>),
// } SleepMs(u64),
}
lazy_static! { macro_rules! make_init_action {
pub static ref INIT_SEQUENCE_COMMANDS: Vec<(Vec<Box<dyn Command + Send + Sync>>, u64)> = vec![ (command: $command:expr$(,)?) => {
(vec![ InitSequenceAction::Command({
Box::new(CmdCn2bkxsel( let command = $command;
let args_slice = command.args_slice();
let mut args_array = [0; _];
let mut args_len = 0;
while args_len < args_slice.len() {
args_array[args_len] = args_slice[args_len];
args_len += 1;
}
ArrayCommand {
address: command.address_const(),
args: match ArrayVec::try_from_array_len(args_array, args_len) {
Ok(args) => args,
Err(_) => panic!("too many command args"),
},
}
})
};
(sleep_ms: $expr:expr) => {
InitSequenceAction::SleepMs($expr)
};
}
macro_rules! make_init_sequence {
($({ $($tt:tt)* }),*$(,)?) => {
[
$( make_init_action!($($tt)*) ),*
]
};
}
pub const INIT_SEQUENCE_COMMANDS: [InitSequenceAction; 46] = make_init_sequence! [
{ command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(), CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(), CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(), CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(), CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true), CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
)), ) },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xEF, address: 0xEF,
args: &[0x08], args: &[0x08],
}), } },
Box::new(CmdCn2bkxsel( { command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(), CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(), CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(), CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(), CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x0).with_cn2(true), CmdCn2bkxselArg4::new().with_bksel(0x0).with_cn2(true),
)), ) },
Box::new(CmdLneset( { command: CmdLneset(
CmdLnesetArg0::new().with_bar(119).with_lde_en(false), CmdLnesetArg0::new().with_bar(119).with_lde_en(false),
CmdLnesetArg1::new().with_line_delta(0), CmdLnesetArg1::new().with_line_delta(0),
)), ) },
Box::new(CmdPorctrl( { command: CmdPorctrl(
CmdPorctrlArg0::new().with_vbp(17), CmdPorctrlArg0::new().with_vbp(17),
CmdPorctrlArg1::new().with_vfp(12), CmdPorctrlArg1::new().with_vfp(12),
)), ) },
Box::new(CmdInvsel( { command: CmdInvsel(
CmdInvselArg0::new().with_nlinv(0b111), CmdInvselArg0::new().with_nlinv(0b111),
CmdInvselArg1::new().with_rtni(2), CmdInvselArg1::new().with_rtni(2),
)), ) },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xCC, address: 0xCC,
args: &[0x30], args: &[0x30],
}), } },
Box::new(CmdPvgamctrl( { command: CmdPvgamctrl(
CmdPvgamctrlArg0::new().with_vc0p(6).with_aj0p(0), CmdPvgamctrlArg0::new().with_vc0p(6).with_aj0p(0),
CmdPvgamctrlArg1::new().with_vc4p(15).with_aj1p(3), CmdPvgamctrlArg1::new().with_vc4p(15).with_aj1p(3),
CmdPvgamctrlArg2::new().with_vc8p(14).with_aj2p(0), CmdPvgamctrlArg2::new().with_vc8p(14).with_aj2p(0),
@ -62,8 +94,8 @@ lazy_static! {
CmdPvgamctrlArg13::new().with_vc247p(37).with_aj5p(0), CmdPvgamctrlArg13::new().with_vc247p(37).with_aj5p(0),
CmdPvgamctrlArg14::new().with_vc251p(54).with_aj6p(0), CmdPvgamctrlArg14::new().with_vc251p(54).with_aj6p(0),
CmdPvgamctrlArg15::new().with_vc255p(30).with_aj7p(0), CmdPvgamctrlArg15::new().with_vc255p(30).with_aj7p(0),
)), ) },
Box::new(CmdNvgamctrl( { command: CmdNvgamctrl(
CmdNvgamctrlArg0::new().with_vc0n(12).with_aj0n(0), CmdNvgamctrlArg0::new().with_vc0n(12).with_aj0n(0),
CmdNvgamctrlArg1::new().with_vc4n(14).with_aj1n(3), CmdNvgamctrlArg1::new().with_vc4n(14).with_aj1n(3),
CmdNvgamctrlArg2::new().with_vc8n(18).with_aj2n(0), CmdNvgamctrlArg2::new().with_vc8n(18).with_aj2n(0),
@ -80,59 +112,59 @@ lazy_static! {
CmdNvgamctrlArg13::new().with_vc247n(48).with_aj5n(0), CmdNvgamctrlArg13::new().with_vc247n(48).with_aj5n(0),
CmdNvgamctrlArg14::new().with_vc251n(47).with_aj6n(0), CmdNvgamctrlArg14::new().with_vc251n(47).with_aj6n(0),
CmdNvgamctrlArg15::new().with_vc255n(31).with_aj7n(0), CmdNvgamctrlArg15::new().with_vc255n(31).with_aj7n(0),
)), ) },
Box::new(CmdCn2bkxsel( { command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(), CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(), CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(), CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(), CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x1).with_cn2(true), CmdCn2bkxselArg4::new().with_bksel(0x1).with_cn2(true),
)), ) },
Box::new(CmdVrhs( { command: CmdVrhs(
CmdVrhsArg0::new().with_vrha(115), CmdVrhsArg0::new().with_vrha(115),
)), ) },
Box::new(CmdVcoms( { command: CmdVcoms(
CmdVcomsArg0::new().with_vcom(124), CmdVcomsArg0::new().with_vcom(124),
)), ) },
Box::new(CmdVghss( { command: CmdVghss(
// The first bit is set to 1 in the original init code, but not here. // The first bit is set to 1 in the original init code, but not here.
CmdVghssArg0::new().with_vghss(0x3), // 13 V CmdVghssArg0::new().with_vghss(0x3), // 13 V
)), ) },
Box::new(CmdTescmd( { command: CmdTescmd(
CmdTescmdArg0::new(), CmdTescmdArg0::new(),
)), ) },
Box::new(CmdVgls( { command: CmdVgls(
CmdVglsArg0::new().with_vgls(0x9) // -10.17 V CmdVglsArg0::new().with_vgls(0x9) // -10.17 V
)), ) },
Box::new(CmdPwctrl1( { command: CmdPwctrl1(
CmdPwctrl1Arg0::new() CmdPwctrl1Arg0::new()
.with_apos(0x3) // Max .with_apos(0x3) // Max
.with_apis(0x1) // Min .with_apis(0x1) // Min
.with_ap(0x2) // Middle .with_ap(0x2) // Middle
)), ) },
Box::new(CmdPwctrl2( { command: CmdPwctrl2(
CmdPwctrl2Arg0::new() CmdPwctrl2Arg0::new()
.with_avcl(0x3) // -5 V .with_avcl(0x3) // -5 V
.with_avdd(0x3) // 6.8 V .with_avdd(0x3) // 6.8 V
)), ) },
Box::new(CmdPwctrl3( { command: CmdPwctrl3(
CmdPwctrl3Arg0::new() CmdPwctrl3Arg0::new()
.with_svno_pum(0) // Cell setting 4 .with_svno_pum(0) // Cell setting 4
.with_svpo_pum(0x1) // Cell setting 5 .with_svpo_pum(0x1) // Cell setting 5
)), ) },
Box::new(CmdPclks2( { command: CmdPclks2(
CmdPclks2Arg0::new().with_sbstcks(0x3) CmdPclks2Arg0::new().with_sbstcks(0x3)
)), ) },
Box::new(CmdPdr1( { command: CmdPdr1(
CmdPdr1Arg0::new().with_t2d(8) // 1.6 us CmdPdr1Arg0::new().with_t2d(8) // 1.6 us
)), ) },
Box::new(CmdPdr2( { command: CmdPdr2(
CmdPdr2Arg0::new().with_t3d(8) // 6.4 us CmdPdr2Arg0::new().with_t3d(8) // 6.4 us
)), ) },
Box::new(CmdMipiset1( { command: CmdMipiset1(
CmdMipiset1Arg0::new().with_err_sel(0).with_eotp_en(true), CmdMipiset1Arg0::new().with_err_sel(0).with_eotp_en(true),
)), ) },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE0, address: 0xE0,
args: &[ args: &[
0x00, 0x00,
@ -142,8 +174,8 @@ lazy_static! {
0x00, 0x00,
0x0C, 0x0C,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE1, address: 0xE1,
args: &[ args: &[
0x05, 0x05,
@ -158,8 +190,8 @@ lazy_static! {
0x44, 0x44,
0x44, 0x44,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE2, address: 0xE2,
args: &[ args: &[
0x00, 0x00,
@ -175,8 +207,8 @@ lazy_static! {
0x02, 0x02,
0x00, 0x00,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE3, address: 0xE3,
args: &[ args: &[
0x00, 0x00,
@ -184,15 +216,15 @@ lazy_static! {
0x33, 0x33,
0x33, 0x33,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE4, address: 0xE4,
args: &[ args: &[
0x44, 0x44,
0x44, 0x44,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE5, address: 0xE5,
args: &[ args: &[
0x0D, 0x0D,
@ -212,8 +244,8 @@ lazy_static! {
0x28, 0x28,
0x8C, 0x8C,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE6, address: 0xE6,
args: &[ args: &[
0x00, 0x00,
@ -221,15 +253,15 @@ lazy_static! {
0x33, 0x33,
0x33, 0x33,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE7, address: 0xE7,
args: &[ args: &[
0x44, 0x44,
0x44, 0x44,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE8, address: 0xE8,
args: &[ args: &[
0x0E, 0x0E,
@ -249,8 +281,8 @@ lazy_static! {
0x28, 0x28,
0x8C, 0x8C,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xEB, address: 0xEB,
args: &[ args: &[
0x00, 0x00,
@ -260,8 +292,8 @@ lazy_static! {
0x44, 0x44,
0x00, 0x00,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xED, address: 0xED,
args: &[ args: &[
0xF3, 0xF3,
@ -281,8 +313,8 @@ lazy_static! {
0x1C, 0x1C,
0x3F, 0x3F,
] ]
}), } },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xEF, address: 0xEF,
args: &[ args: &[
0x10, 0x10,
@ -292,68 +324,65 @@ lazy_static! {
0x3F, 0x3F,
0x1F, 0x1F,
] ]
}), } },
Box::new(CmdCn2bkxsel( { command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(), CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(), CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(), CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(), CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true), CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
)), ) },
Box::new(CustomCommand { { command: CustomCommand {
address: 0xE8, address: 0xE8,
args: &[ args: &[
0x00, 0x00,
0x0E, 0x0E,
], ],
}), } },
Box::new(CmdSlpout()), { command: CmdSlpout() },
], 120), { sleep_ms: 120 },
(vec![ { command: CustomCommand {
Box::new(CustomCommand {
address: 0xE8, address: 0xE8,
args: &[ args: &[
0x00, 0x00,
0x0C, 0x0C,
], ],
}), } },
], 10), { sleep_ms: 10 },
(vec![ { command: CustomCommand {
Box::new(CustomCommand {
address: 0xE8, address: 0xE8,
args: &[ args: &[
0x40, 0x40,
0x00, 0x00,
], ],
}), } },
Box::new(CmdCn2bkxsel( { command: CmdCn2bkxsel(
CmdCn2bkxselArg0::new(), CmdCn2bkxselArg0::new(),
CmdCn2bkxselArg1::new(), CmdCn2bkxselArg1::new(),
CmdCn2bkxselArg2::new(), CmdCn2bkxselArg2::new(),
CmdCn2bkxselArg3::new(), CmdCn2bkxselArg3::new(),
CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false), CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
)), ) },
Box::new(CmdMadctl( { command: CmdMadctl(
CmdMadctlArg0::new().with_bgr(false).with_ml(false), CmdMadctlArg0::new().with_bgr(false).with_ml(false),
)), ) },
Box::new(CmdColmod( { command: CmdColmod(
CmdColmodArg0::new().with_vipf(6) // 18-bit pixel CmdColmodArg0::new().with_vipf(6) // 18-bit pixel
)), ) },
Box::new(CmdDispon()), { command: CmdDispon() },
], 20), { sleep_ms: 20 },
// (vec![ // { command: CmdCn2bkxsel(
// Box::new(CmdCn2bkxsel(
// CmdCn2bkxselArg0::new(), // CmdCn2bkxselArg0::new(),
// CmdCn2bkxselArg1::new(), // CmdCn2bkxselArg1::new(),
// CmdCn2bkxselArg2::new(), // CmdCn2bkxselArg2::new(),
// CmdCn2bkxselArg3::new(), // CmdCn2bkxselArg3::new(),
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(true), // CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(true),
// )), // ) },
// Box::new(CmdPorctrl( // { command: CmdPorctrl(
// CmdPorctrlArg0::new().with_vbp(0xFF), // CmdPorctrlArg0::new().with_vbp(0xFF),
// CmdPorctrlArg1::new().with_vfp(0x0), // CmdPorctrlArg1::new().with_vfp(0x0),
// )), // ) },
// // Box::new(CmdRgbctrl( // // { command: CmdRgbctrl(
// // CmdRgbctrlArg0::new() // // CmdRgbctrlArg0::new()
// // .with_ep(false) // // .with_ep(false)
// // .with_dp(false) // // .with_dp(false)
@ -364,14 +393,13 @@ lazy_static! {
// // .with_hbp_hvrgb(16), // // .with_hbp_hvrgb(16),
// // CmdRgbctrlArg2::new() // // CmdRgbctrlArg2::new()
// // .with_vbp_hvrgb(8), // // .with_vbp_hvrgb(8),
// // )), // // ) },
// Box::new(CmdCn2bkxsel( // { command: CmdCn2bkxsel(
// CmdCn2bkxselArg0::new(), // CmdCn2bkxselArg0::new(),
// CmdCn2bkxselArg1::new(), // CmdCn2bkxselArg1::new(),
// CmdCn2bkxselArg2::new(), // CmdCn2bkxselArg2::new(),
// CmdCn2bkxselArg3::new(), // CmdCn2bkxselArg3::new(),
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false), // CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
// )), // ) },
// ], 20), // { sleep_ms: 20 },
]; ];
}

View file

@ -12,7 +12,7 @@ use esp_hal::{
use log::debug; use log::debug;
use ouroboros::self_referencing; use ouroboros::self_referencing;
use crate::peripherals::st7701s::commands::Command; use crate::peripherals::st7701s::{commands::Command, init_sequence::InitSequenceAction};
mod commands; mod commands;
mod init_sequence; mod init_sequence;
@ -43,8 +43,9 @@ impl<'d> St7701sController<'d> {
pub async fn send_init_sequence(&mut self) { pub async fn send_init_sequence(&mut self) {
debug!("Writing ST7701S init sequence."); debug!("Writing ST7701S init sequence.");
for (subsequence, delay_ms) in &*init_sequence::INIT_SEQUENCE_COMMANDS { for action in &init_sequence::INIT_SEQUENCE_COMMANDS {
for command in subsequence { match action {
InitSequenceAction::Command(command) => {
spi::spi_write( spi::spi_write(
command.address(), command.address(),
command.args_iter().copied(), command.args_iter().copied(),
@ -54,10 +55,12 @@ impl<'d> St7701sController<'d> {
) )
.await; .await;
} }
InitSequenceAction::SleepMs(delay_ms) => {
Timer::after_millis(*delay_ms).await; Timer::after_millis(*delay_ms).await;
} }
} }
}
}
pub async fn send(&mut self, command: impl Command) { pub async fn send(&mut self, command: impl Command) {
spi::spi_write( spi::spi_write(