diff --git a/src/ak4490.rs b/src/ak4490.rs deleted file mode 100644 index 8139cca..0000000 --- a/src/ak4490.rs +++ /dev/null @@ -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(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(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); -} diff --git a/src/dac/ak4490.rs b/src/dac/ak4490.rs new file mode 100644 index 0000000..5c855c2 --- /dev/null +++ b/src/dac/ak4490.rs @@ -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 { + i2c: T, + pins: CodecPins, // this dependency is unfortunate, but non trivial to generalize +} + +impl Ak4490Dac +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 Dac for Ak4490Dac +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); + } +} diff --git a/src/main.rs b/src/main.rs index 3ebe6aa..7de751c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,11 +36,16 @@ use usbd_uac2::{ descriptors::{ChannelConfig, ClockType, FormatType1, LockDelay}, }; +use crate::dac::DacImpl; +use crate::traits::Dac; + #[cfg(feature = "ak4490")] -mod ak4490; -#[cfg(feature = "ak4490")] -use ak4490 as dac; -mod hw; +pub mod dac { + mod ak4490; + pub use self::ak4490::Ak4490Dac as DacImpl; +} + +mod traits; // Fo = M/(N*2*P) * Fin // Fo = 3072/(125*2*8) * 16MHz = 24.576MHz @@ -227,14 +232,20 @@ fn FLEXCOMM7() { } } -struct Audio { +struct Audio, I> { running: AtomicBool, i2s: I2sTx, + dac: D, producer: StreamProducer, integrator: AtomicI32, filtered_fill: AtomicI32, + _marker: core::marker::PhantomData, } -impl Audio { +impl, I> Audio { + fn init(&mut self) { + self.dac.init(); + self.dac.change_rate(SAMPLE_RATE); + } fn start(&self) { self.running.store(true, Ordering::Relaxed); defmt::info!("playback starting, enabling interrupts"); @@ -255,7 +266,7 @@ impl Audio { pac::NVIC::mask(pac::Interrupt::FLEXCOMM7); } } -impl UsbAudioClass<'_, B> for Audio { +impl, I, B: bus::UsbBus> UsbAudioClass<'_, B> for Audio { fn alternate_setting_changed(&mut self, _terminal: usb_device::UsbDirection, alt_setting: u8) { match alt_setting { 0 => self.stop(), @@ -477,7 +488,7 @@ fn main() -> ! { .system_frequency(96.MHz()) .configure(&mut anactrl, &mut pmc, &mut syscon) .unwrap(); - let mut _delay_timer = Timer::new( + let mut delay_timer = Timer::new( hal.ctimer .0 .enabled(&mut syscon, clocks.support_1mhz_fro_token().unwrap()), @@ -490,11 +501,12 @@ fn main() -> ! { .4 .enabled_as_i2c(&mut syscon, &clocks.support_flexcomm_token().unwrap()); - let mut i2c_bus = I2cMaster::new( + let i2c_bus = I2cMaster::new( i2c_peripheral, codec_i2c_pins, Hertz::try_from(400.kHz()).unwrap(), ); + let dac_impl = DacImpl::new(i2c_bus, codec_gpio_pins); let i2s_peripheral = { let fc7 = hal.flexcomm.7.release(); @@ -505,26 +517,41 @@ fn main() -> ! { &mut anactrl, &mut pmc, &mut syscon, - &mut _delay_timer, + &mut delay_timer, 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 mut clock = Clock { pins: clock_sel_pins, cur_rate: SAMPLE_RATE, }; - defmt::debug!("codec init"); - dac::init_dac(&mut i2c_bus, codec_gpio_pins); - - let mut audio = Audio { - i2s: i2s_peripheral, - producer: QUEUE.stream_producer(), - running: AtomicBool::new(false), - integrator: AtomicI32::new(0), - filtered_fill: AtomicI32::new(0), - }; + 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(); let config = AudioClassConfig::new(UsbSpeed::High, FunctionCode::Other, &mut clock, &mut audio) .with_output_config(TerminalConfig::new( @@ -544,19 +571,6 @@ fn main() -> ! { 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"); diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..6f65f9e --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,8 @@ +use crate::CodecPins; +pub trait Dac { + 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) {} +}