From e2ff3740f3c3151241a1e5eedeb794e01761d78e Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 5 Sep 2025 14:04:35 -0600 Subject: [PATCH] working on binary selection --- Cargo.lock | 18 +++++++++ kernel/Cargo.toml | 1 + kernel/src/main.rs | 30 +++++++++++--- kernel/src/storage.rs | 44 +++++++++++++++++---- kernel/src/ui.rs | 92 ++++++++++++++++++++++++++++++------------- 5 files changed, 145 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6551289..737c75e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -893,6 +893,17 @@ dependencies = [ "embedded-storage", ] +[[package]] +name = "embedded-text" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005680edc0d075af5e02d5788ca291737bd9aba7fc404ae031cc9dfa715e5f7d" +dependencies = [ + "az", + "embedded-graphics", + "object-chain", +] + [[package]] name = "ena" version = "0.14.3" @@ -1214,6 +1225,7 @@ dependencies = [ "embedded-hal-bus", "embedded-layout", "embedded-sdmmc", + "embedded-text", "goblin", "heapless", "num_enum 0.7.4", @@ -1417,6 +1429,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "object-chain" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41af26158b0f5530f7b79955006c2727cd23d0d8e7c3109dc316db0a919784dd" + [[package]] name = "once_cell" version = "1.21.3" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 417f175..4843e70 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -72,6 +72,7 @@ defmt-rtt = "0.4.2" embedded-sdmmc = { version = "0.9", default-features = false } st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", rev = "87abf450404865dcb535292e9e1a6a2457fd4599" } # async branch embedded-graphics = { version = "0.8.1" } +embedded-text = "0.7.2" embedded-layout = "0.4.2" static_cell = "2.1.1" diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 1f19627..9d1e6ba 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -17,6 +17,8 @@ mod ui; mod usb; mod utils; +use core::sync::atomic::Ordering; + use crate::{ display::{display_handler, init_display}, elf::load_binary, @@ -25,8 +27,8 @@ use crate::{ keyboard::{KeyCode, KeyState, read_keyboard_fifo}, }, storage::{SDCARD, SdCard}, - ui::ui_handler, - usb::{ENABLE_SCSI, usb_handler}, + ui::{SELECTIONS, ui_handler}, + usb::usb_handler, }; use alloc::vec::Vec; @@ -34,7 +36,7 @@ use {defmt_rtt as _, panic_probe as _}; use defmt::unwrap; use embassy_executor::{Executor, Spawner}; -use embassy_futures::join::{join, join3, join4}; +use embassy_futures::join::{join, join3, join4, join5}; use embassy_rp::{ gpio::{Input, Level, Output, Pull}, i2c::{self, I2c}, @@ -188,6 +190,25 @@ async fn kernel_task(display: Display, sd: Sd, mcu: Mcu, usb: USB) { let ui_fut = ui_handler(); + let binary_search_fut = async { + loop { + { + let mut guard = SDCARD.get().lock().await; + + if let Some(sd) = guard.as_mut() { + let files = sd.list_files_by_extension(".bin").unwrap(); + let mut select = SELECTIONS.lock().await; + + if select.selections != files { + select.selections = files; + select.reset(); + } + } + } + Timer::after_secs(5).await; + } + }; + { let mut config = spi::Config::default(); config.frequency = 400_000; @@ -213,8 +234,7 @@ async fn kernel_task(display: Display, sd: Sd, mcu: Mcu, usb: USB) { } }; - ENABLE_SCSI.store(true, core::sync::atomic::Ordering::Relaxed); - join4(usb_fut, display_fut, key_abi_fut, ui_fut).await; + join4(display_fut, ui_fut, binary_search_fut, key_abi_fut).await; } static mut KEY_CACHE: Queue = Queue::new(); diff --git a/kernel/src/storage.rs b/kernel/src/storage.rs index ea698b0..42b1920 100644 --- a/kernel/src/storage.rs +++ b/kernel/src/storage.rs @@ -1,3 +1,5 @@ +use alloc::{string::String, vec::Vec}; +use core::str::FromStr; use embassy_rp::gpio::{Input, Output}; use embassy_rp::peripherals::SPI0; use embassy_rp::spi::{Blocking, Spi}; @@ -6,6 +8,7 @@ use embassy_sync::lazy_lock::LazyLock; use embassy_sync::mutex::Mutex; use embassy_time::Delay; use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_sdmmc::LfnBuffer; use embedded_sdmmc::{ Block, BlockCount, BlockDevice, BlockIdx, Directory, SdCard as SdmmcSdCard, TimeSource, Timestamp, Volume, VolumeIdx, VolumeManager, sdcard::Error, @@ -57,13 +60,6 @@ impl SdCard { self.det.is_low() } - pub fn open_volume(&mut self) -> Result, ()> { - if self.is_attached() { - return Ok(self.volume_mgr.open_volume(VolumeIdx(0)).map_err(|_| ())?); - } - Err(()) - } - pub fn size(&self) -> u64 { let mut result = 0; @@ -103,4 +99,38 @@ impl SdCard { }); res.map_err(|_| ()) } + + fn access_root_dir(&mut self, mut access: impl FnMut(Dir)) { + let volume0 = self.volume_mgr.open_volume(VolumeIdx(0)).unwrap(); + let root_dir = volume0.open_root_dir().unwrap(); + + access(root_dir); + } + + /// Returns a Vec of file names (long format) that match the given extension (e.g., "BIN") + pub fn list_files_by_extension(&mut self, ext: &str) -> Result, ()> { + let mut result = Vec::new(); + + // Only proceed if card is inserted + if !self.is_attached() { + return Ok(result); + } + + let mut lfn_storage = [0; 50]; + let mut lfn_buffer = LfnBuffer::new(&mut lfn_storage); + + self.access_root_dir(|dir| { + dir.iterate_dir_lfn(&mut lfn_buffer, |_entry, name| { + if let Some(name) = name { + let name = String::from_str(name).unwrap(); + if name.contains(ext) { + result.push(name); + } + } + }) + .unwrap() + }); + + Ok(result) + } } diff --git a/kernel/src/ui.rs b/kernel/src/ui.rs index d90f7a8..ab8bb6a 100644 --- a/kernel/src/ui.rs +++ b/kernel/src/ui.rs @@ -3,9 +3,10 @@ use crate::{ display::{FRAMEBUFFER, SCREEN_HEIGHT, SCREEN_WIDTH}, format, peripherals::keyboard, + usb::RESTART_USB, }; use alloc::{string::String, vec::Vec}; -use core::fmt::Debug; +use core::{fmt::Debug, str::FromStr, sync::atomic::Ordering}; use defmt::info; use embassy_rp::{ gpio::{Level, Output}, @@ -23,10 +24,10 @@ use embedded_graphics::{ draw_target::DrawTarget, mono_font::{ MonoTextStyle, - ascii::{FONT_6X10, FONT_9X15, FONT_10X20}, + ascii::{FONT_6X9, FONT_6X10, FONT_9X15, FONT_10X20}, }, pixelcolor::Rgb565, - prelude::{Dimensions, Point, RgbColor, Size}, + prelude::{Dimensions, Point, Primitive, RgbColor, Size}, primitives::{PrimitiveStyle, Rectangle, StyledDrawable}, text::Text, }; @@ -38,22 +39,29 @@ use embedded_layout::{ object_chain::Chain, prelude::*, }; -use shared::keyboard::KeyCode; +use embedded_text::TextBox; +use shared::keyboard::{KeyCode, KeyState}; -static SELECTIONS: Mutex = +pub static SELECTIONS: Mutex = Mutex::new(SelectionList::new(Vec::new())); pub async fn ui_handler() { loop { - let state = TASK_STATE.lock().await; - if let TaskState::Ui = *state { - let mut selections = SELECTIONS.lock().await; + if let TaskState::Ui = *TASK_STATE.lock().await { if let Some(event) = keyboard::read_keyboard_fifo().await { - match event.key { - KeyCode::JoyUp => selections.up(), - KeyCode::JoyDown => selections.down(), - KeyCode::Enter | KeyCode::JoyRight => (), - _ => (), + if let KeyState::Pressed = event.state { + match event.key { + KeyCode::JoyUp => { + let mut selections = SELECTIONS.lock().await; + selections.up(); + } + KeyCode::JoyDown => { + let mut selections = SELECTIONS.lock().await; + selections.down(); + } + KeyCode::Enter | KeyCode::JoyRight => (), + _ => (), + } } } @@ -63,38 +71,62 @@ pub async fn ui_handler() { } async fn draw_selection() { + let file_names: Vec = { + let guard = SELECTIONS.lock().await; + guard.selections.clone() + }; + let mut fb_lock = FRAMEBUFFER.lock().await; if let Some(fb) = fb_lock.as_mut() { - info!("UIINg"); let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE); + let display_area = fb.bounding_box(); - let guard = SELECTIONS.lock().await; - let mut file_names = guard.selections.iter(); + 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 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", + if file_names.is_empty() { + TextBox::new( + &no_bins, + Rectangle::new( + Point::new(25, 25), + Size::new(display_area.size.width - 50, display_area.size.width - 50), + ), + text_style, + ) + .draw(*fb) + .unwrap(); + } else { + let mut file_names = file_names.iter(); + 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", Point::zero(), text_style).draw(*fb).unwrap(); - return; - }; + return; + }; - let chain = Chain::new(Text::new(first, Point::zero(), text_style)); + let chain = Chain::new(Text::new(first, Point::zero(), text_style)); + + // for _ in 0..file_names.len() { + // let chain = chain.append(Text::new( + // file_names.next().unwrap(), + // Point::zero(), + // text_style, + // )); + // } - for _ in 0..10 { LinearLayout::vertical(chain) .with_alignment(horizontal::Center) .arrange() - .align_to(&fb.bounding_box(), horizontal::Center, vertical::Center) + .align_to(&display_area, horizontal::Center, vertical::Center) .draw(*fb) .unwrap(); - break; - } + }; } } pub struct SelectionList { current_selection: u16, - selections: Vec, + pub selections: Vec, } impl SelectionList { @@ -105,13 +137,17 @@ impl SelectionList { } } - pub fn down(&mut self) { + pub fn reset(&mut self) { + self.current_selection = 1 + } + + fn down(&mut self) { if self.current_selection + 1 < self.selections.len() as u16 { self.current_selection += 1 } } - pub fn up(&mut self) { + fn up(&mut self) { if self.current_selection > self.selections.len() as u16 { self.current_selection -= 1 }