This commit is contained in:
2025-09-30 20:27:07 -06:00
parent f73df7e0b8
commit d8a5ab465e
13 changed files with 285 additions and 7 deletions

View File

@@ -94,3 +94,4 @@ bumpalo = "3.19.0"
shared = { path = "../shared" }
abi_sys = { path = "../abi_sys" }
micromath = "2.1.0"

View File

@@ -1,16 +1,17 @@
use abi_sys::{
DrawIterAbi, FileLen, GenRand, GetKeyAbi, ListDir, LockDisplay, Modifiers, PrintAbi, ReadFile,
RngRequest, SleepAbi,
AUDIO_BUFFER_LEN, AudioBufferReady, DrawIterAbi, FileLen, GenRand, GetKeyAbi, ListDir,
LockDisplay, Modifiers, PrintAbi, ReadFile, RngRequest, SendAudioBuffer, SleepAbi,
};
use alloc::{string::ToString, vec::Vec};
use core::sync::atomic::Ordering;
use embassy_rp::clocks::{RoscRng, clk_sys_freq};
use embedded_graphics::{Pixel, draw_target::DrawTarget, pixelcolor::Rgb565};
use embedded_sdmmc::{DirEntry, LfnBuffer, ShortFileName};
use embedded_sdmmc::{DirEntry, LfnBuffer};
use heapless::spsc::Queue;
use shared::keyboard::KeyEvent;
use crate::{
audio::{AUDIO_BUFFER, AUDIO_BUFFER_READY},
display::{FB_PAUSED, FRAMEBUFFER},
storage::{Dir, File, SDCARD},
};
@@ -181,7 +182,9 @@ pub extern "C" fn read_file(
if !file.is_empty() {
sd.access_root_dir(|root| {
if let Ok(result) = recurse_file(&root, &file[1..], |file| {
file.seek_from_start(start_from as u32).unwrap();
if file.offset() as usize != start_from {
file.seek_from_start(start_from as u32).unwrap();
}
file.read(&mut buf).unwrap()
}) {
read = result
@@ -210,3 +213,21 @@ pub extern "C" fn file_len(str: *const u8, len: usize) -> usize {
}
len as usize
}
const _: AudioBufferReady = audio_buffer_ready;
pub extern "C" fn audio_buffer_ready() -> bool {
AUDIO_BUFFER_READY.load(Ordering::Acquire)
}
const _: SendAudioBuffer = send_audio_buffer;
pub extern "C" fn send_audio_buffer(ptr: *const u8, len: usize) {
// SAFETY: caller guarantees `ptr` is valid for `len` bytes
let buf = unsafe { core::slice::from_raw_parts(ptr, len) };
while !AUDIO_BUFFER_READY.load(Ordering::Acquire) {}
if buf.len() == AUDIO_BUFFER_LEN {
unsafe { AUDIO_BUFFER.copy_from_slice(buf) };
AUDIO_BUFFER_READY.store(false, Ordering::Release)
}
}

52
kernel/src/audio.rs Normal file
View File

@@ -0,0 +1,52 @@
use core::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use embassy_rp::{
Peri,
pio::Pio,
pio_programs::pwm::{PioPwm, PioPwmProgram},
pwm::{Config, Pwm, SetDutyCycle},
};
use embassy_time::Timer;
use crate::{Audio, Irqs};
const AUDIO_BUFFER_LEN: usize = 1024;
const _: () = assert!(AUDIO_BUFFER_LEN == abi_sys::AUDIO_BUFFER_LEN);
pub static mut AUDIO_BUFFER: [u8; AUDIO_BUFFER_LEN] = [0; AUDIO_BUFFER_LEN];
static mut AUDIO_BUFFER_1: [u8; AUDIO_BUFFER_LEN] = [0; AUDIO_BUFFER_LEN];
pub static AUDIO_BUFFER_READY: AtomicBool = AtomicBool::new(true);
pub const SAMPLE_RATE_HZ: u32 = 22_050;
#[embassy_executor::task]
pub async fn audio_handler(audio: Audio) {
let var_name = Pio::new(audio.pio_left, Irqs);
let Pio {
mut common, sm0, ..
} = var_name;
let prg = PioPwmProgram::new(&mut common);
let mut pwm_pio = PioPwm::new(&mut common, sm0, audio.left, &prg);
let period = Duration::from_nanos(1_000_000_000 / SAMPLE_RATE_HZ as u64);
pwm_pio.set_period(period);
pwm_pio.start();
let sample_interval = 1_000_000 / SAMPLE_RATE_HZ as u64; // in µs ≈ 45 µs
loop {
for &sample in unsafe { &AUDIO_BUFFER }.iter() {
let period_ns = period.as_nanos() as u32;
let duty_ns = period_ns * (sample as u32) / 255;
pwm_pio.write(Duration::from_nanos(duty_ns as u64));
Timer::after_micros(sample_interval).await; // sample interval = 1 / sample rate
}
unsafe { core::mem::swap(&mut AUDIO_BUFFER, &mut AUDIO_BUFFER_1) };
AUDIO_BUFFER_READY.store(true, Ordering::Release)
}
}

View File

@@ -204,6 +204,8 @@ fn patch_abi(
CallAbiTable::ListDir => abi::list_dir as usize,
CallAbiTable::ReadFile => abi::read_file as usize,
CallAbiTable::FileLen => abi::file_len as usize,
CallAbiTable::AudioBufferReady => abi::audio_buffer_ready as usize,
CallAbiTable::SendAudioBuffer => abi::send_audio_buffer as usize,
};
unsafe {
table_base.add(idx as usize).write(ptr);

View File

@@ -7,6 +7,7 @@
extern crate alloc;
mod abi;
mod audio;
mod display;
mod elf;
mod framebuffer;
@@ -19,6 +20,7 @@ mod utils;
use crate::{
abi::KEY_CACHE,
audio::audio_handler,
display::{FRAMEBUFFER, display_handler, init_display},
peripherals::{
conf_peripherals,
@@ -48,8 +50,10 @@ use embassy_rp::{
multicore::{Stack, spawn_core1},
peripherals::{
DMA_CH0, DMA_CH1, I2C1, 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_22, SPI0, SPI1, USB,
PIN_16, PIN_17, PIN_18, PIN_19, PIN_22, PIN_26, PIN_27, PIO0, PIO1, PWM_SLICE5, SPI0, SPI1,
USB,
},
pio,
spi::{self, Spi},
usb as embassy_rp_usb,
};
@@ -65,6 +69,7 @@ use talc::*;
embassy_rp::bind_interrupts!(struct Irqs {
I2C1_IRQ => i2c::InterruptHandler<I2C1>;
USBCTRL_IRQ => embassy_rp_usb::InterruptHandler<USB>;
PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
});
static mut CORE1_STACK: Stack<16384> = Stack::new();
@@ -105,6 +110,12 @@ async fn main(_spawner: Spawner) {
data: p.PIN_14,
reset: p.PIN_15,
};
let audio = Audio {
pio_left: p.PIO0,
pio_right: p.PIO1,
left: p.PIN_26,
right: p.PIN_27,
};
let sd = Sd {
spi: p.SPI0,
clk: p.PIN_18,
@@ -119,7 +130,9 @@ async fn main(_spawner: Spawner) {
data: p.PIN_6,
};
let executor0 = EXECUTOR0.init(Executor::new());
executor0.run(|spawner| unwrap!(spawner.spawn(kernel_task(spawner, display, sd, mcu, p.USB))));
executor0.run(|spawner| {
unwrap!(spawner.spawn(kernel_task(spawner, display, audio, sd, mcu, p.USB)))
});
}
// One-slot channel to pass EntryFn from core1
@@ -168,6 +181,12 @@ struct Display {
data: Peri<'static, PIN_14>,
reset: Peri<'static, PIN_15>,
}
struct Audio {
pio_left: Peri<'static, PIO0>,
pio_right: Peri<'static, PIO1>,
left: Peri<'static, PIN_26>,
right: Peri<'static, PIN_27>,
}
struct Sd {
spi: Peri<'static, SPI0>,
clk: Peri<'static, PIN_18>,
@@ -225,6 +244,7 @@ async fn setup_sd(sd: Sd) {
async fn kernel_task(
spawner: Spawner,
display: Display,
audio: Audio,
sd: Sd,
mcu: Mcu,
usb: Peri<'static, USB>,
@@ -234,6 +254,8 @@ async fn kernel_task(
setup_display(display, spawner).await;
setup_sd(sd).await;
spawner.spawn(audio_handler(audio)).unwrap();
let _usb = embassy_rp_usb::Driver::new(usb, Irqs);
// spawner.spawn(usb_handler(usb)).unwrap();