Incremental improvements

This commit is contained in:
Jakub Hlusička 2025-12-27 21:03:52 +01:00
parent 37e0ecbb65
commit a67991027d
10 changed files with 460 additions and 363 deletions

View file

@ -17,3 +17,13 @@ ESP_LOG = "info"
# Needed for nightly, until llvm upstream has support for Rust Xtensa. # Needed for nightly, until llvm upstream has support for Rust Xtensa.
[unstable] [unstable]
build-std = ["alloc", "core"] build-std = ["alloc", "core"]
# [patch.crates-io]
# esp-backtrace = { path = "../../../rust/esp-hal/esp-backtrace" }
# esp-hal = { path = "../../../rust/esp-hal/esp-hal" }
# esp-storage = { path = "../../../rust/esp-hal/esp-storage" }
# esp-alloc = { path = "../../../rust/esp-hal/esp-alloc" }
# esp-println = { path = "../../../rust/esp-hal/esp-println" }
# esp-radio = { path = "../../../rust/esp-hal/esp-radio" }
# esp-rtos = { path = "../../../rust/esp-hal/esp-rtos" }
# esp-bootloader-esp-idf = { path = "../../../rust/esp-hal/esp-bootloader-esp-idf" }

77
firmware2/Cargo.lock generated
View file

@ -11,6 +11,7 @@ dependencies = [
"cc", "cc",
"const-gen", "const-gen",
"embassy-executor", "embassy-executor",
"embassy-time",
"embuild", "embuild",
"esp-alloc", "esp-alloc",
"esp-backtrace", "esp-backtrace",
@ -20,6 +21,7 @@ dependencies = [
"esp-radio", "esp-radio",
"esp-rtos", "esp-rtos",
"esp-storage", "esp-storage",
"itertools",
"json", "json",
"lazy_static", "lazy_static",
"log", "log",
@ -247,9 +249,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.49" version = "1.2.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"shlex", "shlex",
@ -592,6 +594,12 @@ dependencies = [
"litrs", "litrs",
] ]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]] [[package]]
name = "elliptic-curve" name = "elliptic-curve"
version = "0.13.8" version = "0.13.8"
@ -732,6 +740,7 @@ dependencies = [
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-async", "embedded-hal-async",
"futures-core", "futures-core",
"log",
] ]
[[package]] [[package]]
@ -1042,6 +1051,7 @@ dependencies = [
"esp32s3", "esp32s3",
"fugit", "fugit",
"instability", "instability",
"log",
"nb 1.1.0", "nb 1.1.0",
"paste", "paste",
"portable-atomic", "portable-atomic",
@ -1331,9 +1341,9 @@ dependencies = [
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.5" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
[[package]] [[package]]
name = "fnv" name = "fnv"
@ -1665,16 +1675,25 @@ dependencies = [
] ]
[[package]] [[package]]
name = "itoa" name = "itertools"
version = "1.0.15" version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]] [[package]]
name = "jiff" name = "jiff"
version = "0.2.16" version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8"
dependencies = [ dependencies = [
"jiff-static", "jiff-static",
"log", "log",
@ -1685,9 +1704,9 @@ dependencies = [
[[package]] [[package]]
name = "jiff-static" name = "jiff-static"
version = "0.2.16" version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1738,9 +1757,9 @@ checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.11" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"libc", "libc",
@ -1936,9 +1955,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.11.1" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
[[package]] [[package]]
name = "portable-atomic-util" name = "portable-atomic-util"
@ -2032,9 +2051,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.103" version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -2087,9 +2106,9 @@ checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
] ]
@ -2138,9 +2157,9 @@ dependencies = [
[[package]] [[package]]
name = "riscv-rt-macros" name = "riscv-rt-macros"
version = "0.6.0" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c3138fdd8d128b2d81829842a3e0ce771b3712f7b6318ed1476b0695e7d330" checksum = "def519ddeeb5e43c2b4fc3952c27b3a86782fc05192f322b2309125cd85b1fc3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2312,9 +2331,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.20" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
@ -2426,15 +2445,15 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.145" version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu",
"serde", "serde",
"serde_core", "serde_core",
"zmij",
] ]
[[package]] [[package]]
@ -3197,3 +3216,9 @@ name = "zeroize"
version = "1.8.2" version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
name = "zmij"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d6085d62852e35540689d1f97ad663e3971fc19cf5eceab364d62c646ea167"

View file

