mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 07:45:28 +00:00
WIP
This commit is contained in:
@@ -94,3 +94,4 @@ bumpalo = "3.19.0"
|
||||
|
||||
shared = { path = "../shared" }
|
||||
abi_sys = { path = "../abi_sys" }
|
||||
micromath = "2.1.0"
|
||||
|
||||
@@ -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
52
kernel/src/audio.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user