From 4c58f66c94cc224ff265b0fe55f6bb70942ce074 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 27 Oct 2025 18:53:22 -0600 Subject: [PATCH] gif plays at low fps --- Cargo.lock | 1 + Cargo.toml | 4 +- abi/Cargo.toml | 1 + abi/src/lib.rs | 26 +++++---- abi_sys/src/lib.rs | 2 +- kernel/src/abi.rs | 19 +++++-- kernel/src/heap.rs | 1 - kernel/src/main.rs | 90 ++++++++++++++++++++++---------- kernel/src/psram.rs | 8 +-- user-apps/calculator/src/main.rs | 8 +-- user-apps/gallery/src/main.rs | 18 +++---- user-apps/gif/src/main.rs | 17 +++--- user-apps/memory.x | 2 +- user-apps/snake/src/main.rs | 9 +--- 14 files changed, 121 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 544a7cb..a0d722e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,7 @@ dependencies = [ "abi_sys", "embedded-graphics", "embedded-sdmmc", + "once_cell", "rand_core 0.9.3", ] diff --git a/Cargo.toml b/Cargo.toml index 2170336..9e88380 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,11 @@ codegen-units = 1 [profile.release-binary] inherits = "release" +codegen-units = 1 lto = true debug = false -opt-level = "s" +opt-level = "z" +panic = "abort" [profile.dev] lto = true diff --git a/abi/Cargo.toml b/abi/Cargo.toml index 47e7d1f..a197d01 100644 --- a/abi/Cargo.toml +++ b/abi/Cargo.toml @@ -7,4 +7,5 @@ edition = "2024" embedded-sdmmc = { version = "0.9.0", default-features = false } embedded-graphics = "0.8.1" abi_sys = { path = "../abi_sys" } +once_cell = { version = "1", default-features = false } rand_core = "0.9.3" diff --git a/abi/src/lib.rs b/abi/src/lib.rs index 9805a2c..e8b4f40 100644 --- a/abi/src/lib.rs +++ b/abi/src/lib.rs @@ -1,9 +1,11 @@ #![no_std] +#![allow(static_mut_refs)] extern crate alloc; -pub use abi_sys::keyboard; +pub use abi_sys::{self, keyboard}; use abi_sys::{RngRequest, alloc, dealloc, keyboard::KeyEvent}; +pub use alloc::format; use core::alloc::{GlobalAlloc, Layout}; use rand_core::RngCore; @@ -22,8 +24,12 @@ unsafe impl GlobalAlloc for Alloc { } } -pub fn print(msg: &str) { - abi_sys::print(msg.as_ptr(), msg.len()); +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => {{ + let s = $crate::format!($($arg)*); + $crate::abi_sys::print(s.as_ptr(), s.len()); + }}; } pub fn sleep(ms: u64) { @@ -40,6 +46,7 @@ pub fn get_key() -> KeyEvent { pub mod display { use abi_sys::CPixel; + use alloc::{vec, vec::Vec}; use embedded_graphics::{ Pixel, geometry::{Dimensions, Point}, @@ -47,6 +54,7 @@ pub mod display { prelude::{DrawTarget, Size}, primitives::Rectangle, }; + use once_cell::unsync::Lazy; pub const SCREEN_WIDTH: usize = 320; pub const SCREEN_HEIGHT: usize = 320; @@ -57,7 +65,9 @@ pub mod display { abi_sys::lock_display(lock); } - const BUF_SIZE: usize = 1024; // tune this for performance + const BUF_SIZE: usize = 250 * 1024; // tune this for performance + // static mut BUF: [CPixel; BUF_SIZE] = [CPixel::new(); BUF_SIZE]; + static mut BUF: Lazy> = Lazy::new(|| vec![const { CPixel::new() }; BUF_SIZE]); pub struct Display; @@ -81,21 +91,19 @@ pub mod display { where I: IntoIterator>, { - let mut buf: [CPixel; BUF_SIZE] = [CPixel::new(); BUF_SIZE]; - let mut count = 0; for p in pixels { - buf[count] = p.into(); + unsafe { BUF[count] = p.into() }; count += 1; if count == BUF_SIZE { - abi_sys::draw_iter(buf.as_ptr(), count); + abi_sys::draw_iter(unsafe { BUF.as_ptr() }, count); count = 0; } } if count > 0 { - abi_sys::draw_iter(buf.as_ptr(), count); + abi_sys::draw_iter(unsafe { BUF.as_ptr() }, count); } Ok(()) diff --git a/abi_sys/src/lib.rs b/abi_sys/src/lib.rs index db81b70..c66f61a 100644 --- a/abi_sys/src/lib.rs +++ b/abi_sys/src/lib.rs @@ -122,7 +122,7 @@ pub struct CPixel { } impl CPixel { - pub fn new() -> Self { + pub const fn new() -> Self { Self { x: 0, y: 0, diff --git a/kernel/src/abi.rs b/kernel/src/abi.rs index 7a8a843..16eb847 100644 --- a/kernel/src/abi.rs +++ b/kernel/src/abi.rs @@ -3,7 +3,7 @@ use abi_sys::{ LockDisplay, PrintAbi, ReadFile, RngRequest, SleepMsAbi, keyboard::*, }; use alloc::{string::ToString, vec::Vec}; -use core::sync::atomic::Ordering; +use core::{alloc::GlobalAlloc, sync::atomic::Ordering}; use embassy_rp::clocks::{RoscRng, clk_sys_freq}; use embassy_time::Instant; use embedded_graphics::draw_target::DrawTarget; @@ -18,13 +18,25 @@ use crate::{ const _: AllocAbi = alloc; pub extern "C" fn alloc(layout: CLayout) -> *mut u8 { // SAFETY: caller guarantees layout is valid - unsafe { alloc::alloc::alloc(layout.into()) } + unsafe { + if cfg!(feature = "pimoroni2w") { + crate::heap::HEAP.alloc(layout.into()) + } else { + alloc::alloc::alloc(layout.into()) + } + } } const _: DeallocAbi = dealloc; pub extern "C" fn dealloc(ptr: *mut u8, layout: CLayout) { // SAFETY: caller guarantees ptr and layout are valid - unsafe { alloc::alloc::dealloc(ptr, layout.into()) }; + unsafe { + if cfg!(feature = "pimoroni2w") { + crate::heap::HEAP.dealloc(ptr, layout.into()) + } else { + alloc::alloc::dealloc(ptr, layout.into()) + } + } } const _: PrintAbi = print; @@ -66,7 +78,6 @@ pub extern "C" fn lock_display(lock: bool) { } const _: DrawIterAbi = draw_iter; -// TODO: maybe return result pub extern "C" fn draw_iter(cpixels: *const CPixel, len: usize) { // SAFETY: caller guarantees `ptr` is valid for `len` bytes let cpixels = unsafe { core::slice::from_raw_parts(cpixels, len) }; diff --git a/kernel/src/heap.rs b/kernel/src/heap.rs index c4f444c..638e787 100644 --- a/kernel/src/heap.rs +++ b/kernel/src/heap.rs @@ -6,7 +6,6 @@ use core::mem::MaybeUninit; use core::sync::atomic::{AtomicUsize, Ordering}; use embedded_alloc::LlffHeap as Heap; -#[global_allocator] pub static HEAP: DualHeap = DualHeap::empty(); const HEAP_SIZE: usize = 64 * 1024; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 8b0e247..1a15960 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -10,7 +10,6 @@ mod abi; mod display; mod elf; mod framebuffer; -#[cfg(feature = "pimoroni2w")] mod heap; mod peripherals; mod psram; @@ -37,13 +36,6 @@ use crate::{ }; use abi_sys::EntryFn; use bumpalo::Bump; -use embedded_graphics::{ - pixelcolor::Rgb565, - prelude::{DrawTarget, RgbColor}, -}; - -use {defmt_rtt as _, panic_probe as _}; - use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::{Executor, Spawner}; use embassy_futures::{join::join, select::select}; @@ -55,20 +47,26 @@ use embassy_rp::{ peripherals::{ DMA_CH0, DMA_CH1, DMA_CH3, DMA_CH4, I2C1, PIN_2, PIN_3, PIN_6, PIN_7, PIN_10, PIN_11, PIN_12, PIN_13, PIN_14, PIN_15, PIN_16, PIN_17, PIN_18, PIN_19, PIN_20, PIN_21, PIN_22, - PIO0, SPI0, SPI1, USB, + PIO0, SPI0, SPI1, USB, WATCHDOG, }, pio, spi::{self, Spi}, usb as embassy_rp_usb, + watchdog::{ResetReason, Watchdog}, }; use embassy_sync::{ blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, signal::Signal, }; -use embassy_time::{Delay, Instant, Timer}; +use embassy_time::{Delay, Duration, Instant, Ticker, Timer}; +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::{DrawTarget, RgbColor}, +}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_sdmmc::SdCard as SdmmcSdCard; use static_cell::StaticCell; use talc::*; +use {defmt_rtt as _, panic_probe as _}; embassy_rp::bind_interrupts!(struct Irqs { I2C1_IRQ => i2c::InterruptHandler; @@ -80,15 +78,32 @@ static mut CORE1_STACK: Stack<16384> = Stack::new(); static EXECUTOR0: StaticCell = StaticCell::new(); static EXECUTOR1: StaticCell = StaticCell::new(); -#[cfg(not(feature = "pimoroni2w"))] -static mut ARENA: [u8; 200 * 1024] = [0; 200 * 1024]; +static mut ARENA: [u8; 250 * 1024] = [0; 250 * 1024]; -#[cfg(not(feature = "pimoroni2w"))] #[global_allocator] static ALLOCATOR: Talck, ClaimOnOom> = Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) }) .lock(); +#[embassy_executor::task] +async fn watchdog_task(mut watchdog: Watchdog) { + if let Some(reason) = watchdog.reset_reason() { + let reason = match reason { + ResetReason::Forced => "forced", + ResetReason::TimedOut => "timed out", + }; + defmt::error!("Watchdog reset reason: {}", reason); + } + + watchdog.start(Duration::from_secs(3)); + + let mut ticker = Ticker::every(Duration::from_secs(2)); + loop { + watchdog.feed(); + ticker.next().await; + } +} + static ENABLE_UI: AtomicBool = AtomicBool::new(true); static UI_CHANGE: Signal = Signal::new(); @@ -141,7 +156,9 @@ async fn main(_spawner: Spawner) { let executor0 = EXECUTOR0.init(Executor::new()); executor0.run(|spawner| { spawner - .spawn(kernel_task(spawner, display, sd, psram, mcu, p.USB)) + .spawn(kernel_task( + spawner, p.WATCHDOG, display, sd, psram, mcu, p.USB, + )) .unwrap() }); } @@ -244,26 +261,36 @@ async fn setup_display(display: Display, spawner: Spawner) { // psram is kind of useless on the pico calc // ive opted to use the pimoroni with on onboard xip psram instead async fn setup_psram(psram: Psram) { - // let psram = init_psram( - // psram.pio, psram.sclk, psram.mosi, psram.miso, psram.cs, psram.dma1, psram.dma2, - // ) - // .await; + let psram = init_psram( + psram.pio, psram.sclk, psram.mosi, psram.miso, psram.cs, psram.dma1, psram.dma2, + ) + .await; - // #[cfg(feature = "defmt")] - // defmt::info!("psram size: {}", psram.size); + #[cfg(feature = "defmt")] + defmt::info!("psram size: {}", psram.size); - // if psram.size == 0 { - // #[cfg(feature = "defmt")] - // defmt::info!("\u{1b}[1mExternal PSRAM was NOT found!\u{1b}[0m"); - // } + if psram.size == 0 { + #[cfg(feature = "defmt")] + defmt::info!("\u{1b}[1mExternal PSRAM was NOT found!\u{1b}[0m"); + } +} - #[cfg(feature = "pimoroni2w")] - { +#[cfg(feature = "pimoroni2w")] +async fn setup_qmi_psram() { + let mut tries = 5; + Timer::after_millis(250).await; + while tries > 1 { let psram_qmi_size = init_psram_qmi(&embassy_rp::pac::QMI, &embassy_rp::pac::XIP_CTRL); + defmt::info!("size: {}", psram_qmi_size); + Timer::after_millis(100).await; if psram_qmi_size > 0 { init_qmi_psram_heap(psram_qmi_size); + return; } + defmt::info!("failed to init qmi psram... trying again"); + tries -= 1; } + panic!("qmi psram not initialized"); } async fn setup_sd(sd: Sd) { @@ -284,22 +311,27 @@ async fn setup_sd(sd: Sd) { #[embassy_executor::task] async fn kernel_task( spawner: Spawner, + watchdog: Peri<'static, WATCHDOG>, display: Display, sd: Sd, psram: Psram, mcu: Mcu, usb: Peri<'static, USB>, ) { + spawner + .spawn(watchdog_task(Watchdog::new(watchdog))) + .unwrap(); setup_mcu(mcu).await; - Timer::after_millis(250).await; setup_display(display, spawner).await; - #[cfg(feature = "pimoroni2w")] - setup_psram(psram).await; setup_sd(sd).await; let _usb = embassy_rp_usb::Driver::new(usb, Irqs); // spawner.spawn(usb_handler(usb)).unwrap(); + // setup_psram(psram).await; + #[cfg(feature = "pimoroni2w")] + setup_qmi_psram().await; + loop { let ui_enabled = ENABLE_UI.load(Ordering::Relaxed); if ui_enabled { diff --git a/kernel/src/psram.rs b/kernel/src/psram.rs index 9145090..28f47d5 100644 --- a/kernel/src/psram.rs +++ b/kernel/src/psram.rs @@ -526,16 +526,10 @@ pub fn init_psram_qmi( let psram_size = detect_psram_qmi(qmi); if psram_size == 0 { + defmt::error!("qmi psram size 0"); return 0; } - // Set PSRAM timing for APS6404 - // - // Using an rxdelay equal to the divisor isn't enough when running the APS6404 close to 133MHz. - // So: don't allow running at divisor 1 above 100MHz (because delay of 2 would be too late), - // and add an extra 1 to the rxdelay if the divided clock is > 100MHz (i.e. sys clock > 200MHz). - const MAX_PSRAM_FREQ: u32 = 133_000_000; - let clock_hz = clk_peri_freq(); let mut divisor: u32 = (clock_hz + MAX_PSRAM_FREQ - 1) / MAX_PSRAM_FREQ; diff --git a/user-apps/calculator/src/main.rs b/user-apps/calculator/src/main.rs index 2e0bfec..7087e46 100644 --- a/user-apps/calculator/src/main.rs +++ b/user-apps/calculator/src/main.rs @@ -28,11 +28,7 @@ use embedded_layout::{ #[panic_handler] fn panic(info: &PanicInfo) -> ! { - print(&format!( - "user panic: {} @ {:?}", - info.message(), - info.location(), - )); + print!("user panic: {} @ {:?}", info.message(), info.location(),); loop {} } @@ -42,7 +38,7 @@ pub extern "Rust" fn _start() { } pub fn main() { - print("Starting Calculator app"); + print!("Starting Calculator app"); let mut display = Display; let mut input = vec!['e', 'x', 'p', 'r', ':', ' ']; diff --git a/user-apps/gallery/src/main.rs b/user-apps/gallery/src/main.rs index be73636..1b06ee5 100644 --- a/user-apps/gallery/src/main.rs +++ b/user-apps/gallery/src/main.rs @@ -10,7 +10,7 @@ use abi::{ keyboard::{KeyCode, KeyState}, print, }; -use alloc::{format, string::ToString}; +use alloc::{format, string::ToString, vec}; use core::panic::PanicInfo; use embedded_graphics::{ Drawable, image::Image, mono_font::MonoTextStyle, mono_font::ascii::FONT_6X10, @@ -20,11 +20,7 @@ use tinybmp::Bmp; #[panic_handler] fn panic(info: &PanicInfo) -> ! { - print(&format!( - "user panic: {} @ {:?}", - info.message(), - info.location(), - )); + print!("user panic: {} @ {:?}", info.message(), info.location()); loop {} } @@ -34,8 +30,8 @@ pub extern "Rust" fn _start() { } pub fn main() { - print("Starting Gallery app"); - static mut BMP_BUF: [u8; 100_000] = [0_u8; 100_000]; + print!("Starting Gallery app"); + let mut bmp_buf = vec![0_u8; 100_000]; let mut display = Display; // Grid parameters @@ -55,13 +51,13 @@ pub fn main() { } if let Some(f) = file { - print(&format!("file: {}", f.name)); + print!("file: {}", f.name); if f.name.extension() == b"bmp" || f.name.extension() == b"BMP" { let file = format!("/images/{}", f.name); - let read = read_file(&file, 0, &mut unsafe { &mut BMP_BUF[..] }); + let read = read_file(&file, 0, &mut &mut bmp_buf[..]); if read > 0 { - let bmp = Bmp::from_slice(unsafe { &BMP_BUF }).expect("failed to parse bmp"); + let bmp = Bmp::from_slice(&bmp_buf).expect("failed to parse bmp"); let row = images_drawn / grid_cols; let col = images_drawn % grid_cols; diff --git a/user-apps/gif/src/main.rs b/user-apps/gif/src/main.rs index c749bf2..edeba0c 100644 --- a/user-apps/gif/src/main.rs +++ b/user-apps/gif/src/main.rs @@ -9,18 +9,14 @@ use abi::{ keyboard::{KeyCode, KeyState}, print, sleep, }; -use alloc::{format, vec::Vec}; +use alloc::vec; use core::panic::PanicInfo; use embedded_graphics::{image::ImageDrawable, pixelcolor::Rgb565}; use tinygif::Gif; #[panic_handler] fn panic(info: &PanicInfo) -> ! { - print(&format!( - "user panic: {} @ {:?}", - info.message(), - info.location(), - )); + print!("user panic: {} @ {:?}", info.message(), info.location(),); loop {} } @@ -30,16 +26,18 @@ pub extern "Rust" fn _start() { } pub fn main() { - print("Starting Gif app"); + print!("Starting Gif app"); let mut display = Display; let size = file_len("/gifs/bad_apple.gif"); - let mut buf = Vec::with_capacity(size); + let mut buf = vec![0_u8; size]; let read = read_file("/gifs/bad_apple.gif", 0, &mut buf); + print!("read: {}, file size: {}", read, size); assert!(read == size); let gif = Gif::::from_slice(&buf).unwrap(); + let mut frame_num = 0; loop { for frame in gif.frames() { let start = get_ms(); @@ -48,6 +46,9 @@ pub fn main() { frame.draw(&mut display).unwrap(); lock_display(false); + frame_num += 1; + print!("drew {}", frame_num); + sleep(((frame.delay_centis as u64) * 10).saturating_sub(start)); let event = get_key(); diff --git a/user-apps/memory.x b/user-apps/memory.x index 5517509..71677ed 100644 --- a/user-apps/memory.x +++ b/user-apps/memory.x @@ -1,6 +1,6 @@ MEMORY { - RAM : ORIGIN = 0x0, LENGTH = 150K + RAM : ORIGIN = 0x0, LENGTH = 250K } SECTIONS diff --git a/user-apps/snake/src/main.rs b/user-apps/snake/src/main.rs index a979b33..ffb4a2c 100644 --- a/user-apps/snake/src/main.rs +++ b/user-apps/snake/src/main.rs @@ -9,18 +9,13 @@ use abi::{ keyboard::{KeyCode, KeyState}, print, sleep, }; -use alloc::format; use core::panic::PanicInfo; use embedded_graphics::{pixelcolor::Rgb565, prelude::RgbColor}; use embedded_snake::{Direction, SnakeGame}; #[panic_handler] fn panic(info: &PanicInfo) -> ! { - print(&format!( - "user panic: {} @ {:?}", - info.message(), - info.location(), - )); + print!("user panic: {} @ {:?}", info.message(), info.location(),); loop {} } @@ -32,7 +27,7 @@ pub extern "Rust" fn _start() { const CELL_SIZE: usize = 8; pub fn main() { - print("Starting Snake app"); + print!("Starting Snake app"); let mut display = Display; let mut game = SnakeGame::<100, Rgb565, Rng>::new(