@ -8,22 +8,24 @@ repository = "https://github.com/haobogu/rmk"
edition = "2024" edition = "2024"
[features] [features]
default = ["ble"]
no_usb = ["rmk/_no_usb"] no_usb = ["rmk/_no_usb"]
ble = ["rmk/esp32s3_ble"]
[dependencies] [dependencies]
rmk = { version = "0.8.2", default-features = false, features = [ rmk = { version = "0.8.2", default-features = false, features = [
"esp32s3_ble",
"log", "log",
"storage", "storage",
"vial", "vial",
] } ] }
embassy-executor = { version = "0.9" } embassy-executor = { version = "0.9" }
embassy-time = { version = "0.5.0", features = ["log"] }
esp-backtrace = { version = "0.18", features = [ esp-backtrace = { version = "0.18", features = [
"esp32s3", "esp32s3",
"panic-handler", "panic-handler",
"println", "println",
] } ] }
esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram"] } esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram", "log-04"] }
esp-storage = { version = "0.8.0", features = ["esp32s3"] } esp-storage = { version = "0.8.0", features = ["esp32s3"] }
esp-alloc = { version = "0.9.0" } esp-alloc = { version = "0.9.0" }
esp-println = { version = "0.16.0", features = ["esp32s3", "log-04"] } esp-println = { version = "0.16.0", features = ["esp32s3", "log-04"] }
@ -37,6 +39,7 @@ lazy_static = { version = "1.5.0", features = ["spin_no_std"], default-features
log = "0.4.29" log = "0.4.29"
bitflags = "2.10.0" bitflags = "2.10.0"
paste = "1.0.15" paste = "1.0.15"
itertools = { version = "0.14.0", default-features = false }
[build-dependencies] [build-dependencies]
xz2 = "0.1.7" xz2 = "0.1.7"

View file

@ -1,5 +1,5 @@
use rmk::types::action::KeyAction; use rmk::types::action::KeyAction;
use rmk::{a, k, layer, mo}; use rmk::{a, k, layer};
use crate::matrix::{MATRIX_COLS, MATRIX_ROWS}; use crate::matrix::{MATRIX_COLS, MATRIX_ROWS};

View file

@ -1,12 +0,0 @@
macro_rules! config_matrix_pins_esp {
(peripherals: $p:ident, input: [$($in_pin:ident), *], output: [$($out_pin:ident), +]) => {
{
let mut output_pins = [$(Output::new($p.$out_pin, Level::Low, OutputConfig::default())), +];
let input_pins = [$(Input::new($p.$in_pin, InputConfig::default().with_pull(Pull::Down))), +];
output_pins.iter_mut().for_each(|p| {
let _ = p.set_low();
});
(input_pins, output_pins)
}
};
}

View file

@ -5,59 +5,53 @@
extern crate alloc; extern crate alloc;
mod keymap; mod keymap;
#[macro_use]
mod macros;
mod lcd;
mod matrix; mod matrix;
mod st7701s; mod peripherals;
mod vial; mod vial;
use core::alloc::Layout; use core::alloc::Layout;
use core::ptr::addr_of_mut;
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::vec; use alloc::vec;
use bt_hci::controller::ExternalController; use bt_hci::controller::ExternalController;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use esp_alloc::{HeapRegion, MemoryCapability}; use esp_alloc::{HeapRegion, MemoryCapability};
use esp_hal::clock::{CpuClock, RtcClock}; use esp_hal::clock::CpuClock;
use esp_hal::delay::Delay;
use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig}; use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig};
use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull}; use esp_hal::gpio::{Flex, Input, InputConfig, Io, Level, Output, OutputConfig, Pull};
use esp_hal::handler;
use esp_hal::i2c::master::{I2c, I2cAddress}; use esp_hal::i2c::master::{I2c, I2cAddress};
use esp_hal::interrupt::Priority;
use esp_hal::interrupt::software::SoftwareInterruptControl;
use esp_hal::lcd_cam::LcdCam; use esp_hal::lcd_cam::LcdCam;
use esp_hal::lcd_cam::lcd::dpi::{Dpi, Format, FrameTiming}; use esp_hal::lcd_cam::lcd::dpi::Dpi;
use esp_hal::lcd_cam::lcd::{ClockMode, Phase, Polarity};
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig}; use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
use esp_hal::otg_fs::Usb; use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
use esp_hal::otg_fs::asynch::{Config, Driver};
// use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
use esp_hal::rng::TrngSource; use esp_hal::rng::TrngSource;
use esp_hal::rtc_cntl::sleep::RtcSleepConfig; use esp_hal::system::Stack;
use esp_hal::rtc_cntl::{Rtc, sleep};
use esp_hal::time::Rate;
use esp_hal::timer::systimer::SystemTimer;
use esp_hal::timer::timg::TimerGroup; use esp_hal::timer::timg::TimerGroup;
use esp_hal::uart::Uart;
use esp_hal::{Blocking, ram};
use esp_radio::Controller; use esp_radio::Controller;
use esp_radio::ble::controller::BleConnector; use esp_radio::ble::controller::BleConnector;
use esp_rtos::embassy::InterruptExecutor;
use esp_storage::FlashStorage; use esp_storage::FlashStorage;
use log::{LevelFilter, info}; use itertools::chain;
use rmk::ble::build_ble_stack; use log::{error, info};
use rmk::channel::EVENT_CHANNEL; use rmk::channel::EVENT_CHANNEL;
use rmk::config::{BehaviorConfig, PositionalConfig, RmkConfig, StorageConfig, VialConfig}; use rmk::config::{BehaviorConfig, PositionalConfig, RmkConfig, StorageConfig, VialConfig};
use rmk::debounce::default_debouncer::DefaultDebouncer; use rmk::debounce::default_debouncer::DefaultDebouncer;
use rmk::futures::future::join3; use rmk::embassy_futures::yield_now;
use rmk::input_device::Runnable; use rmk::input_device::Runnable;
use rmk::join_all;
use rmk::keyboard::Keyboard; use rmk::keyboard::Keyboard;
use rmk::matrix::Matrix;
use rmk::storage::async_flash_wrapper; use rmk::storage::async_flash_wrapper;
use rmk::{HostResources, initialize_keymap_and_storage, run_devices, run_rmk}; use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
use static_cell::StaticCell; use static_cell::StaticCell;
use {esp_alloc as _, esp_backtrace as _}; use {esp_alloc as _, esp_backtrace as _};
use crate::keymap::*;
use crate::lcd::spi_write;
use crate::matrix::IoeMatrix; use crate::matrix::IoeMatrix;
use crate::peripherals::st7701s::St7701s;
use crate::vial::{VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID}; use crate::vial::{VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID};
// This creates a default app-descriptor required by the esp-idf bootloader. // This creates a default app-descriptor required by the esp-idf bootloader.
@ -67,17 +61,18 @@ esp_bootloader_esp_idf::esp_app_desc!();
static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
#[esp_rtos::main] #[esp_rtos::main]
async fn main(spawner: Spawner) { async fn main(_spawner: Spawner) {
esp_println::logger::init_logger_from_env(); esp_println::logger::init_logger_from_env();
info!("Logger initialized!"); info!("Logger initialized!");
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let config = esp_hal::Config::default()
// .with_psram(PsramConfig { .with_cpu_clock(CpuClock::max())
// size: PsramSize::AutoDetect, .with_psram(PsramConfig {
// core_clock: Some(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m), size: PsramSize::AutoDetect,
// flash_frequency: FlashFreq::default(), core_clock: Some(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m),
// ram_frequency: SpiRamFreq::Freq80m, flash_frequency: FlashFreq::default(),
// }); ram_frequency: SpiRamFreq::Freq80m,
});
let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config); let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config);
info!("System initialized!"); info!("System initialized!");
@ -85,52 +80,20 @@ async fn main(spawner: Spawner) {
esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024); esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024);
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats()); info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
// let timer0 = SystemTimer::new(peripherals.SYSTIMER); // Initialize the PSRAM allocator.
// esp_hal_embassy::init(timer0.alarm0); {
// info!("Embassy initialized!"); let (psram_offset, psram_size) = esp_hal::psram::psram_raw_parts(&peripherals.PSRAM);
unsafe {
PSRAM_ALLOCATOR.add_region(HeapRegion::new(
psram_offset,
psram_size,
MemoryCapability::External.into(),
));
}
}
// // Initialize the PSRAM allocator. let mut io = Io::new(peripherals.IO_MUX);
// { io.set_interrupt_handler(interrupt_handler);
// let (psram_offset, psram_size) = esp_hal::psram::psram_raw_parts(&peripherals.PSRAM);
// unsafe {
// PSRAM_ALLOCATOR.add_region(HeapRegion::new(
// psram_offset,
// psram_size,
// MemoryCapability::External.into(),
// ));
// }
// }
// const BUFFER_LEN: usize = core::mem::size_of::<u16>()
// * (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_caps(
// MemoryCapability::External.into(),
// Layout::from_size_align(BUFFER_LEN, 32).unwrap(),
// )
// };
// let buffer = unsafe { core::slice::from_raw_parts_mut(buffer_ptr, BUFFER_LEN) };
// let burst_config: BurstConfig = ExternalBurstConfig::Size16.into();
// info!(
// "PSRAM SPI burst config: max_compatible_chunk_size={}",
// burst_config.max_compatible_chunk_size()
// );
// 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;
// Enable pull-up on GPIO0 to prevent booting into download mode. // Enable pull-up on GPIO0 to prevent booting into download mode.
let gpio0 = Output::new( let gpio0 = Output::new(
@ -149,20 +112,12 @@ async fn main(spawner: Spawner) {
let mut _pwm = McPwm::new(peripherals.MCPWM0, PeripheralClockConfig::with_prescaler(1)); 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 _pwm_pin = Output::new(peripherals.GPIO21, Level::High, OutputConfig::default());
let mut matrix_interrupt = Input::new(
peripherals.GPIO7,
InputConfig::default().with_pull(Pull::Up),
);
// esp_alloc::heap_allocator!(size: 72 * 1024);
let timg0 = TimerGroup::new(peripherals.TIMG0); let timg0 = TimerGroup::new(peripherals.TIMG0);
#[cfg(target_arch = "riscv32")]
let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start( esp_rtos::start(
timg0.timer0, timg0.timer0, /*, software_interrupt.software_interrupt0 */
#[cfg(target_arch = "riscv32")]
software_interrupt.software_interrupt0,
); );
// Enable the TRNG source, so `Trng` can be constructed.
let _trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1); let _trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
let mut rng = esp_hal::rng::Trng::try_new().unwrap(); let mut rng = esp_hal::rng::Trng::try_new().unwrap();
static RADIO: StaticCell<Controller<'static>> = StaticCell::new(); static RADIO: StaticCell<Controller<'static>> = StaticCell::new();
@ -171,12 +126,19 @@ async fn main(spawner: Spawner) {
let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap(); let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap();
let controller: ExternalController<_, 20> = ExternalController::new(connector); let controller: ExternalController<_, 20> = ExternalController::new(connector);
let central_addr = [0x18, 0xe2, 0x21, 0x80, 0xc0, 0xc7]; let central_addr = [0x18, 0xe2, 0x21, 0x80, 0xc0, 0xc7];
let mut host_resources = HostResources::new(); #[cfg(feature = "ble")]
let stack = build_ble_stack(controller, central_addr, &mut rng, &mut host_resources).await; let mut host_resources = rmk::HostResources::new();
#[cfg(feature = "ble")]
let stack =
rmk::ble::build_ble_stack(controller, central_addr, &mut rng, &mut host_resources).await;
// // Initialize USB // Initialize USB
#[cfg(not(feature = "no_usb"))] #[cfg(not(feature = "no_usb"))]
let usb_driver = { let usb_driver = {
use core::ptr::addr_of_mut;
use esp_hal::otg_fs::Usb;
use esp_hal::otg_fs::asynch::{Config, Driver};
static mut EP_MEMORY: [u8; 1024] = [0; 1024]; static mut EP_MEMORY: [u8; 1024] = [0; 1024];
let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19); let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19);
// Create the driver, from the HAL. // Create the driver, from the HAL.
@ -188,102 +150,12 @@ async fn main(spawner: Spawner) {
let flash = FlashStorage::new(peripherals.FLASH); let flash = FlashStorage::new(peripherals.FLASH);
let flash = async_flash_wrapper(flash); let flash = async_flash_wrapper(flash);
let mut sck = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default()); let sck = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default());
let mut mosi = Flex::new(peripherals.GPIO35); let mosi = Flex::new(peripherals.GPIO35);
let mut cs = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default()); let cs = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default());
mosi.set_input_enable(false); let lcd = LcdCam::new(peripherals.LCD_CAM).lcd;
mosi.set_output_enable(true); let unconfigured_dpi = Dpi::new(lcd, peripherals.DMA_CH2, Default::default())
// info!("init sequence writing");
for (subsequence, delay_ms) in &*lcd::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}");
// }
}
Delay::new().delay_millis(*delay_ms as u32); // TODO: async?
// Timer::after_millis(*delay_ms).await
}
// info!("init sequence written");
let mut lcd = LcdCam::new(peripherals.LCD_CAM).into_async().lcd;
let lcd_config = esp_hal::lcd_cam::lcd::dpi::Config::default()
// 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
})
.with_format(Format {
enable_2byte_mode: true,
..Default::default()
})
.with_timing({
// Adafruit's config for this LCD:
// https://github.com/adafruit/Adafruit_CircuitPython_Qualia/blob/742d336e05e6a4d8bdaa46e15bbf60c9f30d2eba/adafruit_qualia/displays/bar240x960.py#L81-L97
// 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;
let vsync_width = 8;
let hsync_width = 8;
let horizontal_blank_front_porch = 20;
let horizontal_blank_back_porch = 20;
let vertical_blank_front_porch = 20;
let vertical_blank_back_porch = 20;
let hsync_position = 0;
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: hsync_width
+ horizontal_blank_back_porch
+ 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: horizontal_blank_front_porch + hsync_width,
vertical_blank_front_porch: vertical_blank_front_porch + vsync_width,
horizontal_active_width,
vertical_active_height,
vsync_width,
hsync_width,
hsync_position,
}
})
.with_hsync_idle_level(Level::High)
.with_vsync_idle_level(Level::High)
.with_de_idle_level(Level::Low);
let mut dpi = Dpi::new(lcd, peripherals.DMA_CH2, lcd_config)
.unwrap() .unwrap()
.with_de(peripherals.GPIO37) .with_de(peripherals.GPIO37)
.with_pclk(peripherals.GPIO34) .with_pclk(peripherals.GPIO34)
@ -309,6 +181,8 @@ async fn main(spawner: Spawner) {
.with_data14(peripherals.GPIO3) .with_data14(peripherals.GPIO3)
.with_data15(peripherals.GPIO4); .with_data15(peripherals.GPIO4);
let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi).await;
// RMK config // RMK config
let vial_config = VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (1, 1)]); let vial_config = VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (1, 1)]);
let storage_config = StorageConfig { let storage_config = StorageConfig {
@ -345,7 +219,10 @@ async fn main(spawner: Spawner) {
.with_sda(peripherals.GPIO8) .with_sda(peripherals.GPIO8)
.with_scl(peripherals.GPIO9); .with_scl(peripherals.GPIO9);
let matrix_interrupt_low = Input::new(peripherals.GPIO7, InputConfig::default());
let mut matrix = IoeMatrix::new( let mut matrix = IoeMatrix::new(
matrix_interrupt_low,
i2c.into_async(), i2c.into_async(),
DefaultDebouncer::new(), DefaultDebouncer::new(),
[I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT], [I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT],
@ -353,7 +230,31 @@ async fn main(spawner: Spawner) {
.await; .await;
let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller
join3( // spawner.must_spawn(run_lcd(st7701s));
static SECOND_CORE_STACK: StaticCell<Stack<8192>> = StaticCell::new();
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
esp_rtos::start_second_core(
peripherals.CPU_CTRL,
software_interrupt.software_interrupt0,
software_interrupt.software_interrupt1,
second_core_stack,
move || {
static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
let exec = EXECUTOR.init(InterruptExecutor::new(
software_interrupt.software_interrupt2,
));
let spawner = exec.start(Priority::Priority3);
spawner.must_spawn(run_lcd_task(st7701s));
// static EXECUTOR: StaticCell<Executor> = StaticCell::new();
// let executor: &mut Executor = EXECUTOR.init(Executor::new());
// executor.run(|spawner| {
// let task = run_lcd_task(st7701s);
// spawner.must_spawn(task);
// });
},
);
join_all![
run_devices! ( run_devices! (
(matrix) => EVENT_CHANNEL, (matrix) => EVENT_CHANNEL,
), ),
@ -362,102 +263,120 @@ async fn main(spawner: Spawner) {
&keymap, &keymap,
#[cfg(not(feature = "no_usb"))] #[cfg(not(feature = "no_usb"))]
usb_driver, usb_driver,
#[cfg(feature = "ble")]
&stack, &stack,
&mut storage, &mut storage,
rmk_config, rmk_config,
), ) // run_lcd(st7701s)
) ]
.await; .await;
} }
// #[esp_rtos::main] #[embassy_executor::task]
// async fn main(_s: Spawner) { async fn run_lcd_task(st7701s: St7701s<'static, Blocking>) {
// // Initialize the peripherals and bluetooth controller run_lcd(st7701s).await
// esp_println::logger::init_logger(LevelFilter::max()); }
// let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
// esp_alloc::heap_allocator!(size: 72 * 1024);
// let timg0 = TimerGroup::new(peripherals.TIMG0);
// #[cfg(target_arch = "riscv32")]
// let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
// esp_rtos::start(
// timg0.timer0,
// #[cfg(target_arch = "riscv32")]
// software_interrupt.software_interrupt0,
// );
// let _trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
// let mut rng = esp_hal::rng::Trng::try_new().unwrap();
// static RADIO: StaticCell<Controller<'static>> = StaticCell::new();
// let radio = RADIO.init(esp_radio::init().unwrap());
// let bluetooth = peripherals.BT;
// let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap();
// let controller: ExternalController<_, 20> = ExternalController::new(connector);
// let central_addr = [0x18, 0xe2, 0x21, 0x80, 0xc0, 0xc7];
// let mut host_resources = HostResources::new();
// let stack = build_ble_stack(controller, central_addr, &mut rng, &mut host_resources).await;
// // Initialize the flash async fn run_lcd(mut st7701s: St7701s<'static, Blocking>) {
// let flash = FlashStorage::new(peripherals.FLASH); const PADDING_LEFT: usize = 121;
// let flash = async_flash_wrapper(flash); const PADDING_RIGHT: usize = 7;
const COLORS_WIDTH: usize = 240;
const COLORS_HEIGHT: usize = 960;
// // Initialize the IO pins const MAX_RED: u8 = (1 << 5) - 1;
// let (row_pins, col_pins) = config_matrix_pins_esp!(peripherals: peripherals, input: [GPIO6, GPIO7, GPIO21, GPIO35], output: [GPIO3, GPIO4, GPIO5]); const MAX_GREEN: u8 = (1 << 6) - 1;
const MAX_BLUE: u8 = (1 << 5) - 1;
// // RMK config fn rgb(r: u8, g: u8, b: u8) -> u16 {
// let vial_config = VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (1, 1)]); (((r & MAX_RED) as u16) << 11) | (((g & MAX_GREEN) as u16) << 5) | ((b & MAX_BLUE) as u16)
// let storage_config = StorageConfig { }
// start_addr: 0x3f0000,
// num_sectors: 16,
// ..Default::default()
// };
// let rmk_config = RmkConfig {
// vial_config,
// storage_config,
// ..Default::default()
// };
// // Initialze keyboard stuffs fn row(edge: u16) -> impl Iterator<Item = u16> + Clone {
// // Initialize the storage and keymap chain![
// let mut default_keymap = keymap::get_default_keymap(); core::iter::repeat_n(rgb(0, 0, 0xFF), PADDING_LEFT),
// let mut behavior_config = BehaviorConfig::default(); core::iter::once(rgb(0xFF, 0xFF, 0xFF)),
// let mut per_key_config = PositionalConfig::default(); core::iter::repeat_n(edge, COLORS_WIDTH - 2),
// let (keymap, mut storage) = initialize_keymap_and_storage( core::iter::once(rgb(0xFF, 0xFF, 0xFF)),
// &mut default_keymap, core::iter::repeat_n(rgb(0, 0, 0xFF), PADDING_RIGHT),
// flash, ]
// &storage_config, }
// &mut behavior_config,
// &mut per_key_config,
// )
// .await;
// // Initialize the matrix and keyboard let mut colors = chain![
// let debouncer = DefaultDebouncer::new(); row(rgb(0xFF, 0, 0)),
// let mut i2c = I2c::new( core::iter::repeat_n(row(0), COLORS_HEIGHT - 2).flatten(),
// peripherals.I2C0, row(rgb(0xFF, 0xFF, 0)),
// 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 BUFFER_LEN: usize = core::mem::size_of::<u16>()
// const I2C_ADDR_MATRIX_RIGHT: I2cAddress = I2cAddress::SevenBit(0b0100001); * (360 + /* TODO: Figure out why more bytes are needed: */ 8)
* 960;
// Allocate the framebuffer in the external PSRAM memory.
// 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_caps(
MemoryCapability::External.into(),
Layout::from_size_align(BUFFER_LEN, 32).unwrap(),
)
};
let buffer = unsafe { core::slice::from_raw_parts_mut(buffer_ptr, BUFFER_LEN) };
let burst_config: BurstConfig = ExternalBurstConfig::Size16.into();
// let mut matrix = IoeMatrix::new( info!(
// i2c.into_async(), "PSRAM SPI burst config: max_compatible_chunk_size={}",
// debouncer, burst_config.max_compatible_chunk_size()
// [I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT], );
// ) let dma_buf_descs_len =
// .await; esp_hal::dma::descriptor_count(BUFFER_LEN, burst_config.max_compatible_chunk_size(), false);
// // let mut matrix = Matrix::<_, _, _, ROW, COL, true>::new(row_pins, col_pins, debouncer); // Descriptors are initialized by `DmaTxBuf::new`.
// // let mut matrix = rmk::matrix::TestMatrix::<ROW, COL>::new(); let dma_buf_descs = Box::leak(vec![DmaDescriptor::EMPTY; dma_buf_descs_len].into_boxed_slice());
// let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller let mut dma_buf = DmaTxBuf::new(dma_buf_descs, buffer).unwrap();
// join3( {
// run_devices! ( for (chunk, color) in dma_buf.as_mut_slice().chunks_mut(2).zip(&mut colors) {
// (matrix) => EVENT_CHANNEL, chunk.copy_from_slice(&color.to_le_bytes());
// ), }
// keyboard.run(), // Keyboard is special
// run_rmk(&keymap, &stack, &mut storage, rmk_config), info!("chunk addr: {}", dma_buf.as_slice().as_ptr() as usize);
// ) // colors.next(); // Shift colors
// .await; }
// }
loop {
// Timer::after(Duration::from_millis(100)).await;
yield_now().await;
// TODO: Use bounce buffers:
// https://docs.espressif.com/projects/esp-idf/en/v5.0/esp32s3/api-reference/peripherals/lcd.html#bounce-buffer-with-single-psram-frame-buffer
// They need to be implemented in esp-hal.
let transfer = match st7701s.dpi.send(false, dma_buf) {
Err((error, result_dpi, result_dma_buf)) => {
error!(
"An error occurred while initiating transfer of the framebuffer to the LCD display: {error:?}"
);
st7701s.dpi = result_dpi;
dma_buf = result_dma_buf;
continue;
}
Ok(transfer) => transfer,
};
let result;
(result, st7701s.dpi, dma_buf) = transfer.wait();
if let Err(error) = result {
error!(
"An error occurred while transferring framebuffer to the LCD display: {error:?}"
);
}
}
}
#[handler]
#[ram] // TODO: Is this necessary?
fn interrupt_handler() {
esp_println::println!(
"GPIO Interrupt with priority {}",
esp_hal::xtensa_lx::interrupt::get_level()
);
}

View file

@ -1,11 +1,13 @@
use esp_hal::{ use esp_hal::{
Async, Async,
gpio::{Input, InputConfig, Pull},
i2c::master::{BusTimeout, I2c, I2cAddress, SoftwareTimeout}, i2c::master::{BusTimeout, I2c, I2cAddress, SoftwareTimeout},
time::Rate, time::Rate,
}; };
use log::info; use log::info;
use rmk::{ use rmk::{
debounce::{DebounceState, DebouncerTrait}, debounce::{DebounceState, DebouncerTrait},
embassy_futures::yield_now,
event::{Event, KeyboardEvent}, event::{Event, KeyboardEvent},
input_device::InputDevice, input_device::InputDevice,
matrix::{KeyState, MatrixTrait}, matrix::{KeyState, MatrixTrait},
@ -40,6 +42,8 @@ pub struct IoeMatrix<D>
where where
D: DebouncerTrait<MATRIX_ROWS, MATRIX_COLS>, D: DebouncerTrait<MATRIX_ROWS, MATRIX_COLS>,
{ {
/// Goes low when the matrix was changed and should be read.
matrix_interrupt_low: Input<'static>,
i2c: I2c<'static, Async>, i2c: I2c<'static, Async>,
/// IO expander addresses. /// IO expander addresses.
ioe_addresses: [I2cAddress; 2], ioe_addresses: [I2cAddress; 2],
@ -60,10 +64,12 @@ where
D: DebouncerTrait<MATRIX_ROWS, MATRIX_COLS>, D: DebouncerTrait<MATRIX_ROWS, MATRIX_COLS>,
{ {
pub async fn new( pub async fn new(
mut matrix_interrupt_low: Input<'static>,
mut i2c: I2c<'static, Async>, mut i2c: I2c<'static, Async>,
debouncer: D, debouncer: D,
ioe_addresses: [I2cAddress; 2], ioe_addresses: [I2cAddress; 2],
) -> Self { ) -> Self {
matrix_interrupt_low.apply_config(&InputConfig::default().with_pull(Pull::Up));
i2c.apply_config( i2c.apply_config(
&esp_hal::i2c::master::Config::default() &esp_hal::i2c::master::Config::default()
.with_frequency(Rate::from_khz(400)) .with_frequency(Rate::from_khz(400))
@ -73,12 +79,19 @@ where
.unwrap(); .unwrap();
for addr in ioe_addresses { for addr in ioe_addresses {
i2c.write_async(addr, &[0b10010000, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) loop {
.await let Err(error) = i2c
.unwrap(); .write_async(addr, &[0b10010000, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
.await
else {
break;
};
info!("I2C timed out when writing the inversion mask to an IO expander: {error:?}");
}
} }
Self { Self {
matrix_interrupt_low,
i2c, i2c,
ioe_addresses, ioe_addresses,
debouncer, debouncer,
@ -98,6 +111,10 @@ where
async fn read_event(&mut self) -> Event { async fn read_event(&mut self) -> Event {
loop { loop {
if self.scan_pos == 0 { if self.scan_pos == 0 {
// TODO: Only execute if interrupt received.
// Timer::after(Duration::from_millis(100)).await;
yield_now().await;
// Load data from IO expanders. // Load data from IO expanders.
let mut input_register_banks = [0_u8; 10]; let mut input_register_banks = [0_u8; 10];

View file

@ -0,0 +1 @@
pub mod st7701s;

View file

@ -1,95 +1,98 @@
use alloc::{boxed::Box, vec, vec::Vec}; use alloc::{boxed::Box, vec, vec::Vec};
use core::iter::once; use core::iter::once;
use esp_hal::{ use embassy_time::{Duration, Timer};
delay::Delay, use esp_hal::gpio::{Flex, Level, Output};
gpio::{Flex, Level, Output},
};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use crate::st7701s::Command; use crate::peripherals::st7701s::Command;
fn spi_delay() { async fn spi_delay() {
Delay::new().delay_micros(1); // TODO: Async? Timer::after(Duration::from_micros(1)).await;
} }
fn spi_dummy_bit(sck: &mut Output) { async fn spi_dummy_bit(sck: &mut Output<'_>) {
sck.set_low(); sck.set_low();
spi_delay(); spi_delay().await;
sck.set_high(); sck.set_high();
spi_delay(); spi_delay().await;
} }
fn spi_write_bit(bit: bool, mosi: &mut Flex, sck: &mut Output) { async fn spi_write_bit(bit: bool, mosi: &mut Flex<'_>, sck: &mut Output<'_>) {
mosi.set_level(if bit { Level::High } else { Level::Low }); mosi.set_level(if bit { Level::High } else { Level::Low });
sck.set_low(); sck.set_low();
spi_delay(); spi_delay().await;
sck.set_high(); sck.set_high();
spi_delay(); spi_delay().await;
} }
fn spi_read_bit(mosi: &mut Flex, sck: &mut Output) -> bool { async fn spi_read_bit(mosi: &mut Flex<'_>, sck: &mut Output<'_>) -> bool {
sck.set_low(); sck.set_low();
spi_delay(); spi_delay().await;
sck.set_high(); sck.set_high();
spi_delay(); spi_delay().await;
mosi.is_high() mosi.is_high()
} }
fn spi_write_bits(bits: impl Iterator<Item = bool>, mosi: &mut Flex, sck: &mut Output) { async fn spi_write_bits(
bits: impl Iterator<Item = bool>,
mosi: &mut Flex<'_>,
sck: &mut Output<'_>,
) {
for bit in bits { for bit in bits {
spi_write_bit(bit, mosi, sck); spi_write_bit(bit, mosi, sck).await;
} }
} }
fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex, sck: &mut Output) { async fn spi_write_word(is_param: bool, data: u8, mosi: &mut Flex<'_>, sck: &mut Output<'_>) {
assert!(sck.is_set_high()); assert!(sck.is_set_high());
spi_write_bits( spi_write_bits(
once(is_param).chain((0..8).map(|i| (data >> i) & 1 != 0).rev()), once(is_param).chain((0..8).map(|i| (data >> i) & 1 != 0).rev()),
mosi, mosi,
sck, sck,
); )
.await;
} }
pub fn spi_write( pub async fn spi_write(
command: u8, command: u8,
args: impl IntoIterator<Item = u8>, args: impl IntoIterator<Item = u8>,
mosi: &mut Flex, mosi: &mut Flex<'_>,
sck: &mut Output, sck: &mut Output<'_>,
cs: &mut Output, cs: &mut Output<'_>,
) { ) {
cs.set_low(); cs.set_low();
spi_write_word(false, command, mosi, sck); spi_write_word(false, command, mosi, sck).await;
for arg in args { for arg in args {
spi_write_word(true, arg, mosi, sck); spi_write_word(true, arg, mosi, sck).await;
} }
cs.set_high(); cs.set_high();
} }
fn spi_read( pub async fn spi_read(
command: u8, command: u8,
dummy_cycle: bool, dummy_cycle: bool,
mosi: &mut Flex, mosi: &mut Flex<'_>,
sck: &mut Output, sck: &mut Output<'_>,
cs: &mut Output, cs: &mut Output<'_>,
output_buffer: &mut [u8], output_buffer: &mut [u8],
) { ) {
output_buffer.fill(0); output_buffer.fill(0);
cs.set_low(); cs.set_low();
spi_write_word(false, command, mosi, sck); spi_write_word(false, command, mosi, sck).await;
mosi.set_output_enable(false); mosi.set_output_enable(false);
mosi.set_input_enable(true); mosi.set_input_enable(true);
if dummy_cycle { if dummy_cycle {
spi_dummy_bit(sck); spi_dummy_bit(sck).await;
} }
for output_byte in output_buffer { for output_byte in output_buffer {
for i in (0..8).rev() { for i in (0..8).rev() {
if spi_read_bit(mosi, sck) { if spi_read_bit(mosi, sck).await {
*output_byte |= 1 << i; *output_byte |= 1 << i;
} }
} }
@ -101,7 +104,7 @@ fn spi_read(
cs.set_high(); cs.set_high();
} }
use crate::st7701s::*; use crate::peripherals::st7701s::*;
lazy_static! { lazy_static! {
pub static ref INIT_SEQUENCE_COMMANDS: Vec<(Vec<Box<dyn Command + Send + Sync>>, u64)> = vec![ pub static ref INIT_SEQUENCE_COMMANDS: Vec<(Vec<Box<dyn Command + Send + Sync>>, u64)> = vec![

View file

@ -1,7 +1,19 @@
use bitflags::bitflags; use embassy_time::Timer;
use esp_hal::gpio::OutputPin; use esp_hal::{
DriverMode,
gpio::{Flex, Level, Output},
lcd_cam::lcd::{
ClockMode, Phase, Polarity,
dpi::{Dpi, Format, FrameTiming},
},
time::Rate,
};
use lcd::spi_write;
use log::debug;
use paste::paste; use paste::paste;
mod lcd;
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>;
@ -1196,17 +1208,136 @@ define_commands! {
} }
} }
pub struct St7701s {} pub struct St7701s<'d, Dm>
where
Dm: DriverMode,
{
pub sck: Output<'d>,
pub mosi: Flex<'d>,
pub cs: Output<'d>,
pub dpi: Dpi<'d, Dm>,
}
impl St7701s { impl<'d, Dm> St7701s<'d, Dm>
pub fn new() -> Self { where
let _ = CmdNop::default(); Dm: DriverMode,
let x = CmdGsl( {
CmdGslArg0::new().with_tesl_msb(1), pub async fn new(
CmdGslArg1::new().with_tesl_lsb(0), mut sck: Output<'d>,
); mut mosi: Flex<'d>,
x.address(); mut cs: Output<'d>,
x.args_iter(); mut unconfigured_dpi: Dpi<'d, Dm>,
todo!() ) -> Self {
sck.apply_config(&Default::default());
sck.set_high();
cs.apply_config(&Default::default());
cs.set_high();
mosi.apply_input_config(&Default::default());
mosi.apply_output_config(&Default::default());
mosi.set_input_enable(false);
mosi.set_output_enable(true);
let lcd_config = esp_hal::lcd_cam::lcd::dpi::Config::default()
// 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
})
.with_format(Format {
enable_2byte_mode: true,
..Default::default()
})
.with_timing({
// Adafruit's config for this LCD:
// https://github.com/adafruit/Adafruit_CircuitPython_Qualia/blob/742d336e05e6a4d8bdaa46e15bbf60c9f30d2eba/adafruit_qualia/displays/bar240x960.py#L81-L97
// 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;
let vsync_width = 8;
let hsync_width = 8;
let horizontal_blank_front_porch = 20;
let horizontal_blank_back_porch = 20;
let vertical_blank_front_porch = 20;
let vertical_blank_back_porch = 20;
let hsync_position = 0;
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: hsync_width
+ horizontal_blank_back_porch
+ 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: horizontal_blank_front_porch + hsync_width,
vertical_blank_front_porch: vertical_blank_front_porch + vsync_width,
horizontal_active_width,
vertical_active_height,
vsync_width,
hsync_width,
hsync_position,
}
})
.with_hsync_idle_level(Level::High)
.with_vsync_idle_level(Level::High)
.with_de_idle_level(Level::Low);
unconfigured_dpi.apply_config(&lcd_config).unwrap();
let mut lcd = Self {
sck,
mosi,
cs,
dpi: unconfigured_dpi,
};
lcd.send_init_sequence().await;
lcd
}
pub async fn send_init_sequence(&mut self) {
debug!("Writing ST7701S init sequence.");
for (subsequence, delay_ms) in &*lcd::INIT_SEQUENCE_COMMANDS {
for command in subsequence {
spi_write(
command.address(),
command.args_iter().copied(),
&mut self.mosi,
&mut self.sck,
&mut self.cs,
)
.await;
// info!("COMM 0x{:02X}", command.address());
// for arg in command.args_iter() {
// info!("DATA 0x{arg:02X}");
// }
}
Timer::after_millis(*delay_ms).await;
}
} }
} }