Skip to main content

cs43l22_embedded/
lib.rs

1#![no_std]
2#![deny(rust_2018_idioms)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5//! ## Feature flags
6#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
7
8pub(crate) mod fmt;
9use embedded_hal::{digital::OutputPin, i2c};
10use embedded_hal::delay::DelayNs;
11
12const CS43L22_ADDR: u8 = 74;
13
14//Register addresses
15const CS43L22_REG_ID: u8 = 0x01;
16const CS43L22_REG_POWER_CTL1: u8 = 0x02;
17const CS43L22_REG_POWER_CTL2: u8 = 0x04;
18const CS43L22_REG_CLOCKING_CTL: u8 = 0x05;
19const CS43L22_REG_INTERFACE_CTL1: u8 = 0x06;
20/*const CS43L22_REG_INTERFACE_CTL2: u8 = 0x07;
21const CS43L22_REG_PASSTHROUGH_A_SELECT: u8 = 0x08;
22const CS43L22_REG_PASSTHROUGH_B_SELECT: u8 = 0x09;*/
23const CS43L22_REG_ANALOG_ZC_SR_SETT: u8 = 0x0A;
24/*const CS43L22_REG_PASSTHROUGH_GANG_CTL: u8 = 0x0C;
25const CS43L22_REG_PLAYBACK_CTL1: u8 = 0x0D;*/
26const CS43L22_REG_MISC_CTL: u8 = 0x0E;
27/*const CS43L22_REG_PLAYBACK_CTL2: u8 = 0x0F;
28const CS43L22_REG_PASSTHROUGH_A_VOL: u8 = 0x14;
29const CS43L22_REG_PASSTHROUGH_B_VOL: u8 = 0x15;*/
30const CS43L22_REG_PCMA_VOL: u8 = 0x1A;
31const CS43L22_REG_PCMB_VOL: u8 = 0x1B;
32const CS43L22_REG_BEEP_FREQ_ONTIME: u8 = 0x1C;
33const CS43L22_REG_BEEP_VOL_OFFTIME: u8 = 0x1D;
34const CS43L22_REG_BEEP_TONE_CFG: u8 = 0x1E;
35const CS43L22_REG_TONE_CTL: u8 = 0x1F;
36const CS43L22_REG_MASTER_A_VOL: u8 = 0x20;
37const CS43L22_REG_MASTER_B_VOL: u8 = 0x21;
38const CS43L22_REG_HP_A_VOL: u8 = 0x22;
39const CS43L22_REG_HP_B_VOL: u8 = 0x23;
40/*const CS43L22_REG_SPEAKER_A_VOL: u8 = 0x24;
41const CS43L22_REG_SPEAKER_B_VOL: u8 = 0x25;*/
42const CS43L22_REG_PCM_CH_SWAP: u8 = 0x26;
43const CS43L22_REG_LIMIT_CTL1: u8 = 0x27;
44/*const CS43L22_REG_LIMIT_CTL2: u8 = 0x28;
45const CS43L22_REG_LIMIT_ATTACK_RATE: u8 = 0x29;
46const CS43L22_REG_OVF_CLK_STATUS: u8 = 0x2E;
47const CS43L22_REG_BATT_COMP: u8 = 0x2F;
48const CS43L22_REG_VP_BATT_LEVEL: u8 = 0x30;
49const CS43L22_REG_SPEAKER_STATUS: u8 = 0x31;
50const CS43L22_REG_CHARGEPUMP_FREC: u8 = 0x32;*/
51
52#[derive(Debug)]
53/// Errors that can occur when using the CS43L22 driver
54pub enum Error<I2CError> {
55    /// I2C communication error
56    I2C(I2CError),
57    /// Invalid chip ID
58    /// This error is returned when the chip ID read from the CS43L22 is invalid
59    InvalidChipID,
60}
61/// Type alias for the result of a CS43L22 operation
62pub type Result<T, I2CError> = core::result::Result<T, Error<I2CError>>;
63
64impl<I2CError> From<I2CError> for Error<I2CError> {
65    fn from(value: I2CError) -> Self {
66        Self::I2C(value)
67    }
68}
69
70/// CS43L22 configuration
71/// This struct is used to configure the CS43L22 driver
72/// It contains the output device, interface format, word length and volume
73#[derive(Debug)]
74#[cfg_attr(feature = "defmt", derive(defmt::Format))]
75pub struct Config {
76    output_device: OutputDevice,
77    interface_format: InterfaceFormat,
78    word_length: WordLength,
79    treble_cutoff: TrebleCutoff,
80    treble_gain: ToneGain,
81    bass_cutoff: BassCutoff,
82    bass_gain: ToneGain,
83    tone_control: bool,
84    volume_a: u16,
85    volume_b: u16,
86}
87impl Config {
88    /// Create a new CS43L22 configuration
89    pub fn new() -> Self {
90        Self {
91            output_device: OutputDevice::Auto,
92            interface_format: InterfaceFormat::I2S,
93            word_length: WordLength::W32,
94            treble_cutoff: TrebleCutoff::_5kHz,
95            treble_gain: ToneGain::_0dB,
96            bass_cutoff: BassCutoff::_50Hz,
97            bass_gain: ToneGain::_0dB,
98            tone_control: false,
99            volume_a: 70,
100            volume_b: 70,
101        }
102    }
103
104    /// Set the output device
105    pub fn output_device(mut self, output_device: OutputDevice) -> Self {
106        self.output_device = output_device;
107        self
108    }
109
110    /// Set the interface format
111    pub fn interface_format(mut self, interface_format: InterfaceFormat) -> Self {
112        self.interface_format = interface_format;
113        self
114    }
115
116    /// Set the word length
117    pub fn word_length(mut self, word_length: WordLength) -> Self {
118        self.word_length = word_length;
119        self
120    }
121
122    /// Set the volume
123    pub fn volume_a(mut self, volume: u16) -> Self {
124        self.volume_a = volume;
125        self
126    }
127
128    /// Set the volume
129    pub fn volume_b(mut self, volume: u16) -> Self {
130        self.volume_b = volume;
131        self
132    }
133
134    /// Set the treble cutoff frequency
135    pub fn treble_cutoff(mut self, treble_cutoff: TrebleCutoff) -> Self {
136        self.treble_cutoff = treble_cutoff;
137        self
138    }
139
140    /// Set the treble gain
141    pub fn treble_gain(mut self, treble_gain: ToneGain) -> Self {
142        self.treble_gain = treble_gain;
143        self
144    }
145
146    /// Set the bass cutoff frequency
147    pub fn bass_cutoff(mut self, bass_cutoff: BassCutoff) -> Self {
148        self.bass_cutoff = bass_cutoff;
149        self
150    }
151
152    /// Set the bass gain
153    pub fn bass_gain(mut self, bass_gain: ToneGain) -> Self {
154        self.bass_gain = bass_gain;
155        self
156    }
157
158    /// Set the tone control
159    pub fn with_tone_control(mut self, tone_control: bool) -> Self {
160        self.tone_control = tone_control;
161        self
162    }
163}
164
165impl Default for Config {
166    fn default() -> Self {
167        Self::new()
168    }
169}
170
171/// CS43L22 driver
172/// This struct represents a CS43L22 driver
173/// It contains an I2C interface and a configuration
174/// It is used to control the CS43L22
175/// The driver is generic over the I2C interface
176/// The I2C interface must implement the `embedded_hal::i2c::I2c` trait
177/// The I2C error type must implement the `core::fmt::Debug` trait
178
179pub struct Cs43l22<I2C, RST, DL> {
180    /// I2C interface
181    pub i2c: I2C,
182    /// Reset pin
183    pub reset: RST,
184    /// Mutable delay
185    pub delay: DL,
186    /// CS43L22 configuration
187    pub config: Config,
188    /// Beep configuration
189    pub beep_config: BeepConfig,
190    running: bool,
191}
192/// Type alias for the result of a CS43L22 operation
193
194impl<I2C, I2CError, RST, DL> Cs43l22<I2C, RST, DL>
195where
196    I2C: i2c::I2c<u8, Error = I2CError>,
197    I2CError: i2c::Error,
198    RST: OutputPin,
199    DL: DelayNs,
200{
201    /// Create a new CS43L22 driver
202    /// This function creates a new CS43L22 driver from an I2C interface and initializes the device
203    pub fn new(
204        i2c: I2C,
205        reset: RST,
206        delay: DL,
207        config: Config,
208        beep_config: BeepConfig,
209        dsp_mode: bool,
210    ) -> Result<Self, I2CError> {
211        let mut cs43l22 = Self {
212            i2c,
213            reset,
214            delay,
215            config,
216            beep_config,
217            running: false,
218        };
219
220        //Reset the CS43L22
221        cs43l22.reset()?;
222
223        //read the chip id
224        let mut chip_id = [0u8; 1];
225        cs43l22.i2c.write(CS43L22_ADDR, &[CS43L22_REG_ID])?;
226        cs43l22.i2c.read(CS43L22_ADDR, &mut chip_id)?;
227
228        if chip_id[0] & 0xF8 != 0xE0 {
229            return Err(Error::InvalidChipID);
230        }
231        trace!("Chip ID: {}", chip_id[0] & 0xF8);
232
233        //Codec OFF
234        cs43l22
235            .i2c
236            .write(CS43L22_ADDR, &[CS43L22_REG_POWER_CTL1, 0x01])?;
237        //Output device
238        cs43l22.i2c.write(
239            CS43L22_ADDR,
240            &[CS43L22_REG_POWER_CTL2, cs43l22.config.output_device.into()],
241        )?;
242        //Auto clock
243        cs43l22
244            .i2c
245            .write(CS43L22_ADDR, &[CS43L22_REG_CLOCKING_CTL, 0x81])?;
246        //Generate interface control 1 register value
247        // slave mode | inverted sclk | dsp mode | interface format | word length
248        let interface_ctl1 = cs43l22.config.word_length.value()
249            + (cs43l22.config.interface_format.value() << 2)
250            + ((dsp_mode as u8) << 3);
251
252        cs43l22
253            .i2c
254            .write(CS43L22_ADDR, &[CS43L22_REG_INTERFACE_CTL1, interface_ctl1])?;
255
256        //Disable the analog soft ramp
257        cs43l22
258            .i2c
259            .write(CS43L22_ADDR, &[CS43L22_REG_ANALOG_ZC_SR_SETT, 0x00])?;
260        //Disable the digital soft ramp
261        cs43l22
262            .i2c
263            .write(CS43L22_ADDR, &[CS43L22_REG_MISC_CTL, 0x04])?;
264        //Disable the limiter attack level
265        cs43l22
266            .i2c
267            .write(CS43L22_ADDR, &[CS43L22_REG_LIMIT_CTL1, 0x00])?;
268
269        //tone configuration
270        let tone_cfg = cs43l22.config.tone_control as u8
271            | (cs43l22.config.treble_cutoff.value() << 3)
272            | (cs43l22.config.bass_cutoff.value() << 1);
273        cs43l22
274            .i2c
275            .write(CS43L22_ADDR, &[CS43L22_REG_TONE_CTL, tone_cfg])?;
276
277        //Adjust Bass and Treble levels
278        let tone_ctl = cs43l22.config.bass_gain.value() | (cs43l22.config.treble_gain.value() << 4);
279        cs43l22
280            .i2c
281            .write(CS43L22_ADDR, &[CS43L22_REG_TONE_CTL, tone_ctl])?;
282
283        Ok(cs43l22)
284    }
285
286    /// Enables the codec and starts the audio playback
287    pub fn play(&mut self) -> Result<(), I2CError> {
288        if !self.running {
289            // Enable the digital soft ramp
290            self.i2c
291                .write(CS43L22_ADDR, &[CS43L22_REG_MISC_CTL, 0x06])?;
292
293            //self.unmute()?;
294            // Enable the output device
295            self.i2c
296                .write(CS43L22_ADDR, &[CS43L22_REG_POWER_CTL1, 0x9E])?;
297
298            self.running = true;
299        }
300        Ok(())
301    }
302
303    /// Resets the CS43L22 to its default state
304    pub fn reset(&mut self) -> Result<(), I2CError> {
305        self.reset.set_low().ok();
306        self.delay.delay_ms(100);
307        self.reset.set_high().ok();
308        self.delay.delay_ms(100);
309        Ok(())
310    }
311
312    /// Stops the audio playback and disables the codec
313    pub fn stop(&mut self) -> Result<(), I2CError> {
314        if self.running {
315            self.mute()?;
316            // Disable the digital soft ramp
317            self.i2c
318                .write(CS43L22_ADDR, &[CS43L22_REG_MISC_CTL, 0x04])?;
319            // Power down DAC
320            self.i2c
321                .write(CS43L22_ADDR, &[CS43L22_REG_POWER_CTL1, 0x9F])?;
322            self.running = false;
323        }
324        Ok(())
325    }
326
327    /// Pauses the audio playback
328    pub fn pause(&mut self) -> Result<(), I2CError> {
329        if self.running {
330            self.mute()?;
331            // Put codec in low power mode
332            self.i2c
333                .write(CS43L22_ADDR, &[CS43L22_REG_POWER_CTL1, 0x01])?;
334            self.running = false;
335        }
336        Ok(())
337    }
338
339    /// Resumes the audio playback
340    pub fn resume(&mut self) -> Result<(), I2CError> {
341        if !self.running {
342            // Enable the digital soft ramp
343            self.unmute()?;
344            // Exit low power mode
345            self.i2c
346                .write(CS43L22_ADDR, &[CS43L22_REG_POWER_CTL1, 0x9E])?;
347            self.running = true;
348        }
349        Ok(())
350    }
351
352    /// Mutes the audio playback
353    pub fn mute(&mut self) -> Result<(), I2CError> {
354        self.i2c
355            .write(CS43L22_ADDR, &[CS43L22_REG_POWER_CTL2, 0xFF])?;
356        self.i2c
357            .write(CS43L22_ADDR, &[CS43L22_REG_HP_A_VOL, 0x01])?;
358        self.i2c
359            .write(CS43L22_ADDR, &[CS43L22_REG_HP_B_VOL, 0x01])?;
360
361        Ok(())
362    }
363
364    /// Unmutes the audio playback
365    pub fn unmute(&mut self) -> Result<(), I2CError> {
366        self.i2c
367            .write(CS43L22_ADDR, &[CS43L22_REG_HP_A_VOL, 0x00])?;
368        self.i2c
369            .write(CS43L22_ADDR, &[CS43L22_REG_HP_B_VOL, 0x00])?;
370        self.i2c.write(
371            CS43L22_ADDR,
372            &[CS43L22_REG_POWER_CTL2, self.config.output_device.into()],
373        )?;
374        Ok(())
375    }
376
377    /// Changes the output volume for the output A
378    pub fn set_volume_a(&mut self, volume: u16) -> Result<(), I2CError> {
379        self.config.volume_a = volume;
380        self.i2c.write(
381            CS43L22_ADDR,
382            &[
383                CS43L22_REG_MASTER_A_VOL,
384                self.convert_volume(self.config.volume_a),
385            ],
386        )?;
387        Ok(())
388    }
389
390    /// Changes the output volume for the output B
391    pub fn set_volume_b(&mut self, volume: u16) -> Result<(), I2CError> {
392        self.config.volume_b = volume;
393        self.i2c.write(
394            CS43L22_ADDR,
395            &[
396                CS43L22_REG_MASTER_B_VOL,
397                self.convert_volume(self.config.volume_b),
398            ],
399        )?;
400        Ok(())
401    }
402
403    /// Fetch the output volume for the output A
404    pub fn get_volume_a(&mut self) -> Result<u16, I2CError> {
405        Ok(self.config.volume_a)
406    }
407
408    /// Fetch the output volume for the output B
409    pub fn get_volume_b(&mut self) -> Result<u16, I2CError> {
410        Ok(self.config.volume_b)
411    }
412
413    /// Changes the volume of the I2S packets for the A
414    pub fn set_pcm_volume_a(&mut self, volume: u16) -> Result<(), I2CError> {
415        self.i2c.write(
416            CS43L22_ADDR,
417            &[CS43L22_REG_PCMA_VOL, self.convert_pcm_volume(volume)],
418        )?;
419        Ok(())
420    }
421
422    /// Changes the volume of the I2S packets for the B
423    pub fn set_pcm_volume_b(&mut self, volume: u16) -> Result<(), I2CError> {
424        self.i2c.write(
425            CS43L22_ADDR,
426            &[CS43L22_REG_PCMB_VOL, self.convert_pcm_volume(volume)],
427        )?;
428        Ok(())
429    }
430
431    /// Mutes the I2S stream
432    pub fn pcm_mute(&mut self) -> Result<(), I2CError> {
433        let mut current_volume_a = [0u8; 1];
434        let mut current_volume_b = [0u8; 1];
435        self.i2c
436            .write_read(CS43L22_ADDR, &[CS43L22_REG_PCMA_VOL], &mut current_volume_a)?;
437        self.i2c
438            .write_read(CS43L22_ADDR, &[CS43L22_REG_PCMB_VOL], &mut current_volume_b)?;
439        self.i2c.write(
440            CS43L22_ADDR,
441            &[CS43L22_REG_PCMA_VOL, current_volume_a[0] | 0x80],
442        )?;
443        self.i2c.write(
444            CS43L22_ADDR,
445            &[CS43L22_REG_PCMB_VOL, current_volume_b[0] | 0x80],
446        )?;
447        Ok(())
448    }
449
450    /// Unmutes the I2S stream
451    pub fn pcm_unmute(&mut self) -> Result<(), I2CError> {
452        let mut current_volume_a = [0u8; 1];
453        let mut current_volume_b = [0u8; 1];
454        self.i2c
455            .write_read(CS43L22_ADDR, &[CS43L22_REG_PCMA_VOL], &mut current_volume_a)?;
456        self.i2c
457            .write_read(CS43L22_ADDR, &[CS43L22_REG_PCMB_VOL], &mut current_volume_b)?;
458        self.i2c.write(
459            CS43L22_ADDR,
460            &[CS43L22_REG_PCMA_VOL, current_volume_a[0] & 0x7F],
461        )?;
462        self.i2c.write(
463            CS43L22_ADDR,
464            &[CS43L22_REG_PCMB_VOL, current_volume_b[0] & 0x7F],
465        )?;
466        Ok(())
467    }
468
469    /// Sets the beep tone configuration
470    pub fn setup_beep(&mut self) -> Result<(), I2CError> {
471        //read current value of the beep tone configuration
472        let mut tone_cfg = [0u8; 1];
473        self.i2c
474            .write_read(CS43L22_ADDR, &[CS43L22_REG_BEEP_TONE_CFG], &mut tone_cfg)?;
475
476        tone_cfg[0] = tone_cfg[0] & 0x0F;
477
478        let frec_ontime_value =
479            self.beep_config.on_time.value() | (self.beep_config.pitch.value() << 4) + tone_cfg[0];
480        self.i2c.write(
481            CS43L22_ADDR,
482            &[CS43L22_REG_BEEP_FREQ_ONTIME, frec_ontime_value],
483        )?;
484        let vol_offtime_value = self.convert_beep_volume(self.beep_config.volume)
485            | (self.beep_config.off_time.value() << 5);
486        self.i2c.write(
487            CS43L22_ADDR,
488            &[CS43L22_REG_BEEP_VOL_OFFTIME, vol_offtime_value],
489        )?;
490
491        //TODO: Implement tone configuration
492        let tone_cfg_value =
493            (self.beep_config.mode.value() << 6) | ((!self.beep_config.mix as u8) << 5);
494        self.i2c
495            .write(CS43L22_ADDR, &[CS43L22_REG_BEEP_TONE_CFG, tone_cfg_value])?;
496
497        Ok(())
498    }
499
500    /// Changes the beep tone configuration and starts the beep
501    pub fn change_beep_settings(&mut self, beep_config: BeepConfig) -> Result<(), I2CError> {
502        self.beep_config = beep_config;
503        self.setup_beep()
504    }
505
506    /// Allow the I2S stream to mix with other sounds
507    pub fn set_pcm_mix(&mut self, mix: PCMmix) -> Result<(), I2CError> {
508        let pcm_channel_swap_val = mix.value() << 4;
509        self.i2c.write(
510            CS43L22_ADDR,
511            &[CS43L22_REG_PCM_CH_SWAP, pcm_channel_swap_val],
512        )?;
513        Ok(())
514    }
515
516    //utils
517    fn convert_volume(&self, volume: u16) -> u8 {
518        let mut volume = (volume * 255 / 100) as u8;
519        if volume > 0xE6 {
520            volume = volume - 0xE7;
521        } else {
522            volume = volume + 0x19;
523        }
524        volume
525    }
526    fn convert_pcm_volume(&self, volume: u16) -> u8 {
527        let mut volume = (volume * 114 / 100) as u8;
528        if volume > 0x67 {
529            volume = volume - 0x68;
530        } else {
531            volume = volume + 0x19;
532        }
533        volume
534    }
535    fn convert_beep_volume(&self, volume: u16) -> u8 {
536        let mut volume = (volume * 62 / 100) as u8;
537        if volume > 0x36 {
538            volume = volume - 0x37;
539        } else {
540            volume = volume + 0x07;
541        }
542        volume
543    }
544}
545
546#[derive(Debug, Clone, Copy, PartialEq)]
547#[cfg_attr(feature = "defmt", derive(defmt::Format))]
548/// The output device of the CS43L22
549/// This is used to select the output device of the CS43L22
550/// on the register 0x04
551pub enum OutputDevice {
552    /// Speaker
553    Speaker,
554    /// Headphones
555    Headphone,
556    /// Both
557    Both,
558    /// Auto
559    Auto,
560}
561
562impl OutputDevice {
563    fn value(self) -> u8 {
564        match self {
565            Self::Speaker => 0xFA,
566            Self::Headphone => 0xAF,
567            Self::Both => 0xAA,
568            Self::Auto => 0x05,
569        }
570    }
571}
572
573impl From<OutputDevice> for u8 {
574    fn from(device: OutputDevice) -> Self {
575        device.value()
576    }
577}
578
579/// Interface format of the I2S stream
580#[derive(Debug, Clone, Copy, PartialEq)]
581#[cfg_attr(feature = "defmt", derive(defmt::Format))]
582pub enum InterfaceFormat {
583    /// DSP Mode
584    LeftJustified,
585    /// I2S Philips
586    I2S,
587    /// Right Justified
588    RightJustified,
589}
590impl InterfaceFormat {
591    fn value(self) -> u8 {
592        match self {
593            Self::LeftJustified => 0x00,
594            Self::I2S => 0x01,
595            Self::RightJustified => 0x02,
596        }
597    }
598}
599impl From<InterfaceFormat> for u8 {
600    fn from(interface_format: InterfaceFormat) -> Self {
601        interface_format.value()
602    }
603}
604
605/// Word length of the I2S stream
606#[derive(Debug, Clone, Copy, PartialEq)]
607#[cfg_attr(feature = "defmt", derive(defmt::Format))]
608pub enum WordLength {
609    /// 16 bits
610    W16,
611    /// 20 bits
612    W20,
613    /// 24 bits
614    W24,
615    /// 32 bits
616    W32,
617}
618impl WordLength {
619    fn value(self) -> u8 {
620        match self {
621            Self::W16 => 0x03,
622            Self::W20 => 0x02,
623            Self::W24 => 0x01,
624            Self::W32 => 0x00,
625        }
626    }
627}
628impl From<WordLength> for u8 {
629    fn from(word_length: WordLength) -> Self {
630        word_length.value()
631    }
632}
633
634#[derive(Debug, Clone, Copy, PartialEq)]
635#[cfg_attr(feature = "defmt", derive(defmt::Format))]
636/// The output device of the CS43L22
637/// This is used to select the output device of the CS43L22
638/// on the register 0x04
639pub enum BeepPitch {
640    /// 260.87 Hz
641    C4,
642    /// 521.74 Hz
643    C5,
644    /// 585.37 Hz
645    D5,
646    /// 666.67 Hz
647    E5,
648    /// 705.88 Hz
649    F5,
650    /// 774.19 Hz
651    G5,
652    /// 888.89 Hz
653    A5,
654    /// 1000 Hz
655    B5,
656    /// 1043.48 Hz
657    C6,
658    /// 1200.00 Hz
659    D6,
660    /// 1333.33 Hz
661    E6,
662    /// 1411.76 Hz
663    F6,
664    /// 1600.00 Hz
665    G6,
666    /// 1714.29 Hz
667    A6,
668    /// 2000.00 Hz
669    B6,
670    /// 2181.82 Hz
671    C7,
672}
673
674impl BeepPitch {
675    fn value(self) -> u8 {
676        match self {
677            Self::C4 => 0b0000,
678            Self::C5 => 0b0001,
679            Self::D5 => 0b0010,
680            Self::E5 => 0b0011,
681            Self::F5 => 0b0100,
682            Self::G5 => 0b0101,
683            Self::A5 => 0b0110,
684            Self::B5 => 0b0111,
685            Self::C6 => 0b1000,
686            Self::D6 => 0b1001,
687            Self::E6 => 0b1010,
688            Self::F6 => 0b1011,
689            Self::G6 => 0b1100,
690            Self::A6 => 0b1101,
691            Self::B6 => 0b1110,
692            Self::C7 => 0b1111,
693        }
694    }
695}
696
697impl From<BeepPitch> for u8 {
698    fn from(pitch: BeepPitch) -> Self {
699        pitch.value()
700    }
701}
702
703/// Time the beep is on
704#[derive(Debug, Clone, Copy, PartialEq)]
705#[cfg_attr(feature = "defmt", derive(defmt::Format))]
706pub enum BeepOnTime {
707    /// 86ms
708    _86ms,
709    /// 430ms
710    _430ms,
711    /// 780ms
712    _780ms,
713    /// 1200ms
714    _1200ms,
715    /// 1500ms
716    _1500ms,
717    /// 1800ms
718    _1800ms,
719    /// 2200ms
720    _2200ms,
721    /// 2500ms
722    _2500ms,
723    /// 2800ms
724    _2800ms,
725    /// 3200ms
726    _3200ms,
727    /// 3500ms
728    _3500ms,
729    /// 3800ms
730    _3800ms,
731    /// 4200ms
732    _4200ms,
733    /// 4500ms
734    _4500ms,
735    /// 4800ms
736    _4800ms,
737    /// 5200ms
738    _5200ms,
739}
740
741impl BeepOnTime {
742    fn value(self) -> u8 {
743        match self {
744            Self::_86ms => 0b0000,
745            Self::_430ms => 0b0001,
746            Self::_780ms => 0b0010,
747            Self::_1200ms => 0b0011,
748            Self::_1500ms => 0b0100,
749            Self::_1800ms => 0b0101,
750            Self::_2200ms => 0b0110,
751            Self::_2500ms => 0b0111,
752            Self::_2800ms => 0b1000,
753            Self::_3200ms => 0b1001,
754            Self::_3500ms => 0b1010,
755            Self::_3800ms => 0b1011,
756            Self::_4200ms => 0b1100,
757            Self::_4500ms => 0b1101,
758            Self::_4800ms => 0b1110,
759            Self::_5200ms => 0b1111,
760        }
761    }
762}
763
764impl From<BeepOnTime> for u8 {
765    fn from(time: BeepOnTime) -> Self {
766        time.value()
767    }
768}
769
770/// Time the beep is off
771#[derive(Debug, Clone, Copy, PartialEq)]
772#[cfg_attr(feature = "defmt", derive(defmt::Format))]
773pub enum BeepOffTime {
774    /// 1230ms
775    _1230ms,
776    /// 2580ms
777    _2580ms,
778    /// 3900ms
779    _3900ms,
780    /// 5220ms
781    _5220ms,
782    /// 6600ms
783    _6600ms,
784    /// 8050ms
785    _8050ms,
786    /// 9350ms
787    _9350ms,
788    /// 10800ms
789    _10800ms,
790}
791
792impl BeepOffTime {
793    fn value(self) -> u8 {
794        match self {
795            Self::_1230ms => 0b000,
796            Self::_2580ms => 0b001,
797            Self::_3900ms => 0b010,
798            Self::_5220ms => 0b011,
799            Self::_6600ms => 0b100,
800            Self::_8050ms => 0b101,
801            Self::_9350ms => 0b110,
802            Self::_10800ms => 0b111,
803        }
804    }
805}
806
807impl From<BeepOffTime> for u8 {
808    fn from(time: BeepOffTime) -> Self {
809        time.value()
810    }
811}
812
813/// Beep mode
814#[derive(Debug, Clone, Copy, PartialEq)]
815#[cfg_attr(feature = "defmt", derive(defmt::Format))]
816pub enum BeepMode {
817    /// Beep is off
818    Off,
819    /// Beep is on for a single frame
820    Single,
821    /// Beep is on for multiple frames
822    Multiple,
823    /// Beep is on continuously
824    Continuous,
825}
826impl BeepMode {
827    fn value(self) -> u8 {
828        match self {
829            Self::Off => 0b00,
830            Self::Single => 0b01,
831            Self::Multiple => 0b10,
832            Self::Continuous => 0b11,
833        }
834    }
835}
836
837impl From<BeepMode> for u8 {
838    fn from(mode: BeepMode) -> Self {
839        mode.value()
840    }
841}
842
843/// Beep configuration
844/// This struct is used to configure the beep tone of the CS43L22
845/// It contains the pitch, on time, off time, mode, mix and volume
846#[derive(Debug, Clone, Copy, PartialEq)]
847#[cfg_attr(feature = "defmt", derive(defmt::Format))]
848pub struct BeepConfig {
849    pitch: BeepPitch,
850    on_time: BeepOnTime,
851    off_time: BeepOffTime,
852    mode: BeepMode,
853    mix: bool,
854    volume: u16,
855}
856
857impl BeepConfig {
858    /// Create a new CS43L22 configuration
859    pub fn new() -> Self {
860        Self {
861            pitch: BeepPitch::C4,
862            on_time: BeepOnTime::_86ms,
863            off_time: BeepOffTime::_1230ms,
864            mode: BeepMode::Off,
865            mix: false,
866            volume: 70,
867        }
868    }
869
870    /// Set the pitch
871    pub fn pitch(mut self, pitch: BeepPitch) -> Self {
872        self.pitch = pitch;
873        self
874    }
875
876    /// Set the on time
877    pub fn on_time(mut self, on_time: BeepOnTime) -> Self {
878        self.on_time = on_time;
879        self
880    }
881
882    /// Set the off time
883    pub fn off_time(mut self, off_time: BeepOffTime) -> Self {
884        self.off_time = off_time;
885        self
886    }
887
888    /// Set the volume
889    pub fn volume(mut self, volume: u16) -> Self {
890        self.volume = volume;
891        self
892    }
893
894    /// Set the mode
895    pub fn mode(mut self, mode: BeepMode) -> Self {
896        self.mode = mode;
897        self
898    }
899
900    /// Set the mix
901    pub fn mix(mut self, mix: bool) -> Self {
902        self.mix = mix;
903        self
904    }
905}
906
907impl Default for BeepConfig {
908    fn default() -> Self {
909        Self::new()
910    }
911}
912
913/// Treble cutoff frequency
914/// This is used to set the treble cutoff frequency of the CS43L22
915#[derive(Debug, Clone, Copy, PartialEq)]
916#[cfg_attr(feature = "defmt", derive(defmt::Format))]
917pub enum TrebleCutoff {
918    /// 5kHz
919    _5kHz,
920    /// 7kHz
921    _7kHz,
922    /// 10kHz
923    _10kHz,
924    /// 15kHz
925    _15kHz,
926}
927
928impl TrebleCutoff {
929    fn value(self) -> u8 {
930        match self {
931            Self::_5kHz => 0b00,
932            Self::_7kHz => 0b01,
933            Self::_10kHz => 0b10,
934            Self::_15kHz => 0b11,
935        }
936    }
937}
938
939impl From<TrebleCutoff> for u8 {
940    fn from(cutoff: TrebleCutoff) -> Self {
941        cutoff.value()
942    }
943}
944
945/// Bass cutoff frequency
946/// This is used to set the bass cutoff frequency of the CS43L22
947#[derive(Debug, Clone, Copy, PartialEq)]
948#[cfg_attr(feature = "defmt", derive(defmt::Format))]
949pub enum BassCutoff {
950    /// 50Hz
951    _50Hz,
952    /// 100Hz
953    _100Hz,
954    /// 200Hz
955    _200Hz,
956    /// 250Hz
957    _250Hz,
958}
959
960impl BassCutoff {
961    fn value(self) -> u8 {
962        match self {
963            Self::_50Hz => 0b00,
964            Self::_100Hz => 0b01,
965            Self::_200Hz => 0b10,
966            Self::_250Hz => 0b11,
967        }
968    }
969}
970
971impl From<BassCutoff> for u8 {
972    fn from(cutoff: BassCutoff) -> Self {
973        cutoff.value()
974    }
975}
976
977/// Tone gain
978/// This is used to set the treble and bass gain of the CS43L22
979#[derive(Debug, Clone, Copy, PartialEq)]
980#[cfg_attr(feature = "defmt", derive(defmt::Format))]
981pub enum ToneGain {
982    /// 12dB
983    _12dB,
984    /// 10.5dB
985    _10_5dB,
986    /// 9dB
987    _9dB,
988    /// 7.5dB
989    _7_5dB,
990    /// 6dB
991    _6dB,
992    /// 4.5dB
993    _4_5dB,
994    /// 3dB
995    _3dB,
996    /// 1.5dB
997    _1_5dB,
998    /// 0dB
999    _0dB,
1000    /// -1.5dB
1001    Min1_5dB,
1002    /// -3dB
1003    Min3dB,
1004    /// -4.5dB
1005    Min4_5dB,
1006    /// -6dB
1007    Min6dB,
1008    /// -7.5dB
1009    Min7_5dB,
1010    /// -9dB
1011    Min9dB,
1012    /// -10.5dB
1013    Min10_5dB,
1014}
1015
1016impl ToneGain {
1017    fn value(self) -> u8 {
1018        match self {
1019            Self::_12dB => 0b0000,
1020            Self::_10_5dB => 0b0001,
1021            Self::_9dB => 0b0010,
1022            Self::_7_5dB => 0b0011,
1023            Self::_6dB => 0b0100,
1024            Self::_4_5dB => 0b0101,
1025            Self::_3dB => 0b0110,
1026            Self::_1_5dB => 0b0111,
1027            Self::_0dB => 0b1000,
1028            Self::Min1_5dB => 0b1001,
1029            Self::Min3dB => 0b1010,
1030            Self::Min4_5dB => 0b1011,
1031            Self::Min6dB => 0b1100,
1032            Self::Min7_5dB => 0b1101,
1033            Self::Min9dB => 0b1110,
1034            Self::Min10_5dB => 0b1111,
1035        }
1036    }
1037}
1038
1039impl From<ToneGain> for u8 {
1040    fn from(gain: ToneGain) -> Self {
1041        gain.value()
1042    }
1043}
1044
1045/// PCM mix
1046/// This is used to set the PCM mix of the CS43L22
1047/// In summary, it is used to set the gain of the left and right channels
1048/// of the I2S stream
1049#[derive(Debug, Clone, Copy, PartialEq)]
1050#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1051pub enum PCMmix {
1052    /// Full left channel
1053    Left,
1054    /// (Left + Right) / 2
1055    Half,
1056    /// Full right channel
1057    Right,
1058}
1059
1060impl PCMmix {
1061    fn value(self) -> u8 {
1062        match self {
1063            Self::Left => 0b00,
1064            Self::Half => 0b01,
1065            Self::Right => 0b11,
1066        }
1067    }
1068}
1069
1070impl From<PCMmix> for u8 {
1071    fn from(mix: PCMmix) -> Self {
1072        mix.value()
1073    }
1074}