working on bin selection ui

This commit is contained in:
2025-08-01 16:24:37 -06:00
parent db81470531
commit 8ca55fcdaf
11 changed files with 222 additions and 19 deletions

View File

@@ -70,12 +70,10 @@ portable-atomic = { version = "1.11", features = ["critical-section"] }
defmt = { version = "0.3", optional = true }
defmt-rtt = "0.4.2"
embedded-graphics = { version = "0.8.1" }
embedded-sdmmc = { version = "0.9", default-features = false }
st7365p-lcd = { git = "https://github.com/legitcamper/st7365p-lcd-rs", rev = "87abf450404865dcb535292e9e1a6a2457fd4599" } # async branch
shared = { path = "../shared" }
abi = { path = "../abi" }
embedded-graphics = { version = "0.8.1" }
embedded-layout = "0.4.2"
static_cell = "2.1.1"
bitflags = "2.9.1"
@@ -83,3 +81,8 @@ talc = "4.4.3"
spin = "0.10.0"
heapless = "0.8.0"
num_enum = { version = "0.7.4", default-features = false }
elf_loader = {version ="0.12.0", default-features = false, features = ["portable-atomic"]}
shared = { path = "../shared" }
abi = { path = "../abi" }

View File

@@ -10,12 +10,11 @@ use embedded_graphics::{
use crate::display::FRAMEBUFFER;
#[unsafe(no_mangle)]
pub extern "C" fn syscall_dispatch(call: *const Syscall) -> usize {
pub extern "C" fn call_abi(call: *const Syscall) {
let call = unsafe { &*call };
match call {
Syscall::DrawPixel { x, y, color } => {
draw_pixel(*x, *y, *color);
0
}
}
}

View File

@@ -17,8 +17,8 @@ type DISPLAY = ST7365P<
Delay,
>;
const SCREEN_WIDTH: usize = 320;
const SCREEN_HEIGHT: usize = 320;
pub const SCREEN_WIDTH: usize = 320;
pub const SCREEN_HEIGHT: usize = 320;
type FB = FrameBuffer<SCREEN_WIDTH, SCREEN_HEIGHT, { SCREEN_WIDTH * SCREEN_HEIGHT }>;
static FRAMEBUFFER_CELL: StaticCell<FB> = StaticCell::new();

View File

@@ -8,6 +8,7 @@ mod display;
mod peripherals;
mod scsi;
mod storage;
mod ui;
mod usb;
mod utils;
@@ -17,7 +18,7 @@ use crate::{
conf_peripherals,
keyboard::{KeyCode, KeyState, read_keyboard_fifo},
},
storage::SdCard,
storage::{SDCARD, SdCard},
usb::usb_handler,
};
@@ -68,7 +69,7 @@ async fn main(_spawner: Spawner) {
display_handler(display)
};
let sdcard = {
{
let mut config = spi::Config::default();
config.frequency = 400_000;
let clk = p.PIN_18;
@@ -83,11 +84,11 @@ async fn main(_spawner: Spawner) {
config.frequency = 32_000_000;
sdcard.spi(|dev| dev.bus_mut().set_config(&config));
SdCard::new(sdcard, det)
SDCARD.get().lock().await.replace(SdCard::new(sdcard, det));
};
let usb = embassy_rp_usb::Driver::new(p.USB, Irqs);
let usb_fut = usb_handler(usb, sdcard);
let usb_fut = usb_handler(usb);
join(usb_fut, display_fut).await;
}

View File

@@ -12,14 +12,14 @@ use crate::storage::SdCard;
const BULK_ENDPOINT_PACKET_SIZE: usize = 64;
pub struct MassStorageClass<'d, D: Driver<'d>> {
sdcard: SdCard,
pub struct MassStorageClass<'d, 's, D: Driver<'d>> {
sdcard: &'s SdCard,
bulk_out: D::EndpointOut,
bulk_in: D::EndpointIn,
}
impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
pub fn new(builder: &mut Builder<'d, D>, sdcard: SdCard) -> Self {
impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, 's, D> {
pub fn new(builder: &mut Builder<'d, D>, sdcard: &'s SdCard) -> Self {
let mut function = builder.function(0x08, SUBCLASS_SCSI, 0x50); // Mass Storage class
let mut interface = function.interface();
let mut alt = interface.alt_setting(0x08, SUBCLASS_SCSI, 0x50, None);

View File

@@ -1,6 +1,9 @@
use embassy_rp::gpio::{Input, Output};
use embassy_rp::peripherals::SPI0;
use embassy_rp::spi::{Blocking, Spi};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::lazy_lock::LazyLock;
use embassy_sync::mutex::Mutex;
use embassy_time::Delay;
use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::{
@@ -18,6 +21,9 @@ type VolMgr = VolumeManager<SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUME
type Vol<'a> = Volume<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
type Dir<'a> = Directory<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
pub static SDCARD: LazyLock<Mutex<NoopRawMutex, Option<SdCard>>> =
LazyLock::new(|| Mutex::new(None));
pub struct DummyTimeSource {}
impl TimeSource for DummyTimeSource {
fn get_timestamp(&self) -> Timestamp {

117
kernel/src/ui.rs Normal file
View File

@@ -0,0 +1,117 @@
use crate::{
display::{FRAMEBUFFER, SCREEN_HEIGHT, SCREEN_WIDTH},
format,
};
use core::fmt::Debug;
use defmt::info;
use embassy_rp::{
gpio::{Level, Output},
peripherals::{PIN_13, PIN_14, PIN_15, SPI1},
spi::{Async, Spi},
};
use embassy_time::{Delay, Timer};
use embedded_graphics::{
Drawable,
draw_target::DrawTarget,
mono_font::{
MonoTextStyle,
ascii::{FONT_6X10, FONT_9X15, FONT_10X20},
},
pixelcolor::Rgb565,
prelude::{Dimensions, Point, RgbColor, Size},
primitives::{PrimitiveStyle, Rectangle, StyledDrawable},
text::Text,
};
use embedded_hal_async::spi::SpiDevice;
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
use embedded_layout::{
align::{horizontal, vertical},
layout::linear::LinearLayout,
object_chain::Chain,
prelude::*,
};
use heapless::{String, Vec};
pub struct UI<const MAX_SELECTIONS: usize, const MAX_STR_LEN: usize> {
pub selections_list: SelectionList<MAX_SELECTIONS, MAX_STR_LEN>,
}
impl<const MAX_SELECTIONS: usize, const MAX_STR_LEN: usize> UI<MAX_SELECTIONS, MAX_STR_LEN> {
pub fn new() -> Self {
Self {
selections_list: SelectionList::new(Vec::new()),
}
}
pub async fn draw<D: DrawTarget<Color = Rgb565>>(&mut self)
where
<D as DrawTarget>::Error: Debug,
{
self.draw_selection().await;
}
async fn draw_selection(&mut self) {
let text_style = MonoTextStyle::new(&FONT_9X15, Rgb565::WHITE);
let selection = Rectangle::new(
Point::new(0, 0),
Size::new(SCREEN_WIDTH as u32 - 1, SCREEN_HEIGHT as u32 - 1),
);
let mut file_names = self.selections_list.selections.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(*FRAMEBUFFER.lock().await.borrow_mut().as_mut().unwrap()).unwrap();
return;
};
let chain = Chain::new(Text::new(first, Point::zero(), text_style));
LinearLayout::vertical(chain)
.with_alignment(horizontal::Center)
.arrange()
.align_to(
&FRAMEBUFFER
.lock()
.await
.borrow_mut()
.as_mut()
.unwrap()
.bounding_box(),
horizontal::Center,
vertical::Center,
)
.draw(*FRAMEBUFFER.lock().await.borrow_mut().as_mut().unwrap())
.unwrap();
}
}
pub struct SelectionList<const MAX_SELECTION: usize, const MAX_STR_LEN: usize> {
current_selection: u16,
selections: Vec<String<MAX_STR_LEN>, MAX_SELECTION>,
}
impl<const MAX_SELECTION: usize, const MAX_STR_LEN: usize>
SelectionList<MAX_SELECTION, MAX_STR_LEN>
{
pub fn new(selections: Vec<String<MAX_STR_LEN>, MAX_SELECTION>) -> Self {
Self {
selections,
current_selection: 0,
}
}
pub fn down(&mut self) {
if self.current_selection + 1 < self.selections.len() as u16 {
self.current_selection += 1
}
}
pub fn up(&mut self) {
if self.current_selection > self.selections.len() as u16 {
self.current_selection -= 1
}
}
}

View File

@@ -1,6 +1,9 @@
use core::sync::atomic::Ordering;
use crate::{scsi::MassStorageClass, storage::SdCard};
use crate::{
scsi::MassStorageClass,
storage::{SDCARD, SdCard},
};
use embassy_futures::{
join::join,
select::{select, select3},
@@ -13,7 +16,7 @@ use portable_atomic::AtomicBool;
static RESTART_USB: Signal<ThreadModeRawMutex, ()> = Signal::new();
static ENABLE_SCSI: AtomicBool = AtomicBool::new(false);
pub async fn usb_handler(driver: Driver<'static, USB>, sdcard: SdCard) {
pub async fn usb_handler(driver: Driver<'static, USB>) {
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("LegitCamper");
config.product = Some("PicoCalc");
@@ -34,10 +37,13 @@ pub async fn usb_handler(driver: Driver<'static, USB>, sdcard: SdCard) {
&mut control_buf,
);
let lock = SDCARD.get().lock().await;
let mut sdcard = lock.as_ref().unwrap();
if sdcard.is_attached() {
ENABLE_SCSI.store(true, Ordering::Relaxed);
}
let mut scsi = MassStorageClass::new(&mut builder, sdcard);
let mut scsi = MassStorageClass::new(&mut builder, &sdcard);
let mut usb = builder.build();
loop {