vs1003_pac/lib.rs
1#![no_std]
2#![warn(missing_debug_implementations)]
3#![warn(missing_docs)]
4
5//! The low-level peripheral access definitions for VS1003 codec.
6//! Some of those definition may also work for other chips in the family, however
7//! there are some incompatible changes between them.
8//!
9//! Each method checks the DREQ pin and if it is low it will return [`Vs1003InterfaceError::Busy`] if so.
10//! Please note though, that the device may not lower this pin immediately after a successful transfer.
11//! This time doesn't seem well-specified, however on the forums they seem to recommend a 1us delay after
12//! completion of a command and before checking the DREQ pin.
13
14#[derive(Debug, Clone)]
15/// The necessary interfaces to communicate with a VS1003 chip.
16pub struct Vs1003Interface<TCsi, TDreq> {
17 csi: TCsi,
18 dreq: TDreq,
19}
20
21#[derive(thiserror::Error, Debug)]
22#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
23/// The error type that can be returned by various interface APIs
24pub enum Vs1003InterfaceError<ESpi, EDreq> {
25 /// The SPI peripheral has failed to compelte a transfer
26 #[error("SPI error")]
27 Spi(#[source] ESpi),
28 /// The Digital input failed to provide a value
29 #[error("Digital input error")]
30 Dreq(#[source] EDreq),
31 /// The DREQ signal is low and the command cannot be sent
32 #[error("VS1003 is busy (DREQ is low)")]
33 Busy,
34}
35
36impl<TCsi, TDreq> Vs1003Interface<TCsi, TDreq> {
37 /// Create a new interface.
38 ///
39 /// While you can pass anything here tou should pass appropriate HAL
40 /// stucts that implement necessary traits from [`embedded_hal`] and/or [`embedded_hal_async`].
41 pub const fn new(interface: TCsi, dreq: TDreq) -> Self {
42 Vs1003Interface {
43 csi: interface,
44 dreq,
45 }
46 }
47}
48
49impl<TCsi, TDreq> Vs1003Interface<TCsi, TDreq>
50where
51 TDreq: embedded_hal::digital::InputPin,
52{
53 /// Check if the device can accept commands or data
54 pub fn is_busy(&mut self) -> Result<bool, TDreq::Error> {
55 self.dreq.is_low()
56 }
57}
58
59impl<TCsi, TDreq> Vs1003Interface<TCsi, TDreq>
60where
61 TDreq: embedded_hal_async::digital::Wait,
62{
63 /// Wait until the device is ready to accept commands or data.
64 /// If the device is ready returns immediately
65 pub async fn wait_until_ready(&mut self) -> Result<(), TDreq::Error> {
66 self.dreq.wait_for_high().await
67 }
68}
69
70impl<TCsi, TDreq> device_driver::RegisterInterface for Vs1003Interface<TCsi, TDreq>
71where
72 TCsi: embedded_hal::spi::SpiDevice<u8>,
73 TDreq: embedded_hal::digital::InputPin,
74{
75 type Error = Vs1003InterfaceError<TCsi::Error, TDreq::Error>;
76 type AddressType = u8;
77
78 fn write_register(
79 &mut self,
80 address: Self::AddressType,
81 size_bits: u32,
82 data: &[u8],
83 ) -> Result<(), Self::Error> {
84 assert_eq!(size_bits, 16);
85 assert_eq!(data.len(), 2);
86
87 if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
88 return Err(Vs1003InterfaceError::Busy);
89 }
90
91 let setup = [2u8, address, data[0], data[1]];
92
93 self.csi.write(&setup).map_err(Vs1003InterfaceError::Spi)
94 }
95
96 fn read_register(
97 &mut self,
98 address: Self::AddressType,
99 size_bits: u32,
100 data: &mut [u8],
101 ) -> Result<(), Self::Error> {
102 use embedded_hal::spi::Operation as Op;
103
104 assert_eq!(size_bits, 16);
105 assert_eq!(data.len(), 2);
106
107 if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
108 return Err(Vs1003InterfaceError::Busy);
109 }
110
111 let setup = [3u8, address];
112 self.csi
113 .transaction(&mut [Op::Write(&setup), Op::Read(data)])
114 .map_err(Vs1003InterfaceError::Spi)
115 }
116}
117
118impl<TCsi, TDreq> device_driver::AsyncRegisterInterface for Vs1003Interface<TCsi, TDreq>
119where
120 TCsi: embedded_hal_async::spi::SpiDevice<u8>,
121 TDreq: embedded_hal::digital::InputPin,
122{
123 type Error = Vs1003InterfaceError<TCsi::Error, TDreq::Error>;
124 type AddressType = u8;
125
126 async fn write_register(
127 &mut self,
128 address: Self::AddressType,
129 size_bits: u32,
130 data: &[u8],
131 ) -> Result<(), Self::Error> {
132 assert_eq!(size_bits, 16);
133 assert_eq!(data.len(), 2);
134
135 if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
136 return Err(Vs1003InterfaceError::Busy);
137 }
138
139 let setup = [2u8, address, data[0], data[1]];
140
141 self.csi
142 .write(&setup)
143 .await
144 .map_err(Vs1003InterfaceError::Spi)
145 }
146
147 async fn read_register(
148 &mut self,
149 address: Self::AddressType,
150 size_bits: u32,
151 data: &mut [u8],
152 ) -> Result<(), Self::Error> {
153 use embedded_hal::spi::Operation as Op;
154
155 assert_eq!(size_bits, 16);
156 assert_eq!(data.len(), 2);
157
158 if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
159 return Err(Vs1003InterfaceError::Busy);
160 }
161
162 let setup = [3u8, address];
163 self.csi
164 .transaction(&mut [Op::Write(&setup), Op::Read(data)])
165 .await
166 .map_err(Vs1003InterfaceError::Spi)
167 }
168}
169
170impl<TCsi, TDreq> Vs1003<Vs1003Interface<TCsi, TDreq>>
171where
172 TDreq: embedded_hal::digital::InputPin,
173{
174 /// Check if the device is ready to accept commands or data
175 ///
176 /// Other functions may fail with [`Vs1003InterfaceError::Busy`] if this returns `Ok(false)`
177 pub fn is_busy(&mut self) -> Result<bool, TDreq::Error> {
178 self.interface.is_busy()
179 }
180}
181
182impl<TCsi, TDreq> Vs1003<Vs1003Interface<TCsi, TDreq>>
183where
184 TDreq: embedded_hal::digital::InputPin + embedded_hal_async::digital::Wait,
185{
186 /// Asynchronously waits until the device is ready to accept commands and data
187 pub async fn wait_until_ready(&mut self) -> Result<(), TDreq::Error> {
188 self.interface.dreq.wait_for_high().await
189 }
190}
191
192device_driver::create_device!(
193 device_name: Vs1003,
194 dsl: {
195 config {
196 type RegisterAddressType = u8;
197 type DefaultByteOrder = BE;
198 type DefmtFeature = "defmt-03";
199 }
200 /// Access any register to read or write a custom value.
201 ///
202 /// This is especially useful when uploading plugins as they are specified in
203 /// register address + value to write format.
204 register Raw {
205 const ADDRESS = 0x1;
206 const SIZE_BITS = 16;
207 const ALLOW_ADDRESS_OVERLAP = true;
208 const REPEAT = {
209 count: 16,
210 stride: 1,
211 };
212
213 /// The value
214 value: uint = 0..16,
215 },
216 /// The SCI_MODE register - controls various aspects of operation of the VS1003
217 register Mode {
218 const ADDRESS = 0x0;
219 const SIZE_BITS = 16;
220 const ALLOW_ADDRESS_OVERLAP = true;
221
222 /// If true invert left channel for differential output
223 differential: bool = 0,
224
225 /// Reserved - always set to zero.
226 reserved: bool = 1,
227
228 /// Set to true to initiate a software reset.
229 reset: bool = 2,
230
231 /// Jump out of WAV decoding
232 out_of_wav: bool = 3,
233
234 power_down: bool = 4,
235
236 /// Allow SDI tests
237 allow_tests: bool = 5,
238
239 /// Enable stream mode
240 stream_mode: bool = 6,
241
242 /// Reserved - always set to zero.
243 reserved_2: bool = 7,
244
245 /// Active edge for SDI interface
246 dclk_active_edge: uint as enum DclkEdge {
247 Rising = 0,
248 Falling = 1,
249 } = 8..=8,
250
251 /// The bit order for the SDI interface
252 sdi_bit_order: uint as enum SdiBitOrder {
253 MsbFirst = 0,
254 LsbFirst = 1,
255 } = 9..=9,
256
257 /// Whether to share xCS with xDCS (i.e. SCI & SDI interfaces).
258 ///
259 /// If true the xDCS signal is generated by inverting xCS.
260 sdi_share: bool = 10,
261
262 /// Whether to use VS10xx native mode.
263 sdi_new: bool = 11,
264
265 /// Set to true along with reset to start ADPCM recording session.
266 adpcm: bool = 12,
267
268 /// Enable or disable high-pass filter for recording.
269 adpcm_hp: bool = 13,
270
271 /// The recording input
272 adpcm_input: uint as enum AdpcmInput {
273 Microphone = 0,
274 LineIn = 1,
275 } = 14..=14,
276 },
277 /// The SCI_STATUS register
278 register Status {
279 const ADDRESS = 0x1;
280 const SIZE_BITS = 16;
281 const ALLOW_ADDRESS_OVERLAP = true;
282
283 /// The connected device
284 version: uint as enum Version {
285 Vs1001 = 0,
286 Vs1011 = 1,
287 Vs1002 = 2,
288 Vs1003 = 3,
289 Vs1053 = 5,
290 Vs1063 = 6,
291 Unknown = catch_all
292 } = 4..=7,
293
294 /// Controls the analog driver powerdown.
295 /// Normally controlled by the system firmware, however
296 /// can be set to 1 a few milliseconds before reset to reduce transients.
297 analog_driver_powerdown: bool = 3,
298
299 /// Controls internal analog powerdown.
300 /// This is meant for system firmware use only.
301 analog_internal_powerdown: bool = 2,
302
303 /// Analog volumen control.
304 /// This is meant for system firmware use only.
305 ///
306 /// 0 = -0 dB, 1 = -6 dB, 3 = -12 dB
307 analog_volume: uint = 0..=1,
308 },
309
310 /// Bass enhancer settings
311 register Bass {
312 const ADDRESS = 0x2;
313 const SIZE_BITS = 16;
314 const ALLOW_ADDRESS_OVERLAP = true;
315
316 /// Treble control in 1.5 dB steps.
317 /// Set to 0 to disable.
318 treble_amplitude: int = 12..=15,
319
320 /// Lower limit frequency in 1kHz steps.
321 /// ex. When set to 10 the DSP will enhance frequencies >=10kHz
322 treble_bottom_frequency: uint = 8..=11,
323
324 /// Bass enhancement in 1 dB steps.
325 /// Set to 0 to disable.
326 bass_amplitude: uint = 4..=7,
327
328 /// Lower limit frequency in 10Hz steps.
329 /// Range: 2..=15
330 ///
331 /// Typically should be set to the lowest frequency that the audio
332 /// system can reproduce.
333 bass_bottom_frequency: uint = 0..=3,
334 },
335 /// Register responsible for controlling the clock settings
336 register Clockf {
337 const ADDRESS = 0x3;
338 const SIZE_BITS = 16;
339 const ALLOW_ADDRESS_OVERLAP = true;
340
341 /// CLKI multiplier.
342 ///
343 /// CLKI = XTALI * (1 + (multiplier/2)).
344 ///
345 /// I.e. 0 = 1.0x, 7 = 4.5x
346 multiplier: uint = 13..=15,
347
348 /// Maximum extra multiplier for WMA decoding in 0.5x steps.
349 ///
350 /// 0 - no extra multiplier, 3 - up to +1.5x
351 allowed_addition: uint = 11..=12,
352
353 /// Set XTALI frequency input.
354 ///
355 /// Set to:
356 ///
357 /// input_frequency = (XTALI - 8_000_000) / 4_000
358 ///
359 /// where XTALI - clock frequency in Hz.
360 /// Default value 0 is a synonym for the default 12.228 MHz
361 input_frequency: uint = 0..=10,
362 },
363 /// The current decode time in full seconds.
364 ///
365 /// The user may change the value of this register.
366 /// In that case the new value should be written twice.
367 register DecodeTime {
368 const ADDRESS = 0x4;
369 const SIZE_BITS = 16;
370 const ALLOW_ADDRESS_OVERLAP = true;
371
372 /// The current decode time in seconds.
373 value: uint = 0..16,
374 },
375 /// Contains information about the sample rate and number of channels.
376 ///
377 /// Can be written to overwrite the information.
378 register Audata {
379 const ADDRESS = 0x5;
380 const SIZE_BITS = 16;
381 const ALLOW_ADDRESS_OVERLAP = true;
382
383 /// If the sample rate is even: true = stereo, false = mono
384 /// If the sample rate is odd: true = mono, false = stereo
385 ///
386 /// This is due to a bug in the system firmware in VS1003b.
387 stereo: bool = 0,
388 sample_rate: uint = 1..16,
389 },
390 /// Wram is used to upload application programs and data to instruction and data RAMs.
391 /// The start address must be initialized by writing to WramAddr prior to the first write/read
392 /// of Wram. As 16 bits of data can be transferred with one Wram write/read, and the
393 /// instruction word is 32 bits long, two consecutive writes/reads are needed for each instruction
394 /// word. The byte order is big-endian (i.e. most significant words first). After each full-word
395 /// write/read, the internal pointer is autoincremented.
396 register Wram {
397 const ADDRESS = 0x6;
398 const SIZE_BITS = 16;
399 const ALLOW_ADDRESS_OVERLAP = true;
400
401 value: uint = 0..16,
402 },
403 /// WramAddr is used to set the program address for following Wram writes/reads.
404 /// Address offset of 0 is used for X, 0x4000 for Y, and 0x8000 for instruction memory. Peripheral
405 /// registers can also be accessed
406 register WramAddr {
407 type Access = WO;
408
409 const ADDRESS = 0x7;
410 const SIZE_BITS = 16;
411 const ALLOW_ADDRESS_OVERLAP = true;
412
413 /// The read/write pointer
414 value: uint = 0..16,
415 },
416 /// Value depends on decoded file type and running application.
417 /// Check the datasheet for details.
418 register Hdat0 {
419 type Access = RO;
420
421 const ADDRESS = 0x8;
422 const SIZE_BITS = 16;
423 const ALLOW_ADDRESS_OVERLAP = true;
424
425 /// The value
426 value: uint = 0..16,
427 },
428 /// Value depends on decoded file type and running application.
429 /// Check the datasheet for details.
430 register Hdat1 {
431 type Access = RO;
432
433 const ADDRESS = 0x9;
434 const SIZE_BITS = 16;
435 const ALLOW_ADDRESS_OVERLAP = true;
436
437 /// The value
438 value: uint = 0..16,
439 },
440
441 /// AiAddr indicates the start address of the application code written earlier with WramAddr
442 /// and Wram registers. If no application code is used, this register should not be initialized,
443 /// or it should be initialized to zero. For more details, see Application Notes for VS10XX.
444 register AiAddr {
445 const ADDRESS = 0xA;
446 const SIZE_BITS = 16;
447 const ALLOW_ADDRESS_OVERLAP = true;
448
449 /// The application start pointer.
450 value: uint = 0..16,
451 },
452 /// Volume control.
453 ///
454 /// Setting both channels to 255 will activate analog powerdown mode.
455 register Vol {
456 const ADDRESS = 0xB;
457 const SIZE_BITS = 16;
458 const ALLOW_ADDRESS_OVERLAP = true;
459
460 /// Right channel volume in -0.5 dB steps.
461 /// 0 = 0 dB, 254 = -127 dB
462 right: uint = 0..8,
463
464 /// Left channel volume in -0.5 dB steps.
465 /// 0 = 0 dB, 254 = -127 dB
466 left: uint = 8..16,
467 },
468 /// For communication with user's application on the VS1003
469 register AiCtrl {
470 const ADDRESS = 0xC;
471 const SIZE_BITS = 16;
472 const ALLOW_ADDRESS_OVERLAP = true;
473 const REPEAT = {
474 count: 4,
475 stride: 1,
476 };
477
478 /// The value
479 value: uint = 0..16,
480 },
481 }
482);