2 Commits

Author SHA1 Message Date
49b6a99ea8 dont use mutex on framebuffer 2025-09-11 23:39:47 -06:00
718bcd6b5b WIP fixing deadlocks in kernel 2025-09-11 17:36:38 -06:00
7 changed files with 131 additions and 120 deletions

1
Cargo.lock generated
View File

@@ -1941,7 +1941,6 @@ dependencies = [
[[package]] [[package]]
name = "st7365p-lcd" name = "st7365p-lcd"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/legitcamper/st7365p-lcd-rs?rev=87abf450404865dcb535292e9e1a6a2457fd4599#87abf450404865dcb535292e9e1a6a2457fd4599"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"embedded-graphics-core", "embedded-graphics-core",

View File

@@ -70,7 +70,8 @@ defmt = { version = "0.3", optional = true }
defmt-rtt = "0.4.2" defmt-rtt = "0.4.2"
embedded-sdmmc = { version = "0.9", default-features = false } embedded-sdmmc = { version = "0.9", default-features = false }
st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", rev = "87abf450404865dcb535292e9e1a6a2457fd4599" } # async branch # st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", rev = "87abf450404865dcb535292e9e1a6a2457fd4599" } # async branch
st7365p-lcd = { path = "../../ST7365P-lcd-rs" } # async branch
embedded-graphics = { version = "0.8.1" } embedded-graphics = { version = "0.8.1" }
embedded-text = "0.7.2" embedded-text = "0.7.2"
embedded-layout = "0.4.2" embedded-layout = "0.4.2"

View File

