use core::convert::TryInto;
use crate::rcc::{rec, CoreClocks, ResetEnable};
use crate::sai::{GetClkSAI, Sai, SaiChannel, CLEAR_ALL_FLAGS_BITS, INTERFACE};
use crate::stm32;
use crate::time::Hertz;
use crate::stm32::{SAI1, SAI2};
#[cfg(not(feature = "rm0455"))]
use crate::stm32::{SAI3, SAI4};
#[cfg(feature = "rm0455")]
use crate::device::sai1::ch::sr;
#[cfg(not(feature = "rm0455"))]
use crate::device::sai4::ch::sr;
#[cfg(feature = "rm0455")]
type CH = stm32::sai1::CH;
#[cfg(not(feature = "rm0455"))]
type CH = stm32::sai4::CH;
use crate::gpio::gpioa::{PA0, PA1, PA12, PA2};
use crate::gpio::gpiob::PB2;
use crate::gpio::gpioc::{PC0, PC1};
#[cfg(not(feature = "rm0455"))]
use crate::gpio::gpiod::{
PD0, PD1, PD10, PD11, PD12, PD13, PD14, PD15, PD4, PD6, PD8, PD9,
};
#[cfg(feature = "rm0455")]
use crate::gpio::gpiod::{PD11, PD12, PD13, PD6};
use crate::gpio::gpioe::{
PE0, PE11, PE12, PE13, PE14, PE2, PE3, PE4, PE5, PE6,
};
use crate::gpio::gpiof::{PF11, PF6, PF7, PF8, PF9};
use crate::gpio::gpiog::{PG10, PG7, PG9};
use crate::gpio::gpioh::{PH2, PH3};
use crate::gpio::gpioi::{PI4, PI5, PI6, PI7};
use crate::gpio::{Alternate, AF10, AF6, AF8};
use stm32h7::Variant::Val;
use crate::traits::i2s::FullDuplex;
const NUM_SLOTS: u8 = 16;
#[derive(Clone, Copy, PartialEq)]
pub enum I2SMode {
Master = 0b00,
Slave = 0b10,
}
#[derive(Copy, Clone, PartialEq)]
pub enum I2SDir {
Tx = 0b00,
Rx = 0b01,
}
#[derive(Copy, Clone)]
pub enum I2SDataSize {
BITS_8 = 0b001,
BITS_10 = 0b010,
BITS_16 = 0b100,
BITS_20 = 0b101,
BITS_24 = 0b110,
BITS_32 = 0b111,
}
#[derive(Copy, Clone)]
enum I2SSlotSize {
BITS_16 = 0b01,
BITS_32 = 0b10,
}
#[derive(Copy, Clone, PartialEq)]
pub enum I2SProtocol {
MSB,
LSB,
}
#[derive(Copy, Clone)]
pub enum I2SSync {
Master = 0b00,
Internal = 0b01,
External = 0b10,
}
#[derive(Copy, Clone, Debug)]
pub enum I2SError {
NoChannelAvailable,
}
pub enum I2SClockStrobe {
Rising,
Falling,
}
#[derive(Copy, Clone)]
pub enum I2SCompanding {
Disabled = 0b00,
ALaw = 0b10,
MuLaw = 0b11,
}
#[derive(Copy, Clone)]
pub enum I2SComplement {
Ones = 0,
Twos = 1,
}
impl From<I2SComplement> for bool {
fn from(value: I2SComplement) -> Self {
match value {
I2SComplement::Ones => false,
I2SComplement::Twos => true,
}
}
}
pub trait I2SPinsChA<SAI> {}
pub trait I2SPinsChB<SAI> {}
pub trait I2SPinMclkA<SAI> {}
pub trait I2SPinMclkB<SAI> {}
pub trait I2SPinSckA<SAI> {}
pub trait I2SPinSckB<SAI> {}
pub trait I2SPinFsA<SAI> {}
pub trait I2SPinFsB<SAI> {}
pub trait I2SPinSdA<SAI> {}
pub trait I2SPinSdB<SAI> {}
impl<SAI, MCLK, SCK, FS, SD1, SD2> I2SPinsChA<SAI>
for (MCLK, SCK, FS, SD1, Option<SD2>)
where
MCLK: I2SPinMclkA<SAI>,
SCK: I2SPinSckA<SAI>,
FS: I2SPinFsA<SAI>,
SD1: I2SPinSdA<SAI>,
SD2: I2SPinSdB<SAI>,
{
}
impl<SAI, MCLK, SCK, FS, SD1, SD2> I2SPinsChB<SAI>
for (MCLK, SCK, FS, SD1, Option<SD2>)
where
MCLK: I2SPinMclkB<SAI>,
SCK: I2SPinSckB<SAI>,
FS: I2SPinFsB<SAI>,
SD1: I2SPinSdB<SAI>,
SD2: I2SPinSdA<SAI>,
{
}
pub struct I2SChanConfig {
dir: I2SDir,
sync_type: I2SSync,
clock_strobe: I2SClockStrobe,
slots: u8,
first_bit_offset: u8,
frame_sync_before: bool,
frame_sync_active_high: bool,
oversampling: bool,
master_clock_disabled: bool,
companding: I2SCompanding,
complement: I2SComplement,
protocol: I2SProtocol,
mono_mode: bool,
mute_repeat: bool,
mute_counter: u8,
tristate: bool,
frame_size: Option<u8>,
}
impl I2SChanConfig {
pub fn new(dir: I2SDir) -> I2SChanConfig {
I2SChanConfig {
dir,
sync_type: I2SSync::Master,
clock_strobe: I2SClockStrobe::Falling,
slots: 2,
first_bit_offset: 0,
frame_sync_before: false,
frame_sync_active_high: false,
oversampling: false,
master_clock_disabled: false,
companding: I2SCompanding::Disabled,
complement: I2SComplement::Ones,
protocol: I2SProtocol::MSB,
mono_mode: false,
mute_repeat: false,
mute_counter: 0,
tristate: false,
frame_size: None,
}
}
pub fn set_sync_type(mut self, sync_type: I2SSync) -> Self {
self.sync_type = sync_type;
self
}
pub fn set_clock_strobe(mut self, clock_strobe: I2SClockStrobe) -> Self {
self.clock_strobe = clock_strobe;
self
}
pub fn set_slots(mut self, slots: u8) -> Self {
assert!(slots <= 16);
self.slots = slots;
self
}
pub fn set_first_bit_offset(mut self, first_bit_offset: u8) -> Self {
assert!(first_bit_offset < 0b10_0000);
self.first_bit_offset = first_bit_offset;
self
}
pub fn set_frame_sync_before(mut self, frame_sync_before: bool) -> Self {
self.frame_sync_before = frame_sync_before;
self
}
pub fn set_frame_sync_active_high(
mut self,
frame_sync_active_high: bool,
) -> Self {
self.frame_sync_active_high = frame_sync_active_high;
self
}
pub fn set_oversampling(mut self, oversampling: bool) -> Self {
self.oversampling = oversampling;
self
}
pub fn disable_master_clock(mut self) -> Self {
self.master_clock_disabled = true;
self
}
pub fn set_protocol(mut self, protocol: I2SProtocol) -> Self {
self.protocol = protocol;
self
}
pub fn set_mono_mode(mut self, mono_mode: bool) -> Self {
self.mono_mode = mono_mode;
self
}
pub fn set_mute_repeat(mut self, mute_repeat: bool) -> Self {
if self.dir == I2SDir::Rx || self.slots > 2 {
panic!("This only has meaning in I2S::Tx mode when slots <= 2");
}
self.mute_repeat = mute_repeat;
self
}
pub fn set_mute_counter(mut self, mute_counter: u8) -> Self {
if self.dir == I2SDir::Tx {
panic!("This only has meaning in I2S::Rx mode");
}
assert!(mute_counter < 0b100_0000);
self.mute_counter = mute_counter;
self
}
pub fn set_tristate(mut self, tristate: bool) -> Self {
self.tristate = tristate;
self
}
pub fn set_frame_size(mut self, frame_size: Option<u8>) -> Self {
if let Some(frame_size) = frame_size {
assert!(frame_size >= 8);
}
self.frame_size = frame_size;
self
}
}
pub struct I2S {
master: I2SChanConfig,
slave: Option<I2SChanConfig>,
}
impl INTERFACE for I2S {}
pub trait SaiI2sExt<SAI>: Sized {
type Rec: ResetEnable;
fn i2s_ch_a<PINS, T>(
self,
_pins: PINS,
audio_freq: T,
data_size: I2SDataSize,
prec: Self::Rec,
clocks: &CoreClocks,
master: I2SChanConfig,
slave: Option<I2SChanConfig>,
) -> Sai<SAI, I2S>
where
PINS: I2SPinsChA<Self>,
T: Into<Hertz>;
fn i2s_ch_b<PINS, T>(
self,
_pins: PINS,
audio_freq: T,
data_size: I2SDataSize,
prec: Self::Rec,
clocks: &CoreClocks,
master: I2SChanConfig,
slave: Option<I2SChanConfig>,
) -> Sai<SAI, I2S>
where
PINS: I2SPinsChB<Self>,
T: Into<Hertz>;
}
macro_rules! i2s {
( $($SAIX:ident, $Rec:ident: [$i2s_saiX_ch_a:ident, $i2s_saiX_ch_b:ident]),+ ) => {
$(
impl SaiI2sExt<$SAIX> for $SAIX {
type Rec = rec::$Rec;
fn i2s_ch_a<PINS, T>(
self,
_pins: PINS,
audio_freq: T,
data_size: I2SDataSize,
prec: rec::$Rec,
clocks: &CoreClocks,
master: I2SChanConfig,
slave: Option<I2SChanConfig>,
) -> Sai<Self, I2S>
where
PINS: I2SPinsChA<Self>,
T: Into<Hertz>,
{
Sai::$i2s_saiX_ch_a(
self,
_pins,
audio_freq.into(),
data_size,
prec,
clocks,
master,
slave,
)
}
fn i2s_ch_b<PINS, T>(
self,
_pins: PINS,
audio_freq: T,
data_size: I2SDataSize,
prec: rec::$Rec,
clocks: &CoreClocks,
master: I2SChanConfig,
slave: Option<I2SChanConfig>,
) -> Sai<Self, I2S>
where
PINS: I2SPinsChB<Self>,
T: Into<Hertz>,
{
Sai::$i2s_saiX_ch_b(
self,
_pins,
audio_freq.into(),
data_size,
prec,
clocks,
master,
slave,
)
}
}
impl Sai<$SAIX, I2S> {
pub fn $i2s_saiX_ch_a<PINS>(
sai: $SAIX,
_pins: PINS,
audio_freq: Hertz,
data_size: I2SDataSize,
prec: rec::$Rec,
clocks: &CoreClocks,
master: I2SChanConfig,
slave: Option<I2SChanConfig>,
) -> Self
where
PINS: I2SPinsChA<$SAIX>,
{
assert!(master.slots <= NUM_SLOTS);
if let Some(slave) = &slave {
assert!(slave.slots <= NUM_SLOTS);
}
let ker_ck_a = $SAIX::sai_a_ker_ck(&prec, clocks)
.expect("SAI kernel clock must run!");
let clock_ratio = if master.oversampling {
512
} else {
256
};
let mclk_div =
(ker_ck_a.0) / (audio_freq.0 * clock_ratio);
let mclk_div: u8 = mclk_div
.try_into()
.expect("SAI kernel clock is out of range for required MCLK");
let mut per_sai = Sai {
rb: sai,
master_channel: SaiChannel::ChannelA,
slave_channel: if slave.is_some() {
Some(SaiChannel::ChannelB)
} else {
None
},
interface: I2S { master, slave },
};
per_sai.sai_rcc_init(prec);
i2s_config_channel(
&per_sai.rb.cha,
I2SMode::Master,
&per_sai.interface.master,
mclk_div,
data_size,
);
if let Some(slave) = &per_sai.interface.slave {
i2s_config_channel(
&per_sai.rb.chb,
I2SMode::Slave,
slave,
0,
data_size,
);
}
per_sai
}
pub fn $i2s_saiX_ch_b<PINS>(
sai: $SAIX,
_pins: PINS,
audio_freq: Hertz,
data_size: I2SDataSize,
prec: rec::$Rec,
clocks: &CoreClocks,
master: I2SChanConfig,
slave: Option<I2SChanConfig>,
) -> Self
where
PINS: I2SPinsChB<$SAIX>,
{
assert!(master.slots <= NUM_SLOTS);
if let Some(slave) = &slave {
assert!(slave.slots <= NUM_SLOTS);
}
let ker_ck_a = $SAIX::sai_b_ker_ck(&prec, clocks)
.expect("SAI kernel clock must run!");
let clock_ratio = if master.oversampling {
512
} else {
256
};
let mclk_div =
(ker_ck_a.0) / (audio_freq.0 * clock_ratio);
let mclk_div: u8 = mclk_div
.try_into()
.expect("SAI kernel clock is out of range for required MCLK");
let mut per_sai = Sai {
rb: sai,
master_channel: SaiChannel::ChannelB,
slave_channel: if slave.is_some() {
Some(SaiChannel::ChannelA)
} else {
None
},
interface: I2S { master, slave },
};
per_sai.sai_rcc_init(prec);
i2s_config_channel(
&per_sai.rb.chb,
I2SMode::Master,
&per_sai.interface.master,
mclk_div,
data_size,
);
if let Some(slave) = &per_sai.interface.slave {
i2s_config_channel(
&per_sai.rb.cha,
I2SMode::Slave,
slave,
0,
data_size,
);
}
per_sai
}
pub fn enable(&mut self) {
self.slave_channel(enable_ch);
self.master_channel(enable_ch);
}
pub fn disable(&mut self) {
self.master_channel(disable_ch);
self.slave_channel(disable_ch);
}
}
impl FullDuplex<u32> for Sai<$SAIX, I2S> {
type Error = I2SError;
fn try_read(&mut self) -> nb::Result<(u32, u32), Self::Error> {
if self.interface.master.dir == I2SDir::Rx {
return self.master_channel(read);
} else if let Some(slave) = &self.interface.slave {
if slave.dir == I2SDir::Rx {
return self.slave_channel(read).unwrap();
}
}
Err(nb::Error::Other(I2SError::NoChannelAvailable))
}
fn try_send(
&mut self,
left_word: u32,
right_word: u32,
) -> nb::Result<(), Self::Error> {
if self.interface.master.dir == I2SDir::Tx {
return self.master_channel(|audio_ch| {
send(left_word, right_word, audio_ch)
});
} else if let Some(slave) = &self.interface.slave {
if slave.dir == I2SDir::Tx {
return self
.slave_channel(|audio_ch| {
send(left_word, right_word, audio_ch)
})
.unwrap();
}
}
Err(nb::Error::Other(I2SError::NoChannelAvailable))
}
}
)+
}
}
i2s! {
SAI1, Sai1: [i2s_sai1_ch_a, i2s_sai1_ch_b],
SAI2, Sai2: [i2s_sai2_ch_a, i2s_sai2_ch_b]
}
#[cfg(not(feature = "rm0455"))]
i2s! {
SAI3, Sai3: [i2s_sai3_ch_a, i2s_sai3_ch_b],
SAI4, Sai4: [i2s_sai4_ch_a, i2s_sai4_ch_b]
}
fn i2s_config_channel(
audio_ch: &CH,
mode: I2SMode,
config: &I2SChanConfig,
mclk_div: u8,
data_size: I2SDataSize,
) {
let clock_strobe = match config.clock_strobe {
I2SClockStrobe::Rising => false,
I2SClockStrobe::Falling => true,
};
let slot_en_bits: u16 = (2_u32.pow(config.slots.into()) - 1) as u16;
let slot_size = match data_size {
I2SDataSize::BITS_8 => I2SSlotSize::BITS_16,
I2SDataSize::BITS_10 => I2SSlotSize::BITS_16,
I2SDataSize::BITS_16 => I2SSlotSize::BITS_16,
I2SDataSize::BITS_20 => I2SSlotSize::BITS_32,
I2SDataSize::BITS_24 => I2SSlotSize::BITS_32,
I2SDataSize::BITS_32 => I2SSlotSize::BITS_32,
};
let frame_size = match config.frame_size {
Some(frame_size) => frame_size,
None => match data_size {
I2SDataSize::BITS_8 => 16 * (config.slots / 2),
I2SDataSize::BITS_10 => 32 * (config.slots / 2),
I2SDataSize::BITS_16 => 32 * (config.slots / 2),
I2SDataSize::BITS_20 => 64 * (config.slots / 2),
I2SDataSize::BITS_24 => 64 * (config.slots / 2),
I2SDataSize::BITS_32 => 64 * (config.slots / 2),
},
};
let mode_bits = (mode as u8) | (config.dir as u8);
unsafe {
audio_ch.cr1.modify(|_, w| {
w.mode()
.bits(mode_bits as u8)
.prtcfg()
.free()
.ds()
.bits(data_size as u8)
.lsbfirst()
.bit(config.protocol == I2SProtocol::LSB)
.ckstr()
.bit(clock_strobe)
.syncen()
.bits(config.sync_type as u8)
.mono()
.bit(config.mono_mode)
.nodiv()
.bit(config.master_clock_disabled)
.mckdiv()
.bits(mclk_div)
.osr()
.bit(config.oversampling)
});
audio_ch.cr2.modify(|_, w| {
w.fth()
.quarter1()
.tris()
.bit(config.tristate)
.mute()
.clear_bit()
.muteval()
.bit(config.mute_repeat)
.mutecnt()
.bits(config.mute_counter)
.cpl()
.bit(config.complement.into())
.comp()
.bits(config.companding as u8)
});
audio_ch.frcr.modify(|_, w| {
w.frl()
.bits(frame_size - 1)
.fsall()
.bits((frame_size / 2) - 1)
.fsdef()
.set_bit()
.fspol()
.bit(config.frame_sync_active_high)
.fsoff()
.bit(config.frame_sync_before)
});
audio_ch.slotr.modify(|_, w| {
w.fboff()
.bits(config.first_bit_offset)
.slotsz()
.bits(slot_size as u8)
.nbslot()
.bits(config.slots - 1)
.sloten()
.bits(slot_en_bits)
});
}
}
fn enable_ch(audio_ch: &CH) {
unsafe { audio_ch.clrfr.write(|w| w.bits(CLEAR_ALL_FLAGS_BITS)) };
audio_ch.cr2.modify(|_, w| w.fflush().flush());
audio_ch.cr1.modify(|_, w| w.saien().enabled());
}
fn disable_ch(audio_ch: &CH) {
audio_ch.cr1.modify(|_, w| w.saien().disabled());
while audio_ch.cr1.read().saien().bit_is_set() {}
}
fn read(audio_ch: &CH) -> nb::Result<(u32, u32), I2SError> {
match audio_ch.sr.read().flvl().variant() {
Val(sr::FLVL_A::EMPTY) => Err(nb::Error::WouldBlock),
_ => Ok((audio_ch.dr.read().bits(), audio_ch.dr.read().bits())),
}
}
fn send(
left_word: u32,
right_word: u32,
audio_ch: &CH,
) -> nb::Result<(), I2SError> {
match audio_ch.sr.read().flvl().variant() {
Val(sr::FLVL_A::FULL) => Err(nb::Error::WouldBlock),
Val(sr::FLVL_A::QUARTER4) => Err(nb::Error::WouldBlock),
_ => {
unsafe {
audio_ch.dr.write(|w| w.bits(left_word).bits(right_word));
}
Ok(())
}
}
}
macro_rules! pins {
($($SAIX:ty:
MCLK_A: [$($MCLK_A:ty),*]
SCK_A: [$($SCK_A:ty),*]
FS_A: [$($FS_A:ty),*]
SD_A: [$($SD_A:ty),*]
MCLK_B: [$($MCLK_B:ty),*]
SCK_B: [$($SCK_B:ty),*]
FS_B: [$($FS_B:ty),*]
SD_B: [$($SD_B:ty),*]
)+) => {
$(
$(
impl I2SPinMclkA<$SAIX> for $MCLK_A {}
)*
$(
impl I2SPinSckA<$SAIX> for $SCK_A {}
)*
$(
impl I2SPinFsA<$SAIX> for $FS_A {}
)*
$(
impl I2SPinSdA<$SAIX> for $SD_A {}
)*
$(
impl I2SPinMclkB<$SAIX> for $MCLK_B {}
)*
$(
impl I2SPinSckB<$SAIX> for $SCK_B {}
)*
$(
impl I2SPinFsB<$SAIX> for $FS_B {}
)*
$(
impl I2SPinSdB<$SAIX> for $SD_B {}
)*
)+
}
}
pins! {
SAI1:
MCLK_A: [
PE2<Alternate<AF6>>,
PG7<Alternate<AF6>>
]
SCK_A: [
PE5<Alternate<AF6>>
]
FS_A: [
PE4<Alternate<AF6>>
]
SD_A: [
PB2<Alternate<AF6>>,
PC1<Alternate<AF6>>,
PD6<Alternate<AF6>>,
PE6<Alternate<AF6>>
]
MCLK_B: [
PF7<Alternate<AF6>>
]
SCK_B: [
PF8<Alternate<AF6>>
]
FS_B: [
PF9<Alternate<AF6>>
]
SD_B: [
PE3<Alternate<AF6>>,
PF6<Alternate<AF6>>
]
SAI2:
MCLK_A: [
PE0<Alternate<AF10>>,
PI4<Alternate<AF10>>
]
SCK_A: [
PD13<Alternate<AF10>>,
PI5<Alternate<AF10>>
]
FS_A: [
PD12<Alternate<AF10>>,
PI7<Alternate<AF10>>
]
SD_A: [
PD11<Alternate<AF10>>,
PI6<Alternate<AF10>>
]
MCLK_B: [
PA1<Alternate<AF10>>,
PE6<Alternate<AF10>>,
PE14<Alternate<AF10>>,
PH3<Alternate<AF10>>
]
SCK_B: [
PA2<Alternate<AF8>>,
PE12<Alternate<AF10>>,
PH2<Alternate<AF10>>
]
FS_B: [
PA12<Alternate<AF8>>,
PC0<Alternate<AF8>>,
PE13<Alternate<AF10>>,
PG9<Alternate<AF10>>
]
SD_B: [
PA0<Alternate<AF10>>,
PE11<Alternate<AF10>>,
PF11<Alternate<AF10>>,
PG10<Alternate<AF10>>
]
}
#[cfg(not(feature = "rm0455"))]
pins! {
SAI3:
MCLK_A: [
PD15<Alternate<AF6>>
]
SCK_A: [
PD0<Alternate<AF6>>
]
FS_A: [
PD4<Alternate<AF6>>
]
SD_A: [
PD1<Alternate<AF6>>
]
MCLK_B: [
PD14<Alternate<AF6>>
]
SCK_B: [
PD8<Alternate<AF6>>
]
FS_B: [
PD10<Alternate<AF6>>
]
SD_B: [
PD9<Alternate<AF6>>
]
SAI4:
MCLK_A: [
PE2<Alternate<AF8>>
]
SCK_A: [
PE5<Alternate<AF8>>
]
FS_A: [
PE4<Alternate<AF8>>
]
SD_A: [
PB2<Alternate<AF8>>,
PC1<Alternate<AF8>>,
PD6<Alternate<AF8>>,
PE6<Alternate<AF8>>
]
MCLK_B: [
PF7<Alternate<AF8>>
]
SCK_B: [
PF8<Alternate<AF8>>
]
FS_B: [
PF9<Alternate<AF8>>
]
SD_B: [
PE3<Alternate<AF8>>,
PF6<Alternate<AF8>>
]
}