lpc55s28-evk: use bbqueue for demo, it can now run at 96k
This commit is contained in:
@@ -1,8 +1,22 @@
|
||||
//! Contains hardware setup unrelated to Usb Audio Class implementation
|
||||
|
||||
use crate::hal;
|
||||
use core::cell::{OnceCell, UnsafeCell};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr::null_mut;
|
||||
|
||||
use crate::Syscon;
|
||||
use crate::{MCLK_FREQ, SAMPLE_RATE, pac};
|
||||
use defmt::debug;
|
||||
use hal::{
|
||||
Iocon, Pin,
|
||||
drivers::pins,
|
||||
prelude::*,
|
||||
traits::wg::digital::v2::{OutputPin, ToggleableOutputPin},
|
||||
typestates::pin::{gpio::direction::Output, state::Gpio},
|
||||
};
|
||||
use lpc55_hal::Enabled;
|
||||
use static_cell::StaticCell;
|
||||
pub(crate) struct PllConstants {
|
||||
pub m: u16, // 1-65535
|
||||
pub n: u8, // 1-255
|
||||
@@ -79,7 +93,7 @@ pub(crate) fn init_audio_pll() {
|
||||
.xo32m_ctrl
|
||||
.modify(|_, w| w.enable_system_clk_out().enable());
|
||||
|
||||
debug!("init pll: {}", AUDIO_PLL);
|
||||
debug!("init pll0: {}", AUDIO_PLL);
|
||||
pmc.pdruncfg0
|
||||
.modify(|_, w| w.pden_pll0().poweredoff().pden_pll0_sscg().poweredoff());
|
||||
syscon.pll0clksel.write(|w| w.sel().enum_0x1()); // clk_in
|
||||
@@ -132,6 +146,61 @@ pub(crate) fn init_audio_pll() {
|
||||
debug!("pll0 locked after {} tries", i);
|
||||
}
|
||||
|
||||
const SYS_PLL: PllConstants = PllConstants::new(4, 75, 1); // 150MHz
|
||||
|
||||
pub(crate) fn init_sys_pll1() {
|
||||
let syscon = unsafe { &*pac::SYSCON::ptr() };
|
||||
let pmc = unsafe { &*pac::PMC::ptr() };
|
||||
let anactrl = unsafe { &*pac::ANACTRL::ptr() };
|
||||
|
||||
debug!("start clk_in");
|
||||
pmc.pdruncfg0
|
||||
.modify(|_, w| w.pden_xtal32m().poweredon().pden_ldoxo32m().poweredon());
|
||||
syscon.clock_ctrl.modify(|_, w| w.clkin_ena().enable());
|
||||
anactrl
|
||||
.xo32m_ctrl
|
||||
.modify(|_, w| w.enable_system_clk_out().enable());
|
||||
|
||||
debug!("init pll1: {}", SYS_PLL);
|
||||
pmc.pdruncfg0.modify(|_, w| w.pden_pll1().poweredoff());
|
||||
syscon.pll1clksel.write(|w| w.sel().enum_0x1()); // clk_in
|
||||
syscon.pll1ctrl.write(|w| unsafe {
|
||||
w.clken()
|
||||
.enable()
|
||||
.seli()
|
||||
.bits(SYS_PLL.seli)
|
||||
.selp()
|
||||
.bits(SYS_PLL.selp)
|
||||
});
|
||||
|
||||
syscon
|
||||
.pll1ndec
|
||||
.write(|w| unsafe { w.ndiv().bits(SYS_PLL.n) });
|
||||
syscon.pll1ndec.write(|w| unsafe {
|
||||
w.ndiv().bits(SYS_PLL.n).nreq().set_bit() // latch
|
||||
});
|
||||
syscon
|
||||
.pll1mdec
|
||||
.write(|w| unsafe { w.mdiv().bits(SYS_PLL.m) });
|
||||
syscon
|
||||
.pll1pdec
|
||||
.write(|w| unsafe { w.pdiv().bits(SYS_PLL.p) });
|
||||
syscon.pll1pdec.write(|w| unsafe {
|
||||
w.pdiv().bits(SYS_PLL.p).preq().set_bit() // latch
|
||||
});
|
||||
|
||||
pmc.pdruncfg0.modify(|_, w| w.pden_pll1().poweredon());
|
||||
debug!("pll1 wait for lock");
|
||||
let mut i = 0usize;
|
||||
while syscon.pll1stat.read().lock().bit_is_clear() {
|
||||
i += 1;
|
||||
}
|
||||
debug!("pll1 locked after {} tries", i);
|
||||
// switch system clock to pll1
|
||||
syscon.fmccr.modify(|_, w| w.flashtim().flashtim11());
|
||||
syscon.mainclkselb.modify(|_, w| w.sel().enum_0x2()); // pll1
|
||||
}
|
||||
|
||||
pub struct I2sTx {
|
||||
pub i2s: pac::I2S7,
|
||||
}
|
||||
@@ -224,3 +293,74 @@ pub fn init_i2s(mut fc7: pac::FLEXCOMM7, i2s7: pac::I2S7, syscon: &mut Syscon) -
|
||||
|
||||
I2sTx { i2s: regs }
|
||||
}
|
||||
|
||||
pub struct SharedLed<T: OutputPin> {
|
||||
inner: UnsafeCell<T>,
|
||||
}
|
||||
unsafe impl<T: OutputPin> Sync for SharedLed<T> {}
|
||||
impl<T: OutputPin> SharedLed<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner: UnsafeCell::new(inner),
|
||||
}
|
||||
}
|
||||
pub fn on(&self) {
|
||||
unsafe {
|
||||
(*self.inner.get()).set_low().ok();
|
||||
}
|
||||
}
|
||||
pub fn off(&self) {
|
||||
unsafe {
|
||||
(*self.inner.get()).set_high().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: OutputPin + ToggleableOutputPin> SharedLed<T> {
|
||||
pub fn toggle(&self) {
|
||||
unsafe {
|
||||
(*self.inner.get()).toggle().ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type RedLed = Pin<pins::Pio1_6, Gpio<Output>>;
|
||||
type GreenLed = Pin<pins::Pio1_7, Gpio<Output>>;
|
||||
type BlueLed = Pin<pins::Pio1_4, Gpio<Output>>;
|
||||
pub static RED_LED: MaybeUninit<SharedLed<RedLed>> = MaybeUninit::uninit();
|
||||
pub static GREEN_LED: MaybeUninit<SharedLed<GreenLed>> = MaybeUninit::uninit();
|
||||
pub static BLUE_LED: MaybeUninit<SharedLed<BlueLed>> = MaybeUninit::uninit();
|
||||
|
||||
pub fn init_leds(iocon: &mut Iocon<Enabled>, gpio: &mut hal::Gpio<Enabled>) {
|
||||
let red_led = SharedLed::new(
|
||||
pins::Pio1_6::take()
|
||||
.unwrap()
|
||||
.into_gpio_pin(iocon, gpio)
|
||||
.into_output_low(),
|
||||
);
|
||||
let green_led = SharedLed::new(
|
||||
pins::Pio1_7::take()
|
||||
.unwrap()
|
||||
.into_gpio_pin(iocon, gpio)
|
||||
.into_output_low(),
|
||||
);
|
||||
let blue_led = SharedLed::new(
|
||||
pins::Pio1_4::take()
|
||||
.unwrap()
|
||||
.into_gpio_pin(iocon, gpio)
|
||||
.into_output_low(),
|
||||
);
|
||||
unsafe {
|
||||
core::ptr::write(RED_LED.as_ptr() as *mut SharedLed<RedLed>, red_led);
|
||||
core::ptr::write(GREEN_LED.as_ptr() as *mut SharedLed<GreenLed>, green_led);
|
||||
core::ptr::write(BLUE_LED.as_ptr() as *mut SharedLed<BlueLed>, blue_led);
|
||||
}
|
||||
}
|
||||
pub fn red_led() -> &'static SharedLed<RedLed> {
|
||||
unsafe { &*RED_LED.as_ptr() }
|
||||
}
|
||||
pub fn green_led() -> &'static SharedLed<GreenLed> {
|
||||
unsafe { &*GREEN_LED.as_ptr() }
|
||||
}
|
||||
pub fn blue_led() -> &'static SharedLed<BlueLed> {
|
||||
unsafe { &*BLUE_LED.as_ptr() }
|
||||
}
|
||||
|
||||
@@ -17,8 +17,12 @@ fn panic() -> ! {
|
||||
panic_probe::hard_fault()
|
||||
}
|
||||
|
||||
use core::ptr::null_mut;
|
||||
use core::sync::atomic::{AtomicBool, AtomicI32, Ordering};
|
||||
use bbqueue::{
|
||||
nicknames::Churrasco,
|
||||
prod_cons::stream::{StreamConsumer, StreamProducer},
|
||||
traits::bbqhdl::BbqHandle,
|
||||
};
|
||||
use core::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, Ordering};
|
||||
use cortex_m_rt::entry;
|
||||
use defmt::debug;
|
||||
use defmt_rtt as _;
|
||||
@@ -29,10 +33,8 @@ use hal::{
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
};
|
||||
use heapless::spsc::{Consumer, Producer, Queue};
|
||||
use lpc55_hal::{self as hal};
|
||||
use lpc55_hal as hal;
|
||||
use pac::interrupt;
|
||||
use static_cell::StaticCell;
|
||||
use usb_device::{
|
||||
bus::{self},
|
||||
device::{StringDescriptors, UsbDeviceBuilder, UsbVidPid},
|
||||
@@ -45,7 +47,7 @@ use usbd_uac2::{
|
||||
descriptors::{ChannelConfig, ClockType, FormatType1, LockDelay},
|
||||
};
|
||||
|
||||
use crate::hw::I2sTx;
|
||||
use crate::hw::{I2sTx, blue_led, green_led, red_led};
|
||||
|
||||
mod hw;
|
||||
mod wm8904;
|
||||
@@ -53,7 +55,7 @@ mod wm8904;
|
||||
const CODEC_I2C_ADDR: u8 = 0b0011010;
|
||||
const FIFO_LENGTH: usize = 256; // frames
|
||||
const MCLK_FREQ: u32 = 12288000;
|
||||
const SAMPLE_RATE: u32 = 48000; // example implementation runs okay at 48k but not 96k
|
||||
const SAMPLE_RATE: u32 = 96000;
|
||||
type SampleType = (i32, i32);
|
||||
|
||||
struct Clock {}
|
||||
@@ -63,8 +65,8 @@ impl Clock {
|
||||
impl UsbAudioClockImpl for Clock {
|
||||
const CLOCK_TYPE: usbd_uac2::descriptors::ClockType = ClockType::InternalFixed;
|
||||
const SOF_SYNC: bool = false;
|
||||
fn get_sample_rate(&self) -> core::result::Result<u32, usbd_uac2::UsbAudioClassError> {
|
||||
Ok(Clock::RATES[0].min)
|
||||
fn get_sample_rate(&self) -> u32 {
|
||||
Clock::RATES[0].min
|
||||
}
|
||||
fn get_rates(
|
||||
&self,
|
||||
@@ -76,35 +78,63 @@ impl UsbAudioClockImpl for Clock {
|
||||
}
|
||||
}
|
||||
|
||||
static FIFO_CONSUMER_STORE: StaticCell<Consumer<SampleType>> = StaticCell::new();
|
||||
static mut FIFO_CONSUMER: *mut Consumer<SampleType> = null_mut();
|
||||
const BYTES_PER_FRAME: usize = 8;
|
||||
const QUEUE_BYTES: usize = FIFO_LENGTH * BYTES_PER_FRAME;
|
||||
// We use bbqueue here for performance in the USB driver that runs almost entirely in interrupt free critical section.
|
||||
static QUEUE: Churrasco<QUEUE_BYTES> = Churrasco::new();
|
||||
// Used for feedback calculation of current fifo state
|
||||
static PRODUCED: AtomicU32 = AtomicU32::new(0);
|
||||
static CONSUMED: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[inline]
|
||||
fn try_write_one_frame<T: BbqHandle>(
|
||||
cons: &mut StreamConsumer<T>,
|
||||
i2s: &pac::i2s7::RegisterBlock,
|
||||
) -> bool {
|
||||
if let Ok(rgr) = cons.read() {
|
||||
if rgr.len() >= BYTES_PER_FRAME {
|
||||
let l = u32::from_le_bytes(rgr[0..4].try_into().unwrap());
|
||||
let r = u32::from_le_bytes(rgr[4..8].try_into().unwrap());
|
||||
|
||||
i2s.fifowr.write(|w| unsafe { w.bits(l) });
|
||||
i2s.fifowr.write(|w| unsafe { w.bits(r) });
|
||||
|
||||
// consume exactly one frame (8 bytes)
|
||||
rgr.release(BYTES_PER_FRAME);
|
||||
CONSUMED.fetch_add(BYTES_PER_FRAME as u32, Ordering::Relaxed);
|
||||
return true;
|
||||
} else {
|
||||
// Not enough bytes for a full frame: leave it in the queue.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn FLEXCOMM7() {
|
||||
let i2s = unsafe { &*pac::I2S7::ptr() };
|
||||
|
||||
// refil the buffer to 4 frames / 8 samples
|
||||
let fifo = unsafe { &mut *FIFO_CONSUMER };
|
||||
// refill until fifo has >6 words
|
||||
let mut cons = QUEUE.stream_consumer();
|
||||
while i2s.fifostat.read().txlvl().bits() <= 6 {
|
||||
if let Some((l, r)) = fifo.dequeue() {
|
||||
i2s.fifowr.write(|w| unsafe { w.bits(l as u32) });
|
||||
i2s.fifowr.write(|w| unsafe { w.bits(r as u32) });
|
||||
} else {
|
||||
// Queue underflow
|
||||
defmt::error!("queue underflow");
|
||||
if !try_write_one_frame(&mut cons, i2s) {
|
||||
// No complete frame available: write silence to keep FIFO above threshold
|
||||
defmt::error!("underflow");
|
||||
red_led().toggle();
|
||||
i2s.fifowr.write(|w| unsafe { w.bits(0) });
|
||||
i2s.fifowr.write(|w| unsafe { w.bits(0) });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Audio<'a> {
|
||||
struct Audio<T: BbqHandle> {
|
||||
running: AtomicBool,
|
||||
i2s: I2sTx,
|
||||
producer: Producer<'a, SampleType>,
|
||||
producer: StreamProducer<T>,
|
||||
integrator: AtomicI32,
|
||||
}
|
||||
impl<'a> Audio<'a> {
|
||||
impl<T: BbqHandle> Audio<T> {
|
||||
fn start(&self) {
|
||||
self.running.store(true, Ordering::Relaxed);
|
||||
defmt::info!("playback starting, enabling interrupts");
|
||||
@@ -117,6 +147,7 @@ impl<'a> Audio<'a> {
|
||||
// FIFO level interrupt enable
|
||||
self.i2s.i2s.fifointenset.modify(|_, w| w.txlvl().enabled());
|
||||
unsafe { pac::NVIC::unmask(pac::Interrupt::FLEXCOMM7) };
|
||||
green_led().on();
|
||||
}
|
||||
fn stop(&self) {
|
||||
// If we don't disable interrupts while stopped, we will underflow constantly and continuously refill the fifo with 0s
|
||||
@@ -125,9 +156,10 @@ impl<'a> Audio<'a> {
|
||||
self.running.store(true, Ordering::Relaxed);
|
||||
defmt::info!("playback stopped");
|
||||
pac::NVIC::mask(pac::Interrupt::FLEXCOMM7);
|
||||
green_led().off();
|
||||
}
|
||||
}
|
||||
impl<'a, B: bus::UsbBus> UsbAudioClass<'a, B> for Audio<'_> {
|
||||
impl<T: BbqHandle, B: bus::UsbBus> UsbAudioClass<'_, B> for Audio<T> {
|
||||
fn alternate_setting_changed(&mut self, _terminal: usb_device::UsbDirection, alt_setting: u8) {
|
||||
// alt setting 0 means stopped
|
||||
match alt_setting {
|
||||
@@ -138,10 +170,10 @@ impl<'a, B: bus::UsbBus> UsbAudioClass<'a, B> for Audio<'_> {
|
||||
}
|
||||
fn audio_data_rx(
|
||||
&mut self,
|
||||
ep: &usb_device::endpoint::Endpoint<'a, B, usb_device::endpoint::Out>,
|
||||
ep: &usb_device::endpoint::Endpoint<'_, B, usb_device::endpoint::Out>,
|
||||
) {
|
||||
// Buffer must fit 1ms of audio data (based on how `usbd_uac2` sets up the descriptors), calculate that size here.
|
||||
let mut buf = [0; SAMPLE_RATE as usize / 1000 * core::mem::size_of::<SampleType>()];
|
||||
// Buffer must fit 125us of audio data (based on how `usbd_uac2` sets up the descriptors), calculate that size here.
|
||||
let mut buf = [0; SAMPLE_RATE as usize / 8000 * core::mem::size_of::<SampleType>()];
|
||||
let len = match ep.read(&mut buf) {
|
||||
Ok(len) => len,
|
||||
Err(_) => {
|
||||
@@ -150,64 +182,55 @@ impl<'a, B: bus::UsbBus> UsbAudioClass<'a, B> for Audio<'_> {
|
||||
}
|
||||
};
|
||||
let buf = &buf[..len];
|
||||
// Translate the raw USB data into audio frames
|
||||
for sample in buf
|
||||
.chunks_exact(core::mem::size_of::<SampleType>())
|
||||
.map(|b| {
|
||||
// TODO: implement SampleType::from
|
||||
(
|
||||
i32::from_le_bytes(b[..4].try_into().unwrap()),
|
||||
i32::from_le_bytes(b[4..].try_into().unwrap()),
|
||||
)
|
||||
})
|
||||
{
|
||||
if self.producer.enqueue(sample).is_err() {
|
||||
defmt::error!("overflowed fifo, len: {}", self.producer.len());
|
||||
}
|
||||
|
||||
if let Ok(mut wg) = self.producer.grant_exact(buf.len()) {
|
||||
wg.copy_from_slice(buf);
|
||||
wg.commit(buf.len());
|
||||
PRODUCED.fetch_add(buf.len() as u32, Ordering::Relaxed);
|
||||
} else {
|
||||
blue_led().on();
|
||||
defmt::error!("overflowed bbq, asked {}", buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide rate feedback to the host, so that it doesn't over- or underflow
|
||||
/// our queue.
|
||||
fn feedback(&mut self) -> Option<UsbIsochronousFeedback> {
|
||||
// Samples per USB interval (1ms)
|
||||
const FRAME_SAMPLES: i32 = SAMPLE_RATE as i32 / 1000;
|
||||
fn feedback(&mut self, nominal_rate: UsbIsochronousFeedback) -> Option<UsbIsochronousFeedback> {
|
||||
let target = FIFO_LENGTH as i32 / 2 - nominal_rate.int as i32;
|
||||
|
||||
// Keep FIFO around half full, minus one USB packet worth
|
||||
const TARGET: i32 = FIFO_LENGTH as i32 / 2 - FRAME_SAMPLES;
|
||||
let fill = PRODUCED
|
||||
.load(Ordering::Acquire)
|
||||
.wrapping_sub(CONSUMED.load(Ordering::Acquire)) as i32
|
||||
/ BYTES_PER_FRAME as i32;
|
||||
|
||||
// 16.16 fixed-point nominal feedback value
|
||||
const NOMINAL: i32 = FRAME_SAMPLES << 16;
|
||||
const MAX_ERROR: i32 = FRAME_SAMPLES / 2;
|
||||
let error = fill - target;
|
||||
|
||||
let queuelen = self.producer.len() as i32;
|
||||
// Clamp startup excursions.
|
||||
let error = error.clamp(-(nominal_rate.int as i32 * 4), nominal_rate.int as i32 * 4);
|
||||
|
||||
let error = (queuelen - TARGET).clamp(-MAX_ERROR, MAX_ERROR);
|
||||
let mut integrator = self.integrator.load(Ordering::Relaxed);
|
||||
integrator += error / 32;
|
||||
integrator = integrator.clamp(-1024, 1024);
|
||||
self.integrator.store(integrator, Ordering::Relaxed);
|
||||
|
||||
// slow down accumulation of I
|
||||
const I_ACCUM_DIV: i32 = 8;
|
||||
let i_delta = error / I_ACCUM_DIV;
|
||||
let integrator = self.integrator.fetch_add(i_delta, Ordering::Relaxed) + i_delta;
|
||||
// gains
|
||||
let p = error << 8;
|
||||
let i = integrator;
|
||||
|
||||
// Integrator saturates at 1024xFRAME_SAMPLES
|
||||
let i_limit = FRAME_SAMPLES << 10;
|
||||
let integrator = integrator.clamp(-i_limit, i_limit);
|
||||
|
||||
// Gains
|
||||
let p = error << 7;
|
||||
let i = integrator << 3;
|
||||
|
||||
// Total correction in 16.16 space
|
||||
let correction = -(p + i);
|
||||
let nominal_v = nominal_rate.to_u32_12_13() as i32;
|
||||
|
||||
let v = NOMINAL + correction;
|
||||
let mut v = nominal_v + correction;
|
||||
|
||||
// Tight clamp around nominal.
|
||||
v = v.clamp(nominal_v - (1 << 14), nominal_v + (1 << 14));
|
||||
|
||||
defmt::debug!(
|
||||
"q:{} err:{} i:{} fb:{}",
|
||||
queuelen,
|
||||
"fill:{} err:{} int:{} fb:{=u32:x}",
|
||||
fill,
|
||||
error,
|
||||
integrator,
|
||||
v >> 16
|
||||
v
|
||||
);
|
||||
|
||||
Some(UsbIsochronousFeedback::new(v as u32))
|
||||
@@ -227,10 +250,7 @@ fn main() -> ! {
|
||||
|
||||
debug!("start");
|
||||
|
||||
let mut red_led = pins::Pio1_6::take()
|
||||
.unwrap()
|
||||
.into_gpio_pin(&mut iocon, &mut gpio)
|
||||
.into_output_low(); // start turned off
|
||||
hw::init_leds(&mut iocon, &mut gpio);
|
||||
|
||||
debug!("iocon");
|
||||
let usb0_vbus_pin = pins::Pio0_22::take()
|
||||
@@ -249,7 +269,7 @@ fn main() -> ! {
|
||||
);
|
||||
|
||||
debug!("clocks");
|
||||
// Run the system clock at 96MHz. The lpc55-hal will run it from the FRO.
|
||||
// Run the system clock at 96MHz. The lpc55-hal will run it from the FRO. But we won't actually use these clocks, we just need the guards...
|
||||
let clocks = hal::ClockRequirements::default()
|
||||
.system_frequency(96.MHz())
|
||||
.configure(&mut anactrl, &mut pmc, &mut syscon)
|
||||
@@ -259,6 +279,8 @@ fn main() -> ! {
|
||||
.0
|
||||
.enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()),
|
||||
);
|
||||
// Start PLL1 at 150MHz as main system clock
|
||||
hw::init_sys_pll1();
|
||||
// Start PLL0 at 24.576MHz as the audio clock. The FRO cannot evenly divide any common audio frequencies.
|
||||
hw::init_audio_pll();
|
||||
|
||||
@@ -280,44 +302,44 @@ fn main() -> ! {
|
||||
};
|
||||
|
||||
#[cfg(feature = "usbhs")]
|
||||
let usb_peripheral = hal.usbhs.enabled_as_device(
|
||||
&mut anactrl,
|
||||
&mut pmc,
|
||||
&mut syscon,
|
||||
&mut _delay_timer,
|
||||
clocks.support_usbhs_token().unwrap(),
|
||||
let (usb_speed, usb_peripheral) = (
|
||||
UsbSpeed::High,
|
||||
hal.usbhs.enabled_as_device(
|
||||
&mut anactrl,
|
||||
&mut pmc,
|
||||
&mut syscon,
|
||||
&mut _delay_timer,
|
||||
clocks.support_usbhs_token().unwrap(),
|
||||
),
|
||||
);
|
||||
#[cfg(feature = "usbfs")]
|
||||
let usb_peripheral = hal.usbfs.enabled_as_device(
|
||||
&mut anactrl,
|
||||
&mut pmc,
|
||||
&mut syscon,
|
||||
clocks.support_usbfs_token().unwrap(),
|
||||
let (usb_speed, usb_peripheral) = (
|
||||
UsbSpeed::Full,
|
||||
hal.usbfs.enabled_as_device(
|
||||
&mut anactrl,
|
||||
&mut pmc,
|
||||
&mut syscon,
|
||||
clocks.support_usbfs_token().unwrap(),
|
||||
),
|
||||
);
|
||||
|
||||
let usb_bus = UsbBus::new(usb_peripheral, usb0_vbus_pin);
|
||||
|
||||
defmt::debug!("codec init");
|
||||
wm8904::init_codec(&mut i2c_bus);
|
||||
let queue = cortex_m::singleton!(
|
||||
: Queue<SampleType, FIFO_LENGTH>
|
||||
= Queue::new()
|
||||
)
|
||||
.unwrap();
|
||||
let (producer, consumer) = queue.split();
|
||||
|
||||
let consumer_ref = FIFO_CONSUMER_STORE.init(consumer);
|
||||
unsafe { FIFO_CONSUMER = consumer_ref as *mut _ };
|
||||
// let consumer_ref = FIFO_CONSUMER_STORE.init(consumer);
|
||||
// unsafe { FIFO_CONSUMER = consumer_ref as *mut _ };
|
||||
|
||||
let mut clock = Clock {};
|
||||
let mut audio = Audio {
|
||||
i2s: i2s_peripheral,
|
||||
producer,
|
||||
producer: QUEUE.stream_producer(),
|
||||
running: AtomicBool::new(false),
|
||||
integrator: AtomicI32::new(0),
|
||||
};
|
||||
|
||||
let config = AudioClassConfig::new(UsbSpeed::High, FunctionCode::Other, &mut clock, &mut audio)
|
||||
let config = AudioClassConfig::new(usb_speed, FunctionCode::Other, &mut clock, &mut audio)
|
||||
.with_output_config(TerminalConfig::new(
|
||||
4,
|
||||
1,
|
||||
@@ -353,6 +375,5 @@ fn main() -> ! {
|
||||
|
||||
loop {
|
||||
usb_dev.poll(&mut [&mut uac2]);
|
||||
red_led.set_high().ok(); // Turn off
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user