cs4398 support
This commit is contained in:
+3
-2
@@ -4,9 +4,10 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ak4490", "dma"]
|
default = ["cs4398", "hid"]
|
||||||
ak4490 = []
|
ak4490 = []
|
||||||
dma = []
|
cs4398 = []
|
||||||
|
hid = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bbqueue = "0.7.0"
|
bbqueue = "0.7.0"
|
||||||
|
|||||||
@@ -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<T> {
|
||||||
|
i2c: T,
|
||||||
|
pins: CodecPins,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Cs4398Dac<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(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<T> Dac<T> for Cs4398Dac<T>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
-17
@@ -44,6 +44,11 @@ pub mod dac {
|
|||||||
mod ak4490;
|
mod ak4490;
|
||||||
pub use self::ak4490::Ak4490Dac as DacImpl;
|
pub use self::ak4490::Ak4490Dac as DacImpl;
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "cs4398")]
|
||||||
|
pub mod dac {
|
||||||
|
mod cs4398;
|
||||||
|
pub use self::cs4398::Cs4398Dac as DacImpl;
|
||||||
|
}
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
@@ -334,7 +339,7 @@ impl<T: BbqHandle, D: Dac<I>, I, B: bus::UsbBus> UsbAudioClass<'_, B> for Audio<
|
|||||||
v = v.clamp(nominal_v - (1 << 12), nominal_v + (1 << 12));
|
v = v.clamp(nominal_v - (1 << 12), nominal_v + (1 << 12));
|
||||||
|
|
||||||
defmt::debug!(
|
defmt::debug!(
|
||||||
"fill:{} err:{} int:{} fb:{=u32:x}",
|
"fill:{} err:{} int:{} fb:{=i32:x}",
|
||||||
fill,
|
fill,
|
||||||
error,
|
error,
|
||||||
integrator,
|
integrator,
|
||||||
@@ -538,21 +543,6 @@ fn main() -> ! {
|
|||||||
pins: clock_sel_pins,
|
pins: clock_sel_pins,
|
||||||
cur_rate: SAMPLE_RATE,
|
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)
|
let config = AudioClassConfig::new(UsbSpeed::High, FunctionCode::Other, &mut clock, &mut audio)
|
||||||
.with_output_config(TerminalConfig::new(
|
.with_output_config(TerminalConfig::new(
|
||||||
2,
|
2,
|
||||||
@@ -568,9 +558,21 @@ fn main() -> ! {
|
|||||||
LockDelay::Undefined(0),
|
LockDelay::Undefined(0),
|
||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user