use embassy_futures::join::join;
use embassy_hal_internal::into_ref;
use stm32_metapac::spi::vals;
use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer};
use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
use crate::mode::Async;
use crate::spi::{Config as SpiConfig, RegsExt as _, *};
use crate::time::Hertz;
use crate::{Peripheral, PeripheralRef};
#[derive(Copy, Clone)]
pub enum Mode {
Master,
Slave,
}
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum Function {
Transmit,
Receive,
#[cfg(spi_v3)]
FullDuplex,
}
#[derive(Copy, Clone)]
pub enum Standard {
Philips,
MsbFirst,
LsbFirst,
PcmLongSync,
PcmShortSync,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
NotATransmitter,
NotAReceiver,
Overrun,
}
impl From<ringbuffer::Error> for Error {
fn from(#[allow(unused)] err: ringbuffer::Error) -> Self {
#[cfg(feature = "defmt")]
{
if err == ringbuffer::Error::DmaUnsynced {
defmt::error!("Ringbuffer broken invariants detected!");
}
}
Self::Overrun
}
}
impl Standard {
#[cfg(any(spi_v1, spi_v3, spi_f1))]
const fn i2sstd(&self) -> vals::I2sstd {
match self {
Standard::Philips => vals::I2sstd::PHILIPS,
Standard::MsbFirst => vals::I2sstd::MSB,
Standard::LsbFirst => vals::I2sstd::LSB,
Standard::PcmLongSync => vals::I2sstd::PCM,
Standard::PcmShortSync => vals::I2sstd::PCM,
}
}
#[cfg(any(spi_v1, spi_v3, spi_f1))]
const fn pcmsync(&self) -> vals::Pcmsync {
match self {
Standard::PcmLongSync => vals::Pcmsync::LONG,
_ => vals::Pcmsync::SHORT,
}
}
}
#[derive(Copy, Clone)]
pub enum Format {
Data16Channel16,
Data16Channel32,
Data24Channel32,
Data32Channel32,
}
impl Format {
#[cfg(any(spi_v1, spi_v3, spi_f1))]
const fn datlen(&self) -> vals::Datlen {
match self {
Format::Data16Channel16 => vals::Datlen::BITS16,
Format::Data16Channel32 => vals::Datlen::BITS16,
Format::Data24Channel32 => vals::Datlen::BITS24,
Format::Data32Channel32 => vals::Datlen::BITS32,
}
}
#[cfg(any(spi_v1, spi_v3, spi_f1))]
const fn chlen(&self) -> vals::Chlen {
match self {
Format::Data16Channel16 => vals::Chlen::BITS16,
Format::Data16Channel32 => vals::Chlen::BITS32,
Format::Data24Channel32 => vals::Chlen::BITS32,
Format::Data32Channel32 => vals::Chlen::BITS32,
}
}
}
#[derive(Copy, Clone)]
pub enum ClockPolarity {
IdleLow,
IdleHigh,
}
impl ClockPolarity {
#[cfg(any(spi_v1, spi_v3, spi_f1))]
const fn ckpol(&self) -> vals::Ckpol {
match self {
ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH,
ClockPolarity::IdleLow => vals::Ckpol::IDLE_LOW,
}
}
}
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct Config {
pub mode: Mode,
pub standard: Standard,
pub format: Format,
pub clock_polarity: ClockPolarity,
pub master_clock: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
mode: Mode::Master,
standard: Standard::Philips,
format: Format::Data16Channel16,
clock_polarity: ClockPolarity::IdleLow,
master_clock: true,
}
}
}
pub struct Writer<'s, 'd, W: Word>(&'s mut WritableRingBuffer<'d, W>);
impl<'s, 'd, W: Word> Writer<'s, 'd, W> {
pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
self.0.write_exact(data).await?;
Ok(())
}
pub fn reset(&mut self) {
self.0.clear();
}
}
pub struct Reader<'s, 'd, W: Word>(&'s mut ReadableRingBuffer<'d, W>);
impl<'s, 'd, W: Word> Reader<'s, 'd, W> {
pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
self.0.read_exact(data).await?;
Ok(())
}
pub fn reset(&mut self) {
self.0.clear();
}
}
pub struct I2S<'d, W: Word> {
#[allow(dead_code)]
mode: Mode,
spi: Spi<'d, Async>,
txsd: Option<PeripheralRef<'d, AnyPin>>,
rxsd: Option<PeripheralRef<'d, AnyPin>>,
ws: Option<PeripheralRef<'d, AnyPin>>,
ck: Option<PeripheralRef<'d, AnyPin>>,
mck: Option<PeripheralRef<'d, AnyPin>>,
tx_ring_buffer: Option<WritableRingBuffer<'d, W>>,
rx_ring_buffer: Option<ReadableRingBuffer<'d, W>>,
}
impl<'d, W: Word> I2S<'d, W> {
pub fn new_txonly<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
mck: impl Peripheral<P = impl MckPin<T>> + 'd,
txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
txdma_buf: &'d mut [W],
freq: Hertz,
config: Config,
) -> Self {
Self::new_inner(
peri,
new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
None,
ws,
ck,
new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
new_dma!(txdma).map(|d| (d, txdma_buf)),
None,
freq,
config,
Function::Transmit,
)
}
pub fn new_txonly_nomck<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
sd: impl Peripheral<P = impl MosiPin<T>> + 'd,
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
txdma_buf: &'d mut [W],
freq: Hertz,
config: Config,
) -> Self {
Self::new_inner(
peri,
new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
None,
ws,
ck,
None,
new_dma!(txdma).map(|d| (d, txdma_buf)),
None,
freq,
config,
Function::Transmit,
)
}
pub fn new_rxonly<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
sd: impl Peripheral<P = impl MisoPin<T>> + 'd,
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
mck: impl Peripheral<P = impl MckPin<T>> + 'd,
rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
rxdma_buf: &'d mut [W],
freq: Hertz,
config: Config,
) -> Self {
Self::new_inner(
peri,
None,
new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
ws,
ck,
new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
None,
new_dma!(rxdma).map(|d| (d, rxdma_buf)),
freq,
config,
Function::Receive,
)
}
#[cfg(spi_v3)]
pub fn new_full_duplex<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
txsd: impl Peripheral<P = impl MosiPin<T>> + 'd,
rxsd: impl Peripheral<P = impl MisoPin<T>> + 'd,
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
mck: impl Peripheral<P = impl MckPin<T>> + 'd,
txdma: impl Peripheral<P = impl TxDma<T>> + 'd,
txdma_buf: &'d mut [W],
rxdma: impl Peripheral<P = impl RxDma<T>> + 'd,
rxdma_buf: &'d mut [W],
freq: Hertz,
config: Config,
) -> Self {
Self::new_inner(
peri,
new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
ws,
ck,
new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
new_dma!(txdma).map(|d| (d, txdma_buf)),
new_dma!(rxdma).map(|d| (d, rxdma_buf)),
freq,
config,
Function::FullDuplex,
)
}
pub fn start(&mut self) {
self.spi.info.regs.cr1().modify(|w| {
w.set_spe(false);
});
self.spi.set_word_size(W::CONFIG);
if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
tx_ring_buffer.start();
set_txdmaen(self.spi.info.regs, true);
}
if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
rx_ring_buffer.start();
#[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
flush_rx_fifo(self.spi.info.regs);
set_rxdmaen(self.spi.info.regs, true);
}
self.spi.info.regs.cr1().modify(|w| {
w.set_spe(true);
});
#[cfg(any(spi_v3, spi_v4, spi_v5))]
self.spi.info.regs.cr1().modify(|w| {
w.set_cstart(true);
});
}
pub fn clear(&mut self) {
if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
rx_ring_buffer.clear();
}
if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
tx_ring_buffer.clear();
}
}
pub async fn stop(&mut self) {
let regs = self.spi.info.regs;
let tx_f = async {
if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer {
tx_ring_buffer.stop().await;
set_txdmaen(regs, false);
}
};
let rx_f = async {
if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
rx_ring_buffer.stop().await;
set_rxdmaen(regs, false);
}
};
join(rx_f, tx_f).await;
#[cfg(any(spi_v3, spi_v4, spi_v5))]
{
if let Mode::Master = self.mode {
regs.cr1().modify(|w| {
w.set_csusp(true);
});
while regs.cr1().read().cstart() {}
}
}
regs.cr1().modify(|w| {
w.set_spe(false);
});
self.clear();
}
pub fn split<'s>(&'s mut self) -> Result<(Reader<'s, 'd, W>, Writer<'s, 'd, W>), Error> {
match (&mut self.rx_ring_buffer, &mut self.tx_ring_buffer) {
(None, _) => Err(Error::NotAReceiver),
(_, None) => Err(Error::NotATransmitter),
(Some(rx_ring), Some(tx_ring)) => Ok((Reader(rx_ring), Writer(tx_ring))),
}
}
pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
match &mut self.rx_ring_buffer {
Some(ring) => Reader(ring).read(data).await,
_ => Err(Error::NotAReceiver),
}
}
pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
match &mut self.tx_ring_buffer {
Some(ring) => Writer(ring).write(data).await,
_ => Err(Error::NotATransmitter),
}
}
pub async fn write_immediate(&mut self, data: &[W]) -> Result<(usize, usize), Error> {
match &mut self.tx_ring_buffer {
Some(ring) => Ok(ring.write_immediate(data)?),
_ => return Err(Error::NotATransmitter),
}
}
fn new_inner<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
txsd: Option<PeripheralRef<'d, AnyPin>>,
rxsd: Option<PeripheralRef<'d, AnyPin>>,
ws: impl Peripheral<P = impl WsPin<T>> + 'd,
ck: impl Peripheral<P = impl CkPin<T>> + 'd,
mck: Option<PeripheralRef<'d, AnyPin>>,
txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>,
freq: Hertz,
config: Config,
function: Function,
) -> Self {
into_ref!(ws, ck);
ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
let spi = Spi::new_internal(peri, None, None, {
let mut config = SpiConfig::default();
config.frequency = freq;
config
});
let regs = T::info().regs;
#[cfg(all(rcc_f4, not(stm32f410)))]
let pclk = unsafe { crate::rcc::get_freqs() }.plli2s1_r.to_hertz().unwrap();
#[cfg(not(all(rcc_f4, not(stm32f410))))]
let pclk = T::frequency();
let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format);
#[cfg(any(spi_v1, spi_v3, spi_f1))]
{
#[cfg(spi_v3)]
{
regs.cr1().modify(|w| w.set_spe(false));
reset_incompatible_bitfields::<T>();
}
use stm32_metapac::spi::vals::{I2scfg, Odd};
let clk_reg = {
#[cfg(any(spi_v1, spi_f1))]
{
regs.i2spr()
}
#[cfg(spi_v3)]
{
regs.i2scfgr()
}
};
clk_reg.modify(|w| {
w.set_i2sdiv(div);
w.set_odd(match odd {
true => Odd::ODD,
false => Odd::EVEN,
});
w.set_mckoe(config.master_clock);
});
regs.i2scfgr().modify(|w| {
w.set_ckpol(config.clock_polarity.ckpol());
w.set_i2smod(true);
w.set_i2sstd(config.standard.i2sstd());
w.set_pcmsync(config.standard.pcmsync());
w.set_datlen(config.format.datlen());
w.set_chlen(config.format.chlen());
w.set_i2scfg(match (config.mode, function) {
(Mode::Master, Function::Transmit) => I2scfg::MASTER_TX,
(Mode::Master, Function::Receive) => I2scfg::MASTER_RX,
#[cfg(spi_v3)]
(Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX,
(Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX,
(Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX,
#[cfg(spi_v3)]
(Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX,
});
#[cfg(any(spi_v1, spi_f1))]
w.set_i2se(true);
});
let mut opts = TransferOptions::default();
opts.half_transfer_ir = true;
Self {
mode: config.mode,
spi,
txsd: txsd.map(|w| w.map_into()),
rxsd: rxsd.map(|w| w.map_into()),
ws: Some(ws.map_into()),
ck: Some(ck.map_into()),
mck: mck.map(|w| w.map_into()),
tx_ring_buffer: txdma.map(|(ch, buf)| unsafe {
WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts)
}),
rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe {
ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts)
}),
}
}
}
}
impl<'d, W: Word> Drop for I2S<'d, W> {
fn drop(&mut self) {
self.txsd.as_ref().map(|x| x.set_as_disconnected());
self.rxsd.as_ref().map(|x| x.set_as_disconnected());
self.ws.as_ref().map(|x| x.set_as_disconnected());
self.ck.as_ref().map(|x| x.set_as_disconnected());
self.mck.as_ref().map(|x| x.set_as_disconnected());
}
}
fn compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_format: Format) -> (bool, u8) {
let coef = if mclk {
256
} else if let Format::Data16Channel16 = data_format {
32
} else {
64
};
let (n, d) = (i2s_clock.0, coef * request_freq.0);
let division = (n + (d >> 1)) / d;
if division < 4 {
(false, 2)
} else if division > 511 {
(true, 255)
} else {
((division & 1) == 1, (division >> 1) as u8)
}
}
#[cfg(spi_v3)]
fn reset_incompatible_bitfields<T: Instance>() {
let regs = T::info().regs;
regs.cr1().modify(|w| {
let iolock = w.iolock();
let csusp = w.csusp();
let spe = w.cstart();
let cstart = w.cstart();
w.0 = 0;
w.set_iolock(iolock);
w.set_csusp(csusp);
w.set_spe(spe);
w.set_cstart(cstart);
});
regs.cr2().write(|w| w.0 = 0);
regs.cfg1().modify(|w| {
let txdmaen = w.txdmaen();
let rxdmaen = w.rxdmaen();
let fthlv = w.fthlv();
w.0 = 0;
w.set_txdmaen(txdmaen);
w.set_rxdmaen(rxdmaen);
w.set_fthlv(fthlv);
});
regs.cfg2().modify(|w| {
let afcntr = w.afcntr();
let lsbfirst = w.lsbfirst();
let ioswp = w.ioswp();
w.0 = 0;
w.set_afcntr(afcntr);
w.set_lsbfirst(lsbfirst);
w.set_ioswp(ioswp);
});
regs.ier().modify(|w| {
let tifreie = w.tifreie();
let ovrie = w.ovrie();
let udrie = w.udrie();
let txpie = w.txpie();
let rxpie = w.rxpie();
w.0 = 0;
w.set_tifreie(tifreie);
w.set_ovrie(ovrie);
w.set_udrie(udrie);
w.set_txpie(txpie);
w.set_rxpie(rxpie);
});
regs.ifcr().write(|w| {
w.set_suspc(true);
w.set_tifrec(true);
w.set_ovrc(true);
w.set_udrc(true);
});
regs.crcpoly().write(|w| w.0 = 0x107);
regs.txcrc().write(|w| w.0 = 0);
regs.rxcrc().write(|w| w.0 = 0);
regs.udrdr().write(|w| w.0 = 0);
}