vs1003_driver/
lib.rs

1//! A high-level driver for VS1003 audio codec.
2//!
3//! TODO: Provide better description
4//!
5
6#![no_std]
7#![warn(missing_debug_implementations)]
8#![warn(missing_docs)]
9
10pub use vs1003_pac as pac;
11
12use embedded_hal::{delay::DelayNs, digital::OutputPin as _, spi::SpiDevice};
13use fugit::ExtU32;
14
15/// Contains all peripherals required to interface with VS1003
16#[derive(Debug)]
17pub struct Peripherals<TSci, TSdi, TDreq, TRst> {
18    /// The SCI SPI slave.
19    /// Typically that will be the same SPI bus as SDI, with different CS (xCS).
20    pub sci: TSci,
21    /// The SDI SPI Slave.
22    /// Typically that will be the same SPI bus as SCI, with different CS (xDCS).
23    pub sdi: TSdi,
24    /// The DREQ input pin.
25    pub dreq: TDreq,
26    /// The xRST output pin.
27    pub xrst: TRst,
28}
29
30impl<TSci, TSdi, TDreq, TRst> Vs1003Peripherals for Peripherals<TSci, TSdi, TDreq, TRst>
31where
32    TSci: embedded_hal::spi::SpiDevice,
33    TSdi: embedded_hal::spi::SpiDevice,
34    TDreq: embedded_hal::digital::InputPin,
35    TRst: embedded_hal::digital::OutputPin,
36{
37    type TSci = TSci;
38    type TSdi = TSdi;
39    type TDreq = TDreq;
40    type TRst = TRst;
41    type Error = Error<TSci::Error, TSdi::Error, TDreq::Error, TRst::Error>;
42
43    fn take(self) -> Self {
44        self
45    }
46}
47
48/// Helper trait to represent device peripherals.
49///
50/// This is used in the implmenetation to reduce the number of generic arguments everywhere.
51pub trait Vs1003Peripherals {
52    /// The type of the command interface SPI slave
53    type TSci: embedded_hal::spi::SpiDevice;
54    /// The type of the data interface SPI slave
55    type TSdi: embedded_hal::spi::SpiDevice;
56    /// The type of the DREQ input pin
57    type TDreq: embedded_hal::digital::InputPin;
58    /// Tyhe type of the xRST output pin
59    type TRst: embedded_hal::digital::OutputPin;
60
61    /// The error type that will be returned by the driver
62    type Error: core::fmt::Debug
63        + core::error::Error
64        + From<
65            Error<
66                <Self::TSci as embedded_hal::spi::ErrorType>::Error,
67                <Self::TSdi as embedded_hal::spi::ErrorType>::Error,
68                <Self::TDreq as embedded_hal::digital::ErrorType>::Error,
69                <Self::TRst as embedded_hal::digital::ErrorType>::Error,
70            >,
71        >;
72
73    /// Return the peripheral instances
74    fn take(self) -> Peripherals<Self::TSci, Self::TSdi, Self::TDreq, Self::TRst>;
75}
76
77#[derive(Debug, thiserror::Error)]
78#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
79/// The error enum returned by most of [Vs1003] APIs
80pub enum Error<ESci, ESdi, EDreq, ERst> {
81    /// One of the methods on SCI interface failed
82    #[error("Failed to communicate over SCI")]
83    Sci(ESci),
84
85    /// One of the methods on SDI interface failed
86    #[error("Failed to communicate over SDI")]
87    Sdi(ESdi),
88
89    /// Reading of DREQ pin state failed
90    #[error("Failed to read DREQ signal")]
91    DreqRead(EDreq),
92
93    /// Setting xRST state failed
94    #[error("Failed to write xRST signal")]
95    RstWrite(ERst),
96
97    /// Attempted to perform a write while DREQ was low
98    #[error("Device is busy (DREQ is low)")]
99    Busy,
100
101    /// Operation has timed out, likely the DREQ signal didn't rise when expected
102    #[error("Timeout of {}us exceeded", .0.ticks())]
103    Timeout(fugit::MicrosDurationU32),
104}
105impl<ESci, ESdi, EDreq, ERst> From<pac::Vs1003InterfaceError<ESci, EDreq>>
106    for Error<ESci, ESdi, EDreq, ERst>
107{
108    fn from(value: pac::Vs1003InterfaceError<ESci, EDreq>) -> Self {
109        match value {
110            pac::Vs1003InterfaceError::Spi(err) => Self::Sci(err),
111            pac::Vs1003InterfaceError::Dreq(err) => Self::DreqRead(err),
112            pac::Vs1003InterfaceError::Busy => Self::Busy,
113        }
114    }
115}
116
117#[derive(thiserror::Error)]
118#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
119/// The error returned by [Vs1003] APIs that change the typesate
120pub struct ModeChangeError<T: Vs1003Peripherals> {
121    /// The actual error
122    pub error: T::Error,
123    /// The device, so that it can be reset
124    pub device: Vs1003<Errored, T>,
125}
126impl<T: Vs1003Peripherals> core::fmt::Debug for ModeChangeError<T> {
127    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128        // Too bad we can't provide different debug output depending on whether Vs1003<Errored, T> is Debug or not :(
129        f.debug_tuple("ModeChangeError").field(&self.error).finish()
130    }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
134#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
135#[repr(u8)]
136/// Specifies the clock multiplier used by the VS1003
137pub enum ClockMultiplier {
138    /// Use base input clock, no multiplication
139    Times1_0 = 0,
140    /// 1.5x
141    Times1_5 = 1,
142    /// 2.0x
143    Times2_0 = 2,
144    /// 2.5x
145    Times2_5 = 3,
146    /// 3.0x
147    Times3_0 = 4,
148    /// 3.5x
149    Times3_5 = 5,
150    /// 4.0x
151    Times4_0 = 6,
152    /// 4.5x
153    ///
154    /// Note: with the default clock of 12.228MHz this will overclock the device
155    Times4_5 = 7,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
159#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
160#[repr(u8)]
161/// Specifies the maximum clock boost (i.e. multiplier added to [ClockMultiplier])
162/// that can be used for WMA decoding.
163pub enum ClockBoost {
164    /// Do not boost the clock
165    Plus0_0 = 0,
166    /// Clock may be boosted up to [ClockMultiplier] + 0.5x
167    Plus0_5 = 1,
168    /// Clock may be boosted up to [ClockMultiplier] + 1.0x
169    Plus1_0 = 2,
170    /// Clock may be boosted up to [ClockMultiplier] + 1.5x
171    Plus1_5 = 3,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
175#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
176/// Specifies the input used for recording
177pub enum RecordingInput {
178    /// Use the LINEIN input pin
179    LineIn,
180    /// Use the MICP/MICN input pair
181    Microphone,
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
185#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
186/// Specifies the digital gain to use for recording
187pub enum RecordingGain {
188    /// Use automatic gain control
189    Auto,
190    /// Manual gain in range 64x - ~0.0001x where 1024 = 1x
191    Manual(core::num::NonZeroU16),
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
195#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
196/// Specifies whether to use a high-pass filter for recording or not
197pub enum RecordingFilter {
198    /// No filtering of lower-band
199    NoFilter,
200    /// ~300Hz high pass filter.
201    ///
202    /// Note that the datasheet says that this is only usable with 8kHz sample rate.
203    HighPass,
204}
205
206/// Typestate type for freshly constructed VS1003
207#[derive(Debug)]
208#[non_exhaustive]
209pub struct NotInitialized {}
210
211/// Typestate type for a VS1003 that failed state change
212#[derive(Debug)]
213#[non_exhaustive]
214pub struct Errored {}
215
216/// Typestate type for freshly reset VS1003
217#[derive(Debug)]
218#[non_exhaustive]
219pub struct Initialized {
220    /// The internal clock speed
221    internal_clock: fugit::KilohertzU32,
222}
223
224/// Typestate type for VS1003 in standard recording mode
225#[derive(Debug)]
226#[non_exhaustive]
227pub struct AdpcmRecording {}
228
229#[derive(Debug)]
230#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
231/// High level interface for the VS1003 audio codec
232pub struct Vs1003<State, T: Vs1003Peripherals> {
233    sci: pac::Vs1003<pac::Vs1003Interface<T::TSci, T::TDreq>>,
234    sdi: T::TSdi,
235    xrst: T::TRst,
236    state: State,
237}
238
239impl<T: Vs1003Peripherals> Vs1003<NotInitialized, T> {
240    /// Create the interface
241    pub fn new(peripherals: T) -> Self {
242        let Peripherals {
243            sci,
244            sdi,
245            dreq,
246            xrst,
247        } = peripherals.take();
248
249        Self {
250            sci: pac::Vs1003::new(pac::Vs1003Interface::new(sci, dreq)),
251            sdi,
252            xrst,
253            state: NotInitialized {},
254        }
255    }
256}
257
258impl<State, T: Vs1003Peripherals> Vs1003<State, T> {
259    /// Reset the VS1003 and initialize it's clock settings.
260    /// Per datasheet the device supports clock rates between 12 and 13 MHz,
261    /// however the configuration registers allow range 8.001 MHz - 270.14 MHz.
262    ///
263    /// The datasheet also notes that the a minimum clock of 12.228 MHz is needed
264    /// to support the maximum sampling frequency od 48kHz.
265    ///
266    /// Note: before executing this method the SCI clock speed must be set lower than `clki / 4`.
267    /// After executing this method the clock speed may be increased `base_multiplier` times.
268    ///
269    /// # Blocking
270    ///
271    /// This method may block for up to 50ms, assuming that `delay.delay_us(10)` blocks for 10us and a reasonable SPI clock speed.
272    ///
273    /// # Panics
274    ///
275    /// This method will panic in the provided clock frequency is outside
276    /// of legal range for VS1003.
277    pub fn reset_initialize(
278        mut self,
279        delay: &mut impl DelayNs,
280        xtali: fugit::KilohertzU32,
281        base_multiplier: ClockMultiplier,
282        wma_boost: ClockBoost,
283    ) -> Result<Vs1003<Initialized, T>, ModeChangeError<T>> {
284        let xtali_4k: u32 = xtali.to_kHz() / 4;
285        assert!(xtali_4k > 2_000, "Provided frequency is too low!");
286        let clock_input = xtali_4k - 2_000;
287        assert!(
288            clock_input < u16::MAX as u32,
289            "Provided frequency is too high!"
290        );
291        let clock_input = clock_input as u16;
292        let internal_clock = xtali
293            * match base_multiplier {
294                // 2x the actual clock, to avoid using floating point
295                ClockMultiplier::Times1_0 => 2,
296                ClockMultiplier::Times1_5 => 3,
297                ClockMultiplier::Times2_0 => 4,
298                ClockMultiplier::Times2_5 => 5,
299                ClockMultiplier::Times3_0 => 6,
300                ClockMultiplier::Times3_5 => 7,
301                ClockMultiplier::Times4_0 => 8,
302                ClockMultiplier::Times4_5 => 9,
303            }
304            / 2;
305
306        let mut do_reset = move |dev: &mut Self| -> Result<(), T::Error> {
307            dev.xrst.set_low().map_err(Error::RstWrite)?;
308            delay.delay_us(100);
309            dev.xrst.set_high().map_err(Error::RstWrite)?;
310            delay.delay_ms(5);
311            // According to datasheet the maximum reset time is 50000 clock cycles
312            // At 12MHz this is ~4.2ms, at 8MHz it would be 6.25ms
313            // Thus 10ms seems like a good timeout to use
314            dev.wait_for_dreq(delay, 10.millis())?;
315
316            dev.sci()
317                .clockf()
318                .modify(move |r| {
319                    r.set_multiplier(base_multiplier as u8);
320                    r.set_allowed_addition(wma_boost as u8);
321                    r.set_input_frequency(clock_input);
322                })
323                .map_err(Error::from)?;
324
325            // Datasheet specifies maximum execution time of 11000 clock cycles
326            // At 12MHz this is ~1ms, at 12 MHz ~1.4ms.
327            // Thus 5ms timeout seems like a good compromise
328            dev.wait_for_dreq(delay, 5.millis())?;
329            Ok(())
330        };
331
332        match do_reset(&mut self) {
333            Err(error) => Err(ModeChangeError {
334                error,
335                device: Vs1003 {
336                    sci: self.sci,
337                    sdi: self.sdi,
338                    xrst: self.xrst,
339                    state: Errored {},
340                },
341            }),
342            Ok(_) => Ok(Vs1003 {
343                sci: self.sci,
344                sdi: self.sdi,
345                xrst: self.xrst,
346                state: Initialized { internal_clock },
347            }),
348        }
349    }
350
351    /// Proxy for [pac::Vs1003::is_busy].
352    /// Returns true when the device cannot accept commands.
353    pub fn is_busy(&mut self) -> Result<bool, T::Error> {
354        Ok(self.sci.is_busy().map_err(Error::DreqRead)?)
355    }
356
357    /// Get the SCI interface to allow custom configuration of the device
358    pub fn sci(&mut self) -> &mut pac::Vs1003<pac::Vs1003Interface<T::TSci, T::TDreq>> {
359        &mut self.sci
360    }
361
362    fn wait_for_dreq(
363        &mut self,
364        delay: &mut impl embedded_hal::delay::DelayNs,
365        max_delay: fugit::MicrosDurationU32,
366    ) -> Result<(), T::Error> {
367        const DELAY_STEP: u32 = 10;
368
369        let mut remaining_delay_us = max_delay.ticks();
370
371        while remaining_delay_us > 0 {
372            delay.delay_us(remaining_delay_us.min(DELAY_STEP));
373            if !self.is_busy()? {
374                return Ok(());
375            }
376            remaining_delay_us = remaining_delay_us.saturating_sub(DELAY_STEP);
377        }
378
379        Err(Error::Timeout(max_delay))?
380    }
381
382    fn change_state<NewState>(self, state: NewState) -> Vs1003<NewState, T> {
383        Vs1003 {
384            sci: self.sci,
385            sdi: self.sdi,
386            xrst: self.xrst,
387            state,
388        }
389    }
390}
391
392impl<T: Vs1003Peripherals> Vs1003<Initialized, T> {
393    /// Enter the ADPCM recording mode of the device.
394    ///
395    /// In this mode the device will sample the input at provided rate and encode it as ADPCM
396    ///
397    /// The actual sample rate may differ from requested due to divider resolution.
398    ///
399    /// # Blocking
400    ///
401    /// This method may block for up to 25ms, assuming that `delay.delay_us(10)` blocks for 10us and a reasonable SPI clock speed.
402    ///
403    /// # Panics
404    ///
405    /// This call will panic if the requested rate is higher than can be achieved with the configured clock.
406    /// The maximum achievable rate is CLKI / 1024.
407    ///
408    /// This
409    pub fn begin_adpcm_recording(
410        mut self,
411        delay: &mut impl DelayNs,
412        sample_rate: fugit::HertzU32,
413        input: RecordingInput,
414        gain: RecordingGain,
415        filter: RecordingFilter,
416    ) -> Result<Vs1003<AdpcmRecording, T>, ModeChangeError<T>> {
417        let divider = self
418            .state
419            .internal_clock
420            .to_Hz()
421            .div_ceil(256 * sample_rate.to_Hz());
422        assert!(
423            divider >= 4,
424            "Requested sample rate is too high for provided clock"
425        );
426        let mut do_change = move |dev: &mut Self| -> Result<(), T::Error> {
427            // AICTRL0 is the divider
428            dev.sci
429                .ai_ctrl(0)
430                .write(|r| r.set_value(divider as u16))
431                .map_err(Error::from)?;
432
433            dev.wait_for_dreq(delay, 500.micros())?;
434
435            // AICTRL1 is the gain
436            dev.sci
437                .ai_ctrl(1)
438                .write(|r| {
439                    r.set_value(match gain {
440                        RecordingGain::Auto => 0,
441                        RecordingGain::Manual(non_zero) => non_zero.get(),
442                    })
443                })
444                .map_err(Error::from)?;
445
446            dev.wait_for_dreq(delay, 500.micros())?;
447
448            // For VS1033 AICTRL2 is the maximum gain in AGC mode. We won't write to it here, so that
449            // the user may set it before calling us. The default of 0 is maximum of 64x, which is what VS1003 implements.
450
451            dev.sci
452                .mode()
453                .modify(|r| {
454                    r.set_reset(true);
455                    r.set_adpcm(true);
456                    r.set_adpcm_hp(matches!(filter, RecordingFilter::HighPass));
457                    r.set_adpcm_input(match input {
458                        RecordingInput::LineIn => pac::AdpcmInput::LineIn,
459                        RecordingInput::Microphone => pac::AdpcmInput::Microphone,
460                    });
461                })
462                .map_err(Error::from)?;
463
464            dev.wait_for_dreq(delay, 10.millis())?;
465            Ok(())
466        };
467
468        match do_change(&mut self) {
469            Err(error) => Err(ModeChangeError {
470                error,
471                device: self.change_state(Errored {}),
472            }),
473            Ok(_) => Ok(self.change_state(AdpcmRecording {})),
474        }
475    }
476
477    /// Write audio file data to the device. This will send as much data as the device will accept
478    /// and return the number of bytes written.
479    ///
480    /// If DREQ is low on entry then returns [Error::Busy]
481    pub fn send_data(&mut self, buffer: &[u8]) -> Result<usize, T::Error> {
482        if self.is_busy()? {
483            return Err(Error::Busy)?;
484        }
485
486        // Datasheet specifies that low DREQ means that the device can receive at least 32 bytes of data
487        const CHUNK_SIZE: usize = 32;
488
489        let mut remaining = buffer;
490
491        while !remaining.is_empty() {
492            let (taken, leftover) = remaining.split_at(CHUNK_SIZE.min(remaining.len()));
493            remaining = leftover;
494
495            self.sdi.write(taken).map_err(Error::Sdi)?;
496
497            // If DREQ fell during the transfer we shal not transfer any more data
498            if self.is_busy()? {
499                break;
500            }
501        }
502
503        Ok(buffer.len() - remaining.len())
504    }
505
506    /// Changes the typestate to errored.
507    /// Can be useful for simplification of retry loops.
508    pub fn into_errored_state(self) -> Vs1003<Errored, T> {
509        self.change_state(Errored {})
510    }
511}
512
513impl<T: Vs1003Peripherals> Vs1003<AdpcmRecording, T> {
514    /// Changes the typestate to errored.
515    /// Can be useful for simplification of retry loops.
516    pub fn into_errored_state(self) -> Vs1003<Errored, T> {
517        self.change_state(Errored {})
518    }
519
520    /// Checks how many samples are ready to be read.
521    ///
522    /// The internal buffer has size of 1024 words, so that is the maximum value you can read.
523    /// The datasheet recommends to not attempt reading if you see a value >= 896 to avoid block aliasing.
524    pub fn get_ready_sample_count(&mut self) -> Result<usize, T::Error> {
525        self.sci
526            .hdat_1()
527            .read()
528            .map(|v| v.value() as usize)
529            .map_err(Error::from)
530            .map_err(Into::into)
531    }
532
533    /// Read however many samples the device has into the buffer.
534    /// Returns the number of words read.
535    ///
536    /// If you convert these sample to u8 buffers later then remeber that they need to be stored
537    /// as big endian i.e. 0x1234 => 0x12 0x34 as these are not raw 16-bit samples, but rather 4, 4-bit values
538    /// stored as one.
539    ///
540    /// Delay is needed to have an upper bound when waiting for DREQ signal.
541    ///
542    /// It is advised to always read whole blocks (128 words), so that you do not loose
543    /// block synchronization. It is much better to drop a block as the compression scheme
544    /// relies on blocks.
545    ///
546    /// The device always puts whole blocks into the buffer.
547    pub fn read_samples(
548        &mut self,
549        delay: &mut impl DelayNs,
550        buffer: &mut [u16],
551    ) -> Result<usize, T::Error> {
552        let total_samples = self.get_ready_sample_count()?.min(buffer.len());
553        for sample in &mut buffer[0..total_samples] {
554            self.wait_for_dreq(delay, 100.micros())?;
555            *sample = self.sci.hdat_0().read().map_err(Error::from)?.value();
556        }
557
558        Ok(total_samples)
559    }
560
561    /// Same as [Self::read_samples], but takes a u8 buffer instead of u16.
562    /// Returns the number of bytes read (always a multiple of 2).
563    ///
564    /// See the other method for additional information.
565    pub fn read_samples_bytes(
566        &mut self,
567        delay: &mut impl DelayNs,
568        buffer: &mut [u8],
569    ) -> Result<usize, T::Error> {
570        let total_samples = self.get_ready_sample_count()?.min(buffer.len() / 2);
571        for i in 0..total_samples {
572            self.wait_for_dreq(delay, 100.micros())?;
573            let sample = self.sci.hdat_0().read().map_err(Error::from)?.value();
574            let offset = i * 2;
575            buffer[offset..=offset + 1].copy_from_slice(&sample.to_be_bytes());
576        }
577
578        Ok(total_samples * 2)
579    }
580}