mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 07:45:28 +00:00
WIP pio pwm silence
This commit is contained in:
2
justfile
2
justfile
@@ -1,7 +1,7 @@
|
|||||||
target := "thumbv8m.main-none-eabihf"
|
target := "thumbv8m.main-none-eabihf"
|
||||||
|
|
||||||
kernel-dev board:
|
kernel-dev board:
|
||||||
cargo run --bin kernel --features {{board}} --features fps
|
cargo run --bin kernel --features {{board}} --features fps --features defmt
|
||||||
kernel-release-probe board:
|
kernel-release-probe board:
|
||||||
cargo run --bin kernel --profile release --features {{board}} --features fps
|
cargo run --bin kernel --profile release --features {{board}} --features fps
|
||||||
kernel-release board:
|
kernel-release board:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::Audio;
|
use crate::Audio;
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use embassy_futures::join::join;
|
use embassy_futures::{join::join, yield_now};
|
||||||
use embassy_rp::{
|
use embassy_rp::{
|
||||||
Peri,
|
Peri,
|
||||||
clocks::clk_sys_freq,
|
clocks::clk_sys_freq,
|
||||||
@@ -17,16 +17,16 @@ pub const SAMPLE_RATE_HZ: u32 = 22_050;
|
|||||||
const AUDIO_BUFFER_SAMPLES: usize = 1024;
|
const AUDIO_BUFFER_SAMPLES: usize = 1024;
|
||||||
const _: () = assert!(AUDIO_BUFFER_SAMPLES == userlib_sys::AUDIO_BUFFER_SAMPLES);
|
const _: () = assert!(AUDIO_BUFFER_SAMPLES == userlib_sys::AUDIO_BUFFER_SAMPLES);
|
||||||
|
|
||||||
|
const SILENCE: u8 = u8::MAX / 2;
|
||||||
|
|
||||||
// 8bit stereo interleaved PCM audio buffers
|
// 8bit stereo interleaved PCM audio buffers
|
||||||
pub static mut AUDIO_BUFFER: [u8; AUDIO_BUFFER_SAMPLES * 2] = [0; AUDIO_BUFFER_SAMPLES * 2];
|
pub static mut AUDIO_BUFFER: [u8; AUDIO_BUFFER_SAMPLES * 2] = [SILENCE; AUDIO_BUFFER_SAMPLES * 2];
|
||||||
static mut AUDIO_BUFFER_1: [u8; AUDIO_BUFFER_SAMPLES * 2] = [0; AUDIO_BUFFER_SAMPLES * 2];
|
static mut AUDIO_BUFFER_1: [u8; AUDIO_BUFFER_SAMPLES * 2] = [SILENCE; AUDIO_BUFFER_SAMPLES * 2];
|
||||||
|
|
||||||
pub static AUDIO_BUFFER_READY: AtomicBool = AtomicBool::new(true);
|
pub static AUDIO_BUFFER_READY: AtomicBool = AtomicBool::new(true);
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
pub async fn audio_handler(mut audio: Audio) {
|
pub async fn audio_handler(mut audio: Audio) {
|
||||||
const SILENCE_VALUE: u8 = u8::MAX / 2;
|
|
||||||
|
|
||||||
let prg = PioPwmAudioProgram8Bit::new(&mut audio.pio);
|
let prg = PioPwmAudioProgram8Bit::new(&mut audio.pio);
|
||||||
let mut pwm_pio_left =
|
let mut pwm_pio_left =
|
||||||
PioPwmAudio::new(audio.dma0, &mut audio.pio, audio.sm0, audio.left, &prg);
|
PioPwmAudio::new(audio.dma0, &mut audio.pio, audio.sm0, audio.left, &prg);
|
||||||
@@ -34,14 +34,16 @@ pub async fn audio_handler(mut audio: Audio) {
|
|||||||
PioPwmAudio::new(audio.dma1, &mut audio.pio, audio.sm1, audio.right, &prg);
|
PioPwmAudio::new(audio.dma1, &mut audio.pio, audio.sm1, audio.right, &prg);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
write_samples(&mut pwm_pio_left, &mut pwm_pio_right, unsafe {
|
unsafe {
|
||||||
&AUDIO_BUFFER_1
|
// if AUDIO_BUFFER.iter().any(|s| *s != SILENCE) {
|
||||||
})
|
write_samples(&mut pwm_pio_left, &mut pwm_pio_right, &AUDIO_BUFFER_1).await;
|
||||||
.await;
|
AUDIO_BUFFER_1.fill(SILENCE);
|
||||||
unsafe { &mut AUDIO_BUFFER_1 }.fill(SILENCE_VALUE);
|
core::mem::swap(&mut AUDIO_BUFFER, &mut AUDIO_BUFFER_1);
|
||||||
|
|
||||||
unsafe { core::mem::swap(&mut AUDIO_BUFFER, &mut AUDIO_BUFFER_1) };
|
|
||||||
AUDIO_BUFFER_READY.store(true, Ordering::Release)
|
AUDIO_BUFFER_READY.store(true, Ordering::Release)
|
||||||
|
// } else {
|
||||||
|
// yield_now().await;
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,17 +83,27 @@ struct PioPwmAudioProgram8Bit<'d, PIO: Instance>(LoadedProgram<'d, PIO>);
|
|||||||
/// Writes one sample to pwm as high and low time
|
/// Writes one sample to pwm as high and low time
|
||||||
impl<'d, PIO: Instance> PioPwmAudioProgram8Bit<'d, PIO> {
|
impl<'d, PIO: Instance> PioPwmAudioProgram8Bit<'d, PIO> {
|
||||||
fn new(common: &mut Common<'d, PIO>) -> Self {
|
fn new(common: &mut Common<'d, PIO>) -> Self {
|
||||||
// only uses 16 bits top for high, bottom for low
|
// only uses 16 bits top for pwm high, bottom for pwm low
|
||||||
// allows two samples per word
|
// allows storing two samples per word
|
||||||
let prg = pio_asm!(
|
let prg = pio_asm!(
|
||||||
"out x, 8", // pwm high time
|
".side_set 1",
|
||||||
"out y, 8", // pwm low time
|
|
||||||
|
"check:",
|
||||||
|
// "set x, 0 side 1",
|
||||||
|
// "set y, 0 side 0",
|
||||||
|
"pull ifempty noblock side 1", // gets new osr or loads 0 into x, gets second sample if osr not empty
|
||||||
|
"out x, 8 side 0", // pwm high time
|
||||||
|
"out y, 8 side 1", // pwm low time
|
||||||
|
"jmp x!=y play_sample side 0", // x & y are never equal unless osr was empty
|
||||||
|
// play silence for 10 cycles
|
||||||
|
"set x, 5 side 1",
|
||||||
|
"set y, 5 side 0",
|
||||||
|
|
||||||
|
"play_sample:"
|
||||||
"loop_high:",
|
"loop_high:",
|
||||||
"set pins, 1", // keep pin high
|
"jmp x-- loop_high side 1", // keep pwm high, decrement X until 0
|
||||||
"jmp x-- loop_high", // decrement X until 0
|
|
||||||
"loop_low:",
|
"loop_low:",
|
||||||
"set pins, 0", // keep pin low
|
"jmp y-- loop_low side 0", // keep pwm low, decrement Y until 0
|
||||||
"jmp y-- loop_low", // decrement Y until 0
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let prg = common.load_program(&prg.program);
|
let prg = common.load_program(&prg.program);
|
||||||
@@ -121,15 +133,15 @@ impl<'d, PIO: Instance, const SM: usize> PioPwmAudio<'d, PIO, SM> {
|
|||||||
cfg.set_set_pins(&[&pin]);
|
cfg.set_set_pins(&[&pin]);
|
||||||
cfg.fifo_join = FifoJoin::TxOnly;
|
cfg.fifo_join = FifoJoin::TxOnly;
|
||||||
let shift_cfg = ShiftConfig {
|
let shift_cfg = ShiftConfig {
|
||||||
auto_fill: true,
|
auto_fill: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
cfg.shift_out = shift_cfg;
|
cfg.shift_out = shift_cfg;
|
||||||
cfg.use_program(&prg.0, &[]);
|
cfg.use_program(&prg.0, &[&pin]);
|
||||||
sm.set_config(&cfg);
|
sm.set_config(&cfg);
|
||||||
|
|
||||||
let target_clock = (u8::MAX as u32 + 1) * SAMPLE_RATE_HZ;
|
let target_clock = (u8::MAX as u32 + 1) * SAMPLE_RATE_HZ;
|
||||||
let divider = (clk_sys_freq() / (target_clock * 2)).to_fixed();
|
let divider = (clk_sys_freq() / target_clock).to_fixed();
|
||||||
sm.set_clock_divider(divider);
|
sm.set_clock_divider(divider);
|
||||||
|
|
||||||
sm.set_enable(true);
|
sm.set_enable(true);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ impl Region {
|
|||||||
fn contains(&self, address: usize) -> bool {
|
fn contains(&self, address: usize) -> bool {
|
||||||
let start = self.start.load(Ordering::Relaxed);
|
let start = self.start.load(Ordering::Relaxed);
|
||||||
let end = start + self.size.load(Ordering::Relaxed);
|
let end = start + self.size.load(Ordering::Relaxed);
|
||||||
(start..start + end).contains(&address)
|
(start..end).contains(&address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(start: usize, size: usize) -> Self {
|
fn new(start: usize, size: usize) -> Self {
|
||||||
|
|||||||
Reference in New Issue
Block a user