@@ -3,7 +3,6 @@ use core::pin::Pin;
use abi_sys::{DrawIterAbi, GetKeyAbi, Pixel, PrintAbi}; use abi_sys::{DrawIterAbi, GetKeyAbi, Pixel, PrintAbi};
use alloc::boxed::Box; use alloc::boxed::Box;
use defmt::info; use defmt::info;
use embassy_futures::block_on;
use embedded_graphics::{ use embedded_graphics::{
Drawable, Drawable,
draw_target::DrawTarget, draw_target::DrawTarget,
@@ -13,7 +12,7 @@ use embedded_graphics::{
}; };
use shared::keyboard::KeyEvent; use shared::keyboard::KeyEvent;
use crate::{KEY_CACHE, display::FRAMEBUFFER}; use crate::{KEY_CACHE, display::framebuffer_mut};
// ensure the abi and the kernel fn signatures are the same // ensure the abi and the kernel fn signatures are the same
const _: PrintAbi = print; const _: PrintAbi = print;
@@ -26,19 +25,8 @@ pub extern "Rust" fn print(msg: &str) {
// TODO: maybe return result // TODO: maybe return result
pub extern "Rust" fn draw_iter(pixels: &[Pixel<Rgb565>]) { pub extern "Rust" fn draw_iter(pixels: &[Pixel<Rgb565>]) {
for _ in 0..10 { let fb = framebuffer_mut();
if let Some(mut framebuffer) = FRAMEBUFFER.try_lock().ok() { fb.draw_iter(pixels.iter().copied()).unwrap();
for _ in 0..10 {
// kernel takes() framebuffer
if let Some(framebuffer) = framebuffer.as_mut() {
framebuffer.draw_iter(pixels.iter().copied()).unwrap();
}
break;
}
break;
}
cortex_m::asm::nop();
}
} }
pub extern "Rust" fn get_key() -> Option<KeyEvent> { pub extern "Rust" fn get_key() -> Option<KeyEvent> {

View File

@@ -20,8 +20,11 @@ pub const SCREEN_WIDTH: usize = 320;
pub const SCREEN_HEIGHT: usize = 320; pub const SCREEN_HEIGHT: usize = 320;
type FB = FrameBuffer<SCREEN_WIDTH, SCREEN_HEIGHT, { SCREEN_WIDTH * SCREEN_HEIGHT }>; type FB = FrameBuffer<SCREEN_WIDTH, SCREEN_HEIGHT, { SCREEN_WIDTH * SCREEN_HEIGHT }>;
static FRAMEBUFFER_CELL: StaticCell<FB> = StaticCell::new(); static mut FRAMEBUFFER: Option<FB> = None;
pub static FRAMEBUFFER: Mutex<CriticalSectionRawMutex, Option<&'static mut FB>> = Mutex::new(None);
pub fn framebuffer_mut() -> &'static mut FB {
unsafe { FRAMEBUFFER.as_mut().unwrap() }
}
pub async fn init_display( pub async fn init_display(
spi: Spi<'static, SPI1, Async>, spi: Spi<'static, SPI1, Async>,
@@ -38,28 +41,20 @@ pub async fn init_display(
true, true,
Delay, Delay,
); );
let framebuffer = FRAMEBUFFER_CELL.init(FrameBuffer::new());
display.init().await.unwrap(); display.init().await.unwrap();
display.set_custom_orientation(0x40).await.unwrap(); display.set_custom_orientation(0x40).await.unwrap();
framebuffer.draw(&mut display).await.unwrap(); unsafe { FRAMEBUFFER.replace(FrameBuffer::new()) };
display.draw(framebuffer_mut()).await.unwrap();
display.set_on().await.unwrap(); display.set_on().await.unwrap();
FRAMEBUFFER.lock().await.replace(framebuffer);
display display
} }
pub async fn display_handler(mut display: DISPLAY) { pub async fn display_handler(mut display: DISPLAY) {
let fb = framebuffer_mut();
loop { loop {
let fb: &mut FB = { display.partial_draw_batched(fb).await.unwrap();
let mut guard = FRAMEBUFFER.lock().await; embassy_time::Timer::after_millis(32).await; // 30 fps
guard.take().unwrap() // take ownership
}; // guard dropped
fb.partial_draw_batched(&mut display).await.unwrap();
// Put it back
FRAMEBUFFER.lock().await.replace(fb);
Timer::after_millis(32).await; // 30 fps
} }
} }

View File

@@ -17,11 +17,8 @@ mod ui;
mod usb; mod usb;
mod utils; mod utils;
use core::sync::atomic::Ordering;
use crate::{ use crate::{
display::{display_handler, init_display}, display::{display_handler, framebuffer_mut, init_display},
elf::load_binary,
peripherals::{ peripherals::{
conf_peripherals, conf_peripherals,
keyboard::{KeyCode, KeyState, read_keyboard_fifo}, keyboard::{KeyCode, KeyState, read_keyboard_fifo},
@@ -30,11 +27,7 @@ use crate::{
ui::{SELECTIONS, ui_handler}, ui::{SELECTIONS, ui_handler},
usb::usb_handler, usb::usb_handler,
}; };
use abi_sys::EntryFn; use abi_sys::{EntryFn, Rgb565, RgbColor};
use alloc::vec::Vec;
use {defmt_rtt as _, panic_probe as _};
use defmt::unwrap; use defmt::unwrap;
use embassy_executor::{Executor, Spawner}; use embassy_executor::{Executor, Spawner};
use embassy_futures::join::{join, join3, join4, join5}; use embassy_futures::join::{join, join3, join4, join5};
@@ -49,14 +42,18 @@ use embassy_rp::{
spi::{self, Spi}, spi::{self, Spi},
usb as embassy_rp_usb, usb as embassy_rp_usb,
}; };
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, mutex::Mutex}; use embassy_sync::{
blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, mutex::Mutex, signal::Signal,
};
use embassy_time::{Delay, Timer}; use embassy_time::{Delay, Timer};
use embedded_graphics::draw_target::DrawTarget;
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::SdCard as SdmmcSdCard; use embedded_sdmmc::SdCard as SdmmcSdCard;
use heapless::spsc::Queue; use heapless::spsc::Queue;
use shared::keyboard::KeyEvent; use shared::keyboard::KeyEvent;
use static_cell::StaticCell; use static_cell::StaticCell;
use talc::*; use talc::*;
use {defmt_rtt as _, panic_probe as _};
embassy_rp::bind_interrupts!(struct Irqs { embassy_rp::bind_interrupts!(struct Irqs {
I2C1_IRQ => i2c::InterruptHandler<I2C1>; I2C1_IRQ => i2c::InterruptHandler<I2C1>;
@@ -75,7 +72,9 @@ static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
.lock(); .lock();
static TASK_STATE: Mutex<CriticalSectionRawMutex, TaskState> = Mutex::new(TaskState::Ui); static TASK_STATE: Mutex<CriticalSectionRawMutex, TaskState> = Mutex::new(TaskState::Ui);
static TASK_STATE_CHANGED: Signal<CriticalSectionRawMutex, ()> = Signal::new();
#[derive(Copy, Clone)]
enum TaskState { enum TaskState {
Ui, Ui,
Kernel, Kernel,
@@ -131,20 +130,28 @@ async fn userland_task() {
let recv = BINARY_CH.receiver(); let recv = BINARY_CH.receiver();
loop { loop {
let entry = recv.receive().await; let entry = recv.receive().await;
defmt::info!("got bin");
// disable kernel ui // disable kernel ui
{ {
let mut state = TASK_STATE.lock().await; let mut state = TASK_STATE.lock().await;
*state = TaskState::Kernel; *state = TaskState::Kernel;
TASK_STATE_CHANGED.signal(());
} }
defmt::info!("Executing Binary"); {
let fb = framebuffer_mut();
fb.clear(Rgb565::BLACK).unwrap();
}
defmt::info!("running entry");
entry().await; entry().await;
// enable kernel ui // enable kernel ui
{ {
let mut state = TASK_STATE.lock().await; let mut state = TASK_STATE.lock().await;
*state = TaskState::Ui; *state = TaskState::Ui;
TASK_STATE_CHANGED.signal(());
} }
} }
} }

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
BINARY_CH, TASK_STATE, TaskState, BINARY_CH, TASK_STATE, TaskState,
display::{FRAMEBUFFER, SCREEN_HEIGHT, SCREEN_WIDTH}, display::{SCREEN_HEIGHT, SCREEN_WIDTH, framebuffer_mut},
elf::load_binary, elf::load_binary,
format, format,
peripherals::keyboard, peripherals::keyboard,
@@ -47,36 +47,51 @@ use shared::keyboard::{KeyCode, KeyState};
pub static SELECTIONS: Mutex<CriticalSectionRawMutex, SelectionList> = pub static SELECTIONS: Mutex<CriticalSectionRawMutex, SelectionList> =
Mutex::new(SelectionList::new()); Mutex::new(SelectionList::new());
pub async fn run_selected() {
info!("Getting selections lock");
let selections = SELECTIONS.lock().await;
info!("Got selections lock");
let selection = selections.selections[selections.current_selection as usize - 1].clone();
let entry = unsafe { load_binary(&selection.short_name).await.unwrap() };
info!("loaded binary");
BINARY_CH.send(entry).await;
info!("sent bin");
}
pub async fn ui_handler() { pub async fn ui_handler() {
loop { loop {
if let TaskState::Ui = *TASK_STATE.lock().await { let state = *TASK_STATE.lock().await;
if let TaskState::Ui = state {
let mut redraw = false;
if let Some(event) = keyboard::read_keyboard_fifo().await { if let Some(event) = keyboard::read_keyboard_fifo().await {
if let KeyState::Pressed = event.state { if event.state == KeyState::Pressed {
match event.key { match event.key {
KeyCode::JoyUp => { KeyCode::JoyUp => {
let mut selections = SELECTIONS.lock().await; let mut selections = SELECTIONS.lock().await;
selections.up(); selections.up();
redraw = true;
} }
KeyCode::JoyDown => { KeyCode::JoyDown => {
let mut selections = SELECTIONS.lock().await; let mut selections = SELECTIONS.lock().await;
selections.down(); selections.down();
redraw = true;
} }
KeyCode::Enter | KeyCode::JoyRight => { KeyCode::Enter | KeyCode::JoyRight => run_selected().await,
let selections = SELECTIONS.lock().await; _ => {}
let selection = selections.selections
[selections.current_selection as usize - 1]
.clone();
let entry =
unsafe { load_binary(&selection.short_name).await.unwrap() };
BINARY_CH.send(entry).await;
}
_ => (),
} }
} }
} }
draw_selection().await; {
let mut selections = SELECTIONS.lock().await;
redraw |= selections.take_changed();
}
if redraw {
draw_selection().await;
}
} }
} }
} }
@@ -87,58 +102,57 @@ async fn draw_selection() {
guard.selections.clone() guard.selections.clone()
}; };
let mut fb_lock = FRAMEBUFFER.lock().await; let fb = framebuffer_mut();
if let Some(fb) = fb_lock.as_mut() { let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE);
let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE); let display_area = fb.bounding_box();
let display_area = fb.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();
if file_names.is_empty() { if file_names.is_empty() {
TextBox::new( TextBox::new(
&no_bins, &no_bins,
Rectangle::new( Rectangle::new(
Point::new(25, 25), Point::new(25, 25),
Size::new(display_area.size.width - 50, display_area.size.width - 50), Size::new(display_area.size.width - 50, display_area.size.width - 50),
), ),
text_style, text_style,
) )
.draw(*fb) .draw(fb)
.unwrap(); .unwrap();
} else { } else {
let mut file_names = file_names.iter(); let mut file_names = file_names.iter();
let Some(first) = file_names.next() else { let Some(first) = file_names.next() else {
Text::new("No Programs found on SD Card\nEnsure programs end with '.bin',\nand are located in the root directory", Text::new("No Programs found on SD Card\nEnsure programs end with '.bin',\nand are located in the root directory",
Point::zero(), text_style).draw(*fb).unwrap(); Point::zero(), text_style).draw(fb).unwrap();
return; return;
};
let chain = Chain::new(Text::new(&first.long_name, Point::zero(), text_style));
// for _ in 0..file_names.len() {
// let chain = chain.append(Text::new(
// file_names.next().unwrap(),
// Point::zero(),
// text_style,
// ));
// }
LinearLayout::vertical(chain)
.with_alignment(horizontal::Center)
.arrange()
.align_to(&display_area, horizontal::Center, vertical::Center)
.draw(*fb)
.unwrap();
}; };
}
let chain = Chain::new(Text::new(&first.long_name, Point::zero(), text_style));
// for _ in 0..file_names.len() {
// let chain = chain.append(Text::new(
// file_names.next().unwrap(),
// Point::zero(),
// text_style,
// ));
// }
LinearLayout::vertical(chain)
.with_alignment(horizontal::Center)
.arrange()
.align_to(&display_area, horizontal::Center, vertical::Center)
.draw(fb)
.unwrap();
};
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SelectionList { pub struct SelectionList {
current_selection: u16, current_selection: u16,
pub selections: Vec<FileName>, pub selections: Vec<FileName>,
has_changed: bool,
} }
impl SelectionList { impl SelectionList {
@@ -146,6 +160,7 @@ impl SelectionList {
Self { Self {
selections: Vec::new(), selections: Vec::new(),
current_selection: 0, current_selection: 0,
has_changed: true,
} }
} }
@@ -164,4 +179,10 @@ impl SelectionList {
self.current_selection -= 1 self.current_selection -= 1
} }
} }
pub fn take_changed(&mut self) -> bool {
let changed = self.has_changed;
self.has_changed = false;
changed
}
} }

View File

@@ -27,29 +27,29 @@ pub async fn main() {
let mut text = vec!['H', 'E', 'L', 'L', 'O']; let mut text = vec!['H', 'E', 'L', 'L', 'O'];
loop { // loop {
Text::with_alignment( Text::with_alignment(
&text.iter().cloned().collect::<String>(), &text.iter().cloned().collect::<String>(),
display.bounding_box().center() + Point::new(0, 15), display.bounding_box().center() + Point::new(0, 15),
character_style, character_style,
Alignment::Center, Alignment::Center,
) )
.draw(&mut display) .draw(&mut display)
.unwrap(); .unwrap();
if let Some(event) = get_key() { if let Some(event) = get_key() {
print("User got event"); print("User got event");
match event.key { match event.key {
KeyCode::Char(ch) => { KeyCode::Char(ch) => {
text.push(ch); text.push(ch);
}
KeyCode::Backspace => {
text.pop();
}
_ => (),
} }
KeyCode::Backspace => {
text.pop();
}
_ => (),
} }
} }
// }
} }
#[unsafe(no_mangle)] #[unsafe(no_mangle)]