UI rendering beginnings; Add debugging serial interface on GPIO5 & 12
This commit is contained in:
parent
b9789b89ac
commit
47340a63e5
4316
firmware2/Cargo.lock
generated
4316
firmware2/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,9 +8,15 @@ repository = "https://github.com/haobogu/rmk"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ble"]
|
default = ["ble", "usb-log"]
|
||||||
no_usb = ["rmk/_no_usb"]
|
no-usb = ["rmk/_no_usb"]
|
||||||
ble = ["rmk/esp32s3_ble"]
|
ble = ["rmk/esp32s3_ble"]
|
||||||
|
# Use alternative logging via GPIO5 as RX and GPIO12 as TX.
|
||||||
|
# Disables default logging via USB.
|
||||||
|
# Does not support esp-println's `println!`.
|
||||||
|
alt-log = []
|
||||||
|
# Standard logging implementation over USB.
|
||||||
|
usb-log = ["esp-backtrace/panic-handler"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rmk = { version = "0.8.2", default-features = false, features = [
|
rmk = { version = "0.8.2", default-features = false, features = [
|
||||||
|
|
@ -20,9 +26,9 @@ rmk = { version = "0.8.2", default-features = false, features = [
|
||||||
] }
|
] }
|
||||||
embassy-executor = { version = "0.9" }
|
embassy-executor = { version = "0.9" }
|
||||||
embassy-time = { version = "0.5.0", features = ["log"] }
|
embassy-time = { version = "0.5.0", features = ["log"] }
|
||||||
esp-backtrace = { version = "0.18", features = [
|
embassy-embedded-hal = "0.5.0"
|
||||||
|
esp-backtrace = { version = "0.18", default-features = false, features = [
|
||||||
"esp32s3",
|
"esp32s3",
|
||||||
"panic-handler",
|
|
||||||
"println",
|
"println",
|
||||||
] }
|
] }
|
||||||
esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram", "log-04"] }
|
esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram", "log-04"] }
|
||||||
|
|
@ -40,6 +46,9 @@ log = "0.4.29"
|
||||||
bitflags = "2.10.0"
|
bitflags = "2.10.0"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
itertools = { version = "0.14.0", default-features = false }
|
itertools = { version = "0.14.0", default-features = false }
|
||||||
|
bytemuck = "1.24.0"
|
||||||
|
slint = { version = "1.14.1", default-features = false, features = ["compat-1-2", "libm", "log", "unsafe-single-threaded", "renderer-software"]}
|
||||||
|
critical-section = "1.2.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
xz2 = "0.1.7"
|
xz2 = "0.1.7"
|
||||||
|
|
@ -47,6 +56,7 @@ json = "0.12"
|
||||||
const-gen = "1.6"
|
const-gen = "1.6"
|
||||||
embuild = "0.33"
|
embuild = "0.33"
|
||||||
cc = "1.2.9"
|
cc = "1.2.9"
|
||||||
|
slint-build = "1.14.1"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "acid-firmware"
|
name = "acid-firmware"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use std::path::Path;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
use const_gen::*;
|
use const_gen::*;
|
||||||
|
use slint_build::{CompilerConfiguration, EmbedResourcesKind};
|
||||||
use xz2::read::XzEncoder;
|
use xz2::read::XzEncoder;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -15,6 +16,11 @@ fn main() {
|
||||||
|
|
||||||
// Set the extra linker script from defmt
|
// Set the extra linker script from defmt
|
||||||
// println!("cargo:rustc-link-arg=-Tdefmt.x");
|
// println!("cargo:rustc-link-arg=-Tdefmt.x");
|
||||||
|
|
||||||
|
let slint_config = CompilerConfiguration::new()
|
||||||
|
.embed_resources(EmbedResourcesKind::EmbedForSoftwareRenderer);
|
||||||
|
slint_build::compile_with_config("ui/main.slint", slint_config).expect("Slint build failed");
|
||||||
|
slint_build::print_rustc_flags().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_vial_config() {
|
fn generate_vial_config() {
|
||||||
|
|
|
||||||
37
firmware2/src/console.rs
Normal file
37
firmware2/src/console.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
use esp_hal::{Async, uart::UartRx};
|
||||||
|
use log::{debug, info, error};
|
||||||
|
|
||||||
|
pub async fn run_console(mut uart_rx: UartRx<'_, Async>) {
|
||||||
|
let mut buf = [0_u8; 32];
|
||||||
|
|
||||||
|
info!("Debugging console opened.");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
|
let len = match uart_rx.read_async(&mut buf[..]).await {
|
||||||
|
Ok(len) => len,
|
||||||
|
Err(error) => {
|
||||||
|
error!("Failed to read from the alt UART port: {error:?}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut read_data = match str::from_utf8(&buf[0..len]) {
|
||||||
|
Ok(utf8) => utf8,
|
||||||
|
Err(error) => {
|
||||||
|
error!("Failed to parse data from the UART port as UTF-8: {error:?}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Read from alt UART: {read_data:?}");
|
||||||
|
|
||||||
|
match read_data.trim() {
|
||||||
|
"reset" | "reboot" | "rst" | "r" => {
|
||||||
|
info!("Performing software reset.");
|
||||||
|
esp_hal::system::software_reset();
|
||||||
|
}
|
||||||
|
_ => warn!("Command not recognized: {read_data:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
154
firmware2/src/logging.rs
Normal file
154
firmware2/src/logging.rs
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
use core::alloc::Layout;
|
||||||
|
use core::cell::{OnceCell, RefCell};
|
||||||
|
use core::fmt::Write;
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::rc::Rc;
|
||||||
|
use alloc::vec;
|
||||||
|
use bt_hci::controller::ExternalController;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use esp_alloc::{HeapRegion, MemoryCapability};
|
||||||
|
use esp_hal::clock::CpuClock;
|
||||||
|
use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig};
|
||||||
|
use esp_hal::gpio::{Flex, Input, InputConfig, Io, Level, Output, OutputConfig, Pull};
|
||||||
|
use esp_hal::handler;
|
||||||
|
use esp_hal::i2c::master::{I2c, I2cAddress};
|
||||||
|
use esp_hal::interrupt::Priority;
|
||||||
|
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::peripherals::Peripherals;
|
||||||
|
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
||||||
|
use esp_hal::rng::TrngSource;
|
||||||
|
use esp_hal::system::Stack;
|
||||||
|
use esp_hal::time::Instant;
|
||||||
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
|
use esp_hal::uart::{Uart, UartTx};
|
||||||
|
use esp_hal::usb_serial_jtag::{UsbSerialJtag, UsbSerialJtagTx};
|
||||||
|
use esp_hal::{Blocking, ram};
|
||||||
|
use esp_radio::Controller;
|
||||||
|
use esp_radio::ble::controller::BleConnector;
|
||||||
|
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
||||||
|
use esp_storage::FlashStorage;
|
||||||
|
use itertools::chain;
|
||||||
|
use log::{LevelFilter, Log, error, info};
|
||||||
|
use rmk::channel::EVENT_CHANNEL;
|
||||||
|
use rmk::config::{BehaviorConfig, PositionalConfig, RmkConfig, StorageConfig, VialConfig};
|
||||||
|
use rmk::debounce::default_debouncer::DefaultDebouncer;
|
||||||
|
use rmk::embassy_futures::yield_now;
|
||||||
|
use rmk::input_device::Runnable;
|
||||||
|
use rmk::{join_all};
|
||||||
|
use rmk::keyboard::Keyboard;
|
||||||
|
use rmk::storage::async_flash_wrapper;
|
||||||
|
use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
|
||||||
|
use slint::platform::software_renderer::{Rgb565Pixel, TargetPixel};
|
||||||
|
use slint::{ComponentHandle, PhysicalSize, WindowSize};
|
||||||
|
use slint::platform::Platform;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
static ALT_LOGGER_UART: Mutex<RefCell<Option<UartTx<'static, Blocking>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
struct AlternativeLogger;
|
||||||
|
|
||||||
|
impl Log for AlternativeLogger {
|
||||||
|
#[allow(unused)]
|
||||||
|
fn enabled(&self, _: &log::Metadata) -> bool {
|
||||||
|
// Filtered by `log` already
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut uart = ALT_LOGGER_UART.borrow(cs).borrow_mut();
|
||||||
|
let mut uart = uart.as_mut().unwrap();
|
||||||
|
|
||||||
|
print_log_record(&mut uart, record);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RESET: &str = "\u{001B}[0m";
|
||||||
|
const RED: &str = "\u{001B}[31m";
|
||||||
|
const GREEN: &str = "\u{001B}[32m";
|
||||||
|
const YELLOW: &str = "\u{001B}[33m";
|
||||||
|
const BLUE: &str = "\u{001B}[34m";
|
||||||
|
const CYAN: &str = "\u{001B}[35m";
|
||||||
|
|
||||||
|
#[cfg(feature = "alt-log")]
|
||||||
|
macro_rules! println {
|
||||||
|
() => {{
|
||||||
|
do_print(Default::default());
|
||||||
|
}};
|
||||||
|
|
||||||
|
($($arg:tt)*) => {{
|
||||||
|
do_print(::core::format_args!($($arg)*));
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "alt-log"))]
|
||||||
|
use esp_println::println;
|
||||||
|
|
||||||
|
fn do_print(args: core::fmt::Arguments<'_>) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut uart = ALT_LOGGER_UART.borrow(cs).borrow_mut();
|
||||||
|
let mut uart = uart.as_mut().unwrap();
|
||||||
|
uart.write_fmt(args).unwrap();
|
||||||
|
uart.write_str("\n").unwrap();
|
||||||
|
uart.flush().unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_log_record(uart: &mut UartTx<'_, Blocking>, record: &log::Record) {
|
||||||
|
let color = match record.level() {
|
||||||
|
log::Level::Error => RED,
|
||||||
|
log::Level::Warn => YELLOW,
|
||||||
|
log::Level::Info => GREEN,
|
||||||
|
log::Level::Debug => BLUE,
|
||||||
|
log::Level::Trace => CYAN,
|
||||||
|
};
|
||||||
|
let reset = RESET;
|
||||||
|
let args = format_args!("{}{:>5} - {}{}\n", color, record.level(), record.args(), reset);
|
||||||
|
uart.write_fmt(args).unwrap();
|
||||||
|
uart.flush().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alt-log")]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
use esp_backtrace::Backtrace;
|
||||||
|
|
||||||
|
println!("{RED}");
|
||||||
|
println!("====================== PANIC ======================");
|
||||||
|
println!("{info}{RESET}");
|
||||||
|
println!("");
|
||||||
|
println!("Backtrace:");
|
||||||
|
println!("");
|
||||||
|
|
||||||
|
let backtrace = Backtrace::capture();
|
||||||
|
|
||||||
|
for frame in backtrace.frames() {
|
||||||
|
println!("0x{:x}", frame.program_counter());
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_alternative_logging(alt_uart: UartTx<'static, Blocking>, level_filter: LevelFilter)
|
||||||
|
{
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
*ALT_LOGGER_UART.borrow(cs).borrow_mut() = Some(alt_uart);
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
log::set_logger_racy(&AlternativeLogger).unwrap();
|
||||||
|
log::set_max_level_racy(level_filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,13 +8,23 @@ mod keymap;
|
||||||
mod matrix;
|
mod matrix;
|
||||||
mod peripherals;
|
mod peripherals;
|
||||||
mod vial;
|
mod vial;
|
||||||
|
mod ui;
|
||||||
|
mod logging;
|
||||||
|
mod console;
|
||||||
|
|
||||||
use core::alloc::Layout;
|
use core::alloc::Layout;
|
||||||
|
use core::cell::{OnceCell, RefCell};
|
||||||
|
use core::fmt::Write;
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
use alloc::rc::Rc;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use bt_hci::controller::ExternalController;
|
use bt_hci::controller::ExternalController;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_embedded_hal::adapter::BlockingAsync;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::Timer;
|
||||||
use esp_alloc::{HeapRegion, MemoryCapability};
|
use esp_alloc::{HeapRegion, MemoryCapability};
|
||||||
use esp_hal::clock::CpuClock;
|
use esp_hal::clock::CpuClock;
|
||||||
use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig};
|
use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig};
|
||||||
|
|
@ -26,28 +36,35 @@ use esp_hal::interrupt::software::SoftwareInterruptControl;
|
||||||
use esp_hal::lcd_cam::LcdCam;
|
use esp_hal::lcd_cam::LcdCam;
|
||||||
use esp_hal::lcd_cam::lcd::dpi::Dpi;
|
use esp_hal::lcd_cam::lcd::dpi::Dpi;
|
||||||
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
|
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
|
||||||
|
use esp_hal::peripherals::Peripherals;
|
||||||
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
||||||
use esp_hal::rng::TrngSource;
|
use esp_hal::rng::TrngSource;
|
||||||
use esp_hal::system::Stack;
|
use esp_hal::system::Stack;
|
||||||
|
use esp_hal::time::Instant;
|
||||||
use esp_hal::timer::timg::TimerGroup;
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
use esp_hal::uart::Uart;
|
use esp_hal::uart::Uart;
|
||||||
|
use esp_hal::usb_serial_jtag::{UsbSerialJtag, UsbSerialJtagTx};
|
||||||
use esp_hal::{Blocking, ram};
|
use esp_hal::{Blocking, ram};
|
||||||
use esp_radio::Controller;
|
use esp_radio::Controller;
|
||||||
use esp_radio::ble::controller::BleConnector;
|
use esp_radio::ble::controller::BleConnector;
|
||||||
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
||||||
use esp_storage::FlashStorage;
|
use esp_storage::FlashStorage;
|
||||||
use itertools::chain;
|
use itertools::chain;
|
||||||
use log::{error, info};
|
use log::{LevelFilter, Log, debug, error, info};
|
||||||
use rmk::channel::EVENT_CHANNEL;
|
use rmk::channel::EVENT_CHANNEL;
|
||||||
use rmk::config::{BehaviorConfig, PositionalConfig, RmkConfig, StorageConfig, VialConfig};
|
use rmk::config::{BehaviorConfig, PositionalConfig, RmkConfig, StorageConfig, VialConfig};
|
||||||
use rmk::debounce::default_debouncer::DefaultDebouncer;
|
use rmk::debounce::default_debouncer::DefaultDebouncer;
|
||||||
use rmk::embassy_futures::yield_now;
|
use rmk::embassy_futures::yield_now;
|
||||||
use rmk::input_device::Runnable;
|
use rmk::input_device::Runnable;
|
||||||
use rmk::join_all;
|
use rmk::{join_all};
|
||||||
use rmk::keyboard::Keyboard;
|
use rmk::keyboard::Keyboard;
|
||||||
use rmk::storage::async_flash_wrapper;
|
use rmk::storage::async_flash_wrapper;
|
||||||
use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
|
use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
|
||||||
|
use slint::platform::software_renderer::{Rgb565Pixel, TargetPixel};
|
||||||
|
use slint::{ComponentHandle, PhysicalSize, WindowSize};
|
||||||
|
use slint::platform::Platform;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
use ui::AppWindow;
|
||||||
use {esp_alloc as _, esp_backtrace as _};
|
use {esp_alloc as _, esp_backtrace as _};
|
||||||
|
|
||||||
use crate::matrix::IoeMatrix;
|
use crate::matrix::IoeMatrix;
|
||||||
|
|
@ -58,12 +75,18 @@ use crate::vial::{VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID};
|
||||||
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
// 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!();
|
esp_bootloader_esp_idf::esp_app_desc!();
|
||||||
|
|
||||||
|
const LOG_LEVEL_FILTER: LevelFilter = LevelFilter::Info;
|
||||||
|
|
||||||
static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
|
static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
|
||||||
|
|
||||||
#[esp_rtos::main]
|
#[esp_rtos::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
esp_println::logger::init_logger_from_env();
|
#[cfg(not(feature = "alt-log"))]
|
||||||
|
let alt_uart_rx_task = {
|
||||||
|
esp_println::logger::init_logger(LOG_LEVEL_FILTER);
|
||||||
info!("Logger initialized!");
|
info!("Logger initialized!");
|
||||||
|
async {}
|
||||||
|
};
|
||||||
|
|
||||||
let config = esp_hal::Config::default()
|
let config = esp_hal::Config::default()
|
||||||
.with_cpu_clock(CpuClock::max())
|
.with_cpu_clock(CpuClock::max())
|
||||||
|
|
@ -76,6 +99,14 @@ async fn main(_spawner: Spawner) {
|
||||||
let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config);
|
let peripherals: esp_hal::peripherals::Peripherals = esp_hal::init(config);
|
||||||
info!("System initialized!");
|
info!("System initialized!");
|
||||||
|
|
||||||
|
#[cfg(feature = "alt-log")]
|
||||||
|
let alt_uart_rx_task = {
|
||||||
|
let (uart_rx, uart_tx) = Uart::new(peripherals.UART2, Default::default()).unwrap().with_tx(peripherals.GPIO12).with_rx(peripherals.GPIO5).split();
|
||||||
|
logging::setup_alternative_logging(uart_tx, LOG_LEVEL_FILTER);
|
||||||
|
info!("Logger initialized!");
|
||||||
|
console::run_console(uart_rx.into_async())
|
||||||
|
};
|
||||||
|
|
||||||
// Use the internal DRAM as the heap.
|
// Use the internal DRAM as the heap.
|
||||||
esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024);
|
esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024);
|
||||||
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
|
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
|
||||||
|
|
@ -133,7 +164,7 @@ async fn main(_spawner: Spawner) {
|
||||||
rmk::ble::build_ble_stack(controller, central_addr, &mut rng, &mut host_resources).await;
|
rmk::ble::build_ble_stack(controller, central_addr, &mut rng, &mut host_resources).await;
|
||||||
|
|
||||||
// Initialize USB
|
// Initialize USB
|
||||||
#[cfg(not(feature = "no_usb"))]
|
#[cfg(not(feature = "no-usb"))]
|
||||||
let usb_driver = {
|
let usb_driver = {
|
||||||
use core::ptr::addr_of_mut;
|
use core::ptr::addr_of_mut;
|
||||||
use esp_hal::otg_fs::Usb;
|
use esp_hal::otg_fs::Usb;
|
||||||
|
|
@ -168,8 +199,8 @@ async fn main(_spawner: Spawner) {
|
||||||
.with_data3(peripherals.GPIO41)
|
.with_data3(peripherals.GPIO41)
|
||||||
.with_data4(peripherals.GPIO42)
|
.with_data4(peripherals.GPIO42)
|
||||||
// Green
|
// Green
|
||||||
.with_data5(peripherals.GPIO5)
|
// .with_data5(peripherals.GPIO5) TODO
|
||||||
.with_data6(peripherals.GPIO12)
|
// .with_data6(peripherals.GPIO12)
|
||||||
.with_data7(peripherals.GPIO13)
|
.with_data7(peripherals.GPIO13)
|
||||||
.with_data8(peripherals.GPIO14)
|
.with_data8(peripherals.GPIO14)
|
||||||
.with_data9(peripherals.GPIO15)
|
.with_data9(peripherals.GPIO15)
|
||||||
|
|
@ -230,7 +261,15 @@ async fn main(_spawner: Spawner) {
|
||||||
.await;
|
.await;
|
||||||
let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller
|
let mut keyboard = Keyboard::new(&keymap); // Initialize the light controller
|
||||||
|
|
||||||
// spawner.must_spawn(run_lcd(st7701s));
|
static FRAMEBUFFER: StaticCell<Framebuffer> = StaticCell::new();
|
||||||
|
let framebuffer = FRAMEBUFFER.init(Framebuffer::new(
|
||||||
|
360 + /* TODO: Figure out why more bytes are needed: */ 8,
|
||||||
|
960,
|
||||||
|
));
|
||||||
|
|
||||||
|
let window_size = [framebuffer.width, framebuffer.height];
|
||||||
|
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
|
||||||
|
|
||||||
static SECOND_CORE_STACK: StaticCell<Stack<8192>> = StaticCell::new();
|
static SECOND_CORE_STACK: StaticCell<Stack<8192>> = StaticCell::new();
|
||||||
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
||||||
esp_rtos::start_second_core(
|
esp_rtos::start_second_core(
|
||||||
|
|
@ -244,71 +283,64 @@ async fn main(_spawner: Spawner) {
|
||||||
// software_interrupt.software_interrupt2,
|
// software_interrupt.software_interrupt2,
|
||||||
// ));
|
// ));
|
||||||
// let spawner = exec.start(Priority::Priority3);
|
// let spawner = exec.start(Priority::Priority3);
|
||||||
// spawner.must_spawn(run_lcd_task(st7701s));
|
// spawner.must_spawn(run_renderer_task());
|
||||||
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
|
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
|
||||||
let executor: &mut Executor = EXECUTOR.init(Executor::new());
|
let executor: &mut Executor = EXECUTOR.init(Executor::new());
|
||||||
executor.run(|spawner| {
|
executor.run(|spawner| {
|
||||||
spawner.must_spawn(run_lcd_task(st7701s));
|
let slint_backend = SlintBackend {
|
||||||
|
// peripherals: RefCell::new(Some(peripherals)),
|
||||||
|
window_size,
|
||||||
|
window: RefCell::new(None),
|
||||||
|
framebuffer: framebuffer_ptr,
|
||||||
|
};
|
||||||
|
spawner.must_spawn(run_renderer_task(slint_backend));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
join_all![
|
join_all![
|
||||||
|
alt_uart_rx_task,
|
||||||
|
// 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! (
|
run_devices! (
|
||||||
(matrix) => EVENT_CHANNEL,
|
(matrix) => EVENT_CHANNEL,
|
||||||
),
|
),
|
||||||
keyboard.run(), // Keyboard is special
|
keyboard.run(), // Keyboard is special
|
||||||
run_rmk(
|
run_rmk(
|
||||||
&keymap,
|
&keymap,
|
||||||
#[cfg(not(feature = "no_usb"))]
|
#[cfg(not(feature = "no-usb"))]
|
||||||
usb_driver,
|
usb_driver,
|
||||||
#[cfg(feature = "ble")]
|
#[cfg(feature = "ble")]
|
||||||
&stack,
|
&stack,
|
||||||
&mut storage,
|
&mut storage,
|
||||||
rmk_config,
|
rmk_config,
|
||||||
) //, run_lcd(st7701s)
|
)
|
||||||
]
|
]
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn run_lcd_task(st7701s: St7701s<'static, Blocking>) {
|
async fn run_renderer_task(backend: SlintBackend) {
|
||||||
run_lcd(st7701s).await
|
slint::platform::set_platform(Box::new(backend))
|
||||||
|
.expect("backend already initialized");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let main = AppWindow::new().unwrap();
|
||||||
|
|
||||||
|
main.run().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_lcd(mut st7701s: St7701s<'static, Blocking>) {
|
struct Framebuffer {
|
||||||
const PADDING_LEFT: usize = 121;
|
width: u32,
|
||||||
const PADDING_RIGHT: usize = 7;
|
height: u32,
|
||||||
const COLORS_WIDTH: usize = 240;
|
dma_buf: Option<DmaTxBuf>,
|
||||||
const COLORS_HEIGHT: usize = 960;
|
}
|
||||||
|
|
||||||
const MAX_RED: u8 = (1 << 5) - 1;
|
impl Framebuffer {
|
||||||
const MAX_GREEN: u8 = (1 << 6) - 1;
|
pub fn new(width: u32, height: u32) -> Self {
|
||||||
const MAX_BLUE: u8 = (1 << 5) - 1;
|
let buffer_len = width as usize * height as usize * core::mem::size_of::<u16>();
|
||||||
|
|
||||||
fn rgb(r: u8, g: u8, b: u8) -> u16 {
|
|
||||||
(((r & MAX_RED) as u16) << 11) | (((g & MAX_GREEN) as u16) << 5) | ((b & MAX_BLUE) as u16)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn row(edge: u16) -> impl Iterator<Item = u16> + Clone {
|
|
||||||
chain![
|
|
||||||
core::iter::repeat_n(rgb(0, 0, 0xFF), PADDING_LEFT),
|
|
||||||
core::iter::once(rgb(0xFF, 0xFF, 0xFF)),
|
|
||||||
core::iter::repeat_n(edge, COLORS_WIDTH - 2),
|
|
||||||
core::iter::once(rgb(0xFF, 0xFF, 0xFF)),
|
|
||||||
core::iter::repeat_n(rgb(0, 0, 0xFF), PADDING_RIGHT),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut colors = chain![
|
|
||||||
row(rgb(0xFF, 0, 0)),
|
|
||||||
core::iter::repeat_n(row(0), COLORS_HEIGHT - 2).flatten(),
|
|
||||||
row(rgb(0xFF, 0xFF, 0)),
|
|
||||||
];
|
|
||||||
|
|
||||||
const BUFFER_LEN: usize = core::mem::size_of::<u16>()
|
|
||||||
* (360 + /* TODO: Figure out why more bytes are needed: */ 8)
|
|
||||||
* 960;
|
|
||||||
// Allocate the framebuffer in the external PSRAM memory.
|
// Allocate the framebuffer in the external PSRAM memory.
|
||||||
// Note: We just leak this buffer.
|
// Note: We just leak this buffer.
|
||||||
let buffer_ptr = unsafe {
|
let buffer_ptr = unsafe {
|
||||||
|
|
@ -317,30 +349,157 @@ async fn run_lcd(mut st7701s: St7701s<'static, Blocking>) {
|
||||||
// descriptor must be a multiple of the cache line (block) size. This is 32 bytes on ESP32-S3.
|
// descriptor must be a multiple of the cache line (block) size. This is 32 bytes on ESP32-S3.
|
||||||
PSRAM_ALLOCATOR.alloc_caps(
|
PSRAM_ALLOCATOR.alloc_caps(
|
||||||
MemoryCapability::External.into(),
|
MemoryCapability::External.into(),
|
||||||
Layout::from_size_align(BUFFER_LEN, 32).unwrap(),
|
Layout::from_size_align(buffer_len, 32).unwrap(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let buffer = unsafe { core::slice::from_raw_parts_mut(buffer_ptr, BUFFER_LEN) };
|
let buffer = unsafe { core::slice::from_raw_parts_mut(buffer_ptr, buffer_len) };
|
||||||
let burst_config: BurstConfig = ExternalBurstConfig::Size16.into();
|
let burst_config: BurstConfig = ExternalBurstConfig::Size16.into();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"PSRAM SPI burst config: max_compatible_chunk_size={}",
|
"PSRAM SPI burst config: max_compatible_chunk_size={}",
|
||||||
burst_config.max_compatible_chunk_size()
|
burst_config.max_compatible_chunk_size()
|
||||||
);
|
);
|
||||||
let dma_buf_descs_len =
|
let dma_buf_descs_len = esp_hal::dma::descriptor_count(
|
||||||
esp_hal::dma::descriptor_count(BUFFER_LEN, burst_config.max_compatible_chunk_size(), false);
|
buffer_len,
|
||||||
|
burst_config.max_compatible_chunk_size(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
// Descriptors are initialized by `DmaTxBuf::new`.
|
// 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_descs =
|
||||||
let mut dma_buf = DmaTxBuf::new(dma_buf_descs, buffer).unwrap();
|
Box::leak(vec![DmaDescriptor::EMPTY; dma_buf_descs_len].into_boxed_slice());
|
||||||
|
let dma_buf = DmaTxBuf::new(dma_buf_descs, buffer).unwrap();
|
||||||
|
|
||||||
{
|
Self {
|
||||||
for (chunk, color) in dma_buf.as_mut_slice().chunks_mut(2).zip(&mut colors) {
|
width,
|
||||||
chunk.copy_from_slice(&color.to_le_bytes());
|
height,
|
||||||
|
dma_buf: Some(dma_buf),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("chunk addr: {}", dma_buf.as_slice().as_ptr() as usize);
|
pub fn as_target_pixels(&mut self) -> &mut [Rgb565Pixel] {
|
||||||
// colors.next(); // Shift colors
|
bytemuck::cast_slice_mut::<_, Rgb565Pixel>(self.dma_buf.as_mut().unwrap().as_mut_slice())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SlintBackend {
|
||||||
|
window_size: [u32; 2],
|
||||||
|
window: RefCell<Option<Rc<slint::platform::software_renderer::MinimalSoftwareWindow>>>,
|
||||||
|
framebuffer: FramebufferPtr,
|
||||||
|
// peripherals: RefCell<Option<Peripherals>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FramebufferPtr(*mut [Rgb565Pixel]);
|
||||||
|
|
||||||
|
unsafe impl Send for FramebufferPtr {}
|
||||||
|
|
||||||
|
impl slint::platform::Platform for SlintBackend {
|
||||||
|
fn create_window_adapter(&self) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
|
||||||
|
let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(
|
||||||
|
slint::platform::software_renderer::RepaintBufferType::ReusedBuffer,
|
||||||
|
);
|
||||||
|
window.set_size(WindowSize::Physical(PhysicalSize::new(self.window_size[0], self.window_size[1])));
|
||||||
|
self.window.replace(Some(window.clone()));
|
||||||
|
Ok(window)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn duration_since_start(&self) -> Duration {
|
||||||
|
Duration::from_millis(Instant::now().duration_since_epoch().as_millis())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
|
||||||
|
loop {
|
||||||
|
slint::platform::update_timers_and_animations();
|
||||||
|
|
||||||
|
if let Some(window) = self.window.borrow().clone() {
|
||||||
|
// window.try_dispatch_event(todo!())?;
|
||||||
|
|
||||||
|
window.draw_if_needed(|renderer| {
|
||||||
|
// TODO: Proper synchronization.
|
||||||
|
let framebuffer = unsafe { &mut *self.framebuffer.0 };
|
||||||
|
// TODO: Try using height to see if it rotates the screen correctly. Might need
|
||||||
|
// to swap dimensions elsewhere.
|
||||||
|
renderer.render(framebuffer, self.window_size[0] as usize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl DrawTarget for Framebuffer {
|
||||||
|
// type Color = Rgb565;
|
||||||
|
// type Error = ();
|
||||||
|
|
||||||
|
// fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||||
|
// where
|
||||||
|
// I: IntoIterator<Item = Pixel<Self::Color>>,
|
||||||
|
// {
|
||||||
|
// let buffer = bytemuck::try_cast_slice_mut::<_, u16>(self.dma_buf.as_mut_slice()).unwrap();
|
||||||
|
|
||||||
|
// for Pixel(coord, color) in pixels.into_iter() {
|
||||||
|
// // Check if the pixel coordinates are out of bounds.
|
||||||
|
// // `DrawTarget` implementation are required to discard any out of bounds
|
||||||
|
// // pixels without returning an error or causing a panic.
|
||||||
|
// if coord.x >= 0
|
||||||
|
// && coord.x < self.width as i32
|
||||||
|
// && coord.y >= 0
|
||||||
|
// && coord.y < self.height as i32
|
||||||
|
// {
|
||||||
|
// let index = coord.x as usize + coord.y as usize * self.width as usize;
|
||||||
|
// buffer[index] = RawU16::from(color).into_inner();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[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) {
|
||||||
|
// const PADDING_LEFT: usize = 121;
|
||||||
|
// const PADDING_RIGHT: usize = 7;
|
||||||
|
// const COLORS_WIDTH: usize = 240;
|
||||||
|
// const COLORS_HEIGHT: usize = 960;
|
||||||
|
|
||||||
|
// const MAX_RED: u8 = (1 << 5) - 1;
|
||||||
|
// const MAX_GREEN: u8 = (1 << 6) - 1;
|
||||||
|
// const MAX_BLUE: u8 = (1 << 5) - 1;
|
||||||
|
|
||||||
|
// fn rgb(r: u8, g: u8, b: u8) -> u16 {
|
||||||
|
// (((r & MAX_RED) as u16) << 11) | (((g & MAX_GREEN) as u16) << 5) | ((b & MAX_BLUE) as u16)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn row(edge: u16) -> impl Iterator<Item = u16> + Clone {
|
||||||
|
// chain![
|
||||||
|
// core::iter::repeat_n(rgb(0, 0, 0xFF), PADDING_LEFT),
|
||||||
|
// core::iter::once(rgb(0xFF, 0xFF, 0xFF)),
|
||||||
|
// core::iter::repeat_n(edge, COLORS_WIDTH - 2),
|
||||||
|
// core::iter::once(rgb(0xFF, 0xFF, 0xFF)),
|
||||||
|
// core::iter::repeat_n(rgb(0, 0, 0xFF), PADDING_RIGHT),
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let mut colors = chain![
|
||||||
|
// row(rgb(0xFF, 0, 0)),
|
||||||
|
// core::iter::repeat_n(row(0), COLORS_HEIGHT - 2).flatten(),
|
||||||
|
// row(rgb(0xFF, 0xFF, 0)),
|
||||||
|
// ];
|
||||||
|
|
||||||
|
// const BUFFER_LEN: usize = core::mem::size_of::<u16>()
|
||||||
|
// * (360 + /* TODO: Figure out why more bytes are needed: */ 8)
|
||||||
|
// * 960;
|
||||||
|
|
||||||
|
// {
|
||||||
|
// for (chunk, color) in dma_buf.as_mut_slice().chunks_mut(2).zip(&mut colors) {
|
||||||
|
// chunk.copy_from_slice(&color.to_le_bytes());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// info!("chunk addr: {}", dma_buf.as_slice().as_ptr() as usize);
|
||||||
|
// // colors.next(); // Shift colors
|
||||||
|
// }
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Timer::after(Duration::from_millis(100)).await;
|
// Timer::after(Duration::from_millis(100)).await;
|
||||||
|
|
@ -349,19 +508,27 @@ async fn run_lcd(mut st7701s: St7701s<'static, Blocking>) {
|
||||||
// TODO: Use bounce buffers:
|
// 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
|
// https://docs.espressif.com/projects/esp-idf/en/v5.0/esp32s3/api-reference/peripherals/lcd.html#bounce-buffer-with-single-psram-frame-buffer
|
||||||
// They need to be implemented in esp-hal.
|
// They need to be implemented in esp-hal.
|
||||||
let transfer = match st7701s.dpi.send(false, dma_buf) {
|
let transfer = match st7701s.dpi.send(false, framebuffer.dma_buf.take().unwrap()) {
|
||||||
Err((error, result_dpi, result_dma_buf)) => {
|
Err((error, result_dpi, result_dma_buf)) => {
|
||||||
error!(
|
error!(
|
||||||
"An error occurred while initiating transfer of the framebuffer to the LCD display: {error:?}"
|
"An error occurred while initiating transfer of the framebuffer to the LCD display: {error:?}"
|
||||||
);
|
);
|
||||||
st7701s.dpi = result_dpi;
|
st7701s.dpi = result_dpi;
|
||||||
dma_buf = result_dma_buf;
|
framebuffer.dma_buf = Some(result_dma_buf);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Ok(transfer) => transfer,
|
Ok(transfer) => transfer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
while !transfer.is_done() {
|
||||||
|
// Timer::after_millis(1).await;
|
||||||
|
yield_now().await;
|
||||||
|
}
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
|
let dma_buf;
|
||||||
(result, st7701s.dpi, dma_buf) = transfer.wait();
|
(result, st7701s.dpi, dma_buf) = transfer.wait();
|
||||||
|
framebuffer.dma_buf = Some(dma_buf);
|
||||||
|
|
||||||
if let Err(error) = result {
|
if let Err(error) = result {
|
||||||
error!(
|
error!(
|
||||||
|
|
|
||||||
3
firmware2/src/ui/mod.rs
Normal file
3
firmware2/src/ui/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
// #![cfg_attr(not(feature = "simulator"), no_main)]
|
||||||
|
|
||||||
|
slint::include_modules!();
|
||||||
18
firmware2/ui/main.slint
Normal file
18
firmware2/ui/main.slint
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Button, VerticalBox } from "std-widgets.slint";
|
||||||
|
|
||||||
|
export component AppWindow inherits Window {
|
||||||
|
in-out property <int> counter: 42;
|
||||||
|
callback request-increase-value();
|
||||||
|
VerticalBox {
|
||||||
|
Text {
|
||||||
|
text: "Counter: \{root.counter}";
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Increase value";
|
||||||
|
clicked => {
|
||||||
|
root.request-increase-value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue