2023-09-29 21:08:07 -05:00
|
|
|
#![macro_use]
|
|
|
|
|
|
|
|
use embassy_embedded_hal::SetConfig;
|
|
|
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
|
|
|
|
2023-09-29 21:53:01 -05:00
|
|
|
pub use crate::dma::word;
|
2023-09-30 19:43:44 -05:00
|
|
|
use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer};
|
2023-09-29 21:08:07 -05:00
|
|
|
use crate::gpio::sealed::{AFType, Pin as _};
|
|
|
|
use crate::gpio::AnyPin;
|
|
|
|
use crate::pac::sai::{vals, Sai as Regs};
|
|
|
|
use crate::rcc::RccPeripheral;
|
|
|
|
use crate::{peripherals, Peripheral};
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
|
|
pub enum Error {
|
|
|
|
NotATransmitter,
|
|
|
|
NotAReceiver,
|
2023-09-29 21:27:15 -05:00
|
|
|
OverrunError,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ringbuffer::OverrunError> for Error {
|
|
|
|
fn from(_: ringbuffer::OverrunError) -> Self {
|
|
|
|
Self::OverrunError
|
|
|
|
}
|
2023-09-29 21:08:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum SyncBlock {
|
|
|
|
None,
|
|
|
|
Sai1BlockA,
|
|
|
|
Sai1BlockB,
|
|
|
|
Sai2BlockA,
|
|
|
|
Sai2BlockB,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum SyncIn {
|
|
|
|
None,
|
|
|
|
ChannelZero,
|
|
|
|
ChannelOne,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum Mode {
|
|
|
|
Master,
|
|
|
|
Slave,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
2023-09-30 19:43:44 -05:00
|
|
|
pub enum TxRx {
|
|
|
|
Transmitter,
|
2023-09-29 21:08:07 -05:00
|
|
|
Receiver,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Mode {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
const fn mode(&self, tx_rx: TxRx) -> vals::Mode {
|
|
|
|
match tx_rx {
|
2023-09-30 19:43:44 -05:00
|
|
|
TxRx::Transmitter => match self {
|
2023-09-29 21:08:07 -05:00
|
|
|
Mode::Master => vals::Mode::MASTERTX,
|
|
|
|
Mode::Slave => vals::Mode::SLAVETX,
|
|
|
|
},
|
|
|
|
TxRx::Receiver => match self {
|
|
|
|
Mode::Master => vals::Mode::MASTERRX,
|
|
|
|
Mode::Slave => vals::Mode::SLAVERX,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum SlotSize {
|
|
|
|
DataSize,
|
|
|
|
/// 16 bit data length on 16 bit wide channel
|
|
|
|
Channel16,
|
|
|
|
/// 16 bit data length on 32 bit wide channel
|
|
|
|
Channel32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SlotSize {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn slotsz(&self) -> vals::Slotsz {
|
|
|
|
match self {
|
|
|
|
SlotSize::DataSize => vals::Slotsz::DATASIZE,
|
|
|
|
SlotSize::Channel16 => vals::Slotsz::BIT16,
|
|
|
|
SlotSize::Channel32 => vals::Slotsz::BIT32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum DataSize {
|
|
|
|
Data8,
|
|
|
|
Data10,
|
|
|
|
Data16,
|
|
|
|
Data20,
|
|
|
|
Data24,
|
|
|
|
Data32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DataSize {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn ds(&self) -> vals::Ds {
|
|
|
|
match self {
|
|
|
|
DataSize::Data8 => vals::Ds::BIT8,
|
|
|
|
DataSize::Data10 => vals::Ds::BIT10,
|
|
|
|
DataSize::Data16 => vals::Ds::BIT16,
|
|
|
|
DataSize::Data20 => vals::Ds::BIT20,
|
|
|
|
DataSize::Data24 => vals::Ds::BIT24,
|
|
|
|
DataSize::Data32 => vals::Ds::BIT32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum FifoThreshold {
|
|
|
|
Empty,
|
|
|
|
Quarter,
|
|
|
|
Half,
|
|
|
|
ThreeQuarters,
|
|
|
|
Full,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FifoThreshold {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn fth(&self) -> vals::Fth {
|
|
|
|
match self {
|
|
|
|
FifoThreshold::Empty => vals::Fth::EMPTY,
|
|
|
|
FifoThreshold::Quarter => vals::Fth::QUARTER1,
|
|
|
|
FifoThreshold::Half => vals::Fth::QUARTER2,
|
|
|
|
FifoThreshold::ThreeQuarters => vals::Fth::QUARTER3,
|
|
|
|
FifoThreshold::Full => vals::Fth::FULL,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum FifoLevel {
|
|
|
|
Empty,
|
|
|
|
FirstQuarter,
|
|
|
|
SecondQuarter,
|
|
|
|
ThirdQuarter,
|
|
|
|
FourthQuarter,
|
|
|
|
Full,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
impl From<vals::Flvl> for FifoLevel {
|
|
|
|
fn from(flvl: vals::Flvl) -> Self {
|
|
|
|
match flvl {
|
|
|
|
vals::Flvl::EMPTY => FifoLevel::Empty,
|
|
|
|
vals::Flvl::QUARTER1 => FifoLevel::FirstQuarter,
|
|
|
|
vals::Flvl::QUARTER2 => FifoLevel::SecondQuarter,
|
|
|
|
vals::Flvl::QUARTER3 => FifoLevel::ThirdQuarter,
|
|
|
|
vals::Flvl::QUARTER4 => FifoLevel::FourthQuarter,
|
|
|
|
vals::Flvl::FULL => FifoLevel::Full,
|
|
|
|
_ => FifoLevel::Empty,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum MuteDetection {
|
|
|
|
NoMute,
|
|
|
|
Mute,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum MuteValue {
|
|
|
|
Zero,
|
|
|
|
LastValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MuteValue {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn muteval(&self) -> vals::Muteval {
|
|
|
|
match self {
|
|
|
|
MuteValue::Zero => vals::Muteval::SENDZERO,
|
|
|
|
MuteValue::LastValue => vals::Muteval::SENDLAST,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum OverUnderStatus {
|
|
|
|
NoError,
|
|
|
|
OverUnderRunDetected,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum Protocol {
|
|
|
|
Free,
|
|
|
|
Spdif,
|
|
|
|
Ac97,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Protocol {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn prtcfg(&self) -> vals::Prtcfg {
|
|
|
|
match self {
|
|
|
|
Protocol::Free => vals::Prtcfg::FREE,
|
|
|
|
Protocol::Spdif => vals::Prtcfg::SPDIF,
|
|
|
|
Protocol::Ac97 => vals::Prtcfg::AC97,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum SyncEnable {
|
|
|
|
Asynchronous,
|
|
|
|
/// Syncs with the other A/B sub-block within the SAI unit
|
|
|
|
Internal,
|
|
|
|
/// Syncs with a sub-block in the other SAI unit - use set_sync_output() and set_sync_input()
|
2023-09-30 19:43:44 -05:00
|
|
|
#[cfg(any(sai_v4))]
|
2023-09-29 21:08:07 -05:00
|
|
|
External,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SyncEnable {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn syncen(&self) -> vals::Syncen {
|
|
|
|
match self {
|
|
|
|
SyncEnable::Asynchronous => vals::Syncen::ASYNCHRONOUS,
|
|
|
|
SyncEnable::Internal => vals::Syncen::INTERNAL,
|
2023-09-30 19:43:44 -05:00
|
|
|
#[cfg(any(sai_v4))]
|
2023-09-29 21:08:07 -05:00
|
|
|
SyncEnable::External => vals::Syncen::EXTERNAL,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
|
|
pub enum StereoMono {
|
|
|
|
Stereo,
|
|
|
|
Mono,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StereoMono {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn mono(&self) -> vals::Mono {
|
|
|
|
match self {
|
|
|
|
StereoMono::Stereo => vals::Mono::STEREO,
|
|
|
|
StereoMono::Mono => vals::Mono::MONO,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum BitOrder {
|
|
|
|
LsbFirst,
|
|
|
|
MsbFirst,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BitOrder {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn lsbfirst(&self) -> vals::Lsbfirst {
|
|
|
|
match self {
|
|
|
|
BitOrder::LsbFirst => vals::Lsbfirst::LSBFIRST,
|
|
|
|
BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum FrameSyncOffset {
|
|
|
|
/// This is used in modes other than standard I2S phillips mode
|
|
|
|
OnFirstBit,
|
|
|
|
/// This is used in standard I2S phillips mode
|
|
|
|
BeforeFirstBit,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FrameSyncOffset {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn fsoff(&self) -> vals::Fsoff {
|
|
|
|
match self {
|
|
|
|
FrameSyncOffset::OnFirstBit => vals::Fsoff::ONFIRST,
|
|
|
|
FrameSyncOffset::BeforeFirstBit => vals::Fsoff::BEFOREFIRST,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum FrameSyncPolarity {
|
|
|
|
ActiveLow,
|
|
|
|
ActiveHigh,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FrameSyncPolarity {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn fspol(&self) -> vals::Fspol {
|
|
|
|
match self {
|
|
|
|
FrameSyncPolarity::ActiveLow => vals::Fspol::FALLINGEDGE,
|
|
|
|
FrameSyncPolarity::ActiveHigh => vals::Fspol::RISINGEDGE,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum FrameSyncDefinition {
|
|
|
|
StartOfFrame,
|
|
|
|
ChannelIdentification,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FrameSyncDefinition {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn fsdef(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
FrameSyncDefinition::StartOfFrame => false,
|
|
|
|
FrameSyncDefinition::ChannelIdentification => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum ClockStrobe {
|
|
|
|
Falling,
|
|
|
|
Rising,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ClockStrobe {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn ckstr(&self) -> vals::Ckstr {
|
|
|
|
match self {
|
|
|
|
ClockStrobe::Falling => vals::Ckstr::FALLINGEDGE,
|
|
|
|
ClockStrobe::Rising => vals::Ckstr::RISINGEDGE,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum ComplementFormat {
|
|
|
|
OnesComplement,
|
|
|
|
TwosComplement,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ComplementFormat {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn cpl(&self) -> vals::Cpl {
|
|
|
|
match self {
|
|
|
|
ComplementFormat::OnesComplement => vals::Cpl::ONESCOMPLEMENT,
|
|
|
|
ComplementFormat::TwosComplement => vals::Cpl::TWOSCOMPLEMENT,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum Companding {
|
|
|
|
None,
|
|
|
|
MuLaw,
|
|
|
|
ALaw,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Companding {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn comp(&self) -> vals::Comp {
|
|
|
|
match self {
|
|
|
|
Companding::None => vals::Comp::NOCOMPANDING,
|
|
|
|
Companding::MuLaw => vals::Comp::MULAW,
|
|
|
|
Companding::ALaw => vals::Comp::ALAW,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum OutputDrive {
|
|
|
|
OnStart,
|
|
|
|
Immediately,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OutputDrive {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn outdriv(&self) -> vals::Outdriv {
|
|
|
|
match self {
|
|
|
|
OutputDrive::OnStart => vals::Outdriv::ONSTART,
|
|
|
|
OutputDrive::Immediately => vals::Outdriv::IMMEDIATELY,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
|
|
pub enum MasterClockDivider {
|
|
|
|
MasterClockDisabled,
|
|
|
|
Div1,
|
|
|
|
Div2,
|
|
|
|
Div4,
|
|
|
|
Div6,
|
|
|
|
Div8,
|
|
|
|
Div10,
|
|
|
|
Div12,
|
|
|
|
Div14,
|
|
|
|
Div16,
|
|
|
|
Div18,
|
|
|
|
Div20,
|
|
|
|
Div22,
|
|
|
|
Div24,
|
|
|
|
Div26,
|
|
|
|
Div28,
|
|
|
|
Div30,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MasterClockDivider {
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
pub const fn mckdiv(&self) -> u8 {
|
|
|
|
match self {
|
|
|
|
MasterClockDivider::MasterClockDisabled => 0,
|
|
|
|
MasterClockDivider::Div1 => 0,
|
|
|
|
MasterClockDivider::Div2 => 1,
|
|
|
|
MasterClockDivider::Div4 => 2,
|
|
|
|
MasterClockDivider::Div6 => 3,
|
|
|
|
MasterClockDivider::Div8 => 4,
|
|
|
|
MasterClockDivider::Div10 => 5,
|
|
|
|
MasterClockDivider::Div12 => 6,
|
|
|
|
MasterClockDivider::Div14 => 7,
|
|
|
|
MasterClockDivider::Div16 => 8,
|
|
|
|
MasterClockDivider::Div18 => 9,
|
|
|
|
MasterClockDivider::Div20 => 10,
|
|
|
|
MasterClockDivider::Div22 => 11,
|
|
|
|
MasterClockDivider::Div24 => 12,
|
|
|
|
MasterClockDivider::Div26 => 13,
|
|
|
|
MasterClockDivider::Div28 => 14,
|
|
|
|
MasterClockDivider::Div30 => 15,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// [`SAI`] configuration.
|
|
|
|
#[non_exhaustive]
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct Config {
|
|
|
|
pub mode: Mode,
|
2023-09-30 19:43:44 -05:00
|
|
|
pub tx_rx: TxRx,
|
2023-09-29 21:08:07 -05:00
|
|
|
pub sync_enable: SyncEnable,
|
|
|
|
pub is_sync_output: bool,
|
|
|
|
pub protocol: Protocol,
|
|
|
|
pub slot_size: SlotSize,
|
|
|
|
pub slot_count: word::U4,
|
|
|
|
pub slot_enable: u16,
|
|
|
|
pub first_bit_offset: word::U5,
|
|
|
|
pub data_size: DataSize,
|
|
|
|
pub stereo_mono: StereoMono,
|
|
|
|
pub bit_order: BitOrder,
|
|
|
|
pub frame_sync_offset: FrameSyncOffset,
|
|
|
|
pub frame_sync_polarity: FrameSyncPolarity,
|
|
|
|
pub frame_sync_active_level_length: word::U7,
|
|
|
|
pub frame_sync_definition: FrameSyncDefinition,
|
|
|
|
pub frame_length: u8,
|
|
|
|
pub clock_strobe: ClockStrobe,
|
|
|
|
pub output_drive: OutputDrive,
|
|
|
|
pub master_clock_divider: MasterClockDivider,
|
|
|
|
pub is_high_impedenane_on_inactive_slot: bool,
|
|
|
|
pub fifo_threshold: FifoThreshold,
|
|
|
|
pub companding: Companding,
|
|
|
|
pub complement_format: ComplementFormat,
|
|
|
|
pub mute_value: MuteValue,
|
|
|
|
pub mute_detection_counter: word::U5,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Config {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
mode: Mode::Master,
|
2023-09-30 19:43:44 -05:00
|
|
|
tx_rx: TxRx::Transmitter,
|
2023-09-29 21:08:07 -05:00
|
|
|
is_sync_output: false,
|
|
|
|
sync_enable: SyncEnable::Asynchronous,
|
|
|
|
protocol: Protocol::Free,
|
|
|
|
slot_size: SlotSize::DataSize,
|
|
|
|
slot_count: word::U4(2),
|
|
|
|
first_bit_offset: word::U5(0),
|
|
|
|
slot_enable: 0b11,
|
|
|
|
data_size: DataSize::Data16,
|
|
|
|
stereo_mono: StereoMono::Stereo,
|
|
|
|
bit_order: BitOrder::LsbFirst,
|
|
|
|
frame_sync_offset: FrameSyncOffset::BeforeFirstBit,
|
|
|
|
frame_sync_polarity: FrameSyncPolarity::ActiveLow,
|
|
|
|
frame_sync_active_level_length: word::U7(16),
|
|
|
|
frame_sync_definition: FrameSyncDefinition::ChannelIdentification,
|
|
|
|
frame_length: 32,
|
|
|
|
master_clock_divider: MasterClockDivider::MasterClockDisabled,
|
|
|
|
clock_strobe: ClockStrobe::Rising,
|
|
|
|
output_drive: OutputDrive::Immediately,
|
|
|
|
is_high_impedenane_on_inactive_slot: false,
|
|
|
|
fifo_threshold: FifoThreshold::ThreeQuarters,
|
|
|
|
companding: Companding::None,
|
|
|
|
complement_format: ComplementFormat::TwosComplement,
|
|
|
|
mute_value: MuteValue::Zero,
|
|
|
|
mute_detection_counter: word::U5(4),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
|
|
|
pub fn new_i2s() -> Self {
|
|
|
|
return Default::default();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_msb_first() -> Self {
|
|
|
|
Self {
|
|
|
|
bit_order: BitOrder::MsbFirst,
|
|
|
|
frame_sync_offset: FrameSyncOffset::OnFirstBit,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum SubBlock {
|
|
|
|
A = 0,
|
|
|
|
B = 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum RingBuffer<'d, C: Channel, W: word::Word> {
|
|
|
|
Writable(WritableRingBuffer<'d, C, W>),
|
|
|
|
Readable(ReadableRingBuffer<'d, C, W>),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
fn wdr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: SubBlock) -> *mut W {
|
|
|
|
let ch = w.ch(sub_block as usize);
|
|
|
|
ch.dr().as_ptr() as _
|
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
fn rdr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: SubBlock) -> *mut W {
|
|
|
|
let ch = w.ch(sub_block as usize);
|
|
|
|
ch.dr().as_ptr() as _
|
|
|
|
}
|
|
|
|
|
2023-09-29 21:08:07 -05:00
|
|
|
pub struct Sai<'d, T: Instance, C: Channel, W: word::Word> {
|
|
|
|
_peri: PeripheralRef<'d, T>,
|
|
|
|
sd: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
fs: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
sck: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
mclk: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
ring_buffer: RingBuffer<'d, C, W>,
|
|
|
|
sub_block: SubBlock,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'d, T: Instance, C: Channel, W: word::Word> Sai<'d, T, C, W> {
|
2023-09-30 19:43:44 -05:00
|
|
|
// return the type for (sd, sck)
|
|
|
|
fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AFType, AFType) {
|
|
|
|
(
|
|
|
|
//sd is defined by tx/rx mode
|
|
|
|
match tx_rx {
|
|
|
|
TxRx::Transmitter => AFType::OutputPushPull,
|
|
|
|
TxRx::Receiver => AFType::Input,
|
|
|
|
},
|
|
|
|
//clocks (mclk, sck and fs) are defined by master/slave
|
|
|
|
match mode {
|
|
|
|
Mode::Master => AFType::OutputPushPull,
|
|
|
|
Mode::Slave => AFType::Input,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_ring_buffer(
|
|
|
|
dma: impl Peripheral<P = C> + 'd,
|
|
|
|
dma_buf: &'d mut [W],
|
|
|
|
request: Request,
|
|
|
|
sub_block: SubBlock,
|
|
|
|
tx_rx: TxRx,
|
|
|
|
) -> RingBuffer<'d, C, W> {
|
|
|
|
let opts = TransferOptions {
|
|
|
|
half_transfer_ir: true,
|
|
|
|
//the new_write() and new_read() always use circular mode
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
match tx_rx {
|
|
|
|
TxRx::Transmitter => RingBuffer::Writable(unsafe {
|
|
|
|
WritableRingBuffer::new_write(dma, request, wdr(T::REGS, sub_block), dma_buf, opts)
|
|
|
|
}),
|
|
|
|
TxRx::Receiver => RingBuffer::Readable(unsafe {
|
|
|
|
ReadableRingBuffer::new_read(dma, request, rdr(T::REGS, sub_block), dma_buf, opts)
|
|
|
|
}),
|
2023-09-29 21:08:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
pub fn new_asynchronous_block_a_with_mclk(
|
2023-09-29 21:08:07 -05:00
|
|
|
peri: impl Peripheral<P = T> + 'd,
|
|
|
|
sck: impl Peripheral<P = impl SckAPin<T>> + 'd,
|
|
|
|
sd: impl Peripheral<P = impl SdAPin<T>> + 'd,
|
|
|
|
fs: impl Peripheral<P = impl FsAPin<T>> + 'd,
|
|
|
|
mclk: impl Peripheral<P = impl MclkAPin<T>> + 'd,
|
|
|
|
dma: impl Peripheral<P = C> + 'd,
|
|
|
|
dma_buf: &'d mut [W],
|
|
|
|
mut config: Config,
|
|
|
|
) -> Self
|
|
|
|
where
|
|
|
|
C: Channel + DmaA<T>,
|
|
|
|
{
|
|
|
|
into_ref!(mclk);
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
let (_sd_af_type, ck_af_type) = Self::get_af_types(config.mode, config.tx_rx);
|
|
|
|
|
|
|
|
mclk.set_as_af(mclk.af_num(), ck_af_type);
|
2023-09-29 21:08:07 -05:00
|
|
|
mclk.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
|
|
|
|
config.master_clock_divider = MasterClockDivider::Div1;
|
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
Self::new_asynchronous_block_a(peri, sck, sd, fs, dma, dma_buf, config)
|
2023-09-29 21:08:07 -05:00
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
pub fn new_asynchronous_block_a(
|
2023-09-29 21:08:07 -05:00
|
|
|
peri: impl Peripheral<P = T> + 'd,
|
|
|
|
sck: impl Peripheral<P = impl SckAPin<T>> + 'd,
|
|
|
|
sd: impl Peripheral<P = impl SdAPin<T>> + 'd,
|
|
|
|
fs: impl Peripheral<P = impl FsAPin<T>> + 'd,
|
|
|
|
dma: impl Peripheral<P = C> + 'd,
|
|
|
|
dma_buf: &'d mut [W],
|
|
|
|
config: Config,
|
|
|
|
) -> Self
|
|
|
|
where
|
|
|
|
C: Channel + DmaA<T>,
|
|
|
|
{
|
|
|
|
into_ref!(peri, dma, sck, sd, fs);
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
let (sd_af_type, ck_af_type) = Self::get_af_types(config.mode, config.tx_rx);
|
2023-09-29 21:08:07 -05:00
|
|
|
sd.set_as_af(sd.af_num(), sd_af_type);
|
|
|
|
sd.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
sck.set_as_af(sck.af_num(), ck_af_type);
|
|
|
|
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
fs.set_as_af(fs.af_num(), ck_af_type);
|
|
|
|
fs.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
let sub_block = SubBlock::A;
|
2023-09-30 19:43:44 -05:00
|
|
|
let request = dma.request();
|
2023-09-29 21:08:07 -05:00
|
|
|
|
|
|
|
Self::new_inner(
|
|
|
|
peri,
|
|
|
|
sub_block,
|
|
|
|
Some(sck.map_into()),
|
|
|
|
None,
|
|
|
|
Some(sd.map_into()),
|
|
|
|
Some(fs.map_into()),
|
2023-09-30 19:43:44 -05:00
|
|
|
Self::get_ring_buffer(dma, dma_buf, request, sub_block, config.tx_rx),
|
2023-09-29 21:08:07 -05:00
|
|
|
config,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
pub fn new_asynchronous_block_b_with_mclk(
|
2023-09-29 21:08:07 -05:00
|
|
|
peri: impl Peripheral<P = T> + 'd,
|
|
|
|
sck: impl Peripheral<P = impl SckBPin<T>> + 'd,
|
|
|
|
sd: impl Peripheral<P = impl SdBPin<T>> + 'd,
|
|
|
|
fs: impl Peripheral<P = impl FsBPin<T>> + 'd,
|
|
|
|
mclk: impl Peripheral<P = impl MclkBPin<T>> + 'd,
|
|
|
|
dma: impl Peripheral<P = C> + 'd,
|
|
|
|
dma_buf: &'d mut [W],
|
|
|
|
mut config: Config,
|
|
|
|
) -> Self
|
|
|
|
where
|
|
|
|
C: Channel + DmaB<T>,
|
|
|
|
{
|
|
|
|
into_ref!(mclk);
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
let (_sd_af_type, ck_af_type) = Self::get_af_types(config.mode, config.tx_rx);
|
|
|
|
|
|
|
|
mclk.set_as_af(mclk.af_num(), ck_af_type);
|
2023-09-29 21:08:07 -05:00
|
|
|
mclk.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
|
|
|
|
config.master_clock_divider = MasterClockDivider::Div1;
|
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
Self::new_asynchronous_block_b(peri, sck, sd, fs, dma, dma_buf, config)
|
2023-09-29 21:08:07 -05:00
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
pub fn new_asynchronous_block_b(
|
2023-09-29 21:08:07 -05:00
|
|
|
peri: impl Peripheral<P = T> + 'd,
|
|
|
|
sck: impl Peripheral<P = impl SckBPin<T>> + 'd,
|
|
|
|
sd: impl Peripheral<P = impl SdBPin<T>> + 'd,
|
|
|
|
fs: impl Peripheral<P = impl FsBPin<T>> + 'd,
|
|
|
|
dma: impl Peripheral<P = C> + 'd,
|
|
|
|
dma_buf: &'d mut [W],
|
|
|
|
config: Config,
|
|
|
|
) -> Self
|
|
|
|
where
|
|
|
|
C: Channel + DmaB<T>,
|
|
|
|
{
|
|
|
|
into_ref!(dma, peri, sck, sd, fs);
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
let (sd_af_type, ck_af_type) = Self::get_af_types(config.mode, config.tx_rx);
|
2023-09-29 21:08:07 -05:00
|
|
|
|
|
|
|
sd.set_as_af(sd.af_num(), sd_af_type);
|
|
|
|
sd.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
sck.set_as_af(sck.af_num(), ck_af_type);
|
|
|
|
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
fs.set_as_af(fs.af_num(), ck_af_type);
|
|
|
|
fs.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
let sub_block = SubBlock::B;
|
2023-09-30 19:43:44 -05:00
|
|
|
let request = dma.request();
|
2023-09-29 21:08:07 -05:00
|
|
|
|
|
|
|
Self::new_inner(
|
|
|
|
peri,
|
|
|
|
sub_block,
|
|
|
|
Some(sck.map_into()),
|
|
|
|
None,
|
|
|
|
Some(sd.map_into()),
|
|
|
|
Some(fs.map_into()),
|
2023-09-30 19:43:44 -05:00
|
|
|
Self::get_ring_buffer(dma, dma_buf, request, sub_block, config.tx_rx),
|
|
|
|
config,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_synchronous_config(config: &mut Config) {
|
|
|
|
config.mode = Mode::Slave;
|
|
|
|
config.is_sync_output = false;
|
|
|
|
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3))]
|
|
|
|
{
|
|
|
|
config.sync_enable = SyncEnable::Internal;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(sai_v4))]
|
|
|
|
{
|
|
|
|
//this must either be Internal or External
|
|
|
|
//The asynchronous sub-block on the same SAI needs to enable is_sync_output
|
|
|
|
assert!(config.sync_enable != SyncEnable::Asynchronous);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_synchronous_block_a(
|
|
|
|
peri: impl Peripheral<P = T> + 'd,
|
|
|
|
sd: impl Peripheral<P = impl SdAPin<T>> + 'd,
|
|
|
|
dma: impl Peripheral<P = C> + 'd,
|
|
|
|
dma_buf: &'d mut [W],
|
|
|
|
mut config: Config,
|
|
|
|
) -> Self
|
|
|
|
where
|
|
|
|
C: Channel + DmaA<T>,
|
|
|
|
{
|
|
|
|
Self::update_synchronous_config(&mut config);
|
|
|
|
|
|
|
|
into_ref!(dma, peri, sd);
|
|
|
|
|
|
|
|
let (sd_af_type, _ck_af_type) = Self::get_af_types(config.mode, config.tx_rx);
|
|
|
|
|
|
|
|
sd.set_as_af(sd.af_num(), sd_af_type);
|
|
|
|
sd.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
let sub_block = SubBlock::A;
|
|
|
|
let request = dma.request();
|
|
|
|
|
|
|
|
Self::new_inner(
|
|
|
|
peri,
|
|
|
|
sub_block,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
Some(sd.map_into()),
|
|
|
|
None,
|
|
|
|
Self::get_ring_buffer(dma, dma_buf, request, sub_block, config.tx_rx),
|
|
|
|
config,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_synchronous_block_b(
|
|
|
|
peri: impl Peripheral<P = T> + 'd,
|
|
|
|
sd: impl Peripheral<P = impl SdBPin<T>> + 'd,
|
|
|
|
dma: impl Peripheral<P = C> + 'd,
|
|
|
|
dma_buf: &'d mut [W],
|
|
|
|
mut config: Config,
|
|
|
|
) -> Self
|
|
|
|
where
|
|
|
|
C: Channel + DmaB<T>,
|
|
|
|
{
|
|
|
|
Self::update_synchronous_config(&mut config);
|
|
|
|
|
|
|
|
into_ref!(dma, peri, sd);
|
|
|
|
|
|
|
|
let (sd_af_type, _ck_af_type) = Self::get_af_types(config.mode, config.tx_rx);
|
|
|
|
|
|
|
|
sd.set_as_af(sd.af_num(), sd_af_type);
|
|
|
|
sd.set_speed(crate::gpio::Speed::VeryHigh);
|
|
|
|
|
|
|
|
let sub_block = SubBlock::B;
|
|
|
|
let request = dma.request();
|
|
|
|
|
|
|
|
Self::new_inner(
|
|
|
|
peri,
|
|
|
|
sub_block,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
Some(sd.map_into()),
|
|
|
|
None,
|
|
|
|
Self::get_ring_buffer(dma, dma_buf, request, sub_block, config.tx_rx),
|
2023-09-29 21:08:07 -05:00
|
|
|
config,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn start(self: &mut Self) {
|
|
|
|
match self.ring_buffer {
|
|
|
|
RingBuffer::Writable(ref mut rb) => {
|
|
|
|
rb.start();
|
|
|
|
}
|
|
|
|
RingBuffer::Readable(ref mut rb) => {
|
|
|
|
rb.start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_transmitter(ring_buffer: &RingBuffer<C, W>) -> bool {
|
|
|
|
match ring_buffer {
|
|
|
|
RingBuffer::Writable(_) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_inner(
|
|
|
|
peri: impl Peripheral<P = T> + 'd,
|
|
|
|
sub_block: SubBlock,
|
|
|
|
sck: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
mclk: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
sd: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
fs: Option<PeripheralRef<'d, AnyPin>>,
|
|
|
|
ring_buffer: RingBuffer<'d, C, W>,
|
|
|
|
config: Config,
|
|
|
|
) -> Self {
|
|
|
|
T::enable();
|
2023-09-30 19:43:44 -05:00
|
|
|
|
|
|
|
// can't reset here because the other sub-block might be in use
|
|
|
|
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
{
|
|
|
|
let ch = T::REGS.ch(sub_block as usize);
|
|
|
|
ch.cr1().modify(|w| w.set_saien(false));
|
|
|
|
}
|
2023-09-29 21:08:07 -05:00
|
|
|
|
|
|
|
#[cfg(any(sai_v4))]
|
|
|
|
{
|
2023-09-29 21:16:20 -05:00
|
|
|
// Not totally clear from the datasheet if this is right
|
2023-09-30 19:43:44 -05:00
|
|
|
// This is only used if using SyncEnable::External on the other SAI unit
|
|
|
|
// Syncing from SAIX subblock A to subblock B does not require this
|
|
|
|
// Only syncing from SAI1 subblock A/B to SAI2 subblock A/B
|
2023-09-29 21:08:07 -05:00
|
|
|
let value: u8 = if T::REGS.as_ptr() == stm32_metapac::SAI1.as_ptr() {
|
|
|
|
1 //this is SAI1, so sync with SAI2
|
|
|
|
} else {
|
|
|
|
0 //this is SAI2, so sync with SAI1
|
|
|
|
};
|
|
|
|
T::REGS.gcr().modify(|w| {
|
|
|
|
w.set_syncin(value);
|
|
|
|
});
|
|
|
|
|
|
|
|
if config.is_sync_output {
|
|
|
|
let syncout: u8 = match sub_block {
|
|
|
|
SubBlock::A => 0b01,
|
|
|
|
SubBlock::B => 0b10,
|
|
|
|
};
|
|
|
|
T::REGS.gcr().modify(|w| {
|
|
|
|
w.set_syncout(syncout);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
|
|
|
|
{
|
|
|
|
let ch = T::REGS.ch(sub_block as usize);
|
|
|
|
ch.cr1().modify(|w| {
|
|
|
|
w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) {
|
2023-09-30 19:43:44 -05:00
|
|
|
TxRx::Transmitter
|
2023-09-29 21:08:07 -05:00
|
|
|
} else {
|
|
|
|
TxRx::Receiver
|
|
|
|
}));
|
|
|
|
w.set_prtcfg(config.protocol.prtcfg());
|
|
|
|
w.set_ds(config.data_size.ds());
|
|
|
|
w.set_lsbfirst(config.bit_order.lsbfirst());
|
|
|
|
w.set_ckstr(config.clock_strobe.ckstr());
|
|
|
|
w.set_syncen(config.sync_enable.syncen());
|
|
|
|
w.set_mono(config.stereo_mono.mono());
|
|
|
|
w.set_outdriv(config.output_drive.outdriv());
|
|
|
|
w.set_mckdiv(config.master_clock_divider.mckdiv());
|
|
|
|
w.set_nodiv(
|
|
|
|
if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
|
|
|
|
vals::Nodiv::NODIV
|
|
|
|
} else {
|
|
|
|
vals::Nodiv::MASTERCLOCK
|
|
|
|
},
|
|
|
|
);
|
|
|
|
w.set_dmaen(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
ch.cr2().modify(|w| {
|
|
|
|
w.set_fth(config.fifo_threshold.fth());
|
|
|
|
w.set_comp(config.companding.comp());
|
|
|
|
w.set_cpl(config.complement_format.cpl());
|
|
|
|
w.set_muteval(config.mute_value.muteval());
|
|
|
|
w.set_mutecnt(config.mute_detection_counter.0 as u8);
|
|
|
|
w.set_tris(config.is_high_impedenane_on_inactive_slot);
|
|
|
|
});
|
|
|
|
|
|
|
|
ch.frcr().modify(|w| {
|
|
|
|
w.set_fsoff(config.frame_sync_offset.fsoff());
|
|
|
|
w.set_fspol(config.frame_sync_polarity.fspol());
|
|
|
|
w.set_fsdef(config.frame_sync_definition.fsdef());
|
2023-09-30 19:43:44 -05:00
|
|
|
w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1);
|
2023-09-29 21:08:07 -05:00
|
|
|
w.set_frl(config.frame_length - 1);
|
|
|
|
});
|
|
|
|
|
|
|
|
ch.slotr().modify(|w| {
|
|
|
|
w.set_nbslot(config.slot_count.0 as u8 - 1);
|
|
|
|
w.set_slotsz(config.slot_size.slotsz());
|
|
|
|
w.set_fboff(config.first_bit_offset.0 as u8);
|
|
|
|
w.set_sloten(vals::Sloten(config.slot_enable as u16));
|
|
|
|
});
|
|
|
|
|
|
|
|
ch.cr1().modify(|w| w.set_saien(true));
|
2023-09-30 19:43:44 -05:00
|
|
|
|
|
|
|
if ch.cr1().read().saien() == false {
|
|
|
|
panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)");
|
|
|
|
}
|
2023-09-29 21:08:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Self {
|
|
|
|
_peri: peri.into_ref(),
|
|
|
|
sub_block,
|
|
|
|
sck,
|
|
|
|
mclk,
|
|
|
|
sd,
|
|
|
|
fs,
|
|
|
|
ring_buffer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-30 19:43:44 -05:00
|
|
|
pub fn reset() {
|
|
|
|
T::enable();
|
|
|
|
T::reset();
|
|
|
|
}
|
|
|
|
|
2023-09-29 21:16:20 -05:00
|
|
|
pub fn flush(&mut self) {
|
2023-09-29 21:08:07 -05:00
|
|
|
let ch = T::REGS.ch(self.sub_block as usize);
|
|
|
|
ch.cr1().modify(|w| w.set_saien(false));
|
|
|
|
#[cfg(any(sai_v1, sai_v2))]
|
|
|
|
{
|
|
|
|
ch.cr2().modify(|w| w.set_fflush(vals::Fflush::FLUSH));
|
|
|
|
}
|
|
|
|
#[cfg(any(sai_v3, sai_v4))]
|
|
|
|
{
|
|
|
|
ch.cr2().modify(|w| w.set_fflush(true));
|
|
|
|
}
|
|
|
|
ch.cr1().modify(|w| w.set_saien(true));
|
|
|
|
}
|
|
|
|
|
2023-09-29 21:16:20 -05:00
|
|
|
pub fn set_mute(&mut self, value: bool) {
|
2023-09-29 21:08:07 -05:00
|
|
|
let ch = T::REGS.ch(self.sub_block as usize);
|
|
|
|
ch.cr2().modify(|w| w.set_mute(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reconfigures it with the supplied config.
|
2023-09-29 21:16:20 -05:00
|
|
|
pub fn reconfigure(&mut self, _config: Config) {}
|
2023-09-29 21:08:07 -05:00
|
|
|
|
|
|
|
pub fn get_current_config(&self) -> Config {
|
|
|
|
Config::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
|
|
|
|
match &mut self.ring_buffer {
|
|
|
|
RingBuffer::Writable(buffer) => {
|
2023-09-29 21:16:20 -05:00
|
|
|
buffer.write_exact(data).await?;
|
2023-09-29 21:08:07 -05:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
_ => return Err(Error::NotATransmitter),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
|
|
|
|
match &mut self.ring_buffer {
|
|
|
|
RingBuffer::Readable(buffer) => {
|
2023-09-29 21:16:20 -05:00
|
|
|
buffer.read_exact(data).await?;
|
2023-09-29 21:08:07 -05:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
_ => Err(Error::NotAReceiver),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'d, T: Instance, C: Channel, W: word::Word> Drop for Sai<'d, T, C, W> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let ch = T::REGS.ch(self.sub_block as usize);
|
|
|
|
ch.cr1().modify(|w| w.set_saien(false));
|
|
|
|
self.fs.as_ref().map(|x| x.set_as_disconnected());
|
|
|
|
self.sd.as_ref().map(|x| x.set_as_disconnected());
|
|
|
|
self.sck.as_ref().map(|x| x.set_as_disconnected());
|
|
|
|
self.mclk.as_ref().map(|x| x.set_as_disconnected());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) mod sealed {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
pub trait Instance {
|
|
|
|
const REGS: Regs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Word: word::Word {}
|
|
|
|
|
|
|
|
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
|
|
|
pin_trait!(SckAPin, Instance);
|
|
|
|
pin_trait!(SckBPin, Instance);
|
|
|
|
pin_trait!(FsAPin, Instance);
|
|
|
|
pin_trait!(FsBPin, Instance);
|
|
|
|
pin_trait!(SdAPin, Instance);
|
|
|
|
pin_trait!(SdBPin, Instance);
|
|
|
|
pin_trait!(MclkAPin, Instance);
|
|
|
|
pin_trait!(MclkBPin, Instance);
|
|
|
|
|
|
|
|
dma_trait!(DmaA, Instance);
|
|
|
|
dma_trait!(DmaB, Instance);
|
|
|
|
|
|
|
|
foreach_peripheral!(
|
|
|
|
(sai, $inst:ident) => {
|
|
|
|
impl sealed::Instance for peripherals::$inst {
|
|
|
|
const REGS: Regs = crate::pac::$inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Instance for peripherals::$inst {}
|
|
|
|
};
|
|
|
|
);
|
|
|
|
|
|
|
|
impl<'d, T: Instance, C: Channel, W: word::Word> SetConfig for Sai<'d, T, C, W> {
|
|
|
|
type Config = Config;
|
|
|
|
fn set_config(&mut self, config: &Self::Config) {
|
|
|
|
self.reconfigure(*config);
|
|
|
|
}
|
|
|
|
}
|