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"
|
||||
dependencies = [
|
||||
"document-features",
|
||||
"embassy-sync 0.7.2",
|
||||
"esp-hal",
|
||||
"log",
|
||||
"ouroboros",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ 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 = [
|
||||
|
|
@ -87,7 +89,7 @@ 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", 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.
|
||||
# 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")]
|
||||
#[macro_use]
|
||||
pub mod uart {
|
||||
use crate::util::MutexExt;
|
||||
|
||||
use super::*;
|
||||
|
||||
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 log::{Log, info};
|
||||
|
||||
static ALT_LOGGER_UART: Mutex<
|
||||
#[cfg(feature = "racy-logging")]
|
||||
static ALT_LOGGER_UART: embassy_sync::mutex::Mutex<
|
||||
CriticalSectionRawMutex,
|
||||
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 {
|
||||
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.**
|
||||
|
|
@ -94,6 +102,16 @@ pub mod 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)]
|
||||
macro_rules! println {
|
||||
() => {{
|
||||
|
|
@ -156,11 +174,21 @@ pub mod uart {
|
|||
|
||||
pub fn setup_logging(uart_tx: UartTx<'static, Blocking>) {
|
||||
{
|
||||
// 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(feature = "racy-logging")]
|
||||
{
|
||||
use crate::util::MutexExt;
|
||||
|
||||
// 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 {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ use esp_hal::i2c::master::{I2c, I2cAddress};
|
|||
use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl};
|
||||
use esp_hal::lcd_cam::LcdCam;
|
||||
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::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
||||
use esp_hal::ram;
|
||||
|
|
@ -58,7 +58,9 @@ 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, 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_storage::FlashStorage;
|
||||
use i_slint_core::software_renderer::TargetPixel;
|
||||
|
|
@ -82,7 +84,7 @@ use static_cell::StaticCell;
|
|||
use {esp_alloc as _, esp_backtrace as _};
|
||||
|
||||
use crate::matrix::IoeMatrix;
|
||||
use crate::peripherals::st7701s::St7701s;
|
||||
use crate::peripherals::st7701s::{St7701s, St7701sController};
|
||||
use crate::proxy::create_hid_report_interceptor;
|
||||
use crate::ui::backend::SlintBackend;
|
||||
use crate::ui::dpi::Framebuffer;
|
||||
|
|
@ -144,12 +146,14 @@ async fn test_bounce_buffers_task(
|
|||
test_bounce_buffers(channel, peripheral, st7701s).await;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn test_bounce_buffers(
|
||||
channel: DMA_CH0<'static>,
|
||||
peripheral: SPI2<'static>,
|
||||
st7701s: St7701s<'static, Blocking>,
|
||||
mut st7701s: St7701s<'static, Blocking>,
|
||||
) {
|
||||
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;
|
||||
|
|
@ -215,11 +219,7 @@ async fn test_bounce_buffers(
|
|||
}
|
||||
}
|
||||
|
||||
warn!("FRONT_PORCH_SKIPPED_PIXELS: {FRONT_PORCH_SKIPPED_PIXELS}");
|
||||
warn!("WIDTH_STORED_PIXELS: {WIDTH_STORED_PIXELS}");
|
||||
warn!("ROWS_PER_WINDOW: {ROWS_PER_WINDOW}");
|
||||
|
||||
let buf = DmaBounce::new(
|
||||
let mut dma_bounce = DmaBounce::new(
|
||||
Global,
|
||||
channel,
|
||||
AnySpi::from(peripheral),
|
||||
|
|
@ -231,8 +231,18 @@ async fn test_bounce_buffers(
|
|||
burst_config,
|
||||
true,
|
||||
);
|
||||
buf.launch_interrupt_driven_task();
|
||||
error!("TEST BOUNCE BUFFERS SECTION DONE");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#[esp_rtos::main]
|
||||
|
|
@ -313,10 +323,6 @@ 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();
|
||||
|
||||
|
|
@ -341,6 +347,7 @@ async fn main(_spawner: Spawner) {
|
|||
// high_priority_task_spawner: interrupt_core_1_spawner,
|
||||
uart_rx,
|
||||
software_interrupt1: software_interrupt.software_interrupt1,
|
||||
LEDC: peripherals.LEDC,
|
||||
RNG: peripherals.RNG,
|
||||
ADC1: peripherals.ADC1,
|
||||
USB0: peripherals.USB0,
|
||||
|
|
@ -370,6 +377,7 @@ async fn main(_spawner: Spawner) {
|
|||
GPIO16: peripherals.GPIO16,
|
||||
GPIO19: peripherals.GPIO19,
|
||||
GPIO20: peripherals.GPIO20,
|
||||
GPIO21: peripherals.GPIO21,
|
||||
GPIO34: peripherals.GPIO34,
|
||||
GPIO35: peripherals.GPIO35,
|
||||
GPIO36: peripherals.GPIO36,
|
||||
|
|
@ -392,6 +400,7 @@ struct MainPeripherals {
|
|||
// high_priority_task_spawner: SendSpawner,
|
||||
uart_rx: UartRx<'static, Blocking>,
|
||||
software_interrupt1: SoftwareInterrupt<'static, 1>,
|
||||
LEDC: esp_hal::peripherals::LEDC<'static>,
|
||||
RNG: esp_hal::peripherals::RNG<'static>,
|
||||
ADC1: esp_hal::peripherals::ADC1<'static>,
|
||||
USB0: esp_hal::peripherals::USB0<'static>,
|
||||
|
|
@ -423,6 +432,7 @@ struct MainPeripherals {
|
|||
// GPIO18: esp_hal::peripherals::GPIO18<'static>,
|
||||
GPIO19: esp_hal::peripherals::GPIO19<'static>,
|
||||
GPIO20: esp_hal::peripherals::GPIO20<'static>,
|
||||
GPIO21: esp_hal::peripherals::GPIO21<'static>,
|
||||
// GPIO33: esp_hal::peripherals::GPIO33<'static>,
|
||||
GPIO34: esp_hal::peripherals::GPIO34<'static>,
|
||||
GPIO35: esp_hal::peripherals::GPIO35<'static>,
|
||||
|
|
@ -577,6 +587,11 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
|
||||
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 mosi = Flex::new(peripherals.GPIO35);
|
||||
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_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!");
|
||||
|
||||
// Uncomment this to run bounce buffer test code instead.
|
||||
// test_bounce_buffers(peripherals.DMA_CH0, peripherals.SPI2, st7701s).await;
|
||||
// return;
|
||||
|
||||
|
|
@ -722,7 +738,7 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
112,
|
||||
368 - 112,
|
||||
960,
|
||||
8,
|
||||
16,
|
||||
true,
|
||||
));
|
||||
|
||||
|
|
@ -763,16 +779,11 @@ async fn main_task(peripherals: MainPeripherals) {
|
|||
|
||||
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...");
|
||||
|
||||
framebuffer
|
||||
.bounce_buffers
|
||||
.take()
|
||||
.unwrap()
|
||||
.launch_interrupt_driven_task();
|
||||
|
||||
// TODO: Probably want to select! instead and re-try.
|
||||
join_all![
|
||||
run_alloc_stats_reporter(),
|
||||
|
|
@ -835,19 +846,61 @@ async fn run_alloc_stats_reporter() {
|
|||
}
|
||||
}
|
||||
|
||||
struct UserController {
|
||||
sub: ControllerSub,
|
||||
enum DmaBounceControllerInner {
|
||||
Idle(DmaBounce),
|
||||
Transmitting(RunningDmaBounceHandle),
|
||||
}
|
||||
|
||||
impl UserController {
|
||||
fn new() -> Self {
|
||||
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> {
|
||||
sub: ControllerSub,
|
||||
lcd_controller: St7701sController<'a>,
|
||||
bb_controller: DmaBounceController,
|
||||
}
|
||||
|
||||
impl<'a> UserController<'a> {
|
||||
fn new(lcd_controller: St7701sController<'a>, bb_controller: DmaBounceController) -> Self {
|
||||
Self {
|
||||
sub: CONTROLLER_CHANNEL.subscriber().unwrap(),
|
||||
lcd_controller,
|
||||
bb_controller,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Controller for UserController {
|
||||
impl<'a> Controller for UserController<'a> {
|
||||
type Event = ControllerEvent;
|
||||
|
||||
async fn process_event(&mut self, event: Self::Event) {
|
||||
|
|
@ -863,10 +916,14 @@ impl Controller for UserController {
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ use esp_hal::{
|
|||
ClockMode, DelayMode, Phase, Polarity,
|
||||
dpi::{Dpi, Format, FrameTiming},
|
||||
},
|
||||
ledc::{self, LowSpeed, channel::ChannelIFace, timer::TimerIFace},
|
||||
time::Rate,
|
||||
};
|
||||
use lcd::spi_write;
|
||||
use log::debug;
|
||||
use ouroboros::self_referencing;
|
||||
use paste::paste;
|
||||
// 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>
|
||||
where
|
||||
Dm: DriverMode,
|
||||
{
|
||||
pub sck: Output<'d>,
|
||||
pub mosi: Flex<'d>,
|
||||
pub cs: Output<'d>,
|
||||
pub controller: St7701sController<'d>,
|
||||
pub dpi: Dpi<'d, Dm>,
|
||||
}
|
||||
|
||||
|
|
@ -1247,11 +1312,15 @@ impl<'d, Dm> St7701s<'d, Dm>
|
|||
where
|
||||
Dm: DriverMode,
|
||||
{
|
||||
/// Initializes an ST7701S display, and puts it to sleep.
|
||||
/// To turn it on, invoke `St7701sController:sleep_off`.
|
||||
pub async fn new(
|
||||
mut sck: Output<'d>,
|
||||
mut mosi: Flex<'d>,
|
||||
mut cs: Output<'d>,
|
||||
mut unconfigured_dpi: Dpi<'d, Dm>,
|
||||
mut bl_timer: ledc::timer::Timer<'d, LowSpeed>,
|
||||
bl_channel: ledc::channel::Channel<'d, LowSpeed>,
|
||||
) -> Self {
|
||||
sck.apply_config(&Default::default());
|
||||
sck.set_high();
|
||||
|
|
@ -1276,7 +1345,7 @@ where
|
|||
//
|
||||
// Adafruit would use 11 MHz.
|
||||
// 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 {
|
||||
polarity: Polarity::IdleLow, // From Adafruit
|
||||
phase: Phase::ShiftHigh, // From Adafruit
|
||||
|
|
@ -1338,38 +1407,38 @@ where
|
|||
|
||||
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 {
|
||||
sck,
|
||||
mosi,
|
||||
cs,
|
||||
controller: St7701sController {
|
||||
sck,
|
||||
mosi,
|
||||
cs,
|
||||
backlight,
|
||||
},
|
||||
dpi: unconfigured_dpi,
|
||||
};
|
||||
|
||||
lcd.send_init_sequence().await;
|
||||
lcd.controller.send_init_sequence().await;
|
||||
lcd.controller.sleep_on().await;
|
||||
|
||||
lcd
|
||||
}
|
||||
|
||||
pub async fn send_init_sequence(&mut self) {
|
||||
debug!("Writing ST7701S init sequence.");
|
||||
|
||||
for (subsequence, delay_ms) in &*lcd::INIT_SEQUENCE_COMMANDS {
|
||||
for command in subsequence {
|
||||
spi_write(
|
||||
command.address(),
|
||||
command.args_iter().copied(),
|
||||
&mut self.mosi,
|
||||
&mut self.sck,
|
||||
&mut self.cs,
|
||||
)
|
||||
.await;
|
||||
// info!("COMM 0x{:02X}", command.address());
|
||||
// for arg in command.args_iter() {
|
||||
// info!("DATA 0x{arg:02X}");
|
||||
// }
|
||||
}
|
||||
|
||||
Timer::after_millis(*delay_ms).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +1,11 @@
|
|||
use core::{
|
||||
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 alloc::{alloc::Global, boxed::Box};
|
||||
use esp_hal::{
|
||||
Blocking,
|
||||
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,
|
||||
Blocking, dma::BurstConfig, lcd_cam::lcd::dpi::Dpi, peripherals::DMA_CH0, spi::master::AnySpi,
|
||||
};
|
||||
use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in};
|
||||
use log::error;
|
||||
use ouroboros::self_referencing;
|
||||
|
||||
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.
|
||||
pub struct Framebuffer {
|
||||
pub width: u32,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use core::fmt::Display;
|
|||
|
||||
use embassy_sync::{
|
||||
blocking_mutex::raw::RawMutex,
|
||||
mutex::{Mutex, MutexGuard, TryLockError},
|
||||
mutex::{Mutex, MutexGuard},
|
||||
};
|
||||
use embassy_time::Duration;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue