Glitchless LCD driving
This commit is contained in:
parent
a9870fe133
commit
f7fbe366f6
3
firmware2/Cargo.lock
generated
3
firmware2/Cargo.lock
generated
|
|
@ -15,6 +15,7 @@ dependencies = [
|
|||
"critical-section",
|
||||
"embassy-embedded-hal",
|
||||
"embassy-executor",
|
||||
"embassy-sync 0.7.2",
|
||||
"embassy-time",
|
||||
"embedded-cli",
|
||||
"embedded-io 0.6.1",
|
||||
|
|
@ -1521,6 +1522,7 @@ dependencies = [
|
|||
"document-features",
|
||||
"embassy-executor-macros",
|
||||
"embassy-executor-timer-queue",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1599,6 +1601,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-sink",
|
||||
"heapless 0.8.0",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ rmk = { version = "0.8.2", default-features = false, features = [
|
|||
"storage",
|
||||
"vial",
|
||||
] }
|
||||
embassy-executor = { version = "0.9" }
|
||||
embassy-executor = { version = "0.9", features = ["log"] }
|
||||
embassy-time = { version = "0.5.0", features = ["log"] }
|
||||
embassy-embedded-hal = "0.5.0"
|
||||
embassy-sync = { version = "0.7.2", features = ["log"] }
|
||||
esp-backtrace = { version = "0.18", default-features = false, features = [
|
||||
"esp32s3",
|
||||
"println",
|
||||
|
|
|
|||
|
|
@ -6,13 +6,18 @@ extern crate alloc;
|
|||
|
||||
use core::alloc::Layout;
|
||||
use core::cell::RefCell;
|
||||
use core::sync::atomic::AtomicBool;
|
||||
use core::time::Duration;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec;
|
||||
use bt_hci::controller::ExternalController;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::signal::Signal;
|
||||
use embassy_time::{Delay, Timer};
|
||||
use esp_alloc::{HeapRegion, MemoryCapability};
|
||||
use esp_hal::clock::CpuClock;
|
||||
use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig};
|
||||
|
|
@ -45,7 +50,7 @@ use rmk::storage::async_flash_wrapper;
|
|||
use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
|
||||
use slint::platform::WindowAdapter;
|
||||
use slint::platform::software_renderer::{MinimalSoftwareWindow, RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer};
|
||||
use slint::{ComponentHandle, PhysicalSize, WindowSize};
|
||||
use slint::{ComponentHandle, PhysicalSize, PlatformError, WindowSize};
|
||||
use static_cell::StaticCell;
|
||||
use ui::AppWindow;
|
||||
use ui::window_adapter::SoftwareWindowAdapter;
|
||||
|
|
@ -74,6 +79,12 @@ const LOG_LEVEL_FILTER: LevelFilter = LevelFilter::Info;
|
|||
|
||||
static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
|
||||
|
||||
/// 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) {
|
||||
#[cfg(not(feature = "alt-log"))]
|
||||
|
|
@ -199,8 +210,6 @@ async fn main(_spawner: Spawner) {
|
|||
.with_data3(peripherals.GPIO41)
|
||||
.with_data4(peripherals.GPIO42)
|
||||
// Green
|
||||
// .with_data5(peripherals.GPIO5) TODO
|
||||
// .with_data6(peripherals.GPIO12)
|
||||
.with_data7(peripherals.GPIO13)
|
||||
.with_data8(peripherals.GPIO14)
|
||||
.with_data9(peripherals.GPIO15)
|
||||
|
|
@ -212,6 +221,12 @@ async fn main(_spawner: Spawner) {
|
|||
.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;
|
||||
|
||||
// RMK config
|
||||
|
|
@ -326,10 +341,23 @@ async fn run_renderer_task(backend: SlintBackend) {
|
|||
slint::platform::set_platform(Box::new(backend))
|
||||
.expect("backend already initialized");
|
||||
|
||||
loop {
|
||||
let main = AppWindow::new().unwrap();
|
||||
let main = AppWindow::new().unwrap();
|
||||
|
||||
main.run().unwrap();
|
||||
// Instead of having a `loop` in the non-async `SlintBackend::run_event_loop`, we achieve
|
||||
// async by having only one iteration of the loop run, and `await`ing here.
|
||||
// The following block is analogous to `main.run()`.
|
||||
{
|
||||
main.show().unwrap();
|
||||
|
||||
loop {
|
||||
slint::run_event_loop().unwrap();
|
||||
SIGNAL_LCD_SUBMIT.signal(());
|
||||
// Timer::after_millis(50).await;
|
||||
SIGNAL_UI_RENDER.wait().await;
|
||||
}
|
||||
|
||||
#[expect(unreachable_code)]
|
||||
main.hide().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +488,8 @@ async fn run_lcd(mut st7701s: St7701s<'static, Blocking>, framebuffer: &'static
|
|||
|
||||
loop {
|
||||
// Timer::after(Duration::from_millis(100)).await;
|
||||
yield_now().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
|
||||
|
|
@ -477,16 +506,22 @@ async fn run_lcd(mut st7701s: St7701s<'static, Blocking>, framebuffer: &'static
|
|||
Ok(transfer) => transfer,
|
||||
};
|
||||
|
||||
while !transfer.is_done() {
|
||||
// Timer::after_millis(1).await;
|
||||
yield_now().await;
|
||||
}
|
||||
// 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.
|
||||
// while !transfer.is_done() {
|
||||
// // Timer::after_millis(1).await;
|
||||
// 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:?}"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use core::{cell::RefCell, time::Duration};
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use esp_hal::time::Instant;
|
||||
use esp_hal::{time::Instant};
|
||||
use log::info;
|
||||
use slint::{PhysicalSize, WindowSize, platform::software_renderer::{RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer}};
|
||||
|
||||
|
|
@ -36,21 +36,23 @@ impl slint::platform::Platform for SlintBackend {
|
|||
}
|
||||
|
||||
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
|
||||
loop {
|
||||
slint::platform::update_timers_and_animations();
|
||||
|
||||
// Instead of `loop`ing here, we execute a single iteration and handle `loop`ing
|
||||
// in `crate::run_renderer_task`, where we can make use of `await`.
|
||||
|
||||
/* loop */ {
|
||||
if let Some(window) = self.window.borrow().clone() {
|
||||
// TODO: Event dispatch. Here or in `run_renderer_task`?
|
||||
// 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[1] as usize);
|
||||
info!("UI rendered.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue