fbbl
This commit is contained in:
1 parent
c523f504cc
commit
b2b4fc34a1
1 file changed
+20
-38
+20
-38
@@ -5,10 +5,11 @@
|
||||
//! with MCLK (256×fs) enabled; the main loop streams a 400 Hz sawtooth
|
||||
//! (48 000 / 400 = 120 samples per cycle) into both channels.
|
||||
//!
|
||||
//! The USER button (B1, PA0, high = pressed) toggles the tone via EXTI0: the
|
||||
//! interrupt only sets a flag, the audio loop applies it (with a debounce
|
||||
//! hold-off), so no GPIO polling or I2C traffic happens in the sample path.
|
||||
//! While off, silence is streamed so the codec keeps its clocks.
|
||||
//! The USER button (B1, PA0, high = pressed) toggles the tone: the audio
|
||||
//! loop polls it once per frame (a press edge outside the debounce hold-off
|
||||
//! flips the tone). While off, silence is streamed so the codec keeps its
|
||||
//! clocks. Green LED (LD4) mirrors the tone state; blue (LD6) is a 1 Hz
|
||||
//! firmware heartbeat.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
@@ -17,23 +18,19 @@
|
||||
use defmt_rtt as _;
|
||||
use panic_probe as _;
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
// the HAL is re-exported by the f411 board-support crate as `f411::hal`
|
||||
use f411::{
|
||||
button::Buttons,
|
||||
codec::Cs43l22,
|
||||
led::LedCompass,
|
||||
hal::{
|
||||
gpio::{gpioa, gpioa::PA0, gpioc, Edge, Input},
|
||||
gpio::{gpioa, gpioc},
|
||||
i2c::{I2c, Mode},
|
||||
i2s::stm32_i2s_v12x::transfer::*,
|
||||
i2s::I2s,
|
||||
nb::block,
|
||||
pac,
|
||||
pac::interrupt,
|
||||
prelude::*,
|
||||
rcc::{Config, Rcc},
|
||||
},
|
||||
@@ -50,11 +47,6 @@ const AMPLITUDE: i32 = i16::MAX as i32 / 8;
|
||||
// After a toggle, ignore further presses for ~20 ms of samples (bounce).
|
||||
const DEBOUNCE_SAMPLES: u32 = SAMPLE_RATE_HZ / 50;
|
||||
|
||||
/// Button pin, parked here so the EXTI0 handler can clear its pending bit.
|
||||
static BUTTON: Mutex<RefCell<Option<PA0<Input>>>> = Mutex::new(RefCell::new(None));
|
||||
/// Set by EXTI0 on a press edge, consumed by the audio loop.
|
||||
static TOGGLE_REQ: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
type AudioOut = I2sTransfer<I2s<pac::SPI3>, Master, Transmit, Philips, Data16Channel16>;
|
||||
|
||||
/// I2S3 master transmit, 16-bit stereo, MCLK out for the codec.
|
||||
@@ -82,7 +74,7 @@ fn saw_sample(i: u32) -> i16 {
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let cp = cortex_m::Peripherals::take().unwrap();
|
||||
let mut p = pac::Peripherals::take().unwrap();
|
||||
let p = pac::Peripherals::take().unwrap();
|
||||
|
||||
// PLLI2S tuned to 61.44 MHz so 48 kHz divides exactly (61.44 MHz / 256fs / 5).
|
||||
let mut rcc = p
|
||||
@@ -95,14 +87,8 @@ fn main() -> ! {
|
||||
let gpioc = p.GPIOC.split(&mut rcc);
|
||||
let gpiod = p.GPIOD.split(&mut rcc);
|
||||
|
||||
// USER button (B1): rising-edge interrupt on EXTI0.
|
||||
let mut syscfg = p.SYSCFG.constrain(&mut rcc);
|
||||
let mut button = gpioa.pa0.into_pull_down_input();
|
||||
button.make_interrupt_source(&mut syscfg);
|
||||
button.trigger_on_edge(&mut p.EXTI, Edge::Rising);
|
||||
button.enable_interrupt(&mut p.EXTI);
|
||||
cortex_m::interrupt::free(|cs| BUTTON.borrow(cs).replace(Some(button)));
|
||||
unsafe { pac::NVIC::unmask(pac::Interrupt::EXTI0) };
|
||||
// USER button (B1), polled by the audio loop.
|
||||
let mut button = Buttons::new(gpioa.pa0).user;
|
||||
|
||||
// CS43L22 codec: control port + reset via the board-support driver.
|
||||
let i2c = I2c::new(
|
||||
@@ -141,15 +127,21 @@ fn main() -> ! {
|
||||
let mut on = false; // silent at boot; USER button toggles the saw on
|
||||
let mut holdoff: u32 = 0;
|
||||
loop {
|
||||
// Consume any press flag every frame (so bounce during the hold-off
|
||||
// window is discarded), apply it only outside the window.
|
||||
// Poll the button only every 64th frame (~1.3 ms). The gap between
|
||||
// the L and R half-frame writes is ~10 µs; in an unoptimized build a
|
||||
// per-frame poll costs enough to miss that slot every frame, which
|
||||
// starves the I2S shift register and garbles the output. Every 64th
|
||||
// frame the cost amortizes to nothing and 1.3 ms is still instant
|
||||
// for a human press.
|
||||
if frames & 63 == 0 {
|
||||
holdoff = holdoff.saturating_sub(1);
|
||||
if TOGGLE_REQ.swap(false, Ordering::AcqRel) && holdoff == 0 {
|
||||
if button.just_pressed() && holdoff == 0 {
|
||||
on = !on;
|
||||
holdoff = DEBOUNCE_SAMPLES;
|
||||
holdoff = DEBOUNCE_SAMPLES / 64;
|
||||
saw_led.set(on);
|
||||
defmt::info!("saw {}", if on { "ON" } else { "OFF" });
|
||||
}
|
||||
}
|
||||
|
||||
let sample = match on {
|
||||
true => saw_sample(i),
|
||||
@@ -165,13 +157,3 @@ fn main() -> ! {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn EXTI0() {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
if let Some(pin) = BUTTON.borrow(cs).borrow_mut().as_mut() {
|
||||
pin.clear_interrupt_pending_bit();
|
||||
}
|
||||
});
|
||||
TOGGLE_REQ.store(true, Ordering::Release);
|
||||
}
|
||||
Reference in new issue
Block a user