diff --git a/Cargo.toml b/Cargo.toml index e48f8a9..d25a900 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,10 @@ version = "0.1.0" edition = "2024" [features] -default = ["ak4490", "dma"] +default = ["cs4398", "hid"] ak4490 = [] -dma = [] +cs4398 = [] +hid = [] [dependencies] bbqueue = "0.7.0" diff --git a/src/dac/cs4398.rs b/src/dac/cs4398.rs new file mode 100644 index 0000000..0c2e272 --- /dev/null +++ b/src/dac/cs4398.rs @@ -0,0 +1,67 @@ +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 CS4398_I2C_ADDRESS: u8 = 0x4f; + +#[repr(u8)] +#[allow(dead_code)] +enum RegisterAddress { + ChipId = 0x01, + ModeControl = 0x02, + VolMixInvControl = 0x03, + MuteControl = 0x04, + ChAVol = 0x05, + ChBVol = 0x06, + RampFiltControl = 0x07, + MiscControl = 0x08, + MiscControl2 = 0x09, +} + +pub struct Cs4398Dac { + i2c: T, + pins: CodecPins, +} + +impl Cs4398Dac +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(CS4398_I2C_ADDRESS, &[reg as u8, val]).ok(); + } + fn fm_for_rate(&self, rate: u32) -> u8 { + match rate { + r if r <= 50000 => 0b00, + r if r <= 100000 => 0b01, + _ => 0b10, + // DSD mode 0b11 is not used + } + } +} + +impl Dac for Cs4398Dac +where + T: _embedded_hal_blocking_i2c_WriteRead + _embedded_hal_blocking_i2c_Write, +{ + fn new(i2c: T, pins: CodecPins) -> Self { + Cs4398Dac { i2c, pins } + } + fn init(&mut self) { + // reset + self.pins.reset.set_low().ok(); + self.pins.reset.set_high().ok(); + // power up, enable control port + self.write_reg(RegisterAddress::MiscControl, 1 << 6); + // set audio protocol to I2S, Single rate mode + self.write_reg(RegisterAddress::ModeControl, 1 << 4); + self.pins.reset.set_high().ok(); + } + fn change_rate(&mut self, new_rate: u32) { + let mode_control = (1 << 4) | self.fm_for_rate(new_rate); + self.write_reg(RegisterAddress::ModeControl, mode_control); + } +} diff --git a/src/main.rs b/src/main.rs index 7de751c..0d45fdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,11 @@ pub mod dac { mod ak4490; pub use self::ak4490::Ak4490Dac as DacImpl; } +#[cfg(feature = "cs4398")] +pub mod dac { + mod cs4398; + pub use self::cs4398::Cs4398Dac as DacImpl; +} mod traits; @@ -334,7 +339,7 @@ impl, I, B: bus::UsbBus> UsbAudioClass<'_, B> for Audio< v = v.clamp(nominal_v - (1 << 12), nominal_v + (1 << 12)); defmt::debug!( - "fill:{} err:{} int:{} fb:{=u32:x}", + "fill:{} err:{} int:{} fb:{=i32:x}", fill, error, integrator, @@ -538,21 +543,6 @@ fn main() -> ! { pins: clock_sel_pins, cur_rate: SAMPLE_RATE, }; - - 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( 2, @@ -568,9 +558,21 @@ fn main() -> ! { LockDelay::Undefined(0), None, )); - 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");