refactor dac into a trait, add volume support
This commit is contained in:
@@ -1,48 +0,0 @@
|
|||||||
use cortex_m::prelude::{_embedded_hal_blocking_i2c_Write, _embedded_hal_blocking_i2c_WriteRead};
|
|
||||||
use defmt::warn;
|
|
||||||
|
|
||||||
use crate::hal::prelude::*;
|
|
||||||
use crate::{CodecPins, MCLK_FREQ, SAMPLE_RATE, SampleType};
|
|
||||||
|
|
||||||
const AK4490_I2C_ADDRESS: u8 = 0x10;
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
enum RegisterAddress {
|
|
||||||
Control1 = 0x00,
|
|
||||||
Control2 = 0x01,
|
|
||||||
Control3 = 0x02,
|
|
||||||
LeftAtt = 0x03,
|
|
||||||
RightAtt = 0x04,
|
|
||||||
Control4 = 0x05,
|
|
||||||
Control5 = 0x06,
|
|
||||||
Control6 = 0x07,
|
|
||||||
Control7 = 0x08,
|
|
||||||
Control8 = 0x09,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn write_reg<T>(i2c: &mut T, reg: RegisterAddress, val: u8)
|
|
||||||
where
|
|
||||||
T: _embedded_hal_blocking_i2c_WriteRead + _embedded_hal_blocking_i2c_Write,
|
|
||||||
{
|
|
||||||
i2c.write(AK4490_I2C_ADDRESS, &[reg as u8, val]).ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn init_dac<T>(i2c: &mut T, mut pins: CodecPins)
|
|
||||||
where
|
|
||||||
T: _embedded_hal_blocking_i2c_WriteRead + _embedded_hal_blocking_i2c_Write,
|
|
||||||
{
|
|
||||||
// bring out of reset
|
|
||||||
pins.reset.set_high();
|
|
||||||
write_reg(i2c, RegisterAddress::Control1, (1 << 7) | 0x0e | (1 << 0)); // ACKS | I2S-32 | RSTN
|
|
||||||
let dfs = match SAMPLE_RATE {
|
|
||||||
r if r < 54000 => 0,
|
|
||||||
r if r < 108000 => 1,
|
|
||||||
r if r < 216000 => 2,
|
|
||||||
r if r <= 384000 => 4,
|
|
||||||
_ => 5,
|
|
||||||
};
|
|
||||||
// default = 0x22
|
|
||||||
write_reg(i2c, RegisterAddress::Control2, 0x22 | ((dfs & 0x3) << 3));
|
|
||||||
write_reg(i2c, RegisterAddress::Control4, (dfs & 0x4) >> 1);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
use cortex_m::prelude::{_embedded_hal_blocking_i2c_Write, _embedded_hal_blocking_i2c_WriteRead};
|
||||||
|
|
||||||
|
use crate::CodecPins;
|
||||||
|
use crate::hal::prelude::*;
|
||||||
|
use crate::traits::Dac;
|
||||||
|
|
||||||
|
const AK4490_I2C_ADDRESS: u8 = 0x10;
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum RegisterAddress {
|
||||||
|
Control1 = 0x00,
|
||||||
|
Control2 = 0x01,
|
||||||
|
Control3 = 0x02,
|
||||||
|
LeftAtt = 0x03,
|
||||||
|
RightAtt = 0x04,
|
||||||
|
Control4 = 0x05,
|
||||||
|
Control5 = 0x06,
|
||||||
|
Control6 = 0x07,
|
||||||
|
Control7 = 0x08,
|
||||||
|
Control8 = 0x09,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ak4490Dac<T> {
|
||||||
|
i2c: T,
|
||||||
|
pins: CodecPins, // this dependency is unfortunate, but non trivial to generalize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Ak4490Dac<T>
|
||||||
|
where
|
||||||
|
T: _embedded_hal_blocking_i2c_WriteRead + _embedded_hal_blocking_i2c_Write,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn write_reg(&mut self, reg: RegisterAddress, val: u8) {
|
||||||
|
self.i2c.write(AK4490_I2C_ADDRESS, &[reg as u8, val]).ok();
|
||||||
|
}
|
||||||
|
fn dfs_for_rate(&self, rate: u32) -> u8 {
|
||||||
|
match rate {
|
||||||
|
r if r < 54000 => 0,
|
||||||
|
r if r < 108000 => 1,
|
||||||
|
r if r < 216000 => 2,
|
||||||
|
r if r <= 384000 => 4,
|
||||||
|
_ => 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Dac<T> for Ak4490Dac<T>
|
||||||
|
where
|
||||||
|
T: _embedded_hal_blocking_i2c_WriteRead + _embedded_hal_blocking_i2c_Write,
|
||||||
|
{
|
||||||
|
fn new(i2c: T, pins: CodecPins) -> Self {
|
||||||
|
Self { i2c, pins }
|
||||||
|
}
|
||||||
|
fn init(&mut self) {
|
||||||
|
// bring out of reset
|
||||||
|
self.pins.reset.set_high();
|
||||||
|
self.write_reg(RegisterAddress::Control1, (1 << 7) | 0x0e | (1 << 0)); // ACKS | I2S-32 | RSTN
|
||||||
|
let dfs = 0; // start in 48k mode, change_rate will be called after init
|
||||||
|
self.write_reg(RegisterAddress::Control2, 0x22 | ((dfs & 0x3) << 3));
|
||||||
|
self.write_reg(RegisterAddress::Control4, (dfs & 0x4) >> 1);
|
||||||
|
}
|
||||||
|
fn change_rate(&mut self, new_rate: u32) {
|
||||||
|
let dfs = self.dfs_for_rate(new_rate);
|
||||||
|
self.write_reg(RegisterAddress::Control2, 0x22 | ((dfs & 0x3) << 3));
|
||||||
|
self.write_reg(RegisterAddress::Control4, (dfs & 0x4) >> 1);
|
||||||
|
}
|
||||||
|
fn set_volume(&mut self, left: u8, right: u8) {
|
||||||
|
self.write_reg(RegisterAddress::LeftAtt, left);
|
||||||
|
self.write_reg(RegisterAddress::RightAtt, right);
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
-32
@@ -36,11 +36,16 @@ use usbd_uac2::{
|
|||||||
descriptors::{ChannelConfig, ClockType, FormatType1, LockDelay},
|
descriptors::{ChannelConfig, ClockType, FormatType1, LockDelay},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::dac::DacImpl;
|
||||||
|
use crate::traits::Dac;
|
||||||
|
|
||||||
#[cfg(feature = "ak4490")]
|
#[cfg(feature = "ak4490")]
|
||||||
|
pub mod dac {
|
||||||
mod ak4490;
|
mod ak4490;
|
||||||
#[cfg(feature = "ak4490")]
|
pub use self::ak4490::Ak4490Dac as DacImpl;
|
||||||
use ak4490 as dac;
|
}
|
||||||
mod hw;
|
|
||||||
|
mod traits;
|
||||||
|
|
||||||
// Fo = M/(N*2*P) * Fin
|
// Fo = M/(N*2*P) * Fin
|
||||||
// Fo = 3072/(125*2*8) * 16MHz = 24.576MHz
|
// Fo = 3072/(125*2*8) * 16MHz = 24.576MHz
|
||||||
@@ -227,14 +232,20 @@ fn FLEXCOMM7() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Audio<T: BbqHandle> {
|
struct Audio<T: BbqHandle, D: Dac<I>, I> {
|
||||||
running: AtomicBool,
|
running: AtomicBool,
|
||||||
i2s: I2sTx,
|
i2s: I2sTx,
|
||||||
|
dac: D,
|
||||||
producer: StreamProducer<T>,
|
producer: StreamProducer<T>,
|
||||||
integrator: AtomicI32,
|
integrator: AtomicI32,
|
||||||
filtered_fill: AtomicI32,
|
filtered_fill: AtomicI32,
|
||||||
|
_marker: core::marker::PhantomData<I>,
|
||||||
|
}
|
||||||
|
impl<T: BbqHandle, D: Dac<I>, I> Audio<T, D, I> {
|
||||||
|
fn init(&mut self) {
|
||||||
|
self.dac.init();
|
||||||
|
self.dac.change_rate(SAMPLE_RATE);
|
||||||
}
|
}
|
||||||
impl<T: BbqHandle> Audio<T> {
|
|
||||||
fn start(&self) {
|
fn start(&self) {
|
||||||
self.running.store(true, Ordering::Relaxed);
|
self.running.store(true, Ordering::Relaxed);
|
||||||
defmt::info!("playback starting, enabling interrupts");
|
defmt::info!("playback starting, enabling interrupts");
|
||||||
@@ -255,7 +266,7 @@ impl<T: BbqHandle> Audio<T> {
|
|||||||
pac::NVIC::mask(pac::Interrupt::FLEXCOMM7);
|
pac::NVIC::mask(pac::Interrupt::FLEXCOMM7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: BbqHandle, B: bus::UsbBus> UsbAudioClass<'_, B> for Audio<T> {
|
impl<T: BbqHandle, D: Dac<I>, I, B: bus::UsbBus> UsbAudioClass<'_, B> for Audio<T, D, I> {
|
||||||
fn alternate_setting_changed(&mut self, _terminal: usb_device::UsbDirection, alt_setting: u8) {
|
fn alternate_setting_changed(&mut self, _terminal: usb_device::UsbDirection, alt_setting: u8) {
|
||||||
match alt_setting {
|
match alt_setting {
|
||||||
0 => self.stop(),
|
0 => self.stop(),
|
||||||
@@ -477,7 +488,7 @@ fn main() -> ! {
|
|||||||
.system_frequency(96.MHz())
|
.system_frequency(96.MHz())
|
||||||
.configure(&mut anactrl, &mut pmc, &mut syscon)
|
.configure(&mut anactrl, &mut pmc, &mut syscon)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut _delay_timer = Timer::new(
|
let mut delay_timer = Timer::new(
|
||||||
hal.ctimer
|
hal.ctimer
|
||||||
.0
|
.0
|
||||||
.enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()),
|
.enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()),
|
||||||
@@ -490,11 +501,12 @@ fn main() -> ! {
|
|||||||
.4
|
.4
|
||||||
.enabled_as_i2c(&mut syscon, &clocks.support_flexcomm_token().unwrap());
|
.enabled_as_i2c(&mut syscon, &clocks.support_flexcomm_token().unwrap());
|
||||||
|
|
||||||
let mut i2c_bus = I2cMaster::new(
|
let i2c_bus = I2cMaster::new(
|
||||||
i2c_peripheral,
|
i2c_peripheral,
|
||||||
codec_i2c_pins,
|
codec_i2c_pins,
|
||||||
Hertz::try_from(400.kHz()).unwrap(),
|
Hertz::try_from(400.kHz()).unwrap(),
|
||||||
);
|
);
|
||||||
|
let dac_impl = DacImpl::new(i2c_bus, codec_gpio_pins);
|
||||||
|
|
||||||
let i2s_peripheral = {
|
let i2s_peripheral = {
|
||||||
let fc7 = hal.flexcomm.7.release();
|
let fc7 = hal.flexcomm.7.release();
|
||||||
@@ -505,26 +517,41 @@ fn main() -> ! {
|
|||||||
&mut anactrl,
|
&mut anactrl,
|
||||||
&mut pmc,
|
&mut pmc,
|
||||||
&mut syscon,
|
&mut syscon,
|
||||||
&mut _delay_timer,
|
&mut delay_timer,
|
||||||
clocks.support_usbhs_token().unwrap(),
|
clocks.support_usbhs_token().unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
defmt::info!("audio init");
|
||||||
|
let mut audio = Audio {
|
||||||
|
i2s: i2s_peripheral,
|
||||||
|
dac: dac_impl,
|
||||||
|
producer: QUEUE.stream_producer(),
|
||||||
|
running: AtomicBool::new(false),
|
||||||
|
integrator: AtomicI32::new(0),
|
||||||
|
filtered_fill: AtomicI32::new(0),
|
||||||
|
_marker: core::marker::PhantomData,
|
||||||
|
};
|
||||||
|
audio.init();
|
||||||
|
|
||||||
let usb_bus = UsbBus::new(usb_peripheral, usb0_vbus_pin);
|
let usb_bus = UsbBus::new(usb_peripheral, usb0_vbus_pin);
|
||||||
let mut clock = Clock {
|
let mut clock = Clock {
|
||||||
pins: clock_sel_pins,
|
pins: clock_sel_pins,
|
||||||
cur_rate: SAMPLE_RATE,
|
cur_rate: SAMPLE_RATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
defmt::debug!("codec init");
|
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0xcc1d))
|
||||||
dac::init_dac(&mut i2c_bus, codec_gpio_pins);
|
.composite_with_iads()
|
||||||
|
.strings(&[StringDescriptors::default()
|
||||||
let mut audio = Audio {
|
.manufacturer("VE7XEN")
|
||||||
i2s: i2s_peripheral,
|
.product("Guac Tortilla")
|
||||||
producer: QUEUE.stream_producer(),
|
.serial_number("123456789")])
|
||||||
running: AtomicBool::new(false),
|
.unwrap()
|
||||||
integrator: AtomicI32::new(0),
|
.max_packet_size_0(64)
|
||||||
filtered_fill: AtomicI32::new(0),
|
.unwrap()
|
||||||
};
|
.device_class(0xef)
|
||||||
|
.device_sub_class(0x02)
|
||||||
|
.device_protocol(0x01)
|
||||||
|
.build();
|
||||||
|
|
||||||
let config = AudioClassConfig::new(UsbSpeed::High, FunctionCode::Other, &mut clock, &mut audio)
|
let config = AudioClassConfig::new(UsbSpeed::High, FunctionCode::Other, &mut clock, &mut audio)
|
||||||
.with_output_config(TerminalConfig::new(
|
.with_output_config(TerminalConfig::new(
|
||||||
@@ -544,19 +571,6 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let mut uac2 = config.build(&usb_bus).unwrap();
|
let mut uac2 = config.build(&usb_bus).unwrap();
|
||||||
|
|
||||||
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0xcc1d))
|
|
||||||
.composite_with_iads()
|
|
||||||
.strings(&[StringDescriptors::default()
|
|
||||||
.manufacturer("VE7XEN")
|
|
||||||
.product("Guac Tortilla")
|
|
||||||
.serial_number("123456789")])
|
|
||||||
.unwrap()
|
|
||||||
.max_packet_size_0(64)
|
|
||||||
.unwrap()
|
|
||||||
.device_class(0xef)
|
|
||||||
.device_sub_class(0x02)
|
|
||||||
.device_protocol(0x01)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
defmt::info!("main loop");
|
defmt::info!("main loop");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
use crate::CodecPins;
|
||||||
|
pub trait Dac<T> {
|
||||||
|
fn new(i2c: T, pins: CodecPins) -> Self;
|
||||||
|
fn init(&mut self);
|
||||||
|
fn change_rate(&mut self, new_rate: u32);
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn set_volume(&mut self, left: u8, right: u8) {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user