2025-12-24 02:07:21 +01:00
#![ no_std ]
#![ no_main ]
#![ feature(macro_metavar_expr) ]
extern crate alloc ;
mod keymap ;
mod matrix ;
2025-12-27 21:03:52 +01:00
mod peripherals ;
2025-12-24 02:07:21 +01:00
mod vial ;
2025-12-29 19:36:00 +01:00
mod ui ;
mod logging ;
mod console ;
2025-12-24 02:07:21 +01:00
use core ::alloc ::Layout ;
2025-12-29 19:36:00 +01:00
use core ::cell ::{ OnceCell , RefCell } ;
use core ::fmt ::Write ;
use core ::time ::Duration ;
2025-12-24 02:07:21 +01:00
use alloc ::boxed ::Box ;
2025-12-29 19:36:00 +01:00
use alloc ::rc ::Rc ;
2025-12-24 02:07:21 +01:00
use alloc ::vec ;
use bt_hci ::controller ::ExternalController ;
2025-12-29 19:36:00 +01:00
use critical_section ::Mutex ;
use embassy_embedded_hal ::adapter ::BlockingAsync ;
2025-12-24 02:07:21 +01:00
use embassy_executor ::Spawner ;
2025-12-29 19:36:00 +01:00
use embassy_time ::Timer ;
2025-12-24 02:07:21 +01:00
use esp_alloc ::{ HeapRegion , MemoryCapability } ;
2025-12-27 21:03:52 +01:00
use esp_hal ::clock ::CpuClock ;
2025-12-24 02:07:21 +01:00
use esp_hal ::dma ::{ BurstConfig , DmaDescriptor , DmaTxBuf , ExternalBurstConfig } ;
2025-12-27 21:03:52 +01:00
use esp_hal ::gpio ::{ Flex , Input , InputConfig , Io , Level , Output , OutputConfig , Pull } ;
use esp_hal ::handler ;
2025-12-24 02:07:21 +01:00
use esp_hal ::i2c ::master ::{ I2c , I2cAddress } ;
2025-12-27 21:03:52 +01:00
use esp_hal ::interrupt ::Priority ;
use esp_hal ::interrupt ::software ::SoftwareInterruptControl ;
2025-12-24 02:07:21 +01:00
use esp_hal ::lcd_cam ::LcdCam ;
2025-12-27 21:03:52 +01:00
use esp_hal ::lcd_cam ::lcd ::dpi ::Dpi ;
2025-12-24 02:07:21 +01:00
use esp_hal ::mcpwm ::{ McPwm , PeripheralClockConfig } ;
2025-12-29 19:36:00 +01:00
use esp_hal ::peripherals ::Peripherals ;
2025-12-27 21:03:52 +01:00
use esp_hal ::psram ::{ FlashFreq , PsramConfig , PsramSize , SpiRamFreq , SpiTimingConfigCoreClock } ;
2025-12-24 02:07:21 +01:00
use esp_hal ::rng ::TrngSource ;
2025-12-27 21:03:52 +01:00
use esp_hal ::system ::Stack ;
2025-12-29 19:36:00 +01:00
use esp_hal ::time ::Instant ;
2025-12-24 02:07:21 +01:00
use esp_hal ::timer ::timg ::TimerGroup ;
2025-12-27 21:03:52 +01:00
use esp_hal ::uart ::Uart ;
2025-12-29 19:36:00 +01:00
use esp_hal ::usb_serial_jtag ::{ UsbSerialJtag , UsbSerialJtagTx } ;
2025-12-27 21:03:52 +01:00
use esp_hal ::{ Blocking , ram } ;
2025-12-24 02:07:21 +01:00
use esp_radio ::Controller ;
use esp_radio ::ble ::controller ::BleConnector ;
2025-12-27 23:51:46 +01:00
use esp_rtos ::embassy ::{ Executor , InterruptExecutor } ;
2025-12-24 02:07:21 +01:00
use esp_storage ::FlashStorage ;
2025-12-27 21:03:52 +01:00
use itertools ::chain ;
2025-12-29 19:36:00 +01:00
use log ::{ LevelFilter , Log , debug , error , info } ;
2025-12-24 02:07:21 +01:00
use rmk ::channel ::EVENT_CHANNEL ;
use rmk ::config ::{ BehaviorConfig , PositionalConfig , RmkConfig , StorageConfig , VialConfig } ;
use rmk ::debounce ::default_debouncer ::DefaultDebouncer ;
2025-12-27 21:03:52 +01:00
use rmk ::embassy_futures ::yield_now ;
2025-12-24 02:07:21 +01:00
use rmk ::input_device ::Runnable ;
2025-12-29 19:36:00 +01:00
use rmk ::{ join_all } ;
2025-12-24 02:07:21 +01:00
use rmk ::keyboard ::Keyboard ;
use rmk ::storage ::async_flash_wrapper ;
2025-12-27 21:03:52 +01:00
use rmk ::{ initialize_keymap_and_storage , run_devices , run_rmk } ;
2025-12-29 19:36:00 +01:00
use slint ::platform ::software_renderer ::{ Rgb565Pixel , TargetPixel } ;
use slint ::{ ComponentHandle , PhysicalSize , WindowSize } ;
use slint ::platform ::Platform ;
2025-12-24 02:07:21 +01:00
use static_cell ::StaticCell ;
2025-12-29 19:36:00 +01:00
use ui ::AppWindow ;
2025-12-24 02:07:21 +01:00
use { esp_alloc as _ , esp_backtrace as _ } ;
use crate ::matrix ::IoeMatrix ;
2025-12-27 21:03:52 +01:00
use crate ::peripherals ::st7701s ::St7701s ;
2025-12-24 02:07:21 +01:00
use crate ::vial ::{ VIAL_KEYBOARD_DEF , VIAL_KEYBOARD_ID } ;
// This creates a default app-descriptor required by the esp-idf bootloader.
// 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! ( ) ;
2025-12-29 19:36:00 +01:00
const LOG_LEVEL_FILTER : LevelFilter = LevelFilter ::Info ;
2025-12-24 02:07:21 +01:00
static PSRAM_ALLOCATOR : esp_alloc ::EspHeap = esp_alloc ::EspHeap ::empty ( ) ;
#[ esp_rtos::main ]
2025-12-27 21:03:52 +01:00
async fn main ( _spawner : Spawner ) {
2025-12-29 19:36:00 +01:00
#[ cfg(not(feature = " alt-log " )) ]
let alt_uart_rx_task = {
esp_println ::logger ::init_logger ( LOG_LEVEL_FILTER ) ;
info! ( " Logger initialized! " ) ;
async { }
} ;
2025-12-24 02:07:21 +01:00
2025-12-27 21:03:52 +01:00
let config = esp_hal ::Config ::default ( )
. with_cpu_clock ( CpuClock ::max ( ) )
. with_psram ( PsramConfig {
size : PsramSize ::AutoDetect ,
core_clock : Some ( SpiTimingConfigCoreClock ::SpiTimingConfigCoreClock80m ) ,
flash_frequency : FlashFreq ::default ( ) ,
ram_frequency : SpiRamFreq ::Freq80m ,
} ) ;
2025-12-24 02:07:21 +01:00
let peripherals : esp_hal ::peripherals ::Peripherals = esp_hal ::init ( config ) ;
info! ( " System initialized! " ) ;
2025-12-29 19:36:00 +01:00
#[ 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 ( ) )
} ;
2025-12-24 02:07:21 +01:00
// Use the internal DRAM as the heap.
esp_alloc ::heap_allocator! ( #[ unsafe(link_section = " .dram2_uninit " ) ] size : 64 * 1024 ) ;
info! ( " Heap initialized! {:#?} " , esp_alloc ::HEAP . stats ( ) ) ;
2025-12-27 21:03:52 +01:00
// Initialize the PSRAM allocator.
{
let ( psram_offset , psram_size ) = esp_hal ::psram ::psram_raw_parts ( & peripherals . PSRAM ) ;
unsafe {
PSRAM_ALLOCATOR . add_region ( HeapRegion ::new (
psram_offset ,
psram_size ,
MemoryCapability ::External . into ( ) ,
) ) ;
}
}
let mut io = Io ::new ( peripherals . IO_MUX ) ;
io . set_interrupt_handler ( interrupt_handler ) ;
2025-12-24 02:07:21 +01:00
// Enable pull-up on GPIO0 to prevent booting into download mode.
let gpio0 = Output ::new (
peripherals . GPIO0 ,
Level ::High ,
OutputConfig ::default ( ) . with_pull ( Pull ::Up ) ,
) ;
// Enable LDO2
let _ = Output ::new ( peripherals . GPIO17 , Level ::High , OutputConfig ::default ( ) ) ;
// 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 timg0 = TimerGroup ::new ( peripherals . TIMG0 ) ;
let software_interrupt = SoftwareInterruptControl ::new ( peripherals . SW_INTERRUPT ) ;
esp_rtos ::start (
2025-12-27 21:03:52 +01:00
timg0 . timer0 , /* , software_interrupt.software_interrupt0 */
2025-12-24 02:07:21 +01:00
) ;
2025-12-27 21:03:52 +01:00
// Enable the TRNG source, so `Trng` can be constructed.
2025-12-24 02:07:21 +01:00
let _trng_source = TrngSource ::new ( peripherals . RNG , peripherals . ADC1 ) ;
let mut rng = esp_hal ::rng ::Trng ::try_new ( ) . unwrap ( ) ;
static RADIO : StaticCell < Controller < 'static > > = StaticCell ::new ( ) ;
let radio = RADIO . init ( esp_radio ::init ( ) . unwrap ( ) ) ;
let bluetooth = peripherals . BT ;
let connector = BleConnector ::new ( radio , bluetooth , Default ::default ( ) ) . unwrap ( ) ;
let controller : ExternalController < _ , 20 > = ExternalController ::new ( connector ) ;
let central_addr = [ 0x18 , 0xe2 , 0x21 , 0x80 , 0xc0 , 0xc7 ] ;
2025-12-27 21:03:52 +01:00
#[ cfg(feature = " ble " ) ]
let mut host_resources = rmk ::HostResources ::new ( ) ;
#[ cfg(feature = " ble " ) ]
let stack =
rmk ::ble ::build_ble_stack ( controller , central_addr , & mut rng , & mut host_resources ) . await ;
2025-12-24 02:07:21 +01:00
2025-12-27 21:03:52 +01:00
// Initialize USB
2025-12-29 19:36:00 +01:00
#[ cfg(not(feature = " no-usb " )) ]
2025-12-24 02:07:21 +01:00
let usb_driver = {
2025-12-27 21:03:52 +01:00
use core ::ptr ::addr_of_mut ;
use esp_hal ::otg_fs ::Usb ;
use esp_hal ::otg_fs ::asynch ::{ Config , Driver } ;
2025-12-24 02:07:21 +01:00
static mut EP_MEMORY : [ u8 ; 1024 ] = [ 0 ; 1024 ] ;
let usb = Usb ::new ( peripherals . USB0 , peripherals . GPIO20 , peripherals . GPIO19 ) ;
// Create the driver, from the HAL.
let config = Config ::default ( ) ;
Driver ::new ( usb , unsafe { & mut * addr_of_mut! ( EP_MEMORY ) } , config )
} ;
// Initialize the flash
let flash = FlashStorage ::new ( peripherals . FLASH ) ;
let flash = async_flash_wrapper ( flash ) ;
2025-12-27 21:03:52 +01:00
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 ( ) ) ;
2025-12-24 02:07:21 +01:00
2025-12-27 21:03:52 +01:00
let lcd = LcdCam ::new ( peripherals . LCD_CAM ) . lcd ;
let unconfigured_dpi = Dpi ::new ( lcd , peripherals . DMA_CH2 , Default ::default ( ) )
2025-12-24 02:07:21 +01:00
. unwrap ( )
. with_de ( peripherals . GPIO37 )
. with_pclk ( peripherals . GPIO34 )
. with_hsync ( peripherals . GPIO44 )
. with_vsync ( peripherals . GPIO43 )
// Blue
. with_data0 ( peripherals . GPIO38 )
. with_data1 ( peripherals . GPIO39 )
. with_data2 ( peripherals . GPIO40 )
. with_data3 ( peripherals . GPIO41 )
. with_data4 ( peripherals . GPIO42 )
// Green
2025-12-29 19:36:00 +01:00
// .with_data5(peripherals.GPIO5) TODO
// .with_data6(peripherals.GPIO12)
2025-12-24 02:07:21 +01:00
. with_data7 ( peripherals . GPIO13 )
. with_data8 ( peripherals . GPIO14 )
. with_data9 ( peripherals . GPIO15 )
. with_data10 ( peripherals . GPIO16 )
// Red
. with_data11 ( gpio0 )
. with_data12 ( peripherals . GPIO1 )
. with_data13 ( peripherals . GPIO2 )
. with_data14 ( peripherals . GPIO3 )
. with_data15 ( peripherals . GPIO4 ) ;
2025-12-27 21:03:52 +01:00
let st7701s = St7701s ::new ( sck , mosi , cs , unconfigured_dpi ) . await ;
2025-12-24 02:07:21 +01:00
// RMK config
let vial_config = VialConfig ::new ( VIAL_KEYBOARD_ID , VIAL_KEYBOARD_DEF , & [ ( 0 , 0 ) , ( 1 , 1 ) ] ) ;
let storage_config = StorageConfig {
start_addr : 0x3f0000 ,
num_sectors : 16 ,
.. Default ::default ( )
} ;
let rmk_config = RmkConfig {
vial_config ,
storage_config ,
.. Default ::default ( )
} ;
// Initialze keyboard stuffs
// Initialize the storage and keymap
let mut default_keymap = keymap ::get_default_keymap ( ) ;
let mut behavior_config = BehaviorConfig ::default ( ) ;
let mut per_key_config = PositionalConfig ::default ( ) ;
let ( keymap , mut storage ) = initialize_keymap_and_storage (
& mut default_keymap ,
flash ,
& storage_config ,
& mut behavior_config ,
& mut per_key_config ,
)
. await ;
// Initialize the matrix and keyboard
const I2C_ADDR_MATRIX_LEFT : I2cAddress = I2cAddress ::SevenBit ( 0b0100000 ) ;
const I2C_ADDR_MATRIX_RIGHT : I2cAddress = I2cAddress ::SevenBit ( 0b0100001 ) ;
let i2c = I2c ::new ( peripherals . I2C0 , Default ::default ( ) )
. unwrap ( )
. with_sda ( peripherals . GPIO8 )
. with_scl ( peripherals . GPIO9 ) ;
2025-12-27 21:03:52 +01:00
let matrix_interrupt_low = Input ::new ( peripherals . GPIO7 , InputConfig ::default ( ) ) ;
2025-12-24 02:07:21 +01:00
let mut matrix = IoeMatrix ::new (
2025-12-27 21:03:52 +01:00
matrix_interrupt_low ,
2025-12-24 02:07:21 +01:00
i2c . into_async ( ) ,
DefaultDebouncer ::new ( ) ,
[ I2C_ADDR_MATRIX_LEFT , I2C_ADDR_MATRIX_RIGHT ] ,
)
. await ;
let mut keyboard = Keyboard ::new ( & keymap ) ; // Initialize the light controller
2025-12-29 19:36:00 +01:00
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 _ ) ;
2025-12-27 21:03:52 +01:00
static SECOND_CORE_STACK : StaticCell < Stack < 8192 > > = StaticCell ::new ( ) ;
let second_core_stack = SECOND_CORE_STACK . init ( Stack ::new ( ) ) ;
esp_rtos ::start_second_core (
peripherals . CPU_CTRL ,
software_interrupt . software_interrupt0 ,
software_interrupt . software_interrupt1 ,
second_core_stack ,
move | | {
2025-12-27 23:51:46 +01:00
// static EXECUTOR: StaticCell<InterruptExecutor<2>> = StaticCell::new();
// let exec = EXECUTOR.init(InterruptExecutor::new(
// software_interrupt.software_interrupt2,
// ));
// let spawner = exec.start(Priority::Priority3);
2025-12-29 19:36:00 +01:00
// spawner.must_spawn(run_renderer_task());
2025-12-27 23:51:46 +01:00
static EXECUTOR : StaticCell < Executor > = StaticCell ::new ( ) ;
let executor : & mut Executor = EXECUTOR . init ( Executor ::new ( ) ) ;
executor . run ( | spawner | {
2025-12-29 19:36:00 +01:00
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 ) ) ;
2025-12-27 23:51:46 +01:00
} ) ;
2025-12-27 21:03:52 +01:00
} ,
) ;
join_all! [
2025-12-29 19:36:00 +01:00
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 ) ,
2025-12-24 02:07:21 +01:00
run_devices! (
( matrix ) = > EVENT_CHANNEL ,
) ,
keyboard . run ( ) , // Keyboard is special
run_rmk (
& keymap ,
2025-12-29 19:36:00 +01:00
#[ cfg(not(feature = " no-usb " )) ]
2025-12-24 02:07:21 +01:00
usb_driver ,
2025-12-27 21:03:52 +01:00
#[ cfg(feature = " ble " ) ]
2025-12-24 02:07:21 +01:00
& stack ,
& mut storage ,
rmk_config ,
2025-12-29 19:36:00 +01:00
)
2025-12-27 21:03:52 +01:00
]
2025-12-24 02:07:21 +01:00
. await ;
}
2025-12-27 21:03:52 +01:00
#[ embassy_executor::task ]
2025-12-29 19:36:00 +01:00
async fn run_renderer_task ( backend : SlintBackend ) {
slint ::platform ::set_platform ( Box ::new ( backend ) )
. expect ( " backend already initialized " ) ;
2025-12-27 21:03:52 +01:00
2025-12-29 19:36:00 +01:00
loop {
let main = AppWindow ::new ( ) . unwrap ( ) ;
2025-12-27 21:03:52 +01:00
2025-12-29 19:36:00 +01:00
main . run ( ) . unwrap ( ) ;
}
}
struct Framebuffer {
width : u32 ,
height : u32 ,
dma_buf : Option < DmaTxBuf > ,
}
2025-12-27 21:03:52 +01:00
2025-12-29 19:36:00 +01:00
impl Framebuffer {
pub fn new ( width : u32 , height : u32 ) -> Self {
let buffer_len = width as usize * height as usize * core ::mem ::size_of ::< u16 > ( ) ;
// Allocate the framebuffer in the external PSRAM memory.
// Note: We just leak this buffer.
let buffer_ptr = unsafe {
// ⚠️ Note: For chips that support DMA to/from PSRAM (ESP32-S3) DMA transfers to/from PSRAM
// have extra alignment requirements. The address and size of the buffer pointed to by each
// descriptor must be a multiple of the cache line (block) size. This is 32 bytes on ESP32-S3.
PSRAM_ALLOCATOR . alloc_caps (
MemoryCapability ::External . into ( ) ,
Layout ::from_size_align ( buffer_len , 32 ) . unwrap ( ) ,
)
} ;
let buffer = unsafe { core ::slice ::from_raw_parts_mut ( buffer_ptr , buffer_len ) } ;
let burst_config : BurstConfig = ExternalBurstConfig ::Size16 . into ( ) ;
info! (
" PSRAM SPI burst config: max_compatible_chunk_size={} " ,
burst_config . max_compatible_chunk_size ( )
) ;
let dma_buf_descs_len = esp_hal ::dma ::descriptor_count (
buffer_len ,
burst_config . max_compatible_chunk_size ( ) ,
false ,
) ;
// Descriptors are initialized by `DmaTxBuf::new`.
let dma_buf_descs =
Box ::leak ( vec! [ DmaDescriptor ::EMPTY ; dma_buf_descs_len ] . into_boxed_slice ( ) ) ;
let dma_buf = DmaTxBuf ::new ( dma_buf_descs , buffer ) . unwrap ( ) ;
Self {
width ,
height ,
dma_buf : Some ( dma_buf ) ,
}
2025-12-27 21:03:52 +01:00
}
2025-12-29 19:36:00 +01:00
pub fn as_target_pixels ( & mut self ) -> & mut [ Rgb565Pixel ] {
bytemuck ::cast_slice_mut ::< _ , Rgb565Pixel > ( self . dma_buf . as_mut ( ) . unwrap ( ) . as_mut_slice ( ) )
2025-12-27 21:03:52 +01:00
}
2025-12-29 19:36:00 +01:00
}
2025-12-27 21:03:52 +01:00
2025-12-29 19:36:00 +01:00
struct SlintBackend {
window_size : [ u32 ; 2 ] ,
window : RefCell < Option < Rc < slint ::platform ::software_renderer ::MinimalSoftwareWindow > > > ,
framebuffer : FramebufferPtr ,
// peripherals: RefCell<Option<Peripherals>>,
}
2025-12-27 21:03:52 +01:00
2025-12-29 19:36:00 +01:00
struct FramebufferPtr ( * mut [ Rgb565Pixel ] ) ;
2025-12-27 21:03:52 +01:00
2025-12-29 19:36:00 +01:00
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 )
}
2025-12-27 21:03:52 +01:00
2025-12-29 19:36:00 +01:00
fn duration_since_start ( & self ) -> Duration {
Duration ::from_millis ( Instant ::now ( ) . duration_since_epoch ( ) . as_millis ( ) )
2025-12-27 21:03:52 +01:00
}
2025-12-29 19:36:00 +01:00
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
// }
2025-12-27 21:03:52 +01:00
loop {
// Timer::after(Duration::from_millis(100)).await;
yield_now ( ) . 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
// They need to be implemented in esp-hal.
2025-12-29 19:36:00 +01:00
let transfer = match st7701s . dpi . send ( false , framebuffer . dma_buf . take ( ) . unwrap ( ) ) {
2025-12-27 21:03:52 +01:00
Err ( ( error , result_dpi , result_dma_buf ) ) = > {
error! (
" An error occurred while initiating transfer of the framebuffer to the LCD display: {error:?} "
) ;
st7701s . dpi = result_dpi ;
2025-12-29 19:36:00 +01:00
framebuffer . dma_buf = Some ( result_dma_buf ) ;
2025-12-27 21:03:52 +01:00
continue ;
}
Ok ( transfer ) = > transfer ,
} ;
2025-12-29 19:36:00 +01:00
while ! transfer . is_done ( ) {
// Timer::after_millis(1).await;
yield_now ( ) . await ;
}
2025-12-27 21:03:52 +01:00
let result ;
2025-12-29 19:36:00 +01:00
let dma_buf ;
2025-12-27 21:03:52 +01:00
( result , st7701s . dpi , dma_buf ) = transfer . wait ( ) ;
2025-12-29 19:36:00 +01:00
framebuffer . dma_buf = Some ( dma_buf ) ;
2025-12-27 21:03:52 +01:00
if let Err ( error ) = result {
error! (
" An error occurred while transferring framebuffer to the LCD display: {error:?} "
) ;
}
}
}
#[ handler ]
#[ ram ] // TODO: Is this necessary?
fn interrupt_handler ( ) {
esp_println ::println! (
" GPIO Interrupt with priority {} " ,
esp_hal ::xtensa_lx ::interrupt ::get_level ( )
) ;
}