Compare commits
No commits in common. "bounce_buffer" and "master" have entirely different histories.
bounce_buf
...
master
73
firmware/Cargo.lock
generated
73
firmware/Cargo.lock
generated
|
|
@ -30,7 +30,6 @@ dependencies = [
|
|||
"esp-backtrace",
|
||||
"esp-bootloader-esp-idf",
|
||||
"esp-hal",
|
||||
"esp-hal-bounce-buffers",
|
||||
"esp-metadata-generated",
|
||||
"esp-println",
|
||||
"esp-radio",
|
||||
|
|
@ -48,7 +47,6 @@ dependencies = [
|
|||
"lazy_static",
|
||||
"log",
|
||||
"mutually_exclusive_features",
|
||||
"ouroboros",
|
||||
"panic-rtt-target",
|
||||
"password-hash 0.1.0",
|
||||
"pastey 0.2.1",
|
||||
|
|
@ -107,12 +105,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aliasable"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.4.3"
|
||||
|
|
@ -2154,16 +2146,6 @@ dependencies = [
|
|||
"xtensa-lx-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp-hal-bounce-buffers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
"embassy-sync 0.7.2",
|
||||
"esp-hal",
|
||||
"ouroboros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esp-hal-procmacros"
|
||||
version = "0.21.0"
|
||||
|
|
@ -3798,12 +3780,6 @@ dependencies = [
|
|||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
|
|
@ -5377,30 +5353,6 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.18.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59"
|
||||
dependencies = [
|
||||
"aliasable",
|
||||
"ouroboros_macro",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros_macro"
|
||||
version = "0.18.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.13.2"
|
||||
|
|
@ -5809,19 +5761,6 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prodash"
|
||||
version = "31.0.0"
|
||||
|
|
@ -6767,7 +6706,7 @@ dependencies = [
|
|||
"bindgen",
|
||||
"cc",
|
||||
"flate2",
|
||||
"heck 0.5.0",
|
||||
"heck",
|
||||
"pkg-config",
|
||||
"regex",
|
||||
"serde_json",
|
||||
|
|
@ -6916,7 +6855,7 @@ checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c"
|
|||
[[package]]
|
||||
name = "spectre-api-sys"
|
||||
version = "0.1.0"
|
||||
source = "git+c:/Users/Limeth/workspace/rust/spectre-api-sys#9e844eb056c3dfee8286ac21ec40fa689a8b8aa2"
|
||||
source = "git+https://github.com/Limeth/spectre-api-sys?rev=9e844eb056c3dfee8286ac21ec40fa689a8b8aa2#9e844eb056c3dfee8286ac21ec40fa689a8b8aa2"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
|
|
@ -7002,7 +6941,7 @@ version = "0.27.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
|
|
@ -8564,12 +8503,6 @@ dependencies = [
|
|||
"hashlink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "yazi"
|
||||
version = "0.2.1"
|
||||
|
|
|
|||
|
|
@ -38,14 +38,3 @@ ACID_COMPOSE_LOCALE = "cs_CZ.UTF-8"
|
|||
# This can be substituted with a `-Zbuild-std="core,alloc"` cargo flag.
|
||||
[unstable]
|
||||
build-std = ["alloc", "core"]
|
||||
|
||||
[patch.crates-io]
|
||||
# esp-backtrace = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-hal = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-storage = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-alloc = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-println = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-radio = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-rtos = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-bootloader-esp-idf = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
# esp-sync = { git = "https://github.com/Limeth/esp-hal.git", rev = "95d8c8b046e945e41294d5577528d0a1c4b03247" }
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ develop-usb = ["limit-fps", "usb-log", "no-usb", "ble"]
|
|||
probe = ["limit-fps", "rtt-log", "no-usb", "ble"]
|
||||
# Formats the EKV database on boot.
|
||||
format-db = []
|
||||
# Avoid entering the critical section for the whole duration of printing a message to console.
|
||||
racy-logging = []
|
||||
|
||||
[dependencies]
|
||||
rmk = { version = "0.8.2", git = "https://github.com/Limeth/rmk", rev = "1661c55f5c21e7d80ea3f93255df483302c74b84", default-features = false, features = [
|
||||
|
|
@ -84,12 +82,10 @@ serde = { version = "1.0", default-features = false, features = ["derive"] }
|
|||
# serde_with = { version = "3.16", default-features = false, features = ["alloc", "macros"] }
|
||||
serde_bytes = { version = "0.11.19", default-features = false, features = ["alloc"] }
|
||||
chrono = { version = "0.4.43", default-features = false, features = ["alloc", "serde"] } # TODO: defmt
|
||||
tinyvec = { version = "1.10.0", default-features = false, features = ["alloc", "latest_stable_rust"] }
|
||||
tinyvec = { version = "1.10.0", default-features = false, features = ["alloc"] }
|
||||
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"] }
|
||||
|
||||
# A fork of slint with patches for `allocator_api` support.
|
||||
# Don't forget to change `slint-build` in build dependencies, if this is changed.
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ fn main() {
|
|||
.map_err(|_| NotBuilt {
|
||||
lib_build_dir: lib_build_dir_str.clone(),
|
||||
})?;
|
||||
let lib_build_dir = lib_build_dir.display();
|
||||
|
||||
if !lib_library_path.is_file() {
|
||||
return Err(NotBuilt {
|
||||
|
|
@ -88,9 +89,9 @@ fn main() {
|
|||
});
|
||||
}
|
||||
|
||||
println!("cargo:rustc-link-search=native={}", lib_build_dir.display());
|
||||
println!("cargo:rustc-link-search=native={lib_build_dir}");
|
||||
println!("cargo:rustc-link-lib=static={library}");
|
||||
println!("cargo:rerun-if-changed={}", lib_library_path.display());
|
||||
println!("cargo:rerun-if-changed={lib_build_dir}/lib{library}.a");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ impl embedded_io::ErrorType for Writer {
|
|||
|
||||
impl embedded_io::Write for Writer {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
with_uart_tx(|uart| uart.write(buf))
|
||||
with_uart_tx(|_, uart| uart.write(buf))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
with_uart_tx(|uart| uart.flush())
|
||||
with_uart_tx(|_, uart| uart.flush())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,11 @@ use ekv::{
|
|||
};
|
||||
use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync_old::blocking_mutex::raw::CriticalSectionRawMutex as CriticalSectionRawMutexOld;
|
||||
use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash};
|
||||
use esp_hal::rng::Trng;
|
||||
use esp_storage::FlashStorage;
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::ram::PSRAM_ALLOCATOR;
|
||||
|
||||
pub type PartitionAcid =
|
||||
Partition<'static, CriticalSectionRawMutex, BlockingAsync<FlashStorage<'static>>>;
|
||||
|
||||
|
|
@ -27,7 +24,7 @@ struct AlignedBuf<const N: usize>(pub [u8; N]);
|
|||
|
||||
pub struct EkvFlash<T> {
|
||||
flash: T,
|
||||
buffer: Box<AlignedBuf<{ ekv::config::PAGE_SIZE }>, &'static esp_alloc::EspHeap>,
|
||||
buffer: Box<AlignedBuf<{ ekv::config::PAGE_SIZE }>>,
|
||||
}
|
||||
|
||||
impl<T> EkvFlash<T> {
|
||||
|
|
@ -36,7 +33,7 @@ impl<T> EkvFlash<T> {
|
|||
flash,
|
||||
buffer: {
|
||||
// Allocate the buffer directly on the heap.
|
||||
let buffer = Box::new_zeroed_in(&PSRAM_ALLOCATOR);
|
||||
let buffer = Box::new_zeroed();
|
||||
unsafe { buffer.assume_init() }
|
||||
},
|
||||
}
|
||||
|
|
@ -105,11 +102,11 @@ impl<T: NorFlash + ReadNorFlash> ekv::flash::Flash for EkvFlash<T> {
|
|||
}
|
||||
|
||||
pub struct AcidDatabase {
|
||||
db: Database<EkvFlash<PartitionAcid>, CriticalSectionRawMutexOld>,
|
||||
db: Database<EkvFlash<PartitionAcid>, esp_sync::RawMutex>,
|
||||
}
|
||||
|
||||
impl Deref for AcidDatabase {
|
||||
type Target = Database<EkvFlash<PartitionAcid>, CriticalSectionRawMutexOld>;
|
||||
type Target = Database<EkvFlash<PartitionAcid>, esp_sync::RawMutex>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.db
|
||||
|
|
@ -128,7 +125,7 @@ impl AcidDatabase {
|
|||
db_config.random_seed = Trng::try_new()
|
||||
.expect("A `TrngSource` was not initialized before constructing this `Trng`.")
|
||||
.random();
|
||||
let db = Database::<_, CriticalSectionRawMutexOld>::new(EkvFlash::new(flash), db_config);
|
||||
let db = Database::<_, esp_sync::RawMutex>::new(EkvFlash::new(flash), db_config);
|
||||
|
||||
#[cfg(feature = "format-db")]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,256 +0,0 @@
|
|||
use alloc::{alloc::Global, boxed::Box, vec::Vec};
|
||||
use embassy_time::Timer;
|
||||
use esp_hal::{
|
||||
Blocking,
|
||||
dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig},
|
||||
gpio::{Flex, Level, Output, OutputConfig},
|
||||
lcd_cam::{LcdCam, lcd::dpi::Dpi},
|
||||
ledc::{self, LSGlobalClkSource, Ledc, LowSpeed},
|
||||
spi::master::AnySpi,
|
||||
};
|
||||
use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in};
|
||||
use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel};
|
||||
use log::{error, info};
|
||||
|
||||
use crate::{DmaBounceController, PSRAM_ALLOCATOR, peripherals::st7701s::St7701s};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct DisplayPeripherals {
|
||||
pub DMA_CH2: esp_hal::peripherals::DMA_CH2<'static>,
|
||||
pub LCD_CAM: esp_hal::peripherals::LCD_CAM<'static>,
|
||||
pub LEDC: esp_hal::peripherals::LEDC<'static>,
|
||||
pub GPIO0: Output<'static>,
|
||||
pub GPIO1: esp_hal::peripherals::GPIO1<'static>,
|
||||
pub GPIO2: esp_hal::peripherals::GPIO2<'static>,
|
||||
pub GPIO3: esp_hal::peripherals::GPIO3<'static>,
|
||||
pub GPIO4: esp_hal::peripherals::GPIO4<'static>,
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
pub GPIO5: esp_hal::peripherals::GPIO5<'static>,
|
||||
pub GPIO6: esp_hal::peripherals::GPIO6<'static>,
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
pub GPIO12: esp_hal::peripherals::GPIO12<'static>,
|
||||
pub GPIO13: esp_hal::peripherals::GPIO13<'static>,
|
||||
pub GPIO14: esp_hal::peripherals::GPIO14<'static>,
|
||||
pub GPIO15: esp_hal::peripherals::GPIO15<'static>,
|
||||
pub GPIO16: esp_hal::peripherals::GPIO16<'static>,
|
||||
pub GPIO21: esp_hal::peripherals::GPIO21<'static>,
|
||||
pub GPIO34: esp_hal::peripherals::GPIO34<'static>,
|
||||
pub GPIO35: esp_hal::peripherals::GPIO35<'static>,
|
||||
pub GPIO36: esp_hal::peripherals::GPIO36<'static>,
|
||||
pub GPIO37: esp_hal::peripherals::GPIO37<'static>,
|
||||
pub GPIO38: esp_hal::peripherals::GPIO38<'static>,
|
||||
pub GPIO39: esp_hal::peripherals::GPIO39<'static>,
|
||||
pub GPIO40: esp_hal::peripherals::GPIO40<'static>,
|
||||
pub GPIO41: esp_hal::peripherals::GPIO41<'static>,
|
||||
pub GPIO42: esp_hal::peripherals::GPIO42<'static>,
|
||||
pub GPIO43: esp_hal::peripherals::GPIO43<'static>,
|
||||
pub GPIO44: esp_hal::peripherals::GPIO44<'static>,
|
||||
}
|
||||
|
||||
impl DisplayPeripherals {
|
||||
pub async fn into_display(self) -> St7701s<'static, Blocking> {
|
||||
let mut ledc = Ledc::new(self.LEDC);
|
||||
ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
|
||||
let bl_timer = ledc.timer::<LowSpeed>(ledc::timer::Number::Timer0);
|
||||
let bl_channel = ledc.channel::<LowSpeed>(ledc::channel::Number::Channel0, self.GPIO21);
|
||||
|
||||
let sck = Output::new(self.GPIO36, Level::High, OutputConfig::default());
|
||||
let mosi = Flex::new(self.GPIO35);
|
||||
let cs = Output::new(self.GPIO6, Level::High, OutputConfig::default());
|
||||
|
||||
let lcd = LcdCam::new(self.LCD_CAM).lcd;
|
||||
#[allow(unused_mut)]
|
||||
let mut unconfigured_dpi = Dpi::new(lcd, self.DMA_CH2, Default::default())
|
||||
.unwrap()
|
||||
.with_de(self.GPIO37)
|
||||
.with_pclk(self.GPIO34)
|
||||
.with_hsync(self.GPIO44)
|
||||
.with_vsync(self.GPIO43)
|
||||
// Blue
|
||||
.with_data0(self.GPIO38)
|
||||
.with_data1(self.GPIO39)
|
||||
.with_data2(self.GPIO40)
|
||||
.with_data3(self.GPIO41)
|
||||
.with_data4(self.GPIO42)
|
||||
// Green
|
||||
.with_data7(self.GPIO13)
|
||||
.with_data8(self.GPIO14)
|
||||
.with_data9(self.GPIO15)
|
||||
.with_data10(self.GPIO16)
|
||||
// Red
|
||||
.with_data11(self.GPIO0)
|
||||
.with_data12(self.GPIO1)
|
||||
.with_data13(self.GPIO2)
|
||||
.with_data14(self.GPIO3)
|
||||
.with_data15(self.GPIO4);
|
||||
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
{
|
||||
unconfigured_dpi = unconfigured_dpi
|
||||
// Green
|
||||
.with_data5(peripherals.GPIO5)
|
||||
.with_data6(peripherals.GPIO12);
|
||||
}
|
||||
|
||||
let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi, bl_timer, bl_channel).await;
|
||||
info!("ST7701S-based LCD display initialized!");
|
||||
st7701s
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Rename or get rid of.
|
||||
pub struct Framebuffer {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub swapchain: Option<SwapchainWriter>,
|
||||
pub bounce_buffers: Option<DmaBounce>,
|
||||
}
|
||||
|
||||
impl Framebuffer {
|
||||
pub fn new(
|
||||
channel: esp_hal::peripherals::DMA_CH0<'static>,
|
||||
peripheral_src: AnySpi<'static>,
|
||||
peripheral_dst: Dpi<'static, Blocking>,
|
||||
burst_config: BurstConfig,
|
||||
front_porch_pixels: u32,
|
||||
width_pixels: u32,
|
||||
height_pixels: u32,
|
||||
rows_per_window: usize,
|
||||
cyclic: bool,
|
||||
) -> Self {
|
||||
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
|
||||
let buffer_size = width_pixels as usize * height_pixels as usize * BYTES_PER_PIXEL;
|
||||
let framebuffers = [
|
||||
Box::leak(allocate_dma_buffer_in(
|
||||
buffer_size,
|
||||
burst_config,
|
||||
&PSRAM_ALLOCATOR,
|
||||
)),
|
||||
Box::leak(allocate_dma_buffer_in(
|
||||
buffer_size,
|
||||
burst_config,
|
||||
&PSRAM_ALLOCATOR,
|
||||
)),
|
||||
];
|
||||
let (swapchain_reader, swapchain_writer) = Swapchain { framebuffers }.into_reader_writer();
|
||||
let bounce_buffers = DmaBounce::new(
|
||||
Global,
|
||||
channel,
|
||||
peripheral_src,
|
||||
peripheral_dst,
|
||||
swapchain_reader,
|
||||
front_porch_pixels as usize * BYTES_PER_PIXEL,
|
||||
width_pixels as usize * BYTES_PER_PIXEL,
|
||||
rows_per_window,
|
||||
burst_config,
|
||||
cyclic,
|
||||
);
|
||||
|
||||
Self {
|
||||
width: width_pixels,
|
||||
height: height_pixels,
|
||||
swapchain: Some(swapchain_writer),
|
||||
bounce_buffers: Some(bounce_buffers),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub async fn test_bounce_buffers(
|
||||
channel: esp_hal::peripherals::DMA_CH0<'static>,
|
||||
peripheral: esp_hal::peripherals::SPI2<'static>,
|
||||
display_peripherals: DisplayPeripherals,
|
||||
) {
|
||||
error!("TEST BOUNCE BUFFERS SECTION ENTERED");
|
||||
|
||||
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
|
||||
// Assume highest burst config setting.
|
||||
const EXTERNAL_BURST_CONFIG: ExternalBurstConfig = ExternalBurstConfig::Size32;
|
||||
const ALIGNMENT_PIXELS: usize = EXTERNAL_BURST_CONFIG as usize / BYTES_PER_PIXEL;
|
||||
// The total number of pixels demanded by the DPI, per row.
|
||||
const WIDTH_TOTAL_PIXELS: usize = 368;
|
||||
// The total number of rows demanded by the DPI, per frame.
|
||||
const HEIGHT_PIXELS: usize = 960;
|
||||
// The number of unused pixels at the start of the row.
|
||||
const FRONT_PORCH_ACTUAL_PIXELS: usize = 120;
|
||||
// The number of actually visible pixels, per row.
|
||||
const WIDTH_VISIBLE_PIXELS: usize = 240;
|
||||
// The number of pixels not stored in a bounce buffer, per row.
|
||||
// This many arbitrary pixels are sent to the DPI.
|
||||
const FRONT_PORCH_SKIPPED_PIXELS: usize =
|
||||
(FRONT_PORCH_ACTUAL_PIXELS / ALIGNMENT_PIXELS) * ALIGNMENT_PIXELS;
|
||||
const WIDTH_STORED_PIXELS: usize = WIDTH_TOTAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS;
|
||||
const VISIBLE_OFFSET_IN_BUFFER_PIXELS: usize =
|
||||
FRONT_PORCH_ACTUAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS;
|
||||
const ROWS_PER_WINDOW: usize = 16;
|
||||
let burst_config = BurstConfig {
|
||||
internal_memory: InternalBurstConfig::Enabled,
|
||||
external_memory: EXTERNAL_BURST_CONFIG,
|
||||
};
|
||||
let (swapchain_reader, mut swapchain_writer) = Swapchain {
|
||||
framebuffers: [
|
||||
Box::leak(allocate_dma_buffer_in(
|
||||
HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
||||
burst_config,
|
||||
&PSRAM_ALLOCATOR,
|
||||
)),
|
||||
Box::leak(allocate_dma_buffer_in(
|
||||
HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
||||
burst_config,
|
||||
&PSRAM_ALLOCATOR,
|
||||
)),
|
||||
],
|
||||
}
|
||||
.into_reader_writer();
|
||||
|
||||
{
|
||||
let write_guard = &mut swapchain_writer.write();
|
||||
let buffer_src = bytemuck::cast_slice_mut::<u8, Rgb565Pixel>(write_guard);
|
||||
let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2)
|
||||
.rev()
|
||||
.map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0))
|
||||
.collect::<Vec<_>>();
|
||||
for (index, pixel) in buffer_src.iter_mut().enumerate() {
|
||||
let mut x =
|
||||
(index % WIDTH_STORED_PIXELS) as i16 - VISIBLE_OFFSET_IN_BUFFER_PIXELS as i16;
|
||||
let mut y = (index / WIDTH_STORED_PIXELS) as i16;
|
||||
|
||||
if x < WIDTH_VISIBLE_PIXELS as i16 {
|
||||
x = core::cmp::min(x, WIDTH_VISIBLE_PIXELS as i16 - 1 - x);
|
||||
y = core::cmp::min(y, HEIGHT_PIXELS as i16 - 1 - y);
|
||||
let min = core::cmp::min(x, y);
|
||||
|
||||
*pixel = colors[min as usize % colors.len()];
|
||||
continue;
|
||||
}
|
||||
|
||||
*pixel = Rgb565Pixel::default();
|
||||
}
|
||||
}
|
||||
|
||||
let mut st7701s = display_peripherals.into_display().await;
|
||||
let mut dma_bounce = DmaBounce::new(
|
||||
Global,
|
||||
channel,
|
||||
AnySpi::from(peripheral),
|
||||
st7701s.dpi,
|
||||
swapchain_reader,
|
||||
FRONT_PORCH_SKIPPED_PIXELS * BYTES_PER_PIXEL,
|
||||
WIDTH_STORED_PIXELS * BYTES_PER_PIXEL,
|
||||
ROWS_PER_WINDOW,
|
||||
burst_config,
|
||||
true,
|
||||
);
|
||||
let mut bb_controller = DmaBounceController::new(dma_bounce);
|
||||
|
||||
error!("TEST BOUNCE BUFFERS TASK LAUNCHED");
|
||||
|
||||
loop {
|
||||
bb_controller.start().await.unwrap();
|
||||
st7701s.controller.sleep_off().await;
|
||||
Timer::after_secs(1).await;
|
||||
st7701s.controller.sleep_on().await;
|
||||
bb_controller.stop().await.unwrap();
|
||||
Timer::after_secs(1).await;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ use enumset::EnumSet;
|
|||
use crate::ffi::string::__xkbc_memcpy;
|
||||
|
||||
// Here we select the allocator to use for libxkbcommon.
|
||||
pub use crate::ram::PSRAM_ALLOCATOR as XKBC_ALLOCATOR;
|
||||
pub use crate::PSRAM_ALLOCATOR as XKBC_ALLOCATOR;
|
||||
|
||||
// Implementation based on esp-alloc's `compat` feature.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use core::{
|
|||
ffi::{c_char, c_int, c_size_t, c_uchar, c_ulonglong},
|
||||
};
|
||||
|
||||
use critical_section::Mutex;
|
||||
use embassy_sync::blocking_mutex::{self, raw::CriticalSectionRawMutex};
|
||||
use hmac::digest::{FixedOutput, KeyInit, Update};
|
||||
use password_hash::Key;
|
||||
|
|
@ -38,8 +39,7 @@ unsafe extern "C" fn __spre_crypto_hash_sha256(
|
|||
|
||||
/// This is the encrypted user key currently being used in the key derivation function of spectre.
|
||||
/// It decrypts using the user's password into the key that would be derived with the original password hashing function.
|
||||
pub static ACTIVE_ENCRYPTED_USER_KEY: blocking_mutex::Mutex<CriticalSectionRawMutex, Cell<Key>> =
|
||||
blocking_mutex::Mutex::new(Cell::new([0; _]));
|
||||
pub static ACTIVE_ENCRYPTED_USER_KEY: Mutex<Cell<Key>> = Mutex::new(Cell::new([0; _]));
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[must_use]
|
||||
|
|
@ -65,7 +65,7 @@ unsafe extern "C" fn __spre_crypto_pwhash_scryptsalsa208sha256_ll(
|
|||
};
|
||||
|
||||
let output: &mut [u8] = unsafe { core::slice::from_raw_parts_mut(output, output_len) };
|
||||
let mut user_key = ACTIVE_ENCRYPTED_USER_KEY.lock(|user_key| user_key.get());
|
||||
let mut user_key = critical_section::with(|cs| ACTIVE_ENCRYPTED_USER_KEY.borrow(cs).get());
|
||||
|
||||
password_hash::decrypt_with(&mut user_key, &encryption_key);
|
||||
output.copy_from_slice(&user_key);
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
use core::fmt::Write;
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||
use esp_bootloader_esp_idf::partitions::PartitionTable;
|
||||
use esp_storage::FlashStorage;
|
||||
use indoc::writedoc;
|
||||
use log::info;
|
||||
use rmk::storage::async_flash_wrapper;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
use crate::PSRAM_ALLOCATOR;
|
||||
|
||||
pub type Partition = embassy_embedded_hal::flash::partition::Partition<
|
||||
'static,
|
||||
CriticalSectionRawMutex,
|
||||
BlockingAsync<FlashStorage<'static>>,
|
||||
>;
|
||||
|
||||
pub struct Partitions {
|
||||
pub rmk: Partition,
|
||||
pub acid: Partition,
|
||||
}
|
||||
|
||||
/// Initialize the flash
|
||||
pub fn initialize(flash_peripheral: esp_hal::peripherals::FLASH<'static>) -> Partitions {
|
||||
static PARTITION_TABLE_BUFFER: StaticCell<Vec<u8, &'static esp_alloc::EspHeap>> =
|
||||
StaticCell::new();
|
||||
let partition_table_buffer = PARTITION_TABLE_BUFFER.init_with(|| {
|
||||
let mut buffer = Vec::<u8, _>::new_in(&PSRAM_ALLOCATOR);
|
||||
buffer.resize(
|
||||
esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN,
|
||||
0_u8,
|
||||
);
|
||||
buffer
|
||||
});
|
||||
|
||||
static FLASH: StaticCell<(
|
||||
Mutex<CriticalSectionRawMutex, BlockingAsync<FlashStorage>>,
|
||||
PartitionTable<'static>,
|
||||
)> = StaticCell::new();
|
||||
let (flash, partition_table) = FLASH.init_with(|| {
|
||||
let mut flash = FlashStorage::new(flash_peripheral)
|
||||
// Flash memory may not be written to while another core is executing from it.
|
||||
// By default, `FlashStorage` is configured to abort the operation and log an error message.
|
||||
// However, it can also be configured to auto-park the other core, such that writing to
|
||||
// flash succeeds.
|
||||
// Alternatively, XiP from PSRAM could be used along with the `multicore_ignore` strategy,
|
||||
// to avoid having to park the other core, which could result in better performance.
|
||||
// Invalid configuration would then present itself as freezing/UB.
|
||||
.multicore_auto_park();
|
||||
let partition_table = {
|
||||
esp_bootloader_esp_idf::partitions::read_partition_table(
|
||||
&mut flash,
|
||||
partition_table_buffer,
|
||||
)
|
||||
.expect("Failed to read the partition table.")
|
||||
};
|
||||
|
||||
(
|
||||
Mutex::<CriticalSectionRawMutex, _>::new(async_flash_wrapper(flash)),
|
||||
partition_table,
|
||||
)
|
||||
});
|
||||
|
||||
{
|
||||
let mut buffer = String::new();
|
||||
|
||||
writeln!(buffer, "Partition table:").unwrap();
|
||||
|
||||
for (index, partition) in partition_table.iter().enumerate() {
|
||||
writedoc!(
|
||||
buffer,
|
||||
"
|
||||
Partition #{index} {label:?}:
|
||||
offset: 0x{offset:x}
|
||||
length: 0x{len:x}
|
||||
type: 0x{type:?}
|
||||
read only: {read_only}
|
||||
encrypted: {encrypted}
|
||||
magic: {magic}
|
||||
",
|
||||
label = partition.label_as_str(),
|
||||
offset = partition.offset(),
|
||||
len = partition.len(),
|
||||
type = partition.partition_type(),
|
||||
read_only = partition.is_read_only(),
|
||||
encrypted = partition.is_encrypted(),
|
||||
magic = partition.magic(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
info!("{}", buffer);
|
||||
}
|
||||
let flash_part_info_rmk = partition_table
|
||||
.iter()
|
||||
.find(|partition| partition.label_as_str() == "rmk")
|
||||
.expect("No \"rmk\" partition found. Make sure to use the custom partition-table.csv when flashing.");
|
||||
let flash_part_info_acid = partition_table
|
||||
.iter()
|
||||
.find(|partition| partition.label_as_str() == "acid")
|
||||
.expect("No \"acid\" partition found. Make sure to use the custom partition-table.csv when flashing.");
|
||||
|
||||
Partitions {
|
||||
rmk: Partition::new(
|
||||
flash,
|
||||
flash_part_info_rmk.offset(),
|
||||
flash_part_info_rmk.len(),
|
||||
),
|
||||
acid: Partition::new(
|
||||
flash,
|
||||
flash_part_info_acid.offset(),
|
||||
flash_part_info_acid.len(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,9 @@ 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") {
|
||||
LevelFilter::Off
|
||||
} else if string.eq_ignore_ascii_case("ERROR") {
|
||||
if string.eq_ignore_ascii_case("ERROR") {
|
||||
LevelFilter::Error
|
||||
} else if string.eq_ignore_ascii_case("WARN") {
|
||||
LevelFilter::Warn
|
||||
|
|
@ -60,9 +57,10 @@ fn with_formatted_log_record<R>(
|
|||
pub mod usb {
|
||||
use super::*;
|
||||
|
||||
pub fn setup_logging() {
|
||||
pub fn setup_logging() -> impl Future<Output = ()> {
|
||||
esp_println::logger::init_logger(LOG_LEVEL_FILTER);
|
||||
log::info!("Logger initialized!");
|
||||
async {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,45 +69,27 @@ pub mod usb {
|
|||
#[macro_use]
|
||||
pub mod uart {
|
||||
use super::*;
|
||||
|
||||
use crate::console;
|
||||
use core::{cell::RefCell, fmt::Write};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use esp_hal::{Blocking, uart::UartTx};
|
||||
use critical_section::{CriticalSection, Mutex};
|
||||
use esp_hal::{
|
||||
Blocking,
|
||||
gpio::interconnect::{PeripheralInput, PeripheralOutput},
|
||||
uart::{Uart, UartTx},
|
||||
};
|
||||
use log::{Log, info};
|
||||
|
||||
#[cfg(feature = "racy-logging")]
|
||||
static ALT_LOGGER_UART: embassy_sync::mutex::Mutex<
|
||||
CriticalSectionRawMutex,
|
||||
RefCell<Option<UartTx<'static, Blocking>>>,
|
||||
> = embassy_sync::mutex::Mutex::new(RefCell::new(None));
|
||||
static ALT_LOGGER_UART: Mutex<RefCell<Option<UartTx<'static, Blocking>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
|
||||
#[cfg(not(feature = "racy-logging"))]
|
||||
static ALT_LOGGER_UART: embassy_sync::blocking_mutex::Mutex<
|
||||
CriticalSectionRawMutex,
|
||||
RefCell<Option<UartTx<'static, Blocking>>>,
|
||||
> = embassy_sync::blocking_mutex::Mutex::new(RefCell::new(None));
|
||||
|
||||
#[cfg(feature = "racy-logging")]
|
||||
pub fn with_uart_tx<R>(f: impl FnOnce(&'_ mut UartTx<'static, Blocking>) -> R) -> R {
|
||||
use crate::util::MutexExt;
|
||||
|
||||
// Safety:
|
||||
// * The guard is not held across yield points.
|
||||
// * **CARE MUST BE TAKEN NOT TO INVOKE THIS FUNCTION FROM AN INTERRUPT HANDLER.**
|
||||
let uart = unsafe { ALT_LOGGER_UART.lock_blocking() };
|
||||
let mut uart = uart.borrow_mut();
|
||||
pub fn with_uart_tx<R>(
|
||||
f: impl FnOnce(CriticalSection<'_>, &'_ mut UartTx<'static, Blocking>) -> R,
|
||||
) -> R {
|
||||
critical_section::with(|cs| {
|
||||
let mut uart = ALT_LOGGER_UART.borrow(cs).borrow_mut();
|
||||
let uart = uart.as_mut().unwrap();
|
||||
|
||||
(f)(uart)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "racy-logging"))]
|
||||
pub fn with_uart_tx<R>(f: impl FnOnce(&'_ mut UartTx<'static, Blocking>) -> R) -> R {
|
||||
ALT_LOGGER_UART.lock(|uart| {
|
||||
let mut uart = uart.borrow_mut();
|
||||
let uart = uart.as_mut().unwrap();
|
||||
|
||||
(f)(uart)
|
||||
(f)(cs, uart)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +106,7 @@ pub mod uart {
|
|||
|
||||
#[allow(unused)]
|
||||
fn do_print(args: core::fmt::Arguments<'_>) {
|
||||
with_uart_tx(|uart| {
|
||||
with_uart_tx(|_, uart| {
|
||||
uart.write_fmt(format_args!("{}\n", args)).unwrap();
|
||||
uart.flush().unwrap();
|
||||
})
|
||||
|
|
@ -143,7 +123,7 @@ pub mod uart {
|
|||
|
||||
#[allow(unused)]
|
||||
fn log(&self, record: &log::Record) {
|
||||
with_uart_tx(|uart| {
|
||||
with_uart_tx(|cs, uart| {
|
||||
with_formatted_log_record(record, |args| uart.write_fmt(args)).unwrap();
|
||||
uart.flush().unwrap();
|
||||
})
|
||||
|
|
@ -173,24 +153,20 @@ pub mod uart {
|
|||
loop {}
|
||||
}
|
||||
|
||||
pub fn setup_logging(uart_tx: UartTx<'static, Blocking>) {
|
||||
{
|
||||
#[cfg(feature = "racy-logging")]
|
||||
{
|
||||
use crate::util::MutexExt;
|
||||
pub fn setup_logging(
|
||||
uart: impl esp_hal::uart::Instance + 'static,
|
||||
tx: impl PeripheralOutput<'static>,
|
||||
rx: impl PeripheralInput<'static>,
|
||||
) -> impl Future<Output = ()> {
|
||||
let (uart_rx, uart_tx) = Uart::new(uart, Default::default())
|
||||
.unwrap()
|
||||
.with_tx(tx)
|
||||
.with_rx(rx)
|
||||
.split();
|
||||
|
||||
// Safety:
|
||||
// * The guard is not held across yield points.
|
||||
// * This function is not invoked from an interrupt handler.
|
||||
let uart = unsafe { ALT_LOGGER_UART.lock_blocking() };
|
||||
*uart.borrow_mut() = Some(uart_tx);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "racy-logging"))]
|
||||
ALT_LOGGER_UART.lock(move |uart| {
|
||||
*uart.borrow_mut() = Some(uart_tx);
|
||||
critical_section::with(|cs| {
|
||||
*ALT_LOGGER_UART.borrow(cs).borrow_mut() = Some(uart_tx);
|
||||
});
|
||||
}
|
||||
|
||||
unsafe {
|
||||
log::set_logger_racy(&UartLogger).unwrap();
|
||||
|
|
@ -198,6 +174,7 @@ pub mod uart {
|
|||
}
|
||||
|
||||
info!("Logger initialized!");
|
||||
console::run_console(uart_rx.into_async())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -210,9 +187,10 @@ pub mod rtt {
|
|||
use panic_rtt_target as _; // Use the RTT panic handler.
|
||||
use rtt_target::ChannelMode;
|
||||
|
||||
pub fn setup_logging() {
|
||||
pub fn setup_logging() -> impl Future<Output = ()> {
|
||||
rtt_target::rtt_init_log!(LOG_LEVEL_FILTER, ChannelMode::BlockIfFull);
|
||||
log::info!("Logger initialized!");
|
||||
async {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,58 +16,72 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt::Write;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::vec_deque::VecDeque;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||
use embassy_embedded_hal::flash::partition::Partition;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_sync::signal::Signal;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use esp_alloc::MemoryCapability;
|
||||
use esp_alloc::{HeapRegion, MemoryCapability};
|
||||
use esp_bootloader_esp_idf::partitions::PartitionTable;
|
||||
use esp_hal::Blocking;
|
||||
use esp_hal::clock::CpuClock;
|
||||
use esp_hal::dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig};
|
||||
use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig};
|
||||
use esp_hal::efuse::Efuse;
|
||||
use esp_hal::gpio::{Input, InputConfig, Level, Output, OutputConfig, Pull};
|
||||
use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull};
|
||||
use esp_hal::i2c::master::{I2c, I2cAddress};
|
||||
use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl};
|
||||
use esp_hal::interrupt::software::SoftwareInterruptControl;
|
||||
use esp_hal::lcd_cam::LcdCam;
|
||||
use esp_hal::lcd_cam::lcd::dpi::Dpi;
|
||||
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
|
||||
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
||||
use esp_hal::ram;
|
||||
use esp_hal::rng::TrngSource;
|
||||
use esp_hal::sha::ShaBackend;
|
||||
use esp_hal::system::Stack;
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
use esp_hal::uart::{Uart, UartRx};
|
||||
use esp_hal::{Blocking, interrupt};
|
||||
use esp_hal_bounce_buffers::{DmaBounce, RunningDmaBounceHandle};
|
||||
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
||||
use esp_rtos::embassy::Executor;
|
||||
use esp_storage::FlashStorage;
|
||||
use indoc::writedoc;
|
||||
use itertools::Itertools;
|
||||
use log::{info, warn};
|
||||
use log::{error, info, warn};
|
||||
use rmk::channel::{CONTROLLER_CHANNEL, ControllerSub};
|
||||
use rmk::config::{DeviceConfig, RmkConfig, StorageConfig, VialConfig};
|
||||
use rmk::config::{
|
||||
DeviceConfig, RmkConfig, StorageConfig,
|
||||
VialConfig,
|
||||
};
|
||||
use rmk::controller::{Controller, EventController};
|
||||
use rmk::debounce::default_debouncer::DefaultDebouncer;
|
||||
use rmk::event::ControllerEvent;
|
||||
use rmk::hid::Report;
|
||||
use rmk::input_device::Runnable;
|
||||
use rmk::join_all;
|
||||
use rmk::keyboard::Keyboard;
|
||||
use rmk::storage::async_flash_wrapper;
|
||||
use rmk::types::action::{Action, KeyAction};
|
||||
use rmk::join_all;
|
||||
use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
|
||||
use slint::platform::software_renderer::Rgb565Pixel;
|
||||
use static_cell::StaticCell;
|
||||
use {esp_alloc as _, esp_backtrace as _};
|
||||
|
||||
use crate::dpi::{DisplayPeripherals, Framebuffer};
|
||||
use crate::flash::Partition;
|
||||
use crate::matrix::IoeMatrix;
|
||||
use crate::peripherals::st7701s::St7701sController;
|
||||
use crate::peripherals::st7701s::St7701s;
|
||||
use crate::proxy::create_hid_report_interceptor;
|
||||
use crate::ram::{PSRAM_ALLOCATOR, STACK_SIZE_CORE_APP};
|
||||
use crate::ui::backend::SlintBackend;
|
||||
use crate::ui::backend::{FramebufferPtr, SlintBackend};
|
||||
use crate::vial::{
|
||||
CustomKeycodes, VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID, VIAL_KEYBOARD_NAME, VIAL_PRODUCT_ID,
|
||||
VIAL_VENDOR_ID,
|
||||
|
|
@ -78,14 +92,11 @@ mutually_exclusive_features::none_or_one_of!["usb-log", "alt-log", "rtt-log"];
|
|||
mod config;
|
||||
mod crypto;
|
||||
mod db;
|
||||
mod dpi;
|
||||
mod ffi;
|
||||
mod flash;
|
||||
mod logging;
|
||||
mod matrix;
|
||||
mod peripherals;
|
||||
mod proxy;
|
||||
mod ram;
|
||||
mod ui;
|
||||
mod util;
|
||||
mod vial;
|
||||
|
|
@ -97,12 +108,29 @@ mod console;
|
|||
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
||||
esp_bootloader_esp_idf::esp_app_desc!();
|
||||
|
||||
// Memory allocation regions.
|
||||
// These can be debugged using `xtensa-esp32s3-elf-size -A <path-to-binary>`.
|
||||
// A panic such as `memory allocation of 3740121773 bytes failed` is caused by a heap overflow. The size is `DEEDBAAD` in hex.
|
||||
|
||||
/// Total heap size
|
||||
const HEAP_SIZE: usize = 112 * 1024;
|
||||
/// Size of the app core's stack
|
||||
const STACK_SIZE_CORE_APP: usize = 80 * 1024;
|
||||
|
||||
// const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS
|
||||
const FRAME_DURATION_MIN: Duration = Duration::from_millis(100); // 10 FPS
|
||||
|
||||
pub static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
|
||||
|
||||
static KEYBOARD_REPORT_PROXY: Channel<CriticalSectionRawMutex, Report, 16> = Channel::new();
|
||||
static LCD_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Used to signal that MCU is ready to submit the framebuffer to the LCD.
|
||||
static SIGNAL_LCD_SUBMIT: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
|
||||
/// Used to signal that the MCU is ready to render the GUI.
|
||||
static SIGNAL_UI_RENDER: Signal<CriticalSectionRawMutex, ()> = Signal::new();
|
||||
|
||||
#[esp_rtos::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let config = esp_hal::Config::default()
|
||||
|
|
@ -110,34 +138,45 @@ async fn main(_spawner: Spawner) {
|
|||
.with_psram(PsramConfig {
|
||||
size: PsramSize::AutoDetect,
|
||||
core_clock: Some(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m),
|
||||
flash_frequency: FlashFreq::FlashFreq120m,
|
||||
ram_frequency: SpiRamFreq::Freq120m,
|
||||
flash_frequency: FlashFreq::default(),
|
||||
ram_frequency: SpiRamFreq::Freq80m,
|
||||
});
|
||||
let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config);
|
||||
|
||||
#[allow(unused)]
|
||||
let (uart_rx, uart_tx) = {
|
||||
#[cfg(feature = "usb-log")]
|
||||
let console_task = logging::usb::setup_logging();
|
||||
#[cfg(feature = "alt-log")]
|
||||
let (tx, rx) = (peripherals.GPIO12, peripherals.GPIO5);
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
let (tx, rx) = (esp_hal::gpio::NoPin, esp_hal::gpio::NoPin);
|
||||
let console_task =
|
||||
logging::uart::setup_logging(peripherals.UART2, peripherals.GPIO12, peripherals.GPIO5);
|
||||
#[cfg(feature = "rtt-log")]
|
||||
let console_task = logging::rtt::setup_logging();
|
||||
|
||||
Uart::new(peripherals.UART2, Default::default())
|
||||
.unwrap()
|
||||
.with_tx(tx)
|
||||
.with_rx(rx)
|
||||
.split()
|
||||
// Use the internal DRAM as the heap.
|
||||
// Memory reclaimed from the esp-idf bootloader.
|
||||
const HEAP_SIZE_RECLAIMED: usize = const {
|
||||
let range = esp_metadata_generated::memory_range!("DRAM2_UNINIT");
|
||||
range.end - range.start
|
||||
};
|
||||
|
||||
#[cfg(feature = "usb-log")]
|
||||
logging::usb::setup_logging();
|
||||
#[cfg(feature = "alt-log")]
|
||||
logging::uart::setup_logging(uart_tx);
|
||||
#[cfg(feature = "rtt-log")]
|
||||
logging::rtt::setup_logging();
|
||||
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED);
|
||||
esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED);
|
||||
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
|
||||
|
||||
// Set up allocators.
|
||||
ram::initialize(peripherals.PSRAM);
|
||||
// Initialize the PSRAM allocator.
|
||||
{
|
||||
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(),
|
||||
));
|
||||
}
|
||||
info!(
|
||||
"PSRAM allocator initialized with capacity of {} MiB!",
|
||||
psram_size / 1024 / 1024
|
||||
);
|
||||
}
|
||||
|
||||
// let mut io = Io::new(peripherals.IO_MUX);
|
||||
// io.set_interrupt_handler(interrupt_handler);
|
||||
|
|
@ -157,123 +196,21 @@ async fn main(_spawner: Spawner) {
|
|||
// Enable antenna
|
||||
let _ = Output::new(peripherals.GPIO11, Level::Low, OutputConfig::default());
|
||||
|
||||
// TODO: Use PWM to control the pwm_pin.
|
||||
let mut _pwm = McPwm::new(peripherals.MCPWM0, PeripheralClockConfig::with_prescaler(1));
|
||||
let mut _pwm_pin = Output::new(peripherals.GPIO21, Level::High, OutputConfig::default());
|
||||
|
||||
let mut sha_backend = ShaBackend::new(peripherals.SHA);
|
||||
let _sha_driver_handle = sha_backend.start();
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
||||
esp_rtos::start(
|
||||
timg0.timer0,
|
||||
// software_interrupt.software_interrupt0,
|
||||
timg0.timer0, /*, software_interrupt.software_interrupt0 */
|
||||
);
|
||||
|
||||
// A task executor that is able to handle interrupts, and then return back to executing tasks.
|
||||
static EXECUTOR_CORE_0: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
||||
let executor_core_0 = InterruptExecutor::new(software_interrupt.software_interrupt2);
|
||||
let executor_core_0 = EXECUTOR_CORE_0.init(executor_core_0);
|
||||
let interrupt_core_0_spawner = executor_core_0.start(interrupt::Priority::Priority1);
|
||||
|
||||
// static EXECUTOR_CORE_1: StaticCell<InterruptExecutor<3>> = StaticCell::new();
|
||||
// let executor_core_1 = InterruptExecutor::new(software_interrupt.software_interrupt3);
|
||||
// let executor_core_1 = EXECUTOR_CORE_1.init(executor_core_1);
|
||||
// let interrupt_core_1_spawner = executor_core_1.start(interrupt::Priority::Priority2);
|
||||
|
||||
info!("ESP-RTOS started!");
|
||||
|
||||
let main_task_peripherals = MainPeripherals {
|
||||
// high_priority_task_spawner: interrupt_core_1_spawner,
|
||||
uart_rx,
|
||||
software_interrupt1: software_interrupt.software_interrupt1,
|
||||
software_interrupt0: software_interrupt.software_interrupt0,
|
||||
RNG: peripherals.RNG,
|
||||
ADC1: peripherals.ADC1,
|
||||
USB0: peripherals.USB0,
|
||||
FLASH: peripherals.FLASH,
|
||||
DMA_CH0: peripherals.DMA_CH0,
|
||||
SPI2: peripherals.SPI2,
|
||||
CPU_CTRL: peripherals.CPU_CTRL,
|
||||
GPIO19: peripherals.GPIO19,
|
||||
GPIO20: peripherals.GPIO20,
|
||||
display: DisplayPeripherals {
|
||||
DMA_CH2: peripherals.DMA_CH2,
|
||||
LCD_CAM: peripherals.LCD_CAM,
|
||||
LEDC: peripherals.LEDC,
|
||||
GPIO0: gpio0,
|
||||
GPIO1: peripherals.GPIO1,
|
||||
GPIO2: peripherals.GPIO2,
|
||||
GPIO3: peripherals.GPIO3,
|
||||
GPIO4: peripherals.GPIO4,
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
GPIO5: peripherals.GPIO5,
|
||||
GPIO6: peripherals.GPIO6,
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
GPIO12: peripherals.GPIO12,
|
||||
GPIO13: peripherals.GPIO13,
|
||||
GPIO14: peripherals.GPIO14,
|
||||
GPIO15: peripherals.GPIO15,
|
||||
GPIO16: peripherals.GPIO16,
|
||||
GPIO21: peripherals.GPIO21,
|
||||
GPIO34: peripherals.GPIO34,
|
||||
GPIO35: peripherals.GPIO35,
|
||||
GPIO36: peripherals.GPIO36,
|
||||
GPIO37: peripherals.GPIO37,
|
||||
GPIO38: peripherals.GPIO38,
|
||||
GPIO39: peripherals.GPIO39,
|
||||
GPIO40: peripherals.GPIO40,
|
||||
GPIO41: peripherals.GPIO41,
|
||||
GPIO42: peripherals.GPIO42,
|
||||
GPIO43: peripherals.GPIO43,
|
||||
GPIO44: peripherals.GPIO44,
|
||||
},
|
||||
matrix: MatrixPeripherals {
|
||||
I2C0: peripherals.I2C0,
|
||||
GPIO7: peripherals.GPIO7,
|
||||
GPIO8: peripherals.GPIO8,
|
||||
GPIO9: peripherals.GPIO9,
|
||||
},
|
||||
};
|
||||
|
||||
interrupt_core_0_spawner.must_spawn(main_task(main_task_peripherals));
|
||||
}
|
||||
|
||||
/// Peripherals passed to the main task.
|
||||
#[allow(non_snake_case)]
|
||||
struct MainPeripherals {
|
||||
uart_rx: UartRx<'static, Blocking>,
|
||||
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
||||
software_interrupt0: SoftwareInterrupt<'static, 0>,
|
||||
RNG: esp_hal::peripherals::RNG<'static>,
|
||||
ADC1: esp_hal::peripherals::ADC1<'static>,
|
||||
USB0: esp_hal::peripherals::USB0<'static>,
|
||||
FLASH: esp_hal::peripherals::FLASH<'static>,
|
||||
DMA_CH0: esp_hal::peripherals::DMA_CH0<'static>,
|
||||
SPI2: esp_hal::peripherals::SPI2<'static>,
|
||||
CPU_CTRL: esp_hal::peripherals::CPU_CTRL<'static>,
|
||||
// GPIO10: esp_hal::peripherals::GPIO10<'static>,
|
||||
// GPIO18: esp_hal::peripherals::GPIO18<'static>,
|
||||
GPIO19: esp_hal::peripherals::GPIO19<'static>,
|
||||
GPIO20: esp_hal::peripherals::GPIO20<'static>,
|
||||
// GPIO33: esp_hal::peripherals::GPIO33<'static>,
|
||||
// GPIO45: esp_hal::peripherals::GPIO45<'static>,
|
||||
// GPIO46: esp_hal::peripherals::GPIO46<'static>,
|
||||
// GPIO47: esp_hal::peripherals::GPIO47<'static>,
|
||||
// GPIO48: esp_hal::peripherals::GPIO48<'static>,
|
||||
display: DisplayPeripherals,
|
||||
matrix: MatrixPeripherals,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
struct MatrixPeripherals {
|
||||
I2C0: esp_hal::peripherals::I2C0<'static>,
|
||||
GPIO7: esp_hal::peripherals::GPIO7<'static>,
|
||||
GPIO8: esp_hal::peripherals::GPIO8<'static>,
|
||||
GPIO9: esp_hal::peripherals::GPIO9<'static>,
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn main_task(peripherals: MainPeripherals) {
|
||||
// let _spawner = unsafe { Spawner::for_current_executor() }.await;
|
||||
|
||||
// Enable the TRNG source, so `Trng` can be constructed.
|
||||
let _trng_source = TrngSource::new(peripherals.RNG, peripherals.ADC1);
|
||||
|
||||
|
|
@ -319,13 +256,131 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
};
|
||||
|
||||
// Initialize the flash
|
||||
let flash_partitions = flash::initialize(peripherals.FLASH);
|
||||
static PARTITION_TABLE_BUFFER: StaticCell<Vec<u8, &'static esp_alloc::EspHeap>> =
|
||||
StaticCell::new();
|
||||
let partition_table_buffer = PARTITION_TABLE_BUFFER.init_with(|| {
|
||||
let mut buffer = Vec::<u8, _>::new_in(&PSRAM_ALLOCATOR);
|
||||
buffer.resize(1024, 0_u8);
|
||||
buffer
|
||||
});
|
||||
|
||||
static FLASH: StaticCell<(
|
||||
Mutex<CriticalSectionRawMutex, BlockingAsync<FlashStorage>>,
|
||||
PartitionTable<'static>,
|
||||
)> = StaticCell::new();
|
||||
let (flash, partition_table) = FLASH.init_with(|| {
|
||||
let mut flash = FlashStorage::new(peripherals.FLASH)
|
||||
// Flash memory may not be written to while another core is executing from it.
|
||||
// By default, `FlashStorage` is configured to abort the operation and log an error message.
|
||||
// However, it can also be configured to auto-park the other core, such that writing to
|
||||
// flash succeeds.
|
||||
// Alternatively, XiP from PSRAM could be used along with the `multicore_ignore` strategy,
|
||||
// to avoid having to park the other core, which could result in better performance.
|
||||
// Invalid configuration would then present itself as freezing/UB.
|
||||
.multicore_auto_park();
|
||||
let partition_table = {
|
||||
esp_bootloader_esp_idf::partitions::read_partition_table(
|
||||
&mut flash,
|
||||
partition_table_buffer,
|
||||
)
|
||||
.expect("Failed to read the partition table.")
|
||||
};
|
||||
|
||||
(
|
||||
Mutex::<CriticalSectionRawMutex, _>::new(async_flash_wrapper(flash)),
|
||||
partition_table,
|
||||
)
|
||||
});
|
||||
|
||||
{
|
||||
let mut buffer = String::new();
|
||||
|
||||
writeln!(buffer, "Partition table:").unwrap();
|
||||
|
||||
for (index, partition) in partition_table.iter().enumerate() {
|
||||
writedoc!(
|
||||
buffer,
|
||||
"
|
||||
Partition #{index} {label:?}:
|
||||
offset: 0x{offset:x}
|
||||
length: 0x{len:x}
|
||||
type: 0x{type:?}
|
||||
read only: {read_only}
|
||||
encrypted: {encrypted}
|
||||
magic: {magic}
|
||||
",
|
||||
label = partition.label_as_str(),
|
||||
offset = partition.offset(),
|
||||
len = partition.len(),
|
||||
type = partition.partition_type(),
|
||||
read_only = partition.is_read_only(),
|
||||
encrypted = partition.is_encrypted(),
|
||||
magic = partition.magic(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
info!("{}", buffer);
|
||||
}
|
||||
let flash_part_info_rmk = partition_table
|
||||
.iter()
|
||||
.find(|partition| partition.label_as_str() == "rmk")
|
||||
.expect("No \"rmk\" partition found. Make sure to use the custom partition-table.csv when flashing.");
|
||||
let flash_part_info_acid = partition_table
|
||||
.iter()
|
||||
.find(|partition| partition.label_as_str() == "acid")
|
||||
.expect("No \"acid\" partition found. Make sure to use the custom partition-table.csv when flashing.");
|
||||
let flash_part_rmk = Partition::new(
|
||||
flash,
|
||||
flash_part_info_rmk.offset(),
|
||||
flash_part_info_rmk.len(),
|
||||
);
|
||||
let flash_part_acid = Partition::new(
|
||||
flash,
|
||||
flash_part_info_acid.offset(),
|
||||
flash_part_info_acid.len(),
|
||||
);
|
||||
|
||||
info!("Flash memory configured!");
|
||||
|
||||
// Uncomment this to run bounce buffer test code instead.
|
||||
// dpi::test_bounce_buffers(peripherals.DMA_CH0, peripherals.SPI2, peripherals.display).await;
|
||||
// return;
|
||||
let sck = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default());
|
||||
let mosi = Flex::new(peripherals.GPIO35);
|
||||
let cs = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default());
|
||||
|
||||
let lcd = LcdCam::new(peripherals.LCD_CAM).lcd;
|
||||
let unconfigured_dpi = Dpi::new(lcd, peripherals.DMA_CH2, Default::default())
|
||||
.unwrap()
|
||||
.with_de(peripherals.GPIO37)
|
||||
.with_pclk(peripherals.GPIO34)
|
||||
.with_hsync(peripherals.GPIO44)
|
||||
.with_vsync(peripherals.GPIO43)
|
||||
// Blue
|
||||
.with_data0(peripherals.GPIO38)
|
||||
.with_data1(peripherals.GPIO39)
|
||||
.with_data2(peripherals.GPIO40)
|
||||
.with_data3(peripherals.GPIO41)
|
||||
.with_data4(peripherals.GPIO42)
|
||||
// Green
|
||||
.with_data7(peripherals.GPIO13)
|
||||
.with_data8(peripherals.GPIO14)
|
||||
.with_data9(peripherals.GPIO15)
|
||||
.with_data10(peripherals.GPIO16)
|
||||
// Red
|
||||
.with_data11(gpio0)
|
||||
.with_data12(peripherals.GPIO1)
|
||||
.with_data13(peripherals.GPIO2)
|
||||
.with_data14(peripherals.GPIO3)
|
||||
.with_data15(peripherals.GPIO4);
|
||||
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
let unconfigured_dpi = unconfigured_dpi
|
||||
// Green
|
||||
.with_data5(peripherals.GPIO5)
|
||||
.with_data6(peripherals.GPIO12);
|
||||
|
||||
let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi).await;
|
||||
|
||||
info!("ST7701S-based LCD display initialized!");
|
||||
|
||||
// RMK config
|
||||
let vial_config = VialConfig::new(VIAL_KEYBOARD_ID, VIAL_KEYBOARD_DEF, &[(0, 0), (1, 1)]);
|
||||
|
|
@ -333,12 +388,12 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
start_addr: 0,
|
||||
num_sectors: {
|
||||
assert!(
|
||||
flash_partitions.rmk.size() % FlashStorage::SECTOR_SIZE == 0,
|
||||
flash_part_info_rmk.len() % FlashStorage::SECTOR_SIZE == 0,
|
||||
"The size of the RMK partition must be a multiple of {} bytes. Current size: {}",
|
||||
FlashStorage::SECTOR_SIZE,
|
||||
flash_partitions.rmk.size()
|
||||
flash_part_info_rmk.len()
|
||||
);
|
||||
(flash_partitions.rmk.size() / FlashStorage::SECTOR_SIZE) as u8
|
||||
(flash_part_info_rmk.len() / FlashStorage::SECTOR_SIZE) as u8
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -375,7 +430,7 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
let mut positional_config = config::get_positional_config();
|
||||
let (keymap, mut storage) = initialize_keymap_and_storage(
|
||||
&mut default_keymap,
|
||||
flash_partitions.rmk,
|
||||
flash_part_rmk,
|
||||
&storage_config,
|
||||
&mut behavior_config,
|
||||
&mut positional_config,
|
||||
|
|
@ -384,15 +439,85 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
|
||||
info!("Initialized keymap and storage for RMK!");
|
||||
|
||||
// Initialize the matrix and keyboard
|
||||
const I2C_ADDR_MATRIX_LEFT: I2cAddress = I2cAddress::SevenBit(0b0100000);
|
||||
const I2C_ADDR_MATRIX_RIGHT: I2cAddress = I2cAddress::SevenBit(0b0100001);
|
||||
|
||||
let i2c = I2c::new(peripherals.I2C0, Default::default())
|
||||
.unwrap()
|
||||
.with_sda(peripherals.GPIO8)
|
||||
.with_scl(peripherals.GPIO9);
|
||||
|
||||
let matrix_interrupt_low = Input::new(peripherals.GPIO7, InputConfig::default());
|
||||
|
||||
let mut matrix = IoeMatrix::new(
|
||||
matrix_interrupt_low,
|
||||
i2c.into_async(),
|
||||
DefaultDebouncer::new(),
|
||||
[I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT],
|
||||
)
|
||||
.await;
|
||||
let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller
|
||||
|
||||
info!("Keyboard initialized!");
|
||||
|
||||
static FRAMEBUFFER: StaticCell<Framebuffer> = StaticCell::new();
|
||||
let framebuffer = FRAMEBUFFER.init(Framebuffer::new(
|
||||
360 + /* TODO: Figure out why more bytes are needed: */ 8,
|
||||
960,
|
||||
));
|
||||
|
||||
info!("Framebuffer created!");
|
||||
|
||||
// let window_size = [framebuffer.width, framebuffer.height];
|
||||
let window_size = [framebuffer.height, framebuffer.width];
|
||||
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
|
||||
|
||||
static SECOND_CORE_STACK: StaticCell<Stack<STACK_SIZE_CORE_APP>> = 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_renderer_task());
|
||||
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
|
||||
let executor: &mut Executor = EXECUTOR.init(Executor::new());
|
||||
executor.run(|spawner| {
|
||||
let slint_backend = SlintBackend {
|
||||
// peripherals: RefCell::new(Some(peripherals)),
|
||||
window_size,
|
||||
window: RefCell::new(None),
|
||||
framebuffer: framebuffer_ptr,
|
||||
quit_event_loop: Default::default(),
|
||||
events: Arc::new(critical_section::Mutex::new(RefCell::new(VecDeque::new()))),
|
||||
};
|
||||
spawner.must_spawn(ui::run_renderer_task(slint_backend, flash_part_acid));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
info!("Second core started!");
|
||||
|
||||
let mut user_controller = UserController::new();
|
||||
|
||||
info!("Awaiting on all tasks...");
|
||||
|
||||
// TODO: Probably want to select! instead and re-try.
|
||||
join_all![
|
||||
run_alloc_stats_reporter(),
|
||||
initialize_and_run_rmk_devices(peripherals.matrix),
|
||||
// We currently send the framebuffer data using the main core, which does not seem to slow
|
||||
// down the rest of the tasks too much.
|
||||
run_lcd(st7701s, framebuffer),
|
||||
run_devices! (
|
||||
(matrix) => rmk::channel::EVENT_CHANNEL,
|
||||
),
|
||||
keyboard.run(), // Keyboard is special
|
||||
run_rmk(
|
||||
&keymap,
|
||||
|
|
@ -404,16 +529,8 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
rmk_config,
|
||||
),
|
||||
create_hid_report_interceptor(),
|
||||
initialize_display_and_renderer(
|
||||
peripherals.display,
|
||||
peripherals.DMA_CH0,
|
||||
peripherals.SPI2,
|
||||
peripherals.CPU_CTRL,
|
||||
peripherals.software_interrupt0,
|
||||
peripherals.software_interrupt1,
|
||||
flash_partitions.acid
|
||||
),
|
||||
console::run_console(peripherals.uart_rx.into_async())
|
||||
user_controller.event_loop(),
|
||||
console_task
|
||||
]
|
||||
.await;
|
||||
}
|
||||
|
|
@ -446,166 +563,19 @@ async fn run_alloc_stats_reporter() {
|
|||
}
|
||||
}
|
||||
|
||||
async fn initialize_and_run_rmk_devices(matrix_peripherals: MatrixPeripherals) {
|
||||
// Initialize the matrix and keyboard
|
||||
const I2C_ADDR_MATRIX_LEFT: I2cAddress = I2cAddress::SevenBit(0b0100000);
|
||||
const I2C_ADDR_MATRIX_RIGHT: I2cAddress = I2cAddress::SevenBit(0b0100001);
|
||||
|
||||
let i2c = I2c::new(matrix_peripherals.I2C0, Default::default())
|
||||
.unwrap()
|
||||
.with_sda(matrix_peripherals.GPIO8)
|
||||
.with_scl(matrix_peripherals.GPIO9);
|
||||
let matrix_interrupt_low = Input::new(matrix_peripherals.GPIO7, InputConfig::default());
|
||||
let mut matrix = IoeMatrix::new(
|
||||
matrix_interrupt_low,
|
||||
i2c.into_async(),
|
||||
DefaultDebouncer::new(),
|
||||
[I2C_ADDR_MATRIX_LEFT, I2C_ADDR_MATRIX_RIGHT],
|
||||
)
|
||||
.await;
|
||||
run_devices! (
|
||||
(matrix) => rmk::channel::EVENT_CHANNEL,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn initialize_display_and_renderer(
|
||||
display_peripherals: DisplayPeripherals,
|
||||
dma_ch0: esp_hal::peripherals::DMA_CH0<'static>,
|
||||
spi2: esp_hal::peripherals::SPI2<'static>,
|
||||
cpu_ctrl: esp_hal::peripherals::CPU_CTRL<'static>,
|
||||
software_interrupt0: SoftwareInterrupt<'static, 0>,
|
||||
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
||||
partition_acid: Partition,
|
||||
) {
|
||||
let st7701s = display_peripherals.into_display().await;
|
||||
|
||||
static FRAMEBUFFER: StaticCell<Framebuffer> = StaticCell::new();
|
||||
let framebuffer = FRAMEBUFFER.init(Framebuffer::new(
|
||||
dma_ch0,
|
||||
spi2.into(),
|
||||
st7701s.dpi,
|
||||
BurstConfig {
|
||||
internal_memory: InternalBurstConfig::Enabled,
|
||||
external_memory: ExternalBurstConfig::Size32,
|
||||
},
|
||||
// The burst config (16/32/64) doesn't seem to affect the alignment of the row size.
|
||||
//
|
||||
// | | ( displayed range ) |
|
||||
// | [ pad ] [ pad ]
|
||||
// | [ DMA-transmissible range ]
|
||||
// [ DMA-t. left overscan ] | | |
|
||||
// 0 112 120 360 368 (index of u16 pixel)
|
||||
// ^ aligned ^ aligned ^ aligned
|
||||
//
|
||||
// TODO: Compute the appropriate ranges to pass to the renderer and DPI peripheral.
|
||||
// The renderer should pass the size of the `pad`ding to the GUI is parameters,
|
||||
// to align the content to the displayed range.
|
||||
112,
|
||||
368 - 112,
|
||||
960,
|
||||
16,
|
||||
true,
|
||||
));
|
||||
|
||||
info!("Framebuffer created!");
|
||||
|
||||
let window_size = [framebuffer.height, framebuffer.width];
|
||||
let swapchain_writer = framebuffer.swapchain.take().unwrap();
|
||||
|
||||
static SECOND_CORE_STACK: StaticCell<Stack<{ STACK_SIZE_CORE_APP }>> = StaticCell::new();
|
||||
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
||||
esp_rtos::start_second_core(
|
||||
cpu_ctrl,
|
||||
software_interrupt0,
|
||||
software_interrupt1,
|
||||
second_core_stack,
|
||||
move || {
|
||||
// static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
|
||||
// let exec = EXECUTOR.init(InterruptExecutor::new(
|
||||
// peripherals.software_interrupt2,
|
||||
// ));
|
||||
// let spawner = exec.start(Priority::Priority3);
|
||||
// spawner.must_spawn(run_renderer_task());
|
||||
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
|
||||
let executor: &mut Executor = EXECUTOR.init(Executor::new());
|
||||
executor.run(|spawner| {
|
||||
let slint_backend = SlintBackend {
|
||||
// peripherals: RefCell::new(Some(peripherals)),
|
||||
window_size,
|
||||
window: RefCell::new(None),
|
||||
swapchain: RefCell::new(swapchain_writer),
|
||||
quit_event_loop: Default::default(),
|
||||
events: Arc::new(blocking_mutex::Mutex::new(RefCell::new(VecDeque::new()))),
|
||||
};
|
||||
spawner.must_spawn(ui::run_renderer_task(slint_backend, partition_acid));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
info!("Second core started!");
|
||||
|
||||
let bb_controller = DmaBounceController::new(framebuffer.bounce_buffers.take().unwrap());
|
||||
let mut user_controller = UserController::new(st7701s.controller, bb_controller);
|
||||
|
||||
user_controller.event_loop().await
|
||||
}
|
||||
|
||||
enum DmaBounceControllerInner {
|
||||
Idle(DmaBounce),
|
||||
Transmitting(RunningDmaBounceHandle),
|
||||
}
|
||||
|
||||
struct DmaBounceController {
|
||||
inner: Option<DmaBounceControllerInner>,
|
||||
}
|
||||
|
||||
impl DmaBounceController {
|
||||
pub fn new(dma_bounce: DmaBounce) -> Self {
|
||||
Self {
|
||||
inner: Some(DmaBounceControllerInner::Idle(dma_bounce)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) -> Result<(), ()> {
|
||||
let DmaBounceControllerInner::Idle(dma_bounce) = self.inner.take().unwrap() else {
|
||||
return Err(());
|
||||
};
|
||||
self.inner = Some(DmaBounceControllerInner::Transmitting(
|
||||
dma_bounce.launch_interrupt_driven_task().await,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn stop(&mut self) -> Result<(), ()> {
|
||||
let DmaBounceControllerInner::Transmitting(running_dma_bounce) = self.inner.take().unwrap()
|
||||
else {
|
||||
return Err(());
|
||||
};
|
||||
self.inner = Some(DmaBounceControllerInner::Idle(
|
||||
running_dma_bounce.stop().await,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct UserController<'a> {
|
||||
struct UserController {
|
||||
sub: ControllerSub,
|
||||
lcd_controller: St7701sController<'a>,
|
||||
bb_controller: DmaBounceController,
|
||||
}
|
||||
|
||||
impl<'a> UserController<'a> {
|
||||
fn new(lcd_controller: St7701sController<'a>, bb_controller: DmaBounceController) -> Self {
|
||||
impl UserController {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
sub: CONTROLLER_CHANNEL.subscriber().unwrap(),
|
||||
lcd_controller,
|
||||
bb_controller,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Controller for UserController<'a> {
|
||||
impl Controller for UserController {
|
||||
type Event = ControllerEvent;
|
||||
|
||||
async fn process_event(&mut self, event: Self::Event) {
|
||||
|
|
@ -621,14 +591,10 @@ impl<'a> Controller for UserController<'a> {
|
|||
info!("Disabling LCD.");
|
||||
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await =
|
||||
&rmk::channel::KEYBOARD_REPORT_RECEIVER;
|
||||
self.lcd_controller.sleep_on().await;
|
||||
self.bb_controller.stop().await.unwrap();
|
||||
}
|
||||
true => {
|
||||
info!("Enabling LCD.");
|
||||
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await = &KEYBOARD_REPORT_PROXY;
|
||||
self.bb_controller.start().await.unwrap();
|
||||
self.lcd_controller.sleep_off().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -639,9 +605,109 @@ impl<'a> Controller for UserController<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Framebuffer {
|
||||
width: u32,
|
||||
height: u32,
|
||||
dma_buf: Option<DmaTxBuf>,
|
||||
}
|
||||
|
||||
impl Framebuffer {
|
||||
pub fn new(width: u32, height: u32) -> Self {
|
||||
let buffer_len = width as usize * height as usize * core::mem::size_of::<u16>();
|
||||
// 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();
|
||||
|
||||
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 dma_buf = DmaTxBuf::new(dma_buf_descs, buffer).unwrap();
|
||||
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
dma_buf: Some(dma_buf),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_target_pixels(&mut self) -> &mut [Rgb565Pixel] {
|
||||
bytemuck::cast_slice_mut::<_, Rgb565Pixel>(self.dma_buf.as_mut().unwrap().as_mut_slice())
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn run_lcd_task(st7701s: St7701s<'static, Blocking>, framebuffer: &'static mut Framebuffer) {
|
||||
run_lcd(st7701s, framebuffer).await
|
||||
}
|
||||
|
||||
async fn run_lcd(mut st7701s: St7701s<'static, Blocking>, framebuffer: &'static mut Framebuffer) {
|
||||
loop {
|
||||
// Timer::after(Duration::from_millis(100)).await;
|
||||
// yield_now().await;
|
||||
SIGNAL_LCD_SUBMIT.wait().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
|
||||
// This can be implemented as a `DmaTxBuffer`.
|
||||
let transfer = match st7701s.dpi.send(false, framebuffer.dma_buf.take().unwrap()) {
|
||||
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;
|
||||
framebuffer.dma_buf = Some(result_dma_buf);
|
||||
continue;
|
||||
}
|
||||
Ok(transfer) => transfer,
|
||||
};
|
||||
|
||||
// This could be used to allow other tasks to be executed on the first core, but that causes
|
||||
// the flash to be accessed, which interferes with the framebuffer transfer.
|
||||
// For that reason, it is disabled, and this task blocks the first core, until the transfer
|
||||
// is complete.
|
||||
#[cfg(not(feature = "limit-fps"))]
|
||||
while !transfer.is_done() {
|
||||
// Timer::after_millis(1).await;
|
||||
rmk::embassy_futures::yield_now().await;
|
||||
}
|
||||
|
||||
let result;
|
||||
let dma_buf;
|
||||
(result, st7701s.dpi, dma_buf) = transfer.wait();
|
||||
framebuffer.dma_buf = Some(dma_buf);
|
||||
|
||||
SIGNAL_UI_RENDER.signal(());
|
||||
|
||||
if let Err(error) = result {
|
||||
error!(
|
||||
"An error occurred while transferring framebuffer to the LCD display: {error:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // TODO: Not needed currently. If it is ever enabled, don't forget to register it in Io.
|
||||
// #[handler]
|
||||
// #[ram] // Improves performance.
|
||||
// #[ram] // TODO: Is this necessary?
|
||||
// fn interrupt_handler() {
|
||||
// // esp_println::println!(
|
||||
// // "GPIO Interrupt with priority {}",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,405 +0,0 @@
|
|||
use tinyvec::ArrayVec;
|
||||
|
||||
use crate::peripherals::st7701s::commands::*;
|
||||
|
||||
pub enum InitSequenceAction {
|
||||
// TODO: Each command takes up 17 bytes. Consider storing compressed.
|
||||
Command(ArrayCommand<16>),
|
||||
SleepMs(u64),
|
||||
}
|
||||
|
||||
macro_rules! make_init_action {
|
||||
(command: $command:expr$(,)?) => {
|
||||
InitSequenceAction::Command({
|
||||
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(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
|
||||
) },
|
||||
{ command: CustomCommand {
|
||||
address: 0xEF,
|
||||
args: &[0x08],
|
||||
} },
|
||||
{ command: CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x0).with_cn2(true),
|
||||
) },
|
||||
{ command: CmdLneset(
|
||||
CmdLnesetArg0::new().with_bar(119).with_lde_en(false),
|
||||
CmdLnesetArg1::new().with_line_delta(0),
|
||||
) },
|
||||
{ command: CmdPorctrl(
|
||||
CmdPorctrlArg0::new().with_vbp(17),
|
||||
CmdPorctrlArg1::new().with_vfp(12),
|
||||
) },
|
||||
{ command: CmdInvsel(
|
||||
CmdInvselArg0::new().with_nlinv(0b111),
|
||||
CmdInvselArg1::new().with_rtni(2),
|
||||
) },
|
||||
{ command: CustomCommand {
|
||||
address: 0xCC,
|
||||
args: &[0x30],
|
||||
} },
|
||||
{ command: CmdPvgamctrl(
|
||||
CmdPvgamctrlArg0::new().with_vc0p(6).with_aj0p(0),
|
||||
CmdPvgamctrlArg1::new().with_vc4p(15).with_aj1p(3),
|
||||
CmdPvgamctrlArg2::new().with_vc8p(14).with_aj2p(0),
|
||||
CmdPvgamctrlArg3::new().with_vc16p(12),
|
||||
CmdPvgamctrlArg4::new().with_vc24p(15).with_aj3p(0),
|
||||
CmdPvgamctrlArg5::new().with_vc52p(3),
|
||||
CmdPvgamctrlArg6::new().with_vc80p(0),
|
||||
CmdPvgamctrlArg7::new().with_vc108p(10),
|
||||
CmdPvgamctrlArg8::new().with_vc147p(7),
|
||||
CmdPvgamctrlArg9::new().with_vc175p(27),
|
||||
CmdPvgamctrlArg10::new().with_vc203p(3),
|
||||
CmdPvgamctrlArg11::new().with_vc231p(18).with_aj4p(0),
|
||||
CmdPvgamctrlArg12::new().with_vc239p(16),
|
||||
CmdPvgamctrlArg13::new().with_vc247p(37).with_aj5p(0),
|
||||
CmdPvgamctrlArg14::new().with_vc251p(54).with_aj6p(0),
|
||||
CmdPvgamctrlArg15::new().with_vc255p(30).with_aj7p(0),
|
||||
) },
|
||||
{ command: CmdNvgamctrl(
|
||||
CmdNvgamctrlArg0::new().with_vc0n(12).with_aj0n(0),
|
||||
CmdNvgamctrlArg1::new().with_vc4n(14).with_aj1n(3),
|
||||
CmdNvgamctrlArg2::new().with_vc8n(18).with_aj2n(0),
|
||||
CmdNvgamctrlArg3::new().with_vc16n(12),
|
||||
CmdNvgamctrlArg4::new().with_vc24n(14).with_aj3n(0),
|
||||
CmdNvgamctrlArg5::new().with_vc52n(6),
|
||||
CmdNvgamctrlArg6::new().with_vc80n(3),
|
||||
CmdNvgamctrlArg7::new().with_vc108n(6),
|
||||
CmdNvgamctrlArg8::new().with_vc147n(8),
|
||||
CmdNvgamctrlArg9::new().with_vc175n(35),
|
||||
CmdNvgamctrlArg10::new().with_vc203n(6),
|
||||
CmdNvgamctrlArg11::new().with_vc231n(18).with_aj4n(0),
|
||||
CmdNvgamctrlArg12::new().with_vc239n(16),
|
||||
CmdNvgamctrlArg13::new().with_vc247n(48).with_aj5n(0),
|
||||
CmdNvgamctrlArg14::new().with_vc251n(47).with_aj6n(0),
|
||||
CmdNvgamctrlArg15::new().with_vc255n(31).with_aj7n(0),
|
||||
) },
|
||||
{ command: CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x1).with_cn2(true),
|
||||
) },
|
||||
{ command: CmdVrhs(
|
||||
CmdVrhsArg0::new().with_vrha(115),
|
||||
) },
|
||||
{ command: CmdVcoms(
|
||||
CmdVcomsArg0::new().with_vcom(124),
|
||||
) },
|
||||
{ command: CmdVghss(
|
||||
// The first bit is set to 1 in the original init code, but not here.
|
||||
CmdVghssArg0::new().with_vghss(0x3), // 13 V
|
||||
) },
|
||||
{ command: CmdTescmd(
|
||||
CmdTescmdArg0::new(),
|
||||
) },
|
||||
{ command: CmdVgls(
|
||||
CmdVglsArg0::new().with_vgls(0x9) // -10.17 V
|
||||
) },
|
||||
{ command: CmdPwctrl1(
|
||||
CmdPwctrl1Arg0::new()
|
||||
.with_apos(0x3) // Max
|
||||
.with_apis(0x1) // Min
|
||||
.with_ap(0x2) // Middle
|
||||
) },
|
||||
{ command: CmdPwctrl2(
|
||||
CmdPwctrl2Arg0::new()
|
||||
.with_avcl(0x3) // -5 V
|
||||
.with_avdd(0x3) // 6.8 V
|
||||
) },
|
||||
{ command: CmdPwctrl3(
|
||||
CmdPwctrl3Arg0::new()
|
||||
.with_svno_pum(0) // Cell setting 4
|
||||
.with_svpo_pum(0x1) // Cell setting 5
|
||||
) },
|
||||
{ command: CmdPclks2(
|
||||
CmdPclks2Arg0::new().with_sbstcks(0x3)
|
||||
) },
|
||||
{ command: CmdPdr1(
|
||||
CmdPdr1Arg0::new().with_t2d(8) // 1.6 us
|
||||
) },
|
||||
{ command: CmdPdr2(
|
||||
CmdPdr2Arg0::new().with_t3d(8) // 6.4 us
|
||||
) },
|
||||
{ command: CmdMipiset1(
|
||||
CmdMipiset1Arg0::new().with_err_sel(0).with_eotp_en(true),
|
||||
) },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE0,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0C,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE1,
|
||||
args: &[
|
||||
0x05,
|
||||
0x96,
|
||||
0x07,
|
||||
0x96,
|
||||
0x06,
|
||||
0x96,
|
||||
0x08,
|
||||
0x96,
|
||||
0x00,
|
||||
0x44,
|
||||
0x44,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE2,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE3,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x33,
|
||||
0x33,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE4,
|
||||
args: &[
|
||||
0x44,
|
||||
0x44,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE5,
|
||||
args: &[
|
||||
0x0D,
|
||||
0xD4,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0F,
|
||||
0xD6,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x09,
|
||||
0xD0,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0B,
|
||||
0xD2,
|
||||
0x28,
|
||||
0x8C,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE6,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x33,
|
||||
0x33,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE7,
|
||||
args: &[
|
||||
0x44,
|
||||
0x44,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x0E,
|
||||
0xD5,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x10,
|
||||
0xD7,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0A,
|
||||
0xD1,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0C,
|
||||
0xD3,
|
||||
0x28,
|
||||
0x8C,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xEB,
|
||||
args: &[
|
||||
0x00,
|
||||
0x01,
|
||||
0xE4,
|
||||
0xE4,
|
||||
0x44,
|
||||
0x00,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xED,
|
||||
args: &[
|
||||
0xF3,
|
||||
0xC1,
|
||||
0xBA,
|
||||
0x0F,
|
||||
0x66,
|
||||
0x77,
|
||||
0x44,
|
||||
0x55,
|
||||
0x55,
|
||||
0x44,
|
||||
0x77,
|
||||
0x66,
|
||||
0xF0,
|
||||
0xAB,
|
||||
0x1C,
|
||||
0x3F,
|
||||
]
|
||||
} },
|
||||
{ command: CustomCommand {
|
||||
address: 0xEF,
|
||||
args: &[
|
||||
0x10,
|
||||
0x0D,
|
||||
0x04,
|
||||
0x08,
|
||||
0x3F,
|
||||
0x1F,
|
||||
]
|
||||
} },
|
||||
{ command: CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
|
||||
) },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x00,
|
||||
0x0E,
|
||||
],
|
||||
} },
|
||||
{ command: CmdSlpout() },
|
||||
{ sleep_ms: 120 },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x00,
|
||||
0x0C,
|
||||
],
|
||||
} },
|
||||
{ sleep_ms: 10 },
|
||||
{ command: CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x40,
|
||||
0x00,
|
||||
],
|
||||
} },
|
||||
{ command: CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
|
||||
) },
|
||||
{ command: CmdMadctl(
|
||||
CmdMadctlArg0::new().with_bgr(false).with_ml(false),
|
||||
) },
|
||||
{ command: CmdColmod(
|
||||
CmdColmodArg0::new().with_vipf(6) // 18-bit pixel
|
||||
) },
|
||||
{ command: CmdDispon() },
|
||||
{ sleep_ms: 20 },
|
||||
// { command: CmdCn2bkxsel(
|
||||
// CmdCn2bkxselArg0::new(),
|
||||
// CmdCn2bkxselArg1::new(),
|
||||
// CmdCn2bkxselArg2::new(),
|
||||
// CmdCn2bkxselArg3::new(),
|
||||
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(true),
|
||||
// ) },
|
||||
// { command: CmdPorctrl(
|
||||
// CmdPorctrlArg0::new().with_vbp(0xFF),
|
||||
// CmdPorctrlArg1::new().with_vfp(0x0),
|
||||
// ) },
|
||||
// // { command: CmdRgbctrl(
|
||||
// // CmdRgbctrlArg0::new()
|
||||
// // .with_ep(false)
|
||||
// // .with_dp(false)
|
||||
// // .with_hsp(false)
|
||||
// // .with_vsp(false)
|
||||
// // .with_de_hv(true),
|
||||
// // CmdRgbctrlArg1::new()
|
||||
// // .with_hbp_hvrgb(16),
|
||||
// // CmdRgbctrlArg2::new()
|
||||
// // .with_vbp_hvrgb(8),
|
||||
// // ) },
|
||||
// { command: CmdCn2bkxsel(
|
||||
// CmdCn2bkxselArg0::new(),
|
||||
// CmdCn2bkxselArg1::new(),
|
||||
// CmdCn2bkxselArg2::new(),
|
||||
// CmdCn2bkxselArg3::new(),
|
||||
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
|
||||
// ) },
|
||||
// { sleep_ms: 20 },
|
||||
];
|
||||
489
firmware/acid-firmware/src/peripherals/st7701s/lcd.rs
Normal file
489
firmware/acid-firmware/src/peripherals/st7701s/lcd.rs
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
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<Item = bool>,
|
||||
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<Item = u8>,
|
||||
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::*;
|
||||
|
||||
// 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<Box<dyn Command + Send + Sync>>, u64)> = vec![
|
||||
(vec![
|
||||
Box::new(CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
|
||||
)),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xEF,
|
||||
args: &[0x08],
|
||||
}),
|
||||
Box::new(CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x0).with_cn2(true),
|
||||
)),
|
||||
Box::new(CmdLneset(
|
||||
CmdLnesetArg0::new().with_bar(119).with_lde_en(false),
|
||||
CmdLnesetArg1::new().with_line_delta(0),
|
||||
)),
|
||||
Box::new(CmdPorctrl(
|
||||
CmdPorctrlArg0::new().with_vbp(17),
|
||||
CmdPorctrlArg1::new().with_vfp(12),
|
||||
)),
|
||||
Box::new(CmdInvsel(
|
||||
CmdInvselArg0::new().with_nlinv(0b111),
|
||||
CmdInvselArg1::new().with_rtni(2),
|
||||
)),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xCC,
|
||||
args: &[0x30],
|
||||
}),
|
||||
Box::new(CmdPvgamctrl(
|
||||
CmdPvgamctrlArg0::new().with_vc0p(6).with_aj0p(0),
|
||||
CmdPvgamctrlArg1::new().with_vc4p(15).with_aj1p(3),
|
||||
CmdPvgamctrlArg2::new().with_vc8p(14).with_aj2p(0),
|
||||
CmdPvgamctrlArg3::new().with_vc16p(12),
|
||||
CmdPvgamctrlArg4::new().with_vc24p(15).with_aj3p(0),
|
||||
CmdPvgamctrlArg5::new().with_vc52p(3),
|
||||
CmdPvgamctrlArg6::new().with_vc80p(0),
|
||||
CmdPvgamctrlArg7::new().with_vc108p(10),
|
||||
CmdPvgamctrlArg8::new().with_vc147p(7),
|
||||
CmdPvgamctrlArg9::new().with_vc175p(27),
|
||||
CmdPvgamctrlArg10::new().with_vc203p(3),
|
||||
CmdPvgamctrlArg11::new().with_vc231p(18).with_aj4p(0),
|
||||
CmdPvgamctrlArg12::new().with_vc239p(16),
|
||||
CmdPvgamctrlArg13::new().with_vc247p(37).with_aj5p(0),
|
||||
CmdPvgamctrlArg14::new().with_vc251p(54).with_aj6p(0),
|
||||
CmdPvgamctrlArg15::new().with_vc255p(30).with_aj7p(0),
|
||||
)),
|
||||
Box::new(CmdNvgamctrl(
|
||||
CmdNvgamctrlArg0::new().with_vc0n(12).with_aj0n(0),
|
||||
CmdNvgamctrlArg1::new().with_vc4n(14).with_aj1n(3),
|
||||
CmdNvgamctrlArg2::new().with_vc8n(18).with_aj2n(0),
|
||||
CmdNvgamctrlArg3::new().with_vc16n(12),
|
||||
CmdNvgamctrlArg4::new().with_vc24n(14).with_aj3n(0),
|
||||
CmdNvgamctrlArg5::new().with_vc52n(6),
|
||||
CmdNvgamctrlArg6::new().with_vc80n(3),
|
||||
CmdNvgamctrlArg7::new().with_vc108n(6),
|
||||
CmdNvgamctrlArg8::new().with_vc147n(8),
|
||||
CmdNvgamctrlArg9::new().with_vc175n(35),
|
||||
CmdNvgamctrlArg10::new().with_vc203n(6),
|
||||
CmdNvgamctrlArg11::new().with_vc231n(18).with_aj4n(0),
|
||||
CmdNvgamctrlArg12::new().with_vc239n(16),
|
||||
CmdNvgamctrlArg13::new().with_vc247n(48).with_aj5n(0),
|
||||
CmdNvgamctrlArg14::new().with_vc251n(47).with_aj6n(0),
|
||||
CmdNvgamctrlArg15::new().with_vc255n(31).with_aj7n(0),
|
||||
)),
|
||||
Box::new(CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x1).with_cn2(true),
|
||||
)),
|
||||
Box::new(CmdVrhs(
|
||||
CmdVrhsArg0::new().with_vrha(115),
|
||||
)),
|
||||
Box::new(CmdVcoms(
|
||||
CmdVcomsArg0::new().with_vcom(124),
|
||||
)),
|
||||
Box::new(CmdVghss(
|
||||
// The first bit is set to 1 in the original init code, but not here.
|
||||
CmdVghssArg0::new().with_vghss(0x3), // 13 V
|
||||
)),
|
||||
Box::new(CmdTescmd(
|
||||
CmdTescmdArg0::new(),
|
||||
)),
|
||||
Box::new(CmdVgls(
|
||||
CmdVglsArg0::new().with_vgls(0x9) // -10.17 V
|
||||
)),
|
||||
Box::new(CmdPwctrl1(
|
||||
CmdPwctrl1Arg0::new()
|
||||
.with_apos(0x3) // Max
|
||||
.with_apis(0x1) // Min
|
||||
.with_ap(0x2) // Middle
|
||||
)),
|
||||
Box::new(CmdPwctrl2(
|
||||
CmdPwctrl2Arg0::new()
|
||||
.with_avcl(0x3) // -5 V
|
||||
.with_avdd(0x3) // 6.8 V
|
||||
)),
|
||||
Box::new(CmdPwctrl3(
|
||||
CmdPwctrl3Arg0::new()
|
||||
.with_svno_pum(0) // Cell setting 4
|
||||
.with_svpo_pum(0x1) // Cell setting 5
|
||||
)),
|
||||
Box::new(CmdPclks2(
|
||||
CmdPclks2Arg0::new().with_sbstcks(0x3)
|
||||
)),
|
||||
Box::new(CmdPdr1(
|
||||
CmdPdr1Arg0::new().with_t2d(8) // 1.6 us
|
||||
)),
|
||||
Box::new(CmdPdr2(
|
||||
CmdPdr2Arg0::new().with_t3d(8) // 6.4 us
|
||||
)),
|
||||
Box::new(CmdMipiset1(
|
||||
CmdMipiset1Arg0::new().with_err_sel(0).with_eotp_en(true),
|
||||
)),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE0,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0C,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE1,
|
||||
args: &[
|
||||
0x05,
|
||||
0x96,
|
||||
0x07,
|
||||
0x96,
|
||||
0x06,
|
||||
0x96,
|
||||
0x08,
|
||||
0x96,
|
||||
0x00,
|
||||
0x44,
|
||||
0x44,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE2,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE3,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x33,
|
||||
0x33,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE4,
|
||||
args: &[
|
||||
0x44,
|
||||
0x44,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE5,
|
||||
args: &[
|
||||
0x0D,
|
||||
0xD4,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0F,
|
||||
0xD6,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x09,
|
||||
0xD0,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0B,
|
||||
0xD2,
|
||||
0x28,
|
||||
0x8C,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE6,
|
||||
args: &[
|
||||
0x00,
|
||||
0x00,
|
||||
0x33,
|
||||
0x33,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE7,
|
||||
args: &[
|
||||
0x44,
|
||||
0x44,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x0E,
|
||||
0xD5,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x10,
|
||||
0xD7,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0A,
|
||||
0xD1,
|
||||
0x28,
|
||||
0x8C,
|
||||
0x0C,
|
||||
0xD3,
|
||||
0x28,
|
||||
0x8C,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xEB,
|
||||
args: &[
|
||||
0x00,
|
||||
0x01,
|
||||
0xE4,
|
||||
0xE4,
|
||||
0x44,
|
||||
0x00,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xED,
|
||||
args: &[
|
||||
0xF3,
|
||||
0xC1,
|
||||
0xBA,
|
||||
0x0F,
|
||||
0x66,
|
||||
0x77,
|
||||
0x44,
|
||||
0x55,
|
||||
0x55,
|
||||
0x44,
|
||||
0x77,
|
||||
0x66,
|
||||
0xF0,
|
||||
0xAB,
|
||||
0x1C,
|
||||
0x3F,
|
||||
]
|
||||
}),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xEF,
|
||||
args: &[
|
||||
0x10,
|
||||
0x0D,
|
||||
0x04,
|
||||
0x08,
|
||||
0x3F,
|
||||
0x1F,
|
||||
]
|
||||
}),
|
||||
Box::new(CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true),
|
||||
)),
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x00,
|
||||
0x0E,
|
||||
],
|
||||
}),
|
||||
Box::new(CmdSlpout()),
|
||||
], 120),
|
||||
(vec![
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x00,
|
||||
0x0C,
|
||||
],
|
||||
}),
|
||||
], 10),
|
||||
(vec![
|
||||
Box::new(CustomCommand {
|
||||
address: 0xE8,
|
||||
args: &[
|
||||
0x40,
|
||||
0x00,
|
||||
],
|
||||
}),
|
||||
Box::new(CmdCn2bkxsel(
|
||||
CmdCn2bkxselArg0::new(),
|
||||
CmdCn2bkxselArg1::new(),
|
||||
CmdCn2bkxselArg2::new(),
|
||||
CmdCn2bkxselArg3::new(),
|
||||
CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
|
||||
)),
|
||||
Box::new(CmdMadctl(
|
||||
CmdMadctlArg0::new().with_bgr(false).with_ml(false),
|
||||
)),
|
||||
Box::new(CmdColmod(
|
||||
CmdColmodArg0::new().with_vipf(6) // 18-bit pixel
|
||||
)),
|
||||
Box::new(CmdDispon()),
|
||||
], 20),
|
||||
// (vec![
|
||||
// Box::new(CmdCn2bkxsel(
|
||||
// CmdCn2bkxselArg0::new(),
|
||||
// CmdCn2bkxselArg1::new(),
|
||||
// CmdCn2bkxselArg2::new(),
|
||||
// CmdCn2bkxselArg3::new(),
|
||||
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(true),
|
||||
// )),
|
||||
// Box::new(CmdPorctrl(
|
||||
// CmdPorctrlArg0::new().with_vbp(0xFF),
|
||||
// CmdPorctrlArg1::new().with_vfp(0x0),
|
||||
// )),
|
||||
// // Box::new(CmdRgbctrl(
|
||||
// // CmdRgbctrlArg0::new()
|
||||
// // .with_ep(false)
|
||||
// // .with_dp(false)
|
||||
// // .with_hsp(false)
|
||||
// // .with_vsp(false)
|
||||
// // .with_de_hv(true),
|
||||
// // CmdRgbctrlArg1::new()
|
||||
// // .with_hbp_hvrgb(16),
|
||||
// // CmdRgbctrlArg2::new()
|
||||
// // .with_vbp_hvrgb(8),
|
||||
// // )),
|
||||
// Box::new(CmdCn2bkxsel(
|
||||
// CmdCn2bkxselArg0::new(),
|
||||
// CmdCn2bkxselArg1::new(),
|
||||
// CmdCn2bkxselArg2::new(),
|
||||
// CmdCn2bkxselArg3::new(),
|
||||
// CmdCn2bkxselArg4::new().with_bksel(0).with_cn2(false),
|
||||
// )),
|
||||
// ], 20),
|
||||
];
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,102 +0,0 @@
|
|||
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<Item = bool>,
|
||||
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<Item = u8>,
|
||||
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();
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
use esp_alloc::{HeapRegion, MemoryCapability};
|
||||
use esp_hal::ram;
|
||||
use log::info;
|
||||
|
||||
// Memory allocation regions.
|
||||
// These can be debugged using `xtensa-esp32s3-elf-size -A <path-to-binary>`.
|
||||
// A panic such as `memory allocation of 3740121773 bytes failed` is caused by a heap overflow. The size is `DEEDBAAD` in hex.
|
||||
|
||||
/// Total heap size
|
||||
pub const HEAP_SIZE: usize = 112 * 1024;
|
||||
/// Size of the app core's stack
|
||||
pub const STACK_SIZE_CORE_APP: usize = 80 * 1024;
|
||||
|
||||
pub static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
|
||||
|
||||
pub fn initialize(psram_peripheral: esp_hal::peripherals::PSRAM) {
|
||||
// Use the internal DRAM as the heap.
|
||||
// Memory reclaimed from the esp-idf bootloader.
|
||||
const HEAP_SIZE_RECLAIMED: usize = const {
|
||||
let range = esp_metadata_generated::memory_range!("DRAM2_UNINIT");
|
||||
range.end - range.start
|
||||
};
|
||||
|
||||
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED);
|
||||
esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED);
|
||||
info!("IRAM heap initialized!\n{}", esp_alloc::HEAP.stats());
|
||||
|
||||
// Initialize the PSRAM allocator.
|
||||
{
|
||||
let (psram_offset, psram_size) = esp_hal::psram::psram_raw_parts(&psram_peripheral);
|
||||
unsafe {
|
||||
PSRAM_ALLOCATOR.add_region(HeapRegion::new(
|
||||
psram_offset,
|
||||
psram_size,
|
||||
MemoryCapability::External.into(),
|
||||
));
|
||||
}
|
||||
info!(
|
||||
"PSRAM heap initialized with capacity of {} MiB!\n{}",
|
||||
psram_size / 1024 / 1024,
|
||||
PSRAM_ALLOCATOR.stats(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,8 @@ use core::{
|
|||
use alloc::{
|
||||
boxed::Box, collections::vec_deque::VecDeque, rc::Rc, string::ToString, sync::Arc, vec::Vec,
|
||||
};
|
||||
use embassy_sync::blocking_mutex::{self, raw::CriticalSectionRawMutex};
|
||||
use critical_section::Mutex;
|
||||
use esp_hal::time::Instant;
|
||||
use esp_hal_bounce_buffers::SwapchainWriter;
|
||||
use log::{debug, info};
|
||||
use slint::{
|
||||
EventLoopError, PhysicalSize, SharedString, WindowSize,
|
||||
|
|
@ -24,22 +23,25 @@ use crate::proxy::{KEY_MESSAGE_CHANNEL, TryFromKeysym};
|
|||
|
||||
use super::window_adapter::SoftwareWindowAdapter;
|
||||
|
||||
pub struct FramebufferPtr(pub *mut [Rgb565Pixel]);
|
||||
|
||||
unsafe impl Send for FramebufferPtr {}
|
||||
|
||||
pub struct SlintBackend {
|
||||
pub window_size: [u32; 2],
|
||||
pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>,
|
||||
pub swapchain: RefCell<SwapchainWriter>,
|
||||
pub framebuffer: FramebufferPtr,
|
||||
pub quit_event_loop: Arc<AtomicBool>,
|
||||
pub events: Arc<
|
||||
blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>,
|
||||
>,
|
||||
pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
|
||||
}
|
||||
|
||||
impl slint::platform::Platform for SlintBackend {
|
||||
fn create_window_adapter(
|
||||
&self,
|
||||
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
|
||||
let renderer =
|
||||
SoftwareRenderer::new_with_repaint_buffer_type(RepaintBufferType::SwappedBuffers);
|
||||
let renderer = SoftwareRenderer::new_with_repaint_buffer_type(
|
||||
RepaintBufferType::ReusedBuffer, /* TODO: Implement a swapchain */
|
||||
);
|
||||
renderer.set_rendering_rotation(RenderingRotation::Rotate270);
|
||||
let window = SoftwareWindowAdapter::new(renderer);
|
||||
// window.set_scale_factor(4.0);
|
||||
|
|
@ -68,9 +70,13 @@ impl slint::platform::Platform for SlintBackend {
|
|||
|
||||
/* loop */
|
||||
{
|
||||
let drained_events = self
|
||||
.events
|
||||
.lock(|events| events.borrow_mut().drain(..).collect::<Vec<_>>());
|
||||
let drained_events = critical_section::with(|cs| {
|
||||
self.events
|
||||
.borrow(cs)
|
||||
.borrow_mut()
|
||||
.drain(..)
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
for event in drained_events {
|
||||
(event)();
|
||||
|
|
@ -128,10 +134,8 @@ impl slint::platform::Platform for SlintBackend {
|
|||
}
|
||||
|
||||
window.draw_if_needed(|renderer| {
|
||||
// TODO: VSYNC support.
|
||||
let mut swapchain = self.swapchain.borrow_mut();
|
||||
let mut write_guard = swapchain.write();
|
||||
let framebuffer = bytemuck::cast_slice_mut::<u8, Rgb565Pixel>(&mut write_guard);
|
||||
// TODO: Proper synchronization.
|
||||
let framebuffer = unsafe { &mut *self.framebuffer.0 };
|
||||
renderer.render(framebuffer, self.window_size[1] as usize);
|
||||
info!("UI rendered.");
|
||||
});
|
||||
|
|
@ -144,9 +148,7 @@ impl slint::platform::Platform for SlintBackend {
|
|||
|
||||
struct AcidEventLoopProxy {
|
||||
pub quit_event_loop: Arc<AtomicBool>,
|
||||
pub events: Arc<
|
||||
blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>,
|
||||
>,
|
||||
pub events: Arc<Mutex<RefCell<VecDeque<Box<dyn FnOnce() + Send>>>>>,
|
||||
}
|
||||
|
||||
impl EventLoopProxy for AcidEventLoopProxy {
|
||||
|
|
@ -159,8 +161,8 @@ impl EventLoopProxy for AcidEventLoopProxy {
|
|||
&self,
|
||||
event: Box<dyn FnOnce() + Send>,
|
||||
) -> Result<(), EventLoopError> {
|
||||
self.events.lock(|events| {
|
||||
events.borrow_mut().push_back(event);
|
||||
critical_section::with(|cs| {
|
||||
self.events.borrow(cs).borrow_mut().push_back(event);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use spectre_api_sys::{
|
|||
#[cfg(feature = "limit-fps")]
|
||||
use crate::FRAME_DURATION_MIN;
|
||||
use crate::{
|
||||
PSRAM_ALLOCATOR,
|
||||
PSRAM_ALLOCATOR, SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER,
|
||||
db::{
|
||||
AcidDatabase, DbKey, DbPathSpectreUserSite, DbPathSpectreUserSites, DbPathSpectreUsers,
|
||||
PartitionAcid, ReadTransactionExt,
|
||||
|
|
@ -60,8 +60,8 @@ fn spectre_derive_user_key(
|
|||
encrypted_key: Option<Key>,
|
||||
) -> SpectreUserKey {
|
||||
if let Some(encrypted_key) = encrypted_key {
|
||||
ACTIVE_ENCRYPTED_USER_KEY.lock(|user_key| {
|
||||
user_key.set(encrypted_key);
|
||||
critical_section::with(|cs| {
|
||||
ACTIVE_ENCRYPTED_USER_KEY.borrow(cs).set(encrypted_key);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +143,11 @@ struct State {
|
|||
|
||||
impl State {
|
||||
async fn new(db: AcidDatabase, main: AppWindow) -> Rc<RefCell<Self>> {
|
||||
let users = {
|
||||
let users = Self::load_users(&db).await;
|
||||
warn!("Users: {users:#?}");
|
||||
users
|
||||
};
|
||||
let usernames = users.users.clone().map(|user| user.username);
|
||||
|
||||
let state = Rc::new(RefCell::new(State {
|
||||
|
|
@ -372,8 +376,10 @@ impl State {
|
|||
|
||||
loop {
|
||||
slint::run_event_loop().unwrap();
|
||||
SIGNAL_LCD_SUBMIT.signal(());
|
||||
#[cfg(feature = "limit-fps")]
|
||||
embassy_time::Timer::after(FRAME_DURATION_MIN).await;
|
||||
SIGNAL_UI_RENDER.wait().await;
|
||||
}
|
||||
|
||||
#[expect(unreachable_code)]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
use core::fmt::Display;
|
||||
|
||||
use embassy_sync::{
|
||||
blocking_mutex::raw::RawMutex,
|
||||
mutex::{Mutex, MutexGuard},
|
||||
};
|
||||
use embassy_time::Duration;
|
||||
|
||||
pub struct FormattedDuration<'a>(&'a Duration);
|
||||
|
|
@ -50,36 +46,3 @@ pub const fn get_file_name(path: &str) -> &str {
|
|||
Err(_) => panic!("Failed to extract the file name from a path."),
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MutexExt<M, T> {
|
||||
type Guard<'a>
|
||||
where
|
||||
M: 'a,
|
||||
T: 'a,
|
||||
Self: 'a;
|
||||
|
||||
/// Locks the mutex, while entering a critical section only during locking and unlocking.
|
||||
///
|
||||
/// # Safety:
|
||||
/// * The guard must not be kept across async `yield` points.
|
||||
/// * This must not be called from within interrupt handlers.
|
||||
/// Otherwise, a deadlock might occur.
|
||||
unsafe fn lock_blocking(&self) -> Self::Guard<'_>;
|
||||
}
|
||||
|
||||
impl<M: RawMutex, T> MutexExt<M, T> for Mutex<M, T> {
|
||||
type Guard<'a>
|
||||
= MutexGuard<'a, M, T>
|
||||
where
|
||||
M: 'a,
|
||||
T: 'a,
|
||||
Self: 'a;
|
||||
|
||||
unsafe fn lock_blocking(&self) -> Self::Guard<'_> {
|
||||
loop {
|
||||
if let Ok(guard) = self.try_lock() {
|
||||
return guard;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export component AppWindow inherits Window {
|
|||
in property <string> dummy_identicon_symbols: "╔╚╰═█░▒▓☺☻╗╝╯═◈◎◐◑◒◓☀☁☂☃☄★☆☎☏⎈⌂☘☢☣☕⌚⌛⏰⚡⛄⛅☔♔♕♖♗♘♙♚♛♜♝♞♟♨♩♪♫⚐⚑⚔⚖⚙⚠⌘⏎✄✆✈✉✌";
|
||||
default-font-family: "IBM Plex Mono";
|
||||
default-font-size: 16pt;
|
||||
height: 8px + 240px + 8px;
|
||||
height: 368px;
|
||||
width: 960px;
|
||||
forward-focus: focus-scope;
|
||||
in property <AppState> app-state: AppState.login;
|
||||
|
|
@ -69,9 +69,9 @@ export component AppWindow inherits Window {
|
|||
}
|
||||
vertical-box := VerticalBox {
|
||||
width: 960px;
|
||||
height: 8px + 240px + 8px;
|
||||
height: 368px;
|
||||
padding: 0px;
|
||||
padding-top: 8px;
|
||||
padding-top: 120px;
|
||||
padding-bottom: 8px;
|
||||
Rectangle {
|
||||
height: 240px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue