This commit is contained in:
2025-11-18 15:20:03 -07:00
parent 49c11978d5
commit cc31cb4711
20 changed files with 182 additions and 197 deletions

View File

@@ -11,7 +11,7 @@ doctest = false
bench = false bench = false
[features] [features]
default = ["rp235x", "defmt"] default = ["rp235x"]
pimoroni2w = ["rp235x", "psram"] pimoroni2w = ["rp235x", "psram"]
# rp2040 = ["embassy-rp/rp2040"] # unsupported, ram too small for fb # rp2040 = ["embassy-rp/rp2040"] # unsupported, ram too small for fb
rp235x = ["embassy-rp/rp235xb"] rp235x = ["embassy-rp/rp235xb"]

View File

@@ -14,7 +14,7 @@ use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(all(feature = "rp235x", not(feature = "pimoroni2w")))] #[cfg(all(feature = "rp235x", not(feature = "pimoroni2w")))]
const MEMORY: &'static [u8] = include_bytes!("rp2350.x"); const MEMORY: &[u8] = include_bytes!("rp2350.x");
#[cfg(feature = "pimoroni2w")] #[cfg(feature = "pimoroni2w")]
const MEMORY: &'static [u8] = include_bytes!("rp2350.x"); const MEMORY: &'static [u8] = include_bytes!("rp2350.x");

View File

@@ -120,8 +120,10 @@ impl<'d, PIO: Instance, const SM: usize> PioPwmAudio<'d, PIO, SM> {
let mut cfg = Config::default(); let mut cfg = Config::default();
cfg.set_set_pins(&[&pin]); cfg.set_set_pins(&[&pin]);
cfg.fifo_join = FifoJoin::TxOnly; cfg.fifo_join = FifoJoin::TxOnly;
let mut shift_cfg = ShiftConfig::default(); let shift_cfg = ShiftConfig {
shift_cfg.auto_fill = true; auto_fill: true,
..Default::default()
};
cfg.shift_out = shift_cfg; cfg.shift_out = shift_cfg;
cfg.use_program(&prg.0, &[]); cfg.use_program(&prg.0, &[]);
sm.set_config(&cfg); sm.set_config(&cfg);

View File

@@ -1,5 +1,4 @@
use crate::framebuffer::{self, AtomicFrameBuffer, FB_PAUSED}; use crate::framebuffer::{self, AtomicFrameBuffer, FB_PAUSED};
use core::alloc::{GlobalAlloc, Layout};
use core::sync::atomic::Ordering; use core::sync::atomic::Ordering;
use embassy_futures::yield_now; use embassy_futures::yield_now;
use embassy_rp::{ use embassy_rp::{
@@ -9,17 +8,20 @@ use embassy_rp::{
spi::{Async, Spi}, spi::{Async, Spi},
}; };
use embassy_time::Delay; use embassy_time::Delay;
use embedded_graphics::{draw_target::DrawTarget, pixelcolor::Rgb565, prelude::RgbColor};
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use st7365p_lcd::ST7365P; use st7365p_lcd::ST7365P;
#[cfg(feature = "psram")] #[cfg(feature = "psram")]
use crate::heap::HEAP; use crate::heap::HEAP;
#[cfg(feature = "psram")]
use core::alloc::{GlobalAlloc, Layout};
#[cfg(feature = "psram")]
use embedded_graphics::{draw_target::DrawTarget, pixelcolor::Rgb565, prelude::RgbColor};
#[cfg(feature = "fps")] #[cfg(feature = "fps")]
pub use framebuffer::fps::{FPS_CANVAS, FPS_COUNTER}; pub use framebuffer::fps::{FPS_CANVAS, FPS_COUNTER};
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>,
Output<'static>, Output<'static>,
@@ -56,7 +58,7 @@ pub async fn init_display(
cs: Peri<'static, PIN_13>, cs: Peri<'static, PIN_13>,
data: Peri<'static, PIN_14>, data: Peri<'static, PIN_14>,
reset: Peri<'static, PIN_15>, reset: Peri<'static, PIN_15>,
) -> DISPLAY { ) -> Display {
init_fb(); 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();
@@ -84,7 +86,7 @@ pub async fn init_display(
} }
#[embassy_executor::task] #[embassy_executor::task]
pub async fn display_handler(mut display: DISPLAY) { pub async fn display_handler(mut display: Display) {
use embassy_time::{Instant, Timer}; use embassy_time::{Instant, Timer};
// Target ~60 Hz refresh (≈16.67 ms per frame) // Target ~60 Hz refresh (≈16.67 ms per frame)
@@ -111,7 +113,7 @@ pub async fn display_handler(mut display: DISPLAY) {
} }
} }
let elapsed = start.elapsed().as_millis() as u64; let elapsed = start.elapsed().as_millis();
if elapsed < FRAME_TIME_MS { if elapsed < FRAME_TIME_MS {
Timer::after_millis(FRAME_TIME_MS - elapsed).await; Timer::after_millis(FRAME_TIME_MS - elapsed).await;
} else { } else {

View File

@@ -39,7 +39,7 @@ pub async unsafe fn load_binary(name: &ShortFileName) -> Option<(EntryFn, Bump)>
let mut ph_buf = vec![0_u8; elf_header.e_phentsize as usize]; let mut ph_buf = vec![0_u8; elf_header.e_phentsize as usize];
let (total_size, min_vaddr, _max_vaddr) = let (total_size, min_vaddr, _max_vaddr) =
total_loadable_size(&mut file, &elf_header, &mut ph_buf); total_loadable_size(&mut file, elf_header, &mut ph_buf);
let bump = Bump::with_capacity(total_size); let bump = Bump::with_capacity(total_size);
let base = bump.alloc_slice_fill_default::<u8>(total_size); let base = bump.alloc_slice_fill_default::<u8>(total_size);
@@ -52,25 +52,22 @@ pub async unsafe fn load_binary(name: &ShortFileName) -> Option<(EntryFn, Bump)>
let ph = cast_phdr(&ph_buf); let ph = cast_phdr(&ph_buf);
let seg_offset = (ph.p_vaddr - min_vaddr) as usize; let seg_offset = (ph.p_vaddr - min_vaddr) as usize;
let mut segment = &mut base[seg_offset..seg_offset + ph.p_memsz as usize]; let segment = &mut base[seg_offset..seg_offset + ph.p_memsz as usize];
if ph.p_type == PT_LOAD { if ph.p_type == PT_LOAD {
load_segment(&mut file, &ph, &mut segment).unwrap(); load_segment(&mut file, &ph, segment).unwrap();
} }
} }
for i in 0..elf_header.e_shnum { for i in 0..elf_header.e_shnum {
let sh = read_section(&mut file, elf_header, i.into()); let sh = read_section(&mut file, elf_header, i.into());
match sh.sh_type { if sh.sh_type == SHT_REL {
SHT_REL => { apply_relocations(&sh, min_vaddr, base.as_mut_ptr(), &mut file).unwrap();
apply_relocations(&sh, min_vaddr, base.as_mut_ptr(), &mut file).unwrap();
}
_ => {}
} }
} }
patch_syscalls(&elf_header, base.as_mut_ptr(), min_vaddr, &mut file).unwrap(); patch_syscalls(elf_header, base.as_mut_ptr(), min_vaddr, &mut file).unwrap();
// entry pointer is base_ptr + (entry - min_vaddr) // entry pointer is base_ptr + (entry - min_vaddr)
let entry_ptr: EntryFn = unsafe { let entry_ptr: EntryFn = unsafe {
@@ -157,7 +154,7 @@ fn patch_syscalls(
file: &mut File, file: &mut File,
) -> Result<(), ()> { ) -> Result<(), ()> {
for i in 1..=elf_header.e_shnum { for i in 1..=elf_header.e_shnum {
let sh = read_section(file, &elf_header, i.into()); let sh = read_section(file, elf_header, i.into());
// find the symbol table // find the symbol table
if sh.sh_type == SHT_SYMTAB { if sh.sh_type == SHT_SYMTAB {
@@ -172,7 +169,7 @@ fn patch_syscalls(
&symtab_buf[i * sh.sh_entsize as usize..(i + 1) * sh.sh_entsize as usize]; &symtab_buf[i * sh.sh_entsize as usize..(i + 1) * sh.sh_entsize as usize];
let sym = cast_sym(sym_bytes); let sym = cast_sym(sym_bytes);
let str_sh = read_section(file, &elf_header, sh.sh_link); let str_sh = read_section(file, elf_header, sh.sh_link);
let mut name = Vec::new(); let mut name = Vec::new();
file.seek_from_start(str_sh.sh_offset + sym.st_name) file.seek_from_start(str_sh.sh_offset + sym.st_name)
@@ -211,7 +208,7 @@ fn patch_syscalls(
SyscallTable::SendAudioBuffer => syscalls::send_audio_buffer as usize, SyscallTable::SendAudioBuffer => syscalls::send_audio_buffer as usize,
}; };
unsafe { unsafe {
table_base.add(idx as usize).write(ptr); table_base.add(idx).write(ptr);
} }
} }
return Ok(()); return Ok(());
@@ -233,7 +230,7 @@ fn total_loadable_size(
file.seek_from_start(elf_header.e_phoff + (elf_header.e_phentsize * i) as u32) file.seek_from_start(elf_header.e_phoff + (elf_header.e_phentsize * i) as u32)
.unwrap(); .unwrap();
file.read(ph_buf).unwrap(); file.read(ph_buf).unwrap();
let ph = cast_phdr(&ph_buf); let ph = cast_phdr(ph_buf);
if ph.p_type == PT_LOAD { if ph.p_type == PT_LOAD {
if ph.p_vaddr < min_vaddr { if ph.p_vaddr < min_vaddr {

View File

@@ -46,7 +46,7 @@ impl<'a> AtomicFrameBuffer<'a> {
} }
fn mark_tiles_dirty(&mut self, rect: Rectangle) { fn mark_tiles_dirty(&mut self, rect: Rectangle) {
let tiles_x = (SCREEN_WIDTH + TILE_SIZE - 1) / TILE_SIZE; let tiles_x = SCREEN_WIDTH.div_ceil(TILE_SIZE);
let start_tx = (rect.top_left.x as usize) / TILE_SIZE; let start_tx = (rect.top_left.x as usize) / TILE_SIZE;
let end_tx = ((rect.top_left.x + rect.size.width as i32 - 1) as usize) / TILE_SIZE; let end_tx = ((rect.top_left.x + rect.size.width as i32 - 1) as usize) / TILE_SIZE;
let start_ty = (rect.top_left.y as usize) / TILE_SIZE; let start_ty = (rect.top_left.y as usize) / TILE_SIZE;
@@ -346,11 +346,10 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
} }
} }
if changed { if changed
if let Some(rect) = dirty_rect { && let Some(rect) = dirty_rect {
self.mark_tiles_dirty(rect); self.mark_tiles_dirty(rect);
} }
}
Ok(()) Ok(())
} }
@@ -402,7 +401,7 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> { fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
self.fill_contiguous( self.fill_contiguous(
area, area,
core::iter::repeat(color).take((self.size().width * self.size().height) as usize), core::iter::repeat_n(color, (self.size().width * self.size().height) as usize),
) )
} }
@@ -412,8 +411,7 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
0, 0,
self.size().width as u16 - 1, self.size().width as u16 - 1,
self.size().height as u16 - 1, self.size().height as u16 - 1,
core::iter::repeat(RawU16::from(color).into_inner()) core::iter::repeat_n(RawU16::from(color).into_inner(), (self.size().width * self.size().height) as usize),
.take((self.size().width * self.size().height) as usize),
)?; )?;
for tile in self.dirty_tiles.iter() { for tile in self.dirty_tiles.iter() {

View File

@@ -12,11 +12,15 @@ mod audio;
mod display; mod display;
mod elf; mod elf;
mod framebuffer; mod framebuffer;
// TODO: NEED TO UPDATE MCU TO TEST BATTERY READS
#[allow(unused)]
mod peripherals; mod peripherals;
#[allow(unused)]
mod scsi; mod scsi;
mod storage; mod storage;
mod syscalls; mod syscalls;
mod ui; mod ui;
#[allow(unused)]
mod usb; mod usb;
mod utils; mod utils;
@@ -28,7 +32,7 @@ mod heap;
mod psram; mod psram;
#[cfg(feature = "psram")] #[cfg(feature = "psram")]
use crate::{heap::HEAP, heap::init_qmi_psram_heap, psram::init_psram, psram::init_psram_qmi}; use crate::{heap::init_qmi_psram_heap, psram::init_psram_qmi};
use crate::{ use crate::{
audio::audio_handler, audio::audio_handler,
@@ -102,7 +106,7 @@ async fn watchdog_task(mut watchdog: Watchdog) {
ResetReason::Forced => "forced", ResetReason::Forced => "forced",
ResetReason::TimedOut => "timed out", ResetReason::TimedOut => "timed out",
}; };
#[cfg(feature = "debug")] #[cfg(feature = "defmt")]
defmt::error!("Watchdog reset reason: {}", _reason); defmt::error!("Watchdog reset reason: {}", _reason);
} }
@@ -321,7 +325,7 @@ async fn setup_display(display: Display, spawner: Spawner) {
async fn setup_qmi_psram() { async fn setup_qmi_psram() {
Timer::after_millis(250).await; Timer::after_millis(250).await;
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 = "defmt")]
defmt::info!("size: {}", psram_qmi_size); defmt::info!("size: {}", psram_qmi_size);
Timer::after_millis(100).await; Timer::after_millis(100).await;
@@ -363,7 +367,7 @@ async fn kernel_task(
.spawn(watchdog_task(Watchdog::new(watchdog))) .spawn(watchdog_task(Watchdog::new(watchdog)))
.unwrap(); .unwrap();
#[cfg(feature = "debug")] #[cfg(feature = "defmt")]
defmt::info!("Clock: {}", embassy_rp::clocks::clk_sys_freq()); defmt::info!("Clock: {}", embassy_rp::clocks::clk_sys_freq());
setup_mcu(mcu).await; setup_mcu(mcu).await;

View File

@@ -526,7 +526,7 @@ pub fn init_psram_qmi(
let psram_size = detect_psram_qmi(qmi); let psram_size = detect_psram_qmi(qmi);
if psram_size == 0 { if psram_size == 0 {
#[cfg(feature = "debug")] #[cfg(feature = "defmt")]
defmt::error!("qmi psram size 0"); defmt::error!("qmi psram size 0");
return 0; return 0;
} }

View File

@@ -30,7 +30,7 @@ pub struct MassStorageClass<'d, D: Driver<'d>> {
bulk_in: D::EndpointIn, bulk_in: D::EndpointIn,
} }
impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> { impl<'d, D: Driver<'d>> MassStorageClass<'d, D> {
pub fn new(builder: &mut Builder<'d, D>, temp_sd: Option<SdCard>) -> Self { pub fn new(builder: &mut Builder<'d, D>, temp_sd: Option<SdCard>) -> Self {
let mut function = builder.function(0x08, SUBCLASS_SCSI, 0x50); // Mass Storage class let mut function = builder.function(0x08, SUBCLASS_SCSI, 0x50); // Mass Storage class
let mut interface = function.interface(); let mut interface = function.interface();
@@ -72,35 +72,34 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
async fn handle_cbw(&mut self) { async fn handle_cbw(&mut self) {
let mut cbw_buf = [0u8; 31]; let mut cbw_buf = [0u8; 31];
if let Ok(n) = self.bulk_out.read(&mut cbw_buf).await { if let Ok(n) = self.bulk_out.read(&mut cbw_buf).await
if n == 31 { && n == 31
if let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n]) { && let Some(cbw) = CommandBlockWrapper::parse(&cbw_buf[..n])
// Take sdcard to increase speed {
if self.temp_sd.is_none() { // Take sdcard to increase speed
let mut guard = SDCARD.get().lock().await; if self.temp_sd.is_none() {
if let Some(sd) = guard.take() { let mut guard = SDCARD.get().lock().await;
self.temp_sd = Some(sd); if let Some(sd) = guard.take() {
} else { self.temp_sd = Some(sd);
#[cfg(feature = "defmt")] } else {
defmt::warn!("Tried to take SDCARD but it was already taken"); #[cfg(feature = "defmt")]
return; defmt::warn!("Tried to take SDCARD but it was already taken");
} return;
}
let command = parse_cb(&cbw.CBWCB);
if self.handle_command(command).await.is_ok() {
self.send_csw_success(cbw.dCBWTag).await
} else {
self.send_csw_fail(cbw.dCBWTag).await
}
if self.pending_eject {
if let ScsiCommand::Write { lba: _, len: _ } = command {
MSC_SHUTDOWN.signal(());
}
}
} }
} }
let command = parse_cb(&cbw.CBWCB);
if self.handle_command(command).await.is_ok() {
self.send_csw_success(cbw.dCBWTag).await
} else {
self.send_csw_fail(cbw.dCBWTag).await
}
if self.pending_eject
&& let ScsiCommand::Write { lba: _, len: _ } = command
{
MSC_SHUTDOWN.signal(());
}
} }
} }
@@ -238,7 +237,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
let block_size = SdCard::BLOCK_SIZE as u64; let block_size = SdCard::BLOCK_SIZE as u64;
let total_blocks = self.temp_sd.as_ref().unwrap().size() / block_size; let total_blocks = self.temp_sd.as_ref().unwrap().size() / block_size;
let last_lba = total_blocks.checked_sub(1).unwrap_or(0); let last_lba = total_blocks.saturating_sub(1);
response.extend_from_slice(&(last_lba as u32).to_be_bytes())?; response.extend_from_slice(&(last_lba as u32).to_be_bytes())?;
response.extend_from_slice(&(block_size as u32).to_be_bytes())?; response.extend_from_slice(&(block_size as u32).to_be_bytes())?;
@@ -249,7 +248,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
let block_size = SdCard::BLOCK_SIZE as u64; let block_size = SdCard::BLOCK_SIZE as u64;
let total_blocks = self.temp_sd.as_ref().unwrap().size() / block_size; let total_blocks = self.temp_sd.as_ref().unwrap().size() / block_size;
let last_lba = total_blocks.checked_sub(1).unwrap_or(0); let last_lba = total_blocks.saturating_sub(1);
response.extend_from_slice(&last_lba.to_be_bytes())?; // 8 bytes last LBA response.extend_from_slice(&last_lba.to_be_bytes())?; // 8 bytes last LBA
response.extend_from_slice(&(block_size as u32).to_be_bytes())?; // 4 bytes block length response.extend_from_slice(&(block_size as u32).to_be_bytes())?; // 4 bytes block length
@@ -269,7 +268,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
sdcard.read_blocks(block_buf, BlockIdx(idx as u32))?; sdcard.read_blocks(block_buf, BlockIdx(idx as u32))?;
for block in &mut *block_buf { for block in &mut *block_buf {
for chunk in block.contents.chunks(BULK_ENDPOINT_PACKET_SIZE.into()) { for chunk in block.contents.chunks(BULK_ENDPOINT_PACKET_SIZE) {
self.bulk_in.write(chunk).await.map_err(|_| ())?; self.bulk_in.write(chunk).await.map_err(|_| ())?;
} }
} }
@@ -281,7 +280,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
.read_blocks(&mut block_buf[..blocks as usize], BlockIdx(idx as u32))?; .read_blocks(&mut block_buf[..blocks as usize], BlockIdx(idx as u32))?;
for block in &block_buf[..blocks as usize] { for block in &block_buf[..blocks as usize] {
for chunk in block.contents.chunks(BULK_ENDPOINT_PACKET_SIZE.into()) { for chunk in block.contents.chunks(BULK_ENDPOINT_PACKET_SIZE) {
self.bulk_in.write(chunk).await.map_err(|_| ())?; self.bulk_in.write(chunk).await.map_err(|_| ())?;
} }
} }
@@ -301,8 +300,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
while blocks > 0 { while blocks > 0 {
if blocks >= block_buf.len() as u64 { if blocks >= block_buf.len() as u64 {
for block in block_buf.as_mut() { for block in block_buf.as_mut() {
for chunk in block.contents.chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into()) for chunk in block.contents.chunks_mut(BULK_ENDPOINT_PACKET_SIZE) {
{
self.bulk_out.read(chunk).await.map_err(|_| ())?; self.bulk_out.read(chunk).await.map_err(|_| ())?;
} }
} }
@@ -313,8 +311,7 @@ impl<'d, 's, D: Driver<'d>> MassStorageClass<'d, D> {
idx += block_buf.len() as u64; idx += block_buf.len() as u64;
} else { } else {
for block in block_buf[..blocks as usize].as_mut() { for block in block_buf[..blocks as usize].as_mut() {
for chunk in block.contents.chunks_mut(BULK_ENDPOINT_PACKET_SIZE.into()) for chunk in block.contents.chunks_mut(BULK_ENDPOINT_PACKET_SIZE) {
{
self.bulk_out.read(chunk).await.map_err(|_| ())?; self.bulk_out.read(chunk).await.map_err(|_| ())?;
} }
} }

View File

@@ -1,7 +1,7 @@
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
/// THE CODE BELOW ORIGINATES FROM: https://github.com/apohrebniak/usbd-storage/blob/master/usbd-storage/src/subclass/scsi.rs /// THE CODE BELOW ORIGINATES FROM: https://github.com/apohrebniak/usbd-storage/blob/master/usbd-storage/src/subclass/scsi.rs
///
/// SCSI device subclass code /// SCSI device subclass code
pub const SUBCLASS_SCSI: u8 = 0x06; // SCSI Transparent command set pub const SUBCLASS_SCSI: u8 = 0x06; // SCSI Transparent command set
@@ -91,6 +91,7 @@ pub enum ScsiCommand {
}, },
} }
#[allow(clippy::enum_variant_names)]
#[repr(u8)] #[repr(u8)]
#[derive(Copy, Clone, Debug, TryFromPrimitive)] #[derive(Copy, Clone, Debug, TryFromPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]

View File

@@ -9,8 +9,8 @@ use embassy_sync::mutex::Mutex;
use embassy_time::Delay; use embassy_time::Delay;
use embedded_hal_bus::spi::ExclusiveDevice; use embedded_hal_bus::spi::ExclusiveDevice;
use embedded_sdmmc::{ use embedded_sdmmc::{
Block, BlockCount, BlockDevice, BlockIdx, Directory, SdCard as SdmmcSdCard, TimeSource, Block, BlockDevice, BlockIdx, Directory, SdCard as SdmmcSdCard, TimeSource, Timestamp,
Timestamp, Volume, VolumeIdx, VolumeManager, sdcard::Error, VolumeIdx, VolumeManager, sdcard::Error,
}; };
use embedded_sdmmc::{File as SdFile, LfnBuffer, Mode, ShortFileName}; use embedded_sdmmc::{File as SdFile, LfnBuffer, Mode, ShortFileName};
@@ -21,7 +21,6 @@ pub const MAX_VOLUMES: usize = 1;
type Device = ExclusiveDevice<Spi<'static, SPI0, Blocking>, Output<'static>, embassy_time::Delay>; type Device = ExclusiveDevice<Spi<'static, SPI0, Blocking>, Output<'static>, embassy_time::Delay>;
type SD = SdmmcSdCard<Device, Delay>; type SD = SdmmcSdCard<Device, Delay>;
type VolMgr = VolumeManager<SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; type VolMgr = VolumeManager<SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
type Vol<'a> = Volume<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
pub type Dir<'a> = Directory<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; pub type Dir<'a> = Directory<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
pub type File<'a> = SdFile<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; pub type File<'a> = SdFile<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>;
@@ -43,7 +42,7 @@ pub struct FileName {
impl PartialOrd for FileName { impl PartialOrd for FileName {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.long_name.cmp(&other.long_name)) Some(self.cmp(other))
} }
} }
@@ -67,10 +66,7 @@ impl SdCard {
DummyTimeSource {}, DummyTimeSource {},
5000, 5000,
); );
Self { Self { det, volume_mgr }
det: det,
volume_mgr,
}
} }
/// Returns true if an SD card is inserted. /// Returns true if an SD card is inserted.
@@ -90,17 +86,6 @@ impl SdCard {
result result
} }
pub fn num_blocks(&self) -> u32 {
let mut result = 0;
self.volume_mgr.device(|sd| {
result = sd.num_blocks().unwrap_or(BlockCount(0)).0;
DummyTimeSource {}
});
result
}
pub fn read_blocks(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), ()> { pub fn read_blocks(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), ()> {
let mut res: Result<(), Error> = Ok(()); let mut res: Result<(), Error> = Ok(());
self.volume_mgr.device(|sd| { self.volume_mgr.device(|sd| {

View File

@@ -35,7 +35,7 @@ pub extern "C" fn alloc(layout: CLayout) -> *mut u8 {
#[cfg(not(feature = "psram"))] #[cfg(not(feature = "psram"))]
{ {
return alloc::alloc::alloc(layout.into()); alloc::alloc::alloc(layout.into())
} }
} }
} }
@@ -134,7 +134,7 @@ unsafe fn copy_entry_to_user_buf(name: &[u8], dest: *mut c_char, max_str_len: us
if !dest.is_null() { if !dest.is_null() {
let len = name.len().min(max_str_len - 1); let len = name.len().min(max_str_len - 1);
unsafe { unsafe {
ptr::copy_nonoverlapping(name.as_ptr(), dest as *mut u8, len); ptr::copy_nonoverlapping(name.as_ptr(), dest, len);
*dest.add(len) = 0; // nul terminator *dest.add(len) = 0; // nul terminator
} }
} }
@@ -191,7 +191,7 @@ pub extern "C" fn list_dir(
let mut wrote = 0; let mut wrote = 0;
sd.access_root_dir(|root| { sd.access_root_dir(|root| {
if dirs[0] == "" && dirs.len() >= 2 { if dirs[0].is_empty() && dirs.len() >= 2 {
unsafe { unsafe {
if dir == "/" { if dir == "/" {
wrote = get_dir_entries(&root, files, max_entry_str_len); wrote = get_dir_entries(&root, files, max_entry_str_len);
@@ -214,10 +214,10 @@ fn recurse_file<T>(
let mut buf = LfnBuffer::new(&mut b); let mut buf = LfnBuffer::new(&mut b);
let mut short_name = None; let mut short_name = None;
dir.iterate_dir_lfn(&mut buf, |entry, name| { dir.iterate_dir_lfn(&mut buf, |entry, name| {
if let Some(name) = name { if let Some(name) = name
if name == dirs[0] || entry.name.to_string().as_str() == dirs[0] { && (name == dirs[0] || entry.name.to_string().as_str() == dirs[0])
short_name = Some(entry.name.clone()); {
} short_name = Some(entry.name.clone());
} }
}) })
.expect("Failed to iterate dir"); .expect("Failed to iterate dir");
@@ -257,7 +257,7 @@ pub extern "C" fn read_file(
} }
// SAFETY: caller guarantees `ptr` is valid for `len` bytes // SAFETY: caller guarantees `ptr` is valid for `len` bytes
let mut buf = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) }; let buf = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
let mut read = 0; let mut read = 0;
@@ -267,7 +267,7 @@ pub extern "C" fn read_file(
sd.access_root_dir(|root| { sd.access_root_dir(|root| {
if let Ok(result) = recurse_file(&root, &components[1..count], |file| { if let Ok(result) = recurse_file(&root, &components[1..count], |file| {
file.seek_from_start(start_from as u32).unwrap_or(()); file.seek_from_start(start_from as u32).unwrap_or(());
file.read(&mut buf).unwrap() file.read(buf).unwrap()
}) { }) {
read = result read = result
}; };
@@ -306,7 +306,7 @@ pub extern "C" fn write_file(
sd.access_root_dir(|root| { sd.access_root_dir(|root| {
recurse_file(&root, &components[1..count], |file| { recurse_file(&root, &components[1..count], |file| {
file.seek_from_start(start_from as u32).unwrap(); file.seek_from_start(start_from as u32).unwrap();
file.write(&buf).unwrap() file.write(buf).unwrap()
}) })
.unwrap_or(()) .unwrap_or(())
}); });
@@ -343,7 +343,9 @@ pub extern "C" fn send_audio_buffer(ptr: *const u8, len: usize) {
// SAFETY: caller guarantees `ptr` is valid for `len` bytes // SAFETY: caller guarantees `ptr` is valid for `len` bytes
let buf = unsafe { core::slice::from_raw_parts(ptr, len) }; let buf = unsafe { core::slice::from_raw_parts(ptr, len) };
while !AUDIO_BUFFER_READY.load(Ordering::Acquire) {} while !AUDIO_BUFFER_READY.load(Ordering::Acquire) {
core::hint::spin_loop();
}
if buf.len() == AUDIO_BUFFER_SAMPLES * 2 { if buf.len() == AUDIO_BUFFER_SAMPLES * 2 {
AUDIO_BUFFER_READY.store(false, Ordering::Release); AUDIO_BUFFER_READY.store(false, Ordering::Release);

View File

@@ -2,7 +2,6 @@ use crate::{
BINARY_CH, display::FRAMEBUFFER, elf::load_binary, framebuffer::FB_PAUSED, BINARY_CH, display::FRAMEBUFFER, elf::load_binary, framebuffer::FB_PAUSED,
peripherals::keyboard, storage::FileName, peripherals::keyboard, storage::FileName,
}; };
use userlib_sys::keyboard::{KeyCode, KeyState};
use alloc::{str::FromStr, string::String, vec::Vec}; use alloc::{str::FromStr, string::String, vec::Vec};
use core::sync::atomic::Ordering; use core::sync::atomic::Ordering;
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex}; use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
@@ -20,37 +19,38 @@ use embedded_layout::{
prelude::*, prelude::*,
}; };
use embedded_text::TextBox; use embedded_text::TextBox;
use userlib_sys::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 ui_handler() { pub async fn ui_handler() {
loop { loop {
if let Some(event) = keyboard::read_keyboard_fifo().await { if let Some(event) = keyboard::read_keyboard_fifo().await
if let KeyState::Pressed = event.state { && let KeyState::Pressed = event.state
match event.key { {
KeyCode::Up => { match event.key {
let mut selections = SELECTIONS.lock().await; KeyCode::Up => {
selections.up(); let mut selections = SELECTIONS.lock().await;
} selections.up();
KeyCode::Down => {
let mut selections = SELECTIONS.lock().await;
selections.down();
}
KeyCode::Enter | KeyCode::Right => {
let selections = SELECTIONS.lock().await;
let selection =
selections.selections[selections.current_selection as usize].clone();
let entry = unsafe {
load_binary(&selection.short_name)
.await
.expect("unable to load binary")
};
BINARY_CH.send(entry).await;
}
_ => (),
} }
KeyCode::Down => {
let mut selections = SELECTIONS.lock().await;
selections.down();
}
KeyCode::Enter | KeyCode::Right => {
let selections = SELECTIONS.lock().await;
let selection =
selections.selections[selections.current_selection as usize].clone();
let entry = unsafe {
load_binary(&selection.short_name)
.await
.expect("unable to load binary")
};
BINARY_CH.send(entry).await;
}
_ => (),
} }
} }

View File

@@ -51,15 +51,14 @@ impl<'a> SelectionUi<'a> {
let selection; let selection;
loop { loop {
let key = get_key(); let key = get_key();
if key.state == KeyState::Pressed { if key.state == KeyState::Pressed
if let Some(s) = self.update(display, key.key)? { && let Some(s) = self.update(display, key.key)? {
selection = Some(s); selection = Some(s);
display display
.clear(Rgb565::BLACK) .clear(Rgb565::BLACK)
.map_err(|e| SelectionUiError::DisplayError(e))?; .map_err(SelectionUiError::DisplayError)?;
break; break;
} }
}
} }
Ok(selection) Ok(selection)
} }
@@ -100,7 +99,7 @@ impl<'a> SelectionUi<'a> {
Rectangle::new(bounds.top_left, bounds.size) Rectangle::new(bounds.top_left, bounds.size)
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK)) .into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
.draw(display) .draw(display)
.map_err(|e| SelectionUiError::DisplayError(e))?; .map_err(SelectionUiError::DisplayError)?;
} }
let mut views: Vec<Text<MonoTextStyle<Rgb565>>> = Vec::new(); let mut views: Vec<Text<MonoTextStyle<Rgb565>>> = Vec::new();
@@ -119,7 +118,7 @@ impl<'a> SelectionUi<'a> {
layout layout
.draw(display) .draw(display)
.map_err(|e| SelectionUiError::DisplayError(e))?; .map_err(SelectionUiError::DisplayError)?;
// draw selected box // draw selected box
if let Some(selected_bounds) = layout.inner().get(self.selection) { if let Some(selected_bounds) = layout.inner().get(self.selection) {
@@ -127,7 +126,7 @@ impl<'a> SelectionUi<'a> {
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(display) .draw(display)
.map_err(|e| SelectionUiError::DisplayError(e))?; .map_err(SelectionUiError::DisplayError)?;
self.last_bounds = Some(selected_bounds); self.last_bounds = Some(selected_bounds);
} }
@@ -136,9 +135,9 @@ impl<'a> SelectionUi<'a> {
} }
} }
pub fn draw_text_center<'a, S>( pub fn draw_text_center<S>(
display: &mut Display, display: &mut Display,
text: &'a str, text: &str,
style: S, style: S,
) -> Result<Point, <Display as DrawTarget>::Error> ) -> Result<Point, <Display as DrawTarget>::Error>
where where

View File

@@ -83,7 +83,7 @@ pub fn main() {
.align_to(&display.bounding_box(), horizontal::Left, vertical::Center); .align_to(&display.bounding_box(), horizontal::Left, vertical::Center);
let result = if let Ok(result) = evaluate(&input[input_min..]) { let result = if let Ok(result) = evaluate(&input[input_min..]) {
&format!(" = {}", result) &format!(" = {result}")
} else { } else {
" = Error" " = Error"
}; };

View File

@@ -51,9 +51,9 @@ pub fn main() {
println!("file: {}", file); println!("file: {}", file);
if file.extension().unwrap_or("") == "bmp" || file.extension().unwrap_or("") == "BMP" { if file.extension().unwrap_or("") == "bmp" || file.extension().unwrap_or("") == "BMP" {
let file_path = format!("/images/{}", file); let file_path = format!("/images/{file}");
let read = read_file(&file_path, 0, &mut &mut bmp_buf[..]); let read = read_file(&file_path, 0, &mut bmp_buf[..]);
if read > 0 { if read > 0 {
let bmp = Bmp::from_slice(&bmp_buf).expect("failed to parse bmp"); let bmp = Bmp::from_slice(&bmp_buf).expect("failed to parse bmp");
@@ -74,7 +74,7 @@ pub fn main() {
let text_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE); let text_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
let text_y = y + bmp_h + 2; // 2px gap under image let text_y = y + bmp_h + 2; // 2px gap under image
Text::new(&file.base(), Point::new(cell_x + 2, text_y), text_style) Text::new(file.base(), Point::new(cell_x + 2, text_y), text_style)
.draw(&mut display) .draw(&mut display)
.unwrap(); .unwrap();
@@ -85,11 +85,8 @@ pub fn main() {
loop { loop {
let event = get_key(); let event = get_key();
if event.state != KeyState::Idle { if event.state != KeyState::Idle && event.key == KeyCode::Esc {
match event.key { return;
KeyCode::Esc => return, }
_ => (),
}
};
} }
} }

View File

@@ -6,16 +6,16 @@ use alloc::{format, vec, vec::Vec};
use core::panic::PanicInfo; use core::panic::PanicInfo;
use embedded_graphics::{ use embedded_graphics::{
image::ImageDrawable, image::ImageDrawable,
mono_font::{ascii::FONT_6X10, MonoTextStyle}, mono_font::{MonoTextStyle, ascii::FONT_6X10},
pixelcolor::Rgb565, pixelcolor::Rgb565,
prelude::{Point, RgbColor}, prelude::{Point, RgbColor},
transform::Transform, transform::Transform,
}; };
use selection_ui::{draw_text_center, SelectionUi, SelectionUiError}; use selection_ui::{SelectionUi, SelectionUiError, draw_text_center};
use tinygif::Gif; use tinygif::Gif;
use userlib::{ use userlib::{
display::{Display, SCREEN_HEIGHT, SCREEN_WIDTH}, display::{Display, SCREEN_HEIGHT, SCREEN_WIDTH},
fs::{file_len, list_dir, read_file, Entries}, fs::{Entries, file_len, list_dir, read_file},
get_key, get_ms, get_key, get_ms,
keyboard::{KeyCode, KeyState}, keyboard::{KeyCode, KeyState},
println, sleep, println, sleep,
@@ -44,7 +44,7 @@ pub fn main() {
let mut gifs = files.iter().map(|e| e.full_name()).collect::<Vec<&str>>(); let mut gifs = files.iter().map(|e| e.full_name()).collect::<Vec<&str>>();
gifs.sort(); gifs.sort();
let mut selection_ui = SelectionUi::new(&mut gifs); let mut selection_ui = SelectionUi::new(&gifs);
let selection = match selection_ui.run_selection_ui(&mut display) { let selection = match selection_ui.run_selection_ui(&mut display) {
Ok(maybe_sel) => maybe_sel, Ok(maybe_sel) => maybe_sel,
Err(e) => match e { Err(e) => match e {
@@ -87,14 +87,9 @@ pub fn main() {
if frame_num % 5 == 0 { if frame_num % 5 == 0 {
let event = get_key(); let event = get_key();
if event.state != KeyState::Idle { if event.state != KeyState::Idle && event.key == KeyCode::Esc {
match event.key { drop(buf);
KeyCode::Esc => { return;
drop(buf);
return;
}
_ => (),
};
}; };
} }
sleep(((frame.delay_centis as u64) * 10).saturating_sub(start)); sleep(((frame.delay_centis as u64) * 10).saturating_sub(start));

View File

@@ -4,18 +4,18 @@
extern crate alloc; extern crate alloc;
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use core::panic::PanicInfo; use core::panic::PanicInfo;
use embedded_audio::{wav::Wav, AudioFile, PlatformFile, PlatformFileError}; use embedded_audio::{AudioFile, PlatformFile, PlatformFileError, wav::Wav};
use embedded_graphics::{ use embedded_graphics::{
mono_font::{ascii::FONT_6X10, MonoTextStyle}, mono_font::{MonoTextStyle, ascii::FONT_6X10},
pixelcolor::Rgb565, pixelcolor::Rgb565,
prelude::RgbColor, prelude::RgbColor,
}; };
use selection_ui::{draw_text_center, SelectionUi, SelectionUiError}; use selection_ui::{SelectionUi, SelectionUiError, draw_text_center};
use userlib::{ use userlib::{
audio::{audio_buffer_ready, send_audio_buffer, AUDIO_BUFFER_LEN}, audio::{AUDIO_BUFFER_LEN, audio_buffer_ready, send_audio_buffer},
display::Display, display::Display,
format, format,
fs::{file_len, list_dir, read_file, Entries}, fs::{Entries, file_len, list_dir, read_file},
get_key, get_key,
keyboard::{KeyCode, KeyState}, keyboard::{KeyCode, KeyState},
println, println,
@@ -45,7 +45,7 @@ pub fn main() {
let mut wavs = files.iter().map(|e| e.full_name()).collect::<Vec<&str>>(); let mut wavs = files.iter().map(|e| e.full_name()).collect::<Vec<&str>>();
wavs.sort(); wavs.sort();
let mut selection_ui = SelectionUi::new(&mut wavs); let mut selection_ui = SelectionUi::new(&wavs);
let selection = match selection_ui.run_selection_ui(&mut display) { let selection = match selection_ui.run_selection_ui(&mut display) {
Ok(maybe_sel) => maybe_sel, Ok(maybe_sel) => maybe_sel,
Err(e) => match e { Err(e) => match e {
@@ -72,7 +72,7 @@ pub fn main() {
.expect("Display Error"); .expect("Display Error");
let file_name = format!("/music/{}", wavs[selection.unwrap()]); let file_name = format!("/music/{}", wavs[selection.unwrap()]);
let file = File::new(String::from(file_name)); let file = File::new(file_name);
let mut wav = Wav::new(file).unwrap(); let mut wav = Wav::new(file).unwrap();
println!("sample rate: {}", wav.sample_rate()); println!("sample rate: {}", wav.sample_rate());
println!("channels: {:?}", wav.channels() as u8); println!("channels: {:?}", wav.channels() as u8);
@@ -90,11 +90,8 @@ pub fn main() {
} }
let event = get_key(); let event = get_key();
if event.state == KeyState::Released { if event.state == KeyState::Released && event.key == KeyCode::Esc {
match event.key { return;
KeyCode::Esc => return,
_ => (),
}
} }
} }
} }

View File

@@ -228,6 +228,12 @@ pub mod fs {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Entries([[u8; MAX_ENTRY_NAME_LEN]; MAX_ENTRIES]); pub struct Entries([[u8; MAX_ENTRY_NAME_LEN]; MAX_ENTRIES]);
impl Default for Entries {
fn default() -> Self {
Self::new()
}
}
impl Entries { impl Entries {
pub fn new() -> Self { pub fn new() -> Self {
Self([[0; MAX_ENTRY_NAME_LEN]; MAX_ENTRIES]) Self([[0; MAX_ENTRY_NAME_LEN]; MAX_ENTRIES])

View File

@@ -46,9 +46,9 @@ pub struct CLayout {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl Into<Layout> for CLayout { impl From<CLayout> for Layout {
fn into(self) -> Layout { fn from(val: CLayout) -> Self {
unsafe { Layout::from_size_align_unchecked(self.size, self.alignment) } unsafe { Layout::from_size_align_unchecked(val.size, val.alignment) }
} }
} }
@@ -106,7 +106,7 @@ pub extern "C" fn get_ms() -> u64 {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Default, Copy, Clone)]
pub struct CPixel { pub struct CPixel {
pub x: i32, pub x: i32,
pub y: i32, pub y: i32,
@@ -123,19 +123,22 @@ impl CPixel {
} }
} }
impl Into<CPixel> for Pixel<Rgb565> { impl From<CPixel> for Pixel<Rgb565> {
fn into(self) -> CPixel { fn from(value: CPixel) -> Self {
CPixel { Pixel(
x: self.0.x, Point::new(value.x, value.y),
y: self.0.y, RawU16::new(value.color).into(),
color: self.1.into_storage(), )
}
} }
} }
impl Into<Pixel<Rgb565>> for CPixel { impl From<Pixel<Rgb565>> for CPixel {
fn into(self) -> Pixel<Rgb565> { fn from(value: Pixel<Rgb565>) -> Self {
Pixel(Point::new(self.x, self.y), RawU16::new(self.color).into()) CPixel {
x: value.0.x,
y: value.0.y,
color: value.1.into_storage(),
}
} }
} }
@@ -171,12 +174,12 @@ pub mod keyboard {
pub mods: Modifiers, pub mods: Modifiers,
} }
impl Into<KeyEvent> for KeyEventC { impl From<KeyEventC> for KeyEvent {
fn into(self) -> KeyEvent { fn from(val: KeyEventC) -> Self {
KeyEvent { KeyEvent {
key: self.key.into(), key: val.key.into(),
state: self.state, state: val.state,
mods: self.mods, mods: val.mods,
} }
} }
} }
@@ -188,12 +191,12 @@ pub mod keyboard {
pub mods: Modifiers, pub mods: Modifiers,
} }
impl Into<KeyEventC> for KeyEvent { impl From<KeyEvent> for KeyEventC {
fn into(self) -> KeyEventC { fn from(val: KeyEvent) -> Self {
KeyEventC { KeyEventC {
key: self.key.into(), key: val.key.into(),
state: self.state, state: val.state,
mods: self.mods, mods: val.mods,
} }
} }
} }
@@ -267,9 +270,9 @@ pub mod keyboard {
Unknown(u8), Unknown(u8),
} }
impl Into<u8> for KeyCode { impl From<KeyCode> for u8 {
fn into(self) -> u8 { fn from(val: KeyCode) -> Self {
match self { match val {
KeyCode::JoyUp => 0x01, KeyCode::JoyUp => 0x01,
KeyCode::JoyDown => 0x02, KeyCode::JoyDown => 0x02,
KeyCode::JoyLeft => 0x03, KeyCode::JoyLeft => 0x03,