move fb to psram to save 200KB for user apps

This commit is contained in:
2025-10-31 17:28:16 -06:00
parent 2226541813
commit 1d7ab3fb42
4 changed files with 73 additions and 37 deletions

View File

@@ -81,7 +81,7 @@ pub extern "C" fn draw_iter(cpixels: *const CPixel, len: usize) {
let iter = cpixels.iter().copied().map(|c: CPixel| c.into()); let iter = cpixels.iter().copied().map(|c: CPixel| c.into());
FB_PAUSED.store(true, Ordering::Release); FB_PAUSED.store(true, Ordering::Release);
unsafe { FRAMEBUFFER.draw_iter(iter).unwrap() } unsafe { FRAMEBUFFER.as_mut().unwrap().draw_iter(iter).unwrap() }
FB_PAUSED.store(false, Ordering::Release); FB_PAUSED.store(false, Ordering::Release);
} }

View File

@@ -1,6 +1,5 @@
use crate::framebuffer::{self, AtomicFrameBuffer, FB_PAUSED}; use crate::framebuffer::{self, AtomicFrameBuffer, FB_PAUSED};
use alloc::vec; use core::sync::atomic::Ordering;
use core::sync::atomic::{AtomicBool, Ordering};
use embassy_rp::{ use embassy_rp::{
Peri, Peri,
gpio::{Level, Output}, gpio::{Level, Output},
@@ -9,9 +8,16 @@ use embassy_rp::{
}; };
use embassy_time::{Delay, Timer}; use embassy_time::{Delay, Timer};
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use once_cell::unsync::Lazy;
use st7365p_lcd::ST7365P; use st7365p_lcd::ST7365P;
#[cfg(feature = "pimoroni2w")]
use core::alloc::{GlobalAlloc, Layout};
#[cfg(feature = "pimoroni2w")]
use embedded_graphics::{
pixelcolor::Rgb565,
prelude::{DrawTarget, RgbColor},
};
type DISPLAY = ST7365P< type DISPLAY = ST7365P<
ExclusiveDevice<Spi<'static, SPI1, Async>, Output<'static>, Delay>, ExclusiveDevice<Spi<'static, SPI1, Async>, Output<'static>, Delay>,
Output<'static>, Output<'static>,
@@ -22,10 +28,24 @@ type DISPLAY = ST7365P<
pub const SCREEN_WIDTH: usize = 320; pub const SCREEN_WIDTH: usize = 320;
pub const SCREEN_HEIGHT: usize = 320; pub const SCREEN_HEIGHT: usize = 320;
pub static mut FRAMEBUFFER: Lazy<AtomicFrameBuffer> = Lazy::new(|| { pub static mut FRAMEBUFFER: Option<AtomicFrameBuffer> = None;
static mut BUF: [u16; framebuffer::SIZE] = [0; framebuffer::SIZE];
AtomicFrameBuffer::new(unsafe { &mut BUF }) fn init_fb() {
}); unsafe {
FRAMEBUFFER = Some(if cfg!(not(feature = "pimoroni2w")) {
static mut BUF: [u16; framebuffer::SIZE] = [0; framebuffer::SIZE];
AtomicFrameBuffer::new(&mut BUF)
} else {
let slab = crate::heap::HEAP.alloc(Layout::array::<u16>(framebuffer::SIZE).unwrap())
as *mut u16;
let buf = core::slice::from_raw_parts_mut(slab, framebuffer::SIZE);
let mut fb = AtomicFrameBuffer::new(buf);
fb.clear(Rgb565::BLACK).unwrap();
fb
});
}
}
pub async fn init_display( pub async fn init_display(
spi: Spi<'static, SPI1, Async>, spi: Spi<'static, SPI1, Async>,
@@ -33,6 +53,8 @@ pub async fn init_display(
data: Peri<'static, PIN_14>, data: Peri<'static, PIN_14>,
reset: Peri<'static, PIN_15>, reset: Peri<'static, PIN_15>,
) -> DISPLAY { ) -> DISPLAY {
init_fb();
let spi_device = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), Delay).unwrap(); let spi_device = ExclusiveDevice::new(spi, Output::new(cs, Level::Low), Delay).unwrap();
let mut display = ST7365P::new( let mut display = ST7365P::new(
spi_device, spi_device,
@@ -44,7 +66,14 @@ pub async fn init_display(
); );
display.init().await.unwrap(); display.init().await.unwrap();
display.set_custom_orientation(0x40).await.unwrap(); display.set_custom_orientation(0x40).await.unwrap();
unsafe { FRAMEBUFFER.draw(&mut display).await.unwrap() } unsafe {
FRAMEBUFFER
.as_mut()
.unwrap()
.draw(&mut display)
.await
.unwrap()
}
display.set_on().await.unwrap(); display.set_on().await.unwrap();
display display
@@ -54,7 +83,14 @@ pub async fn init_display(
pub async fn display_handler(mut display: DISPLAY) { pub async fn display_handler(mut display: DISPLAY) {
loop { loop {
if !FB_PAUSED.load(Ordering::Acquire) { if !FB_PAUSED.load(Ordering::Acquire) {
unsafe { FRAMEBUFFER.safe_draw(&mut display).await.unwrap() }; unsafe {
FRAMEBUFFER
.as_mut()
.unwrap()
.safe_draw(&mut display)
.await
.unwrap()
};
} }
// small yield to allow other tasks to run // small yield to allow other tasks to run

View File

@@ -80,7 +80,7 @@ static mut CORE1_STACK: Stack<16384> = Stack::new();
static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
static mut ARENA: [u8; 250 * 1024] = [0; 250 * 1024]; static mut ARENA: [u8; 400 * 1024] = [0; 400 * 1024];
#[global_allocator] #[global_allocator]
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> = static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
@@ -90,12 +90,12 @@ static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
#[embassy_executor::task] #[embassy_executor::task]
async fn watchdog_task(mut watchdog: Watchdog) { async fn watchdog_task(mut watchdog: Watchdog) {
if let Some(reason) = watchdog.reset_reason() { if let Some(reason) = watchdog.reset_reason() {
let reason = match reason { let _reason = match reason {
ResetReason::Forced => "forced", ResetReason::Forced => "forced",
ResetReason::TimedOut => "timed out", ResetReason::TimedOut => "timed out",
}; };
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
defmt::error!("Watchdog reset reason: {}", reason); defmt::error!("Watchdog reset reason: {}", _reason);
} }
watchdog.start(Duration::from_secs(3)); watchdog.start(Duration::from_secs(3));
@@ -195,7 +195,7 @@ async fn userland_task() {
{ {
ENABLE_UI.store(true, Ordering::Release); ENABLE_UI.store(true, Ordering::Release);
UI_CHANGE.signal(()); UI_CHANGE.signal(());
unsafe { FRAMEBUFFER.clear(Rgb565::BLACK).unwrap() }; unsafe { FRAMEBUFFER.as_mut().unwrap().clear(Rgb565::BLACK).unwrap() };
let mut selections = SELECTIONS.lock().await; let mut selections = SELECTIONS.lock().await;
selections.set_changed(true); selections.set_changed(true);
@@ -280,22 +280,18 @@ async fn setup_psram(psram: Psram) {
#[cfg(feature = "pimoroni2w")] #[cfg(feature = "pimoroni2w")]
async fn setup_qmi_psram() { async fn setup_qmi_psram() {
let mut tries = 5;
Timer::after_millis(250).await; Timer::after_millis(250).await;
while tries > 1 { let psram_qmi_size = init_psram_qmi(&embassy_rp::pac::QMI, &embassy_rp::pac::XIP_CTRL);
let psram_qmi_size = init_psram_qmi(&embassy_rp::pac::QMI, &embassy_rp::pac::XIP_CTRL); #[cfg(feature = "debug")]
#[cfg(feature = "debug")] defmt::info!("size: {}", psram_qmi_size);
defmt::info!("size: {}", psram_qmi_size); Timer::after_millis(100).await;
Timer::after_millis(100).await;
if psram_qmi_size > 0 { if psram_qmi_size > 0 {
init_qmi_psram_heap(psram_qmi_size); init_qmi_psram_heap(psram_qmi_size);
return; return;
} } else {
#[cfg(feature = "debug")] panic!("qmi psram not initialized");
defmt::info!("failed to init qmi psram... trying again");
tries -= 1;
} }
panic!("qmi psram not initialized");
} }
async fn setup_sd(sd: Sd) { async fn setup_sd(sd: Sd) {
@@ -326,17 +322,19 @@ async fn kernel_task(
spawner spawner
.spawn(watchdog_task(Watchdog::new(watchdog))) .spawn(watchdog_task(Watchdog::new(watchdog)))
.unwrap(); .unwrap();
setup_mcu(mcu).await; setup_mcu(mcu).await;
// setup_psram(psram).await;
#[cfg(feature = "pimoroni2w")]
setup_qmi_psram().await;
setup_display(display, spawner).await; setup_display(display, spawner).await;
setup_sd(sd).await; setup_sd(sd).await;
let _usb = embassy_rp_usb::Driver::new(usb, Irqs); let _usb = embassy_rp_usb::Driver::new(usb, Irqs);
// spawner.spawn(usb_handler(usb)).unwrap(); // spawner.spawn(usb_handler(usb)).unwrap();
// setup_psram(psram).await;
#[cfg(feature = "pimoroni2w")]
setup_qmi_psram().await;
loop { loop {
let ui_enabled = ENABLE_UI.load(Ordering::Relaxed); let ui_enabled = ENABLE_UI.load(Ordering::Relaxed);
if ui_enabled { if ui_enabled {

View File

@@ -68,7 +68,7 @@ pub async fn clear_selection() {
if let Some(area) = sel.last_bounds { if let Some(area) = sel.last_bounds {
Rectangle::new(area.top_left, area.size) Rectangle::new(area.top_left, area.size)
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK)) .into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
.draw(unsafe { &mut *FRAMEBUFFER }) .draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
.unwrap(); .unwrap();
} }
} }
@@ -78,7 +78,7 @@ async fn draw_selection() {
let file_names = &guard.selections.clone(); let file_names = &guard.selections.clone();
let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE);
let display_area = unsafe { FRAMEBUFFER.bounding_box() }; let display_area = unsafe { FRAMEBUFFER.as_mut().unwrap().bounding_box() };
const NO_BINS: &str = "No Programs found on SD Card. Ensure programs end with '.bin', and are located in the root directory"; const NO_BINS: &str = "No Programs found on SD Card. Ensure programs end with '.bin', and are located in the root directory";
let no_bins = String::from_str(NO_BINS).unwrap(); let no_bins = String::from_str(NO_BINS).unwrap();
@@ -94,7 +94,7 @@ async fn draw_selection() {
), ),
text_style, text_style,
) )
.draw(unsafe { &mut *FRAMEBUFFER }) .draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
.unwrap(); .unwrap();
} else { } else {
let mut views: alloc::vec::Vec<Text<MonoTextStyle<Rgb565>>> = Vec::new(); let mut views: alloc::vec::Vec<Text<MonoTextStyle<Rgb565>>> = Vec::new();
@@ -119,12 +119,14 @@ async fn draw_selection() {
.bounding_box(); .bounding_box();
Rectangle::new(selected_bounds.top_left, selected_bounds.size) Rectangle::new(selected_bounds.top_left, selected_bounds.size)
.into_styled(PrimitiveStyle::with_stroke(Rgb565::WHITE, 1)) .into_styled(PrimitiveStyle::with_stroke(Rgb565::WHITE, 1))
.draw(unsafe { &mut *FRAMEBUFFER }) .draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
.unwrap(); .unwrap();
guard.last_bounds = Some(layout.bounds()); guard.last_bounds = Some(layout.bounds());
layout.draw(unsafe { &mut *FRAMEBUFFER }).unwrap(); layout
.draw(unsafe { &mut *FRAMEBUFFER.as_mut().unwrap() })
.unwrap();
} }
guard.changed = false; guard.changed = false;