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
|
//! with MCLK (256×fs) enabled; the main loop streams a 400 Hz sawtooth
|
||||||
//! (48 000 / 400 = 120 samples per cycle) into both channels.
|
//! (48 000 / 400 = 120 samples per cycle) into both channels.
|
||||||
//!
|
//!
|
||||||
//! The USER button (B1, PA0, high = pressed) toggles the tone via EXTI0: the
|
//! The USER button (B1, PA0, high = pressed) toggles the tone: the audio
|
||||||
//! interrupt only sets a flag, the audio loop applies it (with a debounce
|
//! loop polls it once per frame (a press edge outside the debounce hold-off
|
||||||
//! hold-off), so no GPIO polling or I2C traffic happens in the sample path.
|
//! flips the tone). While off, silence is streamed so the codec keeps its
|
||||||
//! While off, silence is streamed so the codec keeps its clocks.
|
//! clocks. Green LED (LD4) mirrors the tone state; blue (LD6) is a 1 Hz
|
||||||
|
//! firmware heartbeat.
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
@@ -17,23 +18,19 @@
|
|||||||
use defmt_rtt as _;
|
use defmt_rtt as _;
|
||||||
use panic_probe 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;
|
use cortex_m_rt::entry;
|
||||||
// the HAL is re-exported by the f411 board-support crate as `f411::hal`
|
// the HAL is re-exported by the f411 board-support crate as `f411::hal`
|
||||||
use f411::{
|
use f411::{
|
||||||
|
button::Buttons,
|
||||||
codec::Cs43l22,
|
codec::Cs43l22,
|
||||||
led::LedCompass,
|
led::LedCompass,
|
||||||
hal::{
|
hal::{
|
||||||
gpio::{gpioa, gpioa::PA0, gpioc, Edge, Input},
|
gpio::{gpioa, gpioc},
|
||||||
i2c::{I2c, Mode},
|
i2c::{I2c, Mode},
|
||||||
i2s::stm32_i2s_v12x::transfer::*,
|
i2s::stm32_i2s_v12x::transfer::*,
|
||||||
i2s::I2s,
|
i2s::I2s,
|
||||||
nb::block,
|
nb::block,
|
||||||
pac,
|
pac,
|
||||||
pac::interrupt,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
rcc::{Config, Rcc},
|
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).
|
// After a toggle, ignore further presses for ~20 ms of samples (bounce).
|
||||||
const DEBOUNCE_SAMPLES: u32 = SAMPLE_RATE_HZ / 50;
|
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>;
|
type AudioOut = I2sTransfer<I2s<pac::SPI3>, Master, Transmit, Philips, Data16Channel16>;
|
||||||
|
|
||||||
/// I2S3 master transmit, 16-bit stereo, MCLK out for the codec.
|
/// I2S3 master transmit, 16-bit stereo, MCLK out for the codec.
|
||||||
@@ -82,7 +74,7 @@ fn saw_sample(i: u32) -> i16 {
|
|||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
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).
|
// PLLI2S tuned to 61.44 MHz so 48 kHz divides exactly (61.44 MHz / 256fs / 5).
|
||||||
let mut rcc = p
|
let mut rcc = p
|
||||||
@@ -95,14 +87,8 @@ fn main() -> ! {
|
|||||||
let gpioc = p.GPIOC.split(&mut rcc);
|
let gpioc = p.GPIOC.split(&mut rcc);
|
||||||
let gpiod = p.GPIOD.split(&mut rcc);
|
let gpiod = p.GPIOD.split(&mut rcc);
|
||||||
|
|
||||||
// USER button (B1): rising-edge interrupt on EXTI0.
|
// USER button (B1), polled by the audio loop.
|
||||||
let mut syscfg = p.SYSCFG.constrain(&mut rcc);
|
let mut button = Buttons::new(gpioa.pa0).user;
|
||||||
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) };
|
|
||||||
|
|
||||||
// CS43L22 codec: control port + reset via the board-support driver.
|
// CS43L22 codec: control port + reset via the board-support driver.
|
||||||
let i2c = I2c::new(
|
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 on = false; // silent at boot; USER button toggles the saw on
|
||||||
let mut holdoff: u32 = 0;
|
let mut holdoff: u32 = 0;
|
||||||
loop {
|
loop {
|
||||||
// Consume any press flag every frame (so bounce during the hold-off
|
// Poll the button only every 64th frame (~1.3 ms). The gap between
|
||||||
// window is discarded), apply it only outside the window.
|
// 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);
|
holdoff = holdoff.saturating_sub(1);
|
||||||
if TOGGLE_REQ.swap(false, Ordering::AcqRel) && holdoff == 0 {
|
if button.just_pressed() && holdoff == 0 {
|
||||||
on = !on;
|
on = !on;
|
||||||
holdoff = DEBOUNCE_SAMPLES;
|
holdoff = DEBOUNCE_SAMPLES / 64;
|
||||||
saw_led.set(on);
|
saw_led.set(on);
|
||||||
defmt::info!("saw {}", if on { "ON" } else { "OFF" });
|
defmt::info!("saw {}", if on { "ON" } else { "OFF" });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let sample = match on {
|
let sample = match on {
|
||||||
true => saw_sample(i),
|
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