Implement display sleep, backlight disabling and bounce buffer
transmission stopping
This commit is contained in:
parent
6f42bf7a7b
commit
682522d556
2
firmware/Cargo.lock
generated
2
firmware/Cargo.lock
generated
|
|
@ -2149,7 +2149,9 @@ name = "esp-hal-bounce-buffers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
|
"embassy-sync 0.7.2",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
|
"log",
|
||||||
"ouroboros",
|
"ouroboros",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ develop-usb = ["limit-fps", "usb-log", "no-usb", "ble"]
|
||||||
probe = ["limit-fps", "rtt-log", "no-usb", "ble"]
|
probe = ["limit-fps", "rtt-log", "no-usb", "ble"]
|
||||||
# Formats the EKV database on boot.
|
# Formats the EKV database on boot.
|
||||||
format-db = []
|
format-db = []
|
||||||
|
# Avoid entering the critical section for the whole duration of printing a message to console.
|
||||||
|
racy-logging = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rmk = { version = "0.8.2", git = "https://github.com/Limeth/rmk", rev = "1661c55f5c21e7d80ea3f93255df483302c74b84", default-features = false, features = [
|
rmk = { version = "0.8.2", git = "https://github.com/Limeth/rmk", rev = "1661c55f5c21e7d80ea3f93255df483302c74b84", default-features = false, features = [
|
||||||
|
|
@ -87,7 +89,7 @@ esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] }
|
||||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||||
indoc = "2.0.7"
|
indoc = "2.0.7"
|
||||||
ouroboros = "0.18.5"
|
ouroboros = "0.18.5"
|
||||||
esp-hal-bounce-buffers = { git = "https://forgejo.limeth.cz/limeth/esp-hal-bounce-buffers", features = ["esp32s3"] }
|
esp-hal-bounce-buffers = { git = "https://forgejo.limeth.cz/limeth/esp-hal-bounce-buffers", rev = "8d3763a190368f476aed6d98777264c959bfdc2d", features = ["esp32s3", "log"] }
|
||||||
|
|
||||||
# A fork of slint with patches for `allocator_api` support.
|
# A fork of slint with patches for `allocator_api` support.
|
||||||
# Don't forget to change `slint-build` in build dependencies, if this is changed.
|
# Don't forget to change `slint-build` in build dependencies, if this is changed.
|
||||||
|
|
|
||||||
|
|
@ -69,21 +69,29 @@ pub mod usb {
|
||||||
#[cfg(feature = "alt-log")]
|
#[cfg(feature = "alt-log")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod uart {
|
pub mod uart {
|
||||||
use crate::util::MutexExt;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use core::{cell::RefCell, fmt::Write};
|
use core::{cell::RefCell, fmt::Write};
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use esp_hal::{Blocking, uart::UartTx};
|
use esp_hal::{Blocking, uart::UartTx};
|
||||||
use log::{Log, info};
|
use log::{Log, info};
|
||||||
|
|
||||||
static ALT_LOGGER_UART: Mutex<
|
#[cfg(feature = "racy-logging")]
|
||||||
|
static ALT_LOGGER_UART: embassy_sync::mutex::Mutex<
|
||||||
CriticalSectionRawMutex,
|
CriticalSectionRawMutex,
|
||||||
RefCell<Option<UartTx<'static, Blocking>>>,
|
RefCell<Option<UartTx<'static, Blocking>>>,
|
||||||
> = Mutex::new(RefCell::new(None));
|
> = embassy_sync::mutex::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 {
|
pub fn with_uart_tx<R>(f: impl FnOnce(&'_ mut UartTx<'static, Blocking>) -> R) -> R {
|
||||||
|
use crate::util::MutexExt;
|
||||||
|
|
||||||
// Safety:
|
// Safety:
|
||||||
// * The guard is not held across yield points.
|
// * The guard is not held across yield points.
|
||||||
// * **CARE MUST BE TAKEN NOT TO INVOKE THIS FUNCTION FROM AN INTERRUPT HANDLER.**
|
// * **CARE MUST BE TAKEN NOT TO INVOKE THIS FUNCTION FROM AN INTERRUPT HANDLER.**
|
||||||
|
|
@ -94,6 +102,16 @@ pub mod uart {
|
||||||
(f)(uart)
|
(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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
macro_rules! println {
|
macro_rules! println {
|
||||||
() => {{
|
() => {{
|
||||||
|
|
@ -156,11 +174,21 @@ pub mod uart {
|
||||||
|
|
||||||
pub fn setup_logging(uart_tx: UartTx<'static, Blocking>) {
|
pub fn setup_logging(uart_tx: UartTx<'static, Blocking>) {
|
||||||
{
|
{
|
||||||
// Safety:
|
#[cfg(feature = "racy-logging")]
|
||||||
// * The guard is not held across yield points.
|
{
|
||||||
// * This function is not invoked from an interrupt handler.
|
use crate::util::MutexExt;
|
||||||
let uart = unsafe { ALT_LOGGER_UART.lock_blocking() };
|
|
||||||
*uart.borrow_mut() = Some(uart_tx);
|
// 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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ use esp_hal::i2c::master::{I2c, I2cAddress};
|
||||||
use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl};
|
use esp_hal::interrupt::software::{SoftwareInterrupt, 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::ledc::{self, LSGlobalClkSource, Ledc, LowSpeed};
|
||||||
use esp_hal::peripherals::{DMA_CH0, SPI2};
|
use esp_hal::peripherals::{DMA_CH0, SPI2};
|
||||||
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
||||||
use esp_hal::ram;
|
use esp_hal::ram;
|
||||||
|
|
@ -58,7 +58,9 @@ use esp_hal::system::Stack;
|
||||||
use esp_hal::timer::timg::TimerGroup;
|
use esp_hal::timer::timg::TimerGroup;
|
||||||
use esp_hal::uart::{Uart, UartRx};
|
use esp_hal::uart::{Uart, UartRx};
|
||||||
use esp_hal::{Blocking, interrupt};
|
use esp_hal::{Blocking, interrupt};
|
||||||
use esp_hal_bounce_buffers::{DmaBounce, Swapchain, allocate_dma_buffer_in};
|
use esp_hal_bounce_buffers::{
|
||||||
|
DmaBounce, RunningDmaBounceHandle, Swapchain, allocate_dma_buffer_in,
|
||||||
|
};
|
||||||
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
||||||
use esp_storage::FlashStorage;
|
use esp_storage::FlashStorage;
|
||||||
use i_slint_core::software_renderer::TargetPixel;
|
use i_slint_core::software_renderer::TargetPixel;
|
||||||
|
|
@ -82,7 +84,7 @@ use static_cell::StaticCell;
|
||||||
use {esp_alloc as _, esp_backtrace as _};
|
use {esp_alloc as _, esp_backtrace as _};
|
||||||
|
|
||||||
use crate::matrix::IoeMatrix;
|
use crate::matrix::IoeMatrix;
|
||||||
use crate::peripherals::st7701s::St7701s;
|
use crate::peripherals::st7701s::{St7701s, St7701sController};
|
||||||
use crate::proxy::create_hid_report_interceptor;
|
use crate::proxy::create_hid_report_interceptor;
|
||||||
use crate::ui::backend::SlintBackend;
|
use crate::ui::backend::SlintBackend;
|
||||||
use crate::ui::dpi::Framebuffer;
|
use crate::ui::dpi::Framebuffer;
|
||||||
|
|
@ -144,12 +146,14 @@ async fn test_bounce_buffers_task(
|
||||||
test_bounce_buffers(channel, peripheral, st7701s).await;
|
test_bounce_buffers(channel, peripheral, st7701s).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
async fn test_bounce_buffers(
|
async fn test_bounce_buffers(
|
||||||
channel: DMA_CH0<'static>,
|
channel: DMA_CH0<'static>,
|
||||||
peripheral: SPI2<'static>,
|
peripheral: SPI2<'static>,
|
||||||
st7701s: St7701s<'static, Blocking>,
|
mut st7701s: St7701s<'static, Blocking>,
|
||||||
) {
|
) {
|
||||||
error!("TEST BOUNCE BUFFERS SECTION ENTERED");
|
error!("TEST BOUNCE BUFFERS SECTION ENTERED");
|
||||||
|
|
||||||
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
|
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
|
||||||
// Assume highest burst config setting.
|
// Assume highest burst config setting.
|
||||||
const EXTERNAL_BURST_CONFIG: ExternalBurstConfig = ExternalBurstConfig::Size32;
|
const EXTERNAL_BURST_CONFIG: ExternalBurstConfig = ExternalBurstConfig::Size32;
|
||||||
|
|
@ -215,11 +219,7 @@ async fn test_bounce_buffers(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("FRONT_PORCH_SKIPPED_PIXELS: {FRONT_PORCH_SKIPPED_PIXELS}");
|
let mut dma_bounce = DmaBounce::new(
|
||||||
warn!("WIDTH_STORED_PIXELS: {WIDTH_STORED_PIXELS}");
|
|
||||||
warn!("ROWS_PER_WINDOW: {ROWS_PER_WINDOW}");
|
|
||||||
|
|
||||||
let buf = DmaBounce::new(
|
|
||||||
Global,
|
Global,
|
||||||
channel,
|
channel,
|
||||||
AnySpi::from(peripheral),
|
AnySpi::from(peripheral),
|
||||||
|
|
@ -231,8 +231,18 @@ async fn test_bounce_buffers(
|
||||||
burst_config,
|
burst_config,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
buf.launch_interrupt_driven_task();
|
let mut bb_controller = DmaBounceController::new(dma_bounce);
|
||||||
error!("TEST BOUNCE BUFFERS SECTION DONE");
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[esp_rtos::main]
|
#[esp_rtos::main]
|
||||||
|
|
@ -313,10 +323,6 @@ async fn main(_spawner: Spawner) {
|
||||||
// Enable antenna
|
// Enable antenna
|
||||||
let _ = Output::new(peripherals.GPIO11, Level::Low, OutputConfig::default());
|
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 mut sha_backend = ShaBackend::new(peripherals.SHA);
|
||||||
let _sha_driver_handle = sha_backend.start();
|
let _sha_driver_handle = sha_backend.start();
|
||||||
|
|
||||||
|
|
@ -341,6 +347,7 @@ async fn main(_spawner: Spawner) {
|
||||||
// high_priority_task_spawner: interrupt_core_1_spawner,
|
// high_priority_task_spawner: interrupt_core_1_spawner,
|
||||||
uart_rx,
|
uart_rx,
|
||||||
software_interrupt1: software_interrupt.software_interrupt1,
|
software_interrupt1: software_interrupt.software_interrupt1,
|
||||||
|
LEDC: peripherals.LEDC,
|
||||||
RNG: peripherals.RNG,
|
RNG: peripherals.RNG,
|
||||||
ADC1: peripherals.ADC1,
|
ADC1: peripherals.ADC1,
|
||||||
USB0: peripherals.USB0,
|
USB0: peripherals.USB0,
|
||||||
|
|
@ -370,6 +377,7 @@ async fn main(_spawner: Spawner) {
|
||||||
GPIO16: peripherals.GPIO16,
|
GPIO16: peripherals.GPIO16,
|
||||||
GPIO19: peripherals.GPIO19,
|
GPIO19: peripherals.GPIO19,
|
||||||
GPIO20: peripherals.GPIO20,
|
GPIO20: peripherals.GPIO20,
|
||||||
|
GPIO21: peripherals.GPIO21,
|
||||||
GPIO34: peripherals.GPIO34,
|
GPIO34: peripherals.GPIO34,
|
||||||
GPIO35: peripherals.GPIO35,
|
GPIO35: peripherals.GPIO35,
|
||||||
GPIO36: peripherals.GPIO36,
|
GPIO36: peripherals.GPIO36,
|
||||||
|
|
@ -392,6 +400,7 @@ struct MainPeripherals {
|
||||||
// high_priority_task_spawner: SendSpawner,
|
// high_priority_task_spawner: SendSpawner,
|
||||||
uart_rx: UartRx<'static, Blocking>,
|
uart_rx: UartRx<'static, Blocking>,
|
||||||
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
||||||
|
LEDC: esp_hal::peripherals::LEDC<'static>,
|
||||||
RNG: esp_hal::peripherals::RNG<'static>,
|
RNG: esp_hal::peripherals::RNG<'static>,
|
||||||
ADC1: esp_hal::peripherals::ADC1<'static>,
|
ADC1: esp_hal::peripherals::ADC1<'static>,
|
||||||
USB0: esp_hal::peripherals::USB0<'static>,
|
USB0: esp_hal::peripherals::USB0<'static>,
|
||||||
|
|
@ -423,6 +432,7 @@ struct MainPeripherals {
|
||||||
// GPIO18: esp_hal::peripherals::GPIO18<'static>,
|
// GPIO18: esp_hal::peripherals::GPIO18<'static>,
|
||||||
GPIO19: esp_hal::peripherals::GPIO19<'static>,
|
GPIO19: esp_hal::peripherals::GPIO19<'static>,
|
||||||
GPIO20: esp_hal::peripherals::GPIO20<'static>,
|
GPIO20: esp_hal::peripherals::GPIO20<'static>,
|
||||||
|
GPIO21: esp_hal::peripherals::GPIO21<'static>,
|
||||||
// GPIO33: esp_hal::peripherals::GPIO33<'static>,
|
// GPIO33: esp_hal::peripherals::GPIO33<'static>,
|
||||||
GPIO34: esp_hal::peripherals::GPIO34<'static>,
|
GPIO34: esp_hal::peripherals::GPIO34<'static>,
|
||||||
GPIO35: esp_hal::peripherals::GPIO35<'static>,
|
GPIO35: esp_hal::peripherals::GPIO35<'static>,
|
||||||
|
|
@ -577,6 +587,11 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
|
|
||||||
info!("Flash memory configured!");
|
info!("Flash memory configured!");
|
||||||
|
|
||||||
|
let mut ledc = Ledc::new(peripherals.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, peripherals.GPIO21);
|
||||||
|
|
||||||
let sck = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default());
|
let sck = Output::new(peripherals.GPIO36, Level::High, OutputConfig::default());
|
||||||
let mosi = Flex::new(peripherals.GPIO35);
|
let mosi = Flex::new(peripherals.GPIO35);
|
||||||
let cs = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default());
|
let cs = Output::new(peripherals.GPIO6, Level::High, OutputConfig::default());
|
||||||
|
|
@ -612,10 +627,11 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
.with_data5(peripherals.GPIO5)
|
.with_data5(peripherals.GPIO5)
|
||||||
.with_data6(peripherals.GPIO12);
|
.with_data6(peripherals.GPIO12);
|
||||||
|
|
||||||
let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi).await;
|
let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi, bl_timer, bl_channel).await;
|
||||||
|
|
||||||
info!("ST7701S-based LCD display initialized!");
|
info!("ST7701S-based LCD display initialized!");
|
||||||
|
|
||||||
|
// Uncomment this to run bounce buffer test code instead.
|
||||||
// test_bounce_buffers(peripherals.DMA_CH0, peripherals.SPI2, st7701s).await;
|
// test_bounce_buffers(peripherals.DMA_CH0, peripherals.SPI2, st7701s).await;
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
|
|
@ -722,7 +738,7 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
112,
|
112,
|
||||||
368 - 112,
|
368 - 112,
|
||||||
960,
|
960,
|
||||||
8,
|
16,
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -763,16 +779,11 @@ async fn main_task(peripherals: MainPeripherals) {
|
||||||
|
|
||||||
info!("Second core started!");
|
info!("Second core started!");
|
||||||
|
|
||||||
let mut user_controller = UserController::new();
|
let bb_controller = DmaBounceController::new(framebuffer.bounce_buffers.take().unwrap());
|
||||||
|
let mut user_controller = UserController::new(st7701s.controller, bb_controller);
|
||||||
|
|
||||||
info!("Awaiting on all tasks...");
|
info!("Awaiting on all tasks...");
|
||||||
|
|
||||||
framebuffer
|
|
||||||
.bounce_buffers
|
|
||||||
.take()
|
|
||||||
.unwrap()
|
|
||||||
.launch_interrupt_driven_task();
|
|
||||||
|
|
||||||
// TODO: Probably want to select! instead and re-try.
|
// TODO: Probably want to select! instead and re-try.
|
||||||
join_all![
|
join_all![
|
||||||
run_alloc_stats_reporter(),
|
run_alloc_stats_reporter(),
|
||||||
|
|
@ -835,19 +846,61 @@ async fn run_alloc_stats_reporter() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserController {
|
enum DmaBounceControllerInner {
|
||||||
sub: ControllerSub,
|
Idle(DmaBounce),
|
||||||
|
Transmitting(RunningDmaBounceHandle),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserController {
|
struct DmaBounceController {
|
||||||
fn new() -> Self {
|
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> {
|
||||||
|
sub: ControllerSub,
|
||||||
|
lcd_controller: St7701sController<'a>,
|
||||||
|
bb_controller: DmaBounceController,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> UserController<'a> {
|
||||||
|
fn new(lcd_controller: St7701sController<'a>, bb_controller: DmaBounceController) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sub: CONTROLLER_CHANNEL.subscriber().unwrap(),
|
sub: CONTROLLER_CHANNEL.subscriber().unwrap(),
|
||||||
|
lcd_controller,
|
||||||
|
bb_controller,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Controller for UserController {
|
impl<'a> Controller for UserController<'a> {
|
||||||
type Event = ControllerEvent;
|
type Event = ControllerEvent;
|
||||||
|
|
||||||
async fn process_event(&mut self, event: Self::Event) {
|
async fn process_event(&mut self, event: Self::Event) {
|
||||||
|
|
@ -863,10 +916,14 @@ impl Controller for UserController {
|
||||||
info!("Disabling LCD.");
|
info!("Disabling LCD.");
|
||||||
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await =
|
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await =
|
||||||
&rmk::channel::KEYBOARD_REPORT_RECEIVER;
|
&rmk::channel::KEYBOARD_REPORT_RECEIVER;
|
||||||
|
self.lcd_controller.sleep_on().await;
|
||||||
|
self.bb_controller.stop().await.unwrap();
|
||||||
}
|
}
|
||||||
true => {
|
true => {
|
||||||
info!("Enabling LCD.");
|
info!("Enabling LCD.");
|
||||||
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await = &KEYBOARD_REPORT_PROXY;
|
*rmk::channel::KEYBOARD_REPORT_SENDER.write().await = &KEYBOARD_REPORT_PROXY;
|
||||||
|
self.bb_controller.start().await.unwrap();
|
||||||
|
self.lcd_controller.sleep_off().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,12 @@ use esp_hal::{
|
||||||
ClockMode, DelayMode, Phase, Polarity,
|
ClockMode, DelayMode, Phase, Polarity,
|
||||||
dpi::{Dpi, Format, FrameTiming},
|
dpi::{Dpi, Format, FrameTiming},
|
||||||
},
|
},
|
||||||
|
ledc::{self, LowSpeed, channel::ChannelIFace, timer::TimerIFace},
|
||||||
time::Rate,
|
time::Rate,
|
||||||
};
|
};
|
||||||
use lcd::spi_write;
|
use lcd::spi_write;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use ouroboros::self_referencing;
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
// use tinyvec::ArrayVec;
|
// use tinyvec::ArrayVec;
|
||||||
|
|
||||||
|
|
@ -1233,13 +1235,76 @@ define_commands! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[self_referencing]
|
||||||
|
pub struct Backlight<'d> {
|
||||||
|
pub timer: ledc::timer::Timer<'d, LowSpeed>,
|
||||||
|
#[borrows(timer)]
|
||||||
|
#[covariant]
|
||||||
|
pub channel: ledc::channel::Channel<'this, LowSpeed>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Backlight<'a> {
|
||||||
|
pub fn set_duty(&mut self, duty_pct: u8) -> Result<(), ledc::channel::Error> {
|
||||||
|
self.borrow_channel().set_duty(duty_pct)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct St7701sController<'d> {
|
||||||
|
pub sck: Output<'d>,
|
||||||
|
pub mosi: Flex<'d>,
|
||||||
|
pub cs: Output<'d>,
|
||||||
|
pub backlight: Backlight<'d>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> St7701sController<'d> {
|
||||||
|
pub async fn send_init_sequence(&mut self) {
|
||||||
|
debug!("Writing ST7701S init sequence.");
|
||||||
|
|
||||||
|
for (subsequence, delay_ms) in &*lcd::INIT_SEQUENCE_COMMANDS {
|
||||||
|
for command in subsequence {
|
||||||
|
spi_write(
|
||||||
|
command.address(),
|
||||||
|
command.args_iter().copied(),
|
||||||
|
&mut self.mosi,
|
||||||
|
&mut self.sck,
|
||||||
|
&mut self.cs,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer::after_millis(*delay_ms).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send(&mut self, command: impl Command) {
|
||||||
|
spi_write(
|
||||||
|
command.address(),
|
||||||
|
command.args_iter().copied(),
|
||||||
|
&mut self.mosi,
|
||||||
|
&mut self.sck,
|
||||||
|
&mut self.cs,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Puts the display into sleep mode and disables the backlight.
|
||||||
|
pub async fn sleep_on(&mut self) {
|
||||||
|
self.backlight.set_duty(0).unwrap();
|
||||||
|
self.send(CmdSlpin()).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Brings the display out of sleep mode and enables the backlight.
|
||||||
|
pub async fn sleep_off(&mut self) {
|
||||||
|
self.send(CmdSlpout()).await;
|
||||||
|
self.backlight.set_duty(100).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct St7701s<'d, Dm>
|
pub struct St7701s<'d, Dm>
|
||||||
where
|
where
|
||||||
Dm: DriverMode,
|
Dm: DriverMode,
|
||||||
{
|
{
|
||||||
pub sck: Output<'d>,
|
pub controller: St7701sController<'d>,
|
||||||
pub mosi: Flex<'d>,
|
|
||||||
pub cs: Output<'d>,
|
|
||||||
pub dpi: Dpi<'d, Dm>,
|
pub dpi: Dpi<'d, Dm>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1247,11 +1312,15 @@ impl<'d, Dm> St7701s<'d, Dm>
|
||||||
where
|
where
|
||||||
Dm: DriverMode,
|
Dm: DriverMode,
|
||||||
{
|
{
|
||||||
|
/// Initializes an ST7701S display, and puts it to sleep.
|
||||||
|
/// To turn it on, invoke `St7701sController:sleep_off`.
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
mut sck: Output<'d>,
|
mut sck: Output<'d>,
|
||||||
mut mosi: Flex<'d>,
|
mut mosi: Flex<'d>,
|
||||||
mut cs: Output<'d>,
|
mut cs: Output<'d>,
|
||||||
mut unconfigured_dpi: Dpi<'d, Dm>,
|
mut unconfigured_dpi: Dpi<'d, Dm>,
|
||||||
|
mut bl_timer: ledc::timer::Timer<'d, LowSpeed>,
|
||||||
|
bl_channel: ledc::channel::Channel<'d, LowSpeed>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
sck.apply_config(&Default::default());
|
sck.apply_config(&Default::default());
|
||||||
sck.set_high();
|
sck.set_high();
|
||||||
|
|
@ -1276,7 +1345,7 @@ where
|
||||||
//
|
//
|
||||||
// Adafruit would use 11 MHz.
|
// Adafruit would use 11 MHz.
|
||||||
// I had lowered the frequency, so that `DmaBounce` could keep up.
|
// I had lowered the frequency, so that `DmaBounce` could keep up.
|
||||||
.with_frequency(Rate::from_mhz(6)) // From Adafruit
|
.with_frequency(Rate::from_mhz(9)) // From Adafruit
|
||||||
.with_clock_mode(ClockMode {
|
.with_clock_mode(ClockMode {
|
||||||
polarity: Polarity::IdleLow, // From Adafruit
|
polarity: Polarity::IdleLow, // From Adafruit
|
||||||
phase: Phase::ShiftHigh, // From Adafruit
|
phase: Phase::ShiftHigh, // From Adafruit
|
||||||
|
|
@ -1338,38 +1407,38 @@ where
|
||||||
|
|
||||||
unconfigured_dpi.apply_config(&lcd_config).unwrap();
|
unconfigured_dpi.apply_config(&lcd_config).unwrap();
|
||||||
|
|
||||||
|
bl_timer
|
||||||
|
.configure(ledc::timer::config::Config {
|
||||||
|
duty: ledc::timer::config::Duty::Duty5Bit,
|
||||||
|
clock_source: ledc::timer::LSClockSource::APBClk,
|
||||||
|
frequency: Rate::from_khz(24),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let backlight = Backlight::new(bl_timer, move |bl_timer| {
|
||||||
|
let mut bl_channel = bl_channel; // Forces bl_channel to be moved before it is mutated.
|
||||||
|
bl_channel
|
||||||
|
.configure(ledc::channel::config::Config {
|
||||||
|
timer: bl_timer,
|
||||||
|
drive_mode: esp_hal::gpio::DriveMode::PushPull,
|
||||||
|
duty_pct: 0,
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
bl_channel
|
||||||
|
});
|
||||||
|
|
||||||
let mut lcd = Self {
|
let mut lcd = Self {
|
||||||
sck,
|
controller: St7701sController {
|
||||||
mosi,
|
sck,
|
||||||
cs,
|
mosi,
|
||||||
|
cs,
|
||||||
|
backlight,
|
||||||
|
},
|
||||||
dpi: unconfigured_dpi,
|
dpi: unconfigured_dpi,
|
||||||
};
|
};
|
||||||
|
|
||||||
lcd.send_init_sequence().await;
|
lcd.controller.send_init_sequence().await;
|
||||||
|
lcd.controller.sleep_on().await;
|
||||||
|
|
||||||
lcd
|
lcd
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_init_sequence(&mut self) {
|
|
||||||
debug!("Writing ST7701S init sequence.");
|
|
||||||
|
|
||||||
for (subsequence, delay_ms) in &*lcd::INIT_SEQUENCE_COMMANDS {
|
|
||||||
for command in subsequence {
|
|
||||||
spi_write(
|
|
||||||
command.address(),
|
|
||||||
command.args_iter().copied(),
|
|
||||||
&mut self.mosi,
|
|
||||||
&mut self.sck,
|
|
||||||
&mut self.cs,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
// info!("COMM 0x{:02X}", command.address());
|
|
||||||
// for arg in command.args_iter() {
|
|
||||||
// info!("DATA 0x{arg:02X}");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer::after_millis(*delay_ms).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,11 @@
|
||||||
use core::{
|
use alloc::{alloc::Global, boxed::Box};
|
||||||
alloc::Layout,
|
|
||||||
cell::UnsafeCell,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
sync::atomic::{self, AtomicBool},
|
|
||||||
};
|
|
||||||
|
|
||||||
use alloc::{
|
|
||||||
alloc::{Allocator, Global},
|
|
||||||
boxed::Box,
|
|
||||||
sync::Arc,
|
|
||||||
vec,
|
|
||||||
};
|
|
||||||
use bytemuck::{AnyBitPattern, NoUninit};
|
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
Blocking,
|
Blocking, dma::BurstConfig, lcd_cam::lcd::dpi::Dpi, peripherals::DMA_CH0, spi::master::AnySpi,
|
||||||
dma::{
|
|
||||||
self, BurstConfig, DmaDescriptor, DmaTxBuffer, Mem2Mem, SimpleMem2Mem,
|
|
||||||
SimpleMem2MemTransfer,
|
|
||||||
},
|
|
||||||
handler,
|
|
||||||
interrupt::{self, Priority},
|
|
||||||
lcd_cam::lcd::dpi::{Dpi, DpiTransfer},
|
|
||||||
peripherals::{DMA, DMA_CH0, Interrupt},
|
|
||||||
ram,
|
|
||||||
spi::master::AnySpi,
|
|
||||||
};
|
};
|
||||||
use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in};
|
use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in};
|
||||||
use log::error;
|
|
||||||
use ouroboros::self_referencing;
|
|
||||||
|
|
||||||
use crate::PSRAM_ALLOCATOR;
|
use crate::PSRAM_ALLOCATOR;
|
||||||
|
|
||||||
/// THIS IS TAKEN FROM https://github.com/esp-rs/esp-hal/blob/main/esp-hal/src/soc/esp32s3/mod.rs
|
|
||||||
/// Write back a specific range of data in the cache.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[unsafe(link_section = ".rwtext")]
|
|
||||||
pub unsafe fn cache_writeback_addr(addr: u32, size: u32) {
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn rom_Cache_WriteBack_Addr(addr: u32, size: u32);
|
|
||||||
fn Cache_Suspend_DCache_Autoload() -> u32;
|
|
||||||
fn Cache_Resume_DCache_Autoload(value: u32);
|
|
||||||
}
|
|
||||||
// suspend autoload, avoid load cachelines being written back
|
|
||||||
unsafe {
|
|
||||||
let autoload = Cache_Suspend_DCache_Autoload();
|
|
||||||
rom_Cache_WriteBack_Addr(addr, size);
|
|
||||||
Cache_Resume_DCache_Autoload(autoload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// THIS IS TAKEN FROM https://github.com/esp-rs/esp-hal/blob/main/esp-hal/src/soc/esp32s3/mod.rs
|
|
||||||
/// Invalidate a specific range of addresses in the cache.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[unsafe(link_section = ".rwtext")]
|
|
||||||
pub unsafe fn cache_invalidate_addr(addr: u32, size: u32) {
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn Cache_Invalidate_Addr(addr: u32, size: u32);
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
Cache_Invalidate_Addr(addr, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Rename or get rid of.
|
// TODO: Rename or get rid of.
|
||||||
pub struct Framebuffer {
|
pub struct Framebuffer {
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use core::fmt::Display;
|
||||||
|
|
||||||
use embassy_sync::{
|
use embassy_sync::{
|
||||||
blocking_mutex::raw::RawMutex,
|
blocking_mutex::raw::RawMutex,
|
||||||
mutex::{Mutex, MutexGuard, TryLockError},
|
mutex::{Mutex, MutexGuard},
|
||||||
};
|
};
|
||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue