use core::fmt;
use embedded_hal_async::i2c::I2c as _;
use crate::{
Decibels, Error, I2cBus, I2cDevice, Percent,
convert::{microphone_gain_step, volume_register},
};
const ES8311: u8 = 0x18;
const ES8311_DAC_VOLUME: u8 = 0x32;
const ES8311_DAC_POWER: u8 = 0x12;
const ES8311_OUTPUT: u8 = 0x13;
const ES8311_CLOCKS: u8 = 0x01;
const CLOCKS_DAC: u8 = 0xB5;
const CLOCKS_DAC_ADC: u8 = 0xBF;
const ES8311_ADC_PGA: u8 = 0x14;
const MIC1: u8 = 0x10;
pub struct Codec {
device: I2cDevice,
microphone_gain: u8,
}
impl Codec {
pub async fn set_volume(&mut self, volume: Percent) -> Result<(), Error> {
self.write(&[[ES8311_DAC_VOLUME, volume_register(volume)]])
.await
}
pub async fn set_output(&mut self, on: bool) -> Result<(), Error> {
if on {
self.write(&[[ES8311_DAC_POWER, 0x00], [ES8311_OUTPUT, 0x10]])
.await
} else {
self.write(&[[ES8311_OUTPUT, 0x00], [ES8311_DAC_POWER, 0x02]])
.await
}
}
pub async fn set_microphone(&mut self, on: bool) -> Result<(), Error> {
if on {
self.write(&[
[ES8311_CLOCKS, CLOCKS_DAC_ADC],
[0x0A, 0x0C], [0x0E, 0x02], [ES8311_ADC_PGA, MIC1 | self.microphone_gain],
[0x17, 0xBF], [0x1C, 0x6A], ])
.await
} else {
self.write(&[[0x0E, 0x6A], [ES8311_CLOCKS, CLOCKS_DAC]])
.await
}
}
pub async fn set_microphone_gain(&mut self, gain: Decibels) -> Result<(), Error> {
let step = microphone_gain_step(gain);
self.write(&[[ES8311_ADC_PGA, MIC1 | step]]).await?;
self.microphone_gain = step;
Ok(())
}
async fn write(&mut self, writes: &[[u8; 2]]) -> Result<(), Error> {
for write in writes {
self.device
.write(ES8311, write)
.await
.map_err(|_| Error::Codec)?;
}
Ok(())
}
}
pub async fn codec(bus: &'static I2cBus) -> Result<Codec, Error> {
let mut codec = Codec {
device: I2cDevice::new(bus),
microphone_gain: 0,
};
codec
.write(&[
[0x00, 0x80], [ES8311_CLOCKS, CLOCKS_DAC], [0x02, 0x18], [0x0D, 0x01], [ES8311_DAC_POWER, 0x00], [ES8311_OUTPUT, 0x10], [ES8311_DAC_VOLUME, 0xBF], [0x37, 0x08], ])
.await?;
Ok(codec)
}
impl fmt::Debug for Codec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Codec")
.field("microphone_gain", &Decibels(self.microphone_gain * 3))
.finish_non_exhaustive()
}
}