#![no_std]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#[derive(Debug, Clone)]
pub struct Vs1003Interface<TCsi, TDreq> {
csi: TCsi,
dreq: TDreq,
}
#[derive(thiserror::Error, Debug)]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
pub enum Vs1003InterfaceError<ESpi, EDreq> {
#[error("SPI error")]
Spi(#[source] ESpi),
#[error("Digital input error")]
Dreq(#[source] EDreq),
#[error("VS1003 is busy (DREQ is low)")]
Busy,
}
impl<TCsi, TDreq> Vs1003Interface<TCsi, TDreq> {
pub const fn new(interface: TCsi, dreq: TDreq) -> Self {
Vs1003Interface {
csi: interface,
dreq,
}
}
}
impl<TCsi, TDreq> Vs1003Interface<TCsi, TDreq>
where
TDreq: embedded_hal::digital::InputPin,
{
pub fn is_busy(&mut self) -> Result<bool, TDreq::Error> {
self.dreq.is_low()
}
}
impl<TCsi, TDreq> Vs1003Interface<TCsi, TDreq>
where
TDreq: embedded_hal_async::digital::Wait,
{
pub async fn wait_until_ready(&mut self) -> Result<(), TDreq::Error> {
self.dreq.wait_for_high().await
}
}
impl<TCsi, TDreq> device_driver::RegisterInterface for Vs1003Interface<TCsi, TDreq>
where
TCsi: embedded_hal::spi::SpiDevice<u8>,
TDreq: embedded_hal::digital::InputPin,
{
type Error = Vs1003InterfaceError<TCsi::Error, TDreq::Error>;
type AddressType = u8;
fn write_register(
&mut self,
address: Self::AddressType,
size_bits: u32,
data: &[u8],
) -> Result<(), Self::Error> {
assert_eq!(size_bits, 16);
assert_eq!(data.len(), 2);
if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
return Err(Vs1003InterfaceError::Busy);
}
let setup = [2u8, address, data[0], data[1]];
self.csi.write(&setup).map_err(Vs1003InterfaceError::Spi)
}
fn read_register(
&mut self,
address: Self::AddressType,
size_bits: u32,
data: &mut [u8],
) -> Result<(), Self::Error> {
use embedded_hal::spi::Operation as Op;
assert_eq!(size_bits, 16);
assert_eq!(data.len(), 2);
if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
return Err(Vs1003InterfaceError::Busy);
}
let setup = [3u8, address];
self.csi
.transaction(&mut [Op::Write(&setup), Op::Read(data)])
.map_err(Vs1003InterfaceError::Spi)
}
}
impl<TCsi, TDreq> device_driver::AsyncRegisterInterface for Vs1003Interface<TCsi, TDreq>
where
TCsi: embedded_hal_async::spi::SpiDevice<u8>,
TDreq: embedded_hal::digital::InputPin,
{
type Error = Vs1003InterfaceError<TCsi::Error, TDreq::Error>;
type AddressType = u8;
async fn write_register(
&mut self,
address: Self::AddressType,
size_bits: u32,
data: &[u8],
) -> Result<(), Self::Error> {
assert_eq!(size_bits, 16);
assert_eq!(data.len(), 2);
if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
return Err(Vs1003InterfaceError::Busy);
}
let setup = [2u8, address, data[0], data[1]];
self.csi
.write(&setup)
.await
.map_err(Vs1003InterfaceError::Spi)
}
async fn read_register(
&mut self,
address: Self::AddressType,
size_bits: u32,
data: &mut [u8],
) -> Result<(), Self::Error> {
use embedded_hal::spi::Operation as Op;
assert_eq!(size_bits, 16);
assert_eq!(data.len(), 2);
if !self.dreq.is_high().map_err(Vs1003InterfaceError::Dreq)? {
return Err(Vs1003InterfaceError::Busy);
}
let setup = [3u8, address];
self.csi
.transaction(&mut [Op::Write(&setup), Op::Read(data)])
.await
.map_err(Vs1003InterfaceError::Spi)
}
}
impl<TCsi, TDreq> Vs1003<Vs1003Interface<TCsi, TDreq>>
where
TDreq: embedded_hal::digital::InputPin,
{
pub fn is_busy(&mut self) -> Result<bool, TDreq::Error> {
self.interface.is_busy()
}
}
impl<TCsi, TDreq> Vs1003<Vs1003Interface<TCsi, TDreq>>
where
TDreq: embedded_hal::digital::InputPin + embedded_hal_async::digital::Wait,
{
pub async fn wait_until_ready(&mut self) -> Result<(), TDreq::Error> {
self.interface.dreq.wait_for_high().await
}
}
device_driver::create_device!(
device_name: Vs1003,
dsl: {
config {
type RegisterAddressType = u8;
type DefaultByteOrder = BE;
type DefmtFeature = "defmt-03";
}
register Raw {
const ADDRESS = 0x1;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
const REPEAT = {
count: 16,
stride: 1,
};
value: uint = 0..16,
},
register Mode {
const ADDRESS = 0x0;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
differential: bool = 0,
reserved: bool = 1,
reset: bool = 2,
out_of_wav: bool = 3,
power_down: bool = 4,
allow_tests: bool = 5,
stream_mode: bool = 6,
reserved_2: bool = 7,
dclk_active_edge: uint as enum DclkEdge {
Rising = 0,
Falling = 1,
} = 8..=8,
sdi_bit_order: uint as enum SdiBitOrder {
MsbFirst = 0,
LsbFirst = 1,
} = 9..=9,
sdi_share: bool = 10,
sdi_new: bool = 11,
adpcm: bool = 12,
adpcm_hp: bool = 13,
adpcm_input: uint as enum AdpcmInput {
Microphone = 0,
LineIn = 1,
} = 14..=14,
},
register Status {
const ADDRESS = 0x1;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
version: uint as enum Version {
Vs1001 = 0,
Vs1011 = 1,
Vs1002 = 2,
Vs1003 = 3,
Vs1053 = 5,
Vs1063 = 6,
Unknown = catch_all
} = 4..=7,
analog_driver_powerdown: bool = 3,
analog_internal_powerdown: bool = 2,
analog_volume: uint = 0..=1,
},
register Bass {
const ADDRESS = 0x2;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
treble_amplitude: int = 12..=15,
treble_bottom_frequency: uint = 8..=11,
bass_amplitude: uint = 4..=7,
bass_bottom_frequency: uint = 0..=3,
},
register Clockf {
const ADDRESS = 0x3;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
multiplier: uint = 13..=15,
allowed_addition: uint = 11..=12,
input_frequency: uint = 0..=10,
},
register DecodeTime {
const ADDRESS = 0x4;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
value: uint = 0..16,
},
register Audata {
const ADDRESS = 0x5;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
stereo: bool = 0,
sample_rate: uint = 1..16,
},
register Wram {
const ADDRESS = 0x6;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
value: uint = 0..16,
},
register WramAddr {
type Access = WO;
const ADDRESS = 0x7;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
value: uint = 0..16,
},
register Hdat0 {
type Access = RO;
const ADDRESS = 0x8;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
value: uint = 0..16,
},
register Hdat1 {
type Access = RO;
const ADDRESS = 0x9;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
value: uint = 0..16,
},
register AiAddr {
const ADDRESS = 0xA;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
value: uint = 0..16,
},
register Vol {
const ADDRESS = 0xB;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
right: uint = 0..8,
left: uint = 8..16,
},
register AiCtrl {
const ADDRESS = 0xC;
const SIZE_BITS = 16;
const ALLOW_ADDRESS_OVERLAP = true;
const REPEAT = {
count: 4,
stride: 1,
};
value: uint = 0..16,
},
}
);