stm32_hal2/
sai.rs

1//! Serial audio interface (SAI) support, for digital audio input and output. Used for I2S, PCM/DSP, TDM,
2//! AC'97 etc. See L443 Reference Manual, section 41, or H743 RM, section 51.
3
4use core::ops::Deref;
5
6#[cfg(any(feature = "f3", feature = "l4"))]
7use crate::dma::DmaInput;
8#[cfg(not(any(feature = "f4", feature = "l552")))]
9use crate::dma::{self, ChannelCfg, Dma, DmaChannel};
10#[cfg(feature = "g0")]
11use crate::pac::dma as dma_p;
12#[cfg(any(
13    feature = "f3",
14    feature = "l4",
15    feature = "l5",
16    feature = "g4",
17    feature = "h7",
18    feature = "wb"
19))]
20use crate::pac::dma1 as dma_p;
21#[cfg(feature = "g4")]
22use crate::pac::sai;
23#[cfg(not(any(feature = "g4", feature = "h7")))]
24use crate::pac::sai1 as sai;
25#[cfg(feature = "h7")]
26use crate::pac::sai4 as sai;
27use crate::{clocks::Clocks, pac::RCC, util::RccPeriph};
28
29#[derive(Clone, Copy)]
30#[repr(u8)]
31/// Select Master or Slave mode. Sets xCR1 register, MODE field.
32/// The audio subblock can be a transmitter or receiver, in master or slave mode. The master
33/// mode means the SCK_x bit clock and the frame synchronization signal are generated from
34/// the SAI, whereas in slave mode, they come from another external or internal master. There
35/// is a particular case for which the FS signal direction is not directly linked to the master or
36/// slave mode definition. In AC’97 protocol, it will be an SAI output even if the SAI (link
37/// controller) is set-up to consume the SCK clock (and so to be in Slave mode).
38pub enum SaiMode {
39    /// The SAI delivers the timing signals to the external connected device:
40    /// The bit clock and the frame synchronization are output on pin SCK_x and FS_x,
41    /// respectively. Both SCK_x, FS_x and MCLK_x are configured as outputs.
42    /// If needed, the SAI can also generate a master clock on MCLK_x pin.
43    MasterTransmitter = 0b00,
44    MasterReceiver = 0b01,
45    /// The SAI expects to receive timing signals from an external device.
46    /// If the SAI subblock is configured in asynchronous mode, then SCK_x and FS_x pins
47    /// are configured as inputs.
48    /// If the SAI subblock is configured to operate synchronously with another SAI interface or
49    /// with the second audio subblock, the corresponding SCK_x and FS_x pins are left free
50    /// to be used as general purpose I/Os.
51    SlaveTransmitter = 0b10,
52    SlaveReceiver = 0b11,
53}
54
55#[derive(Clone, Copy)]
56#[repr(u8)]
57/// Select Stereo or Mono mode. Sets xCR1 register, MONO field.
58pub enum Mono {
59    Stereo = 0,
60    Mono = 1,
61}
62
63#[derive(Clone, Copy)]
64#[repr(u8)]
65/// Select if signals generated by SAI change on SCK rising, or falling edge. Sets xCR1 register,
66/// CKSTR field.
67pub enum ClockStrobe {
68    /// Signals generated by the SAI change on SCK rising edge, while signals received by the SAI are
69    /// sampled on the SCK falling edge.
70    TransmitRisingEdge = 0,
71    /// Signals generated by the SAI change on SCK falling edge, while signals received by the SAI are
72    /// sampled on the SCK rising edge.
73    TransmitFallingEdge = 1,
74}
75
76#[derive(Clone, Copy)]
77#[repr(u8)]
78/// Frame synchronization offset.
79/// Depending on the audio protocol targeted in the application, the Frame synchronization
80/// signal can be asserted when transmitting the last bit or the first bit of the audio frame (this is
81/// the case in I2S standard protocol and in MSB-justified protocol, respectively).
82/// Sets FRCR register, FSOFF field.
83pub enum FsOffset {
84    /// FS is asserted on the first bit of the slot 0.
85    FirstBit = 0,
86    /// FS is asserted one bit before the first bit of the slot 0
87    BeforeFirstBit = 1,
88}
89
90#[derive(Clone, Copy)]
91#[repr(u8)]
92/// This bit is set and cleared by software. It is used to configure the level of the start of frame on the FS
93/// signal. It is meaningless and is not used in AC’97 or SPDIF audio block configuration.
94/// This bit must be configured when the audio block is disabled.
95/// Sets FRCR register, FSPOL field.
96pub enum FsPolarity {
97    /// FS is active low (falling edge)
98    ActiveLow = 0,
99    /// FS is active high (rising edge)
100    ActiveHigh = 1,
101}
102
103#[derive(Clone, Copy)]
104#[repr(u8)]
105/// Set Where the signal is in the frame. Sets FRCR register, FSDEF field.
106pub enum FsSignal {
107    /// Start of frame, like for instance the PCM/DSP, TDM, AC’97, audio protocols.
108    /// Sets FRCR register, FSDEF field.
109    Frame = 0,
110    /// Start of frame and channel side identification within the audio frame like for the I2S,
111    /// the MSB or LSB-justified protocols.
112    FrameAndChannel = 1,
113}
114
115#[derive(Clone, Copy)]
116#[repr(u8)]
117/// Set which bit is transmitted first: Least significant, or Most significant. You may have to
118/// choose the one used by your SAI device. Sets xCR1 register, LSBFIRST field.
119pub enum FirstBit {
120    /// Data are transferred with MSB first
121    MsbFirst = 0,
122    /// Data are transferred with LSB first
123    LsbFirst = 1,
124}
125
126#[derive(Clone, Copy)]
127#[repr(u8)]
128/// Select the number of connected PDM mics. It is possible to select
129/// between 2,4,6 or 8 microphones. For example, if the application is using 3 microphones, the
130/// user has to select 4.
131/// Sets PDMCR register, MICNBR field.
132pub enum NumPdmMics {
133    /// Configuration with 2 microphones
134    N2 = 0b00,
135    /// Configuration with 4 microphones
136    N4 = 0b01,
137    /// Configuration with 6 microphones
138    N6 = 0b10,
139    /// Configuration with 8 microphones
140    N7 = 0b11,
141}
142
143#[derive(Clone, Copy)]
144#[repr(u8)]
145/// FIFO threshold. Affects xCR2 reg, FTH field. Affects when SAI interrupts, and
146/// DMA transfers occur.
147pub enum FifoThresh {
148    /// FIFO empty (transmitter and receiver modes)
149    Empty = 0b000,
150    /// FIFO ≤ ¼ but not empty (transmitter mode), FIFO < ¼ but not empty (receiver mode)
151    T1_4 = 0b001,
152    /// ¼ < FIFO ≤ ½ (transmitter mode), ¼ ≤ FIFO < ½ (receiver mode)
153    T1_2 = 0b010,
154    /// ½ < FIFO ≤ ¾ (transmitter mode), ½ ≤ FIFO < ¾ (receiver mode)
155    T3_4 = 0b011,
156    /// ¾ < FIFO but not full (transmitter mode), ¾ ≤ FIFO but not full (receiver mode)
157    T3_4B = 0b100,
158    /// FIFO full (transmitter and receiver modes)
159    Full = 0b101,
160}
161
162#[derive(Clone, Copy)]
163#[repr(u8)]
164/// Oversampling ratio for master clock. You may have to
165/// choose the one used by your SAI device. Sets xCR1 register, OSR field.
166pub enum OversamplingRatio {
167    /// Master clock frequency = F_FS x 256
168    FMul256 = 0,
169    /// Master clock frequency = F_FS x 512
170    FMul512 = 1,
171}
172
173#[derive(Clone, Copy)]
174#[repr(u8)]
175/// Specify wheather sub-clocks A and B are synchronized. Sets xCR1 register, SYNCEN field.
176pub enum SyncMode {
177    /// Audio sub-block in asynchronous mode
178    Async = 0b00,
179    /// Audio sub-block is synchronous with the other internal audio sub-block. In this case, the audio
180    /// sub-block must be configured in slave mode
181    Sync = 0b01,
182    /// Audio subblock is synchronous with an external SAI embedded peripheral. In this case the audio
183    /// subblock should be configured in Slave mode.
184    SyncExternal = 0b10, // todo: May only be valid for some MCUs, eg ones with multiple SAI devices.
185}
186
187#[cfg(not(feature = "l4"))]
188#[derive(Clone, Copy)]
189#[repr(u8)]
190/// Synchronization outputs. Sets xGCR register, SYNCOUT field. Must be set when SAI is diabled.
191/// Not block specific.
192pub enum SyncOut {
193    /// No synchronization output signals. SYNCOUT[1:0] should be configured as “No synchronization
194    /// output signals” when audio block is configured as SPDIF
195    NoSync = 0b00,
196    /// Block A used for further synchronization for others SAI
197    BlockA = 0b01,
198    ///  Block B used for further synchronization for others SAI
199    BlockB = 0b10,
200}
201
202#[cfg(not(feature = "l4"))]
203#[derive(Clone, Copy)]
204#[repr(u8)]
205/// Synchronization inputs. Sets xGCR register, SYNCIn field. Must be set when SAI is diabled.
206/// Not block specific. Syncs this SAI peripheral with the one specified here, if `sync_mode` is
207/// configured as `SyncExternal`. The block synchronized with is controlled by that SAI's `sync_out`
208/// setting.
209pub enum SyncIn {
210    /// Sync with SAI1 as master.
211    Sai1 = 0,
212    /// Sync with SAI2 as master.
213    Sai2 = 1,
214    /// Sync with SAI3 as master.
215    Sai3 = 2,
216    /// Sync with SAI4 as master.
217    Sai4 = 3,
218}
219
220#[derive(Clone, Copy)]
221#[repr(u8)]
222/// Select the audio protocol to use. xCR1 register, PRTCFG field.
223pub enum Protocol {
224    /// Free protocol. Free protocol allows to use the powerful configuration of the audio block to
225    /// address a specific audio protocol (such as I2S, LSB/MSB justified, TDM, PCM/DSP...) by setting
226    /// most of the configuration register bits as well as frame configuration register.
227    Free = 0b00,
228    /// SPDIF protocol
229    Spdif = 0b01,
230    /// AC'97 protocol
231    Ac97 = 0b10,
232}
233
234#[derive(Clone, Copy)]
235#[repr(u8)]
236/// Select the data size to use. xCR1 register, DS field.
237pub enum DataSize {
238    /// 8 bits
239    S8 = 0b010,
240    /// 10 bits
241    S10 = 0b011,
242    /// 16 bits
243    S16 = 0b100,
244    /// 20 bits
245    S20 = 0b101,
246    /// 24 bits
247    S24 = 0b110,
248    /// 32 bits
249    S32 = 0b111,
250}
251
252#[derive(Clone, Copy)]
253#[repr(u8)]
254/// Select the slot size to use. xSLOTR register, SLOTSZ field.
255pub enum SlotSize {
256    /// The slot size is equivalent to the data size (specified in DS[3:0] in the SAI_xCR1 register)
257    DataSize = 0b00,
258    /// 16 bits
259    S16 = 0b01,
260    /// 32 bits
261    S32 = 0b10,
262}
263
264#[derive(Clone, Copy)]
265#[repr(u8)]
266/// Select wheather the master clock is generated. xDR1 register, NOMCK field on H7.
267/// on other variants such as WB, affects the MCKEN and NODIV fields (?).
268pub enum MasterClock {
269    // These bit values are for NOMCK, ie on H7. We use inverse logic when setting the bits
270    // on other variants.
271    /// (H7): Master clock generator is enabled
272    Used = 0,
273    /// (H7):  Master clock generator is disabled. The clock divider controlled by MCKDIV can still be used to
274    /// generate the bit clock.
275    NotUsed = 1,
276}
277
278#[derive(Clone, Copy)]
279/// The type of SAI interrupt to configure. Reference Section 41.5 of the L4 RM.
280/// Enabled in xIM register, yIE fields. See H743 RM, section 51.5: SAI interrupts.
281pub enum SaiInterrupt {
282    /// FIFO request interrupt enable. When this bit is set, an interrupt is generated if the FREQ bit in the SAI_xSR register is set.
283    /// Since the audio block defaults to operate as a transmitter after reset, the MODE bit must be
284    /// configured before setting FREQIE to avoid a parasitic interrupt in receiver mode
285    Freq,
286    /// When the audio block is configured as receiver, an overrun condition may appear if data are
287    /// received in an audio frame when the FIFO is full and not able to store the received data. In
288    /// this case, the received data are lost, the flag OVRUDR in the SAI_xSR register is set and an
289    /// interrupt is generated if OVRUDRIE bit is set in the SAI_xIM register.
290    ///
291    /// An underrun may occur when the audio block in the SAI is a transmitter and the FIFO is
292    /// empty when data need to be transmitted. If an underrun is detected, the slot number for
293    /// which the event occurs is stored and MUTE value (00) is sent until the FIFO is ready to
294    /// transmit the data corresponding to the slot for which the underrun was detected (refer to
295    /// Figure 664). This avoids desynchronization between the memory pointer and the slot in the
296    /// audio frame.
297    Ovrudr,
298    /// The AFSDET flag is used only in slave mode. It is never asserted in master mode. It
299    /// indicates that a frame synchronization (FS) has been detected earlier than expected since
300    /// the frame length, the frame polarity, the frame offset are defined and known.
301    AfsDet,
302    /// The LFSDET flag in the SAI_xSR register can be set only when the SAI audio block
303    /// operates as a slave. The frame length, the frame polarity and the frame offset configuration
304    /// are known in register SAI_xFRCR.
305    LfsDet,
306    /// The CNRDY flag in the SAI_xSR register is relevant only if the SAI audio block is configured
307    /// to operate in AC’97 mode (PRTCFG[1:0] = 10 in the SAI_xCR1 register). If CNRDYIE bit is
308    /// set in the SAI_xIM register, an interrupt is generated when the CNRDY flag is set.
309    /// CNRDY is asserted when the Codec is not ready to communicate during the reception of
310    /// the TAG 0 (slot0) of the AC’97 audio frame.
311    CnRdy,
312    /// Mute detection
313    MuteDet,
314    /// When the audio block operates as a master (MODE[1] = 0) and NOMCK bit is equal to 0,
315    /// the WCKCFG flag is set as soon as the SAI is enabled if the following conditions are met:
316    /// • (FRL+1) is not a power of 2, and
317    /// • (FRL+1) is not between 8 and 256.
318    /// MODE, NOMCK, and SAIEN bits belong to SAI_xCR1 register and FRL to SAI_xFRCR
319    /// register.
320    WckCfg,
321}
322
323#[derive(Clone, Copy)]
324pub enum SaiChannel {
325    A,
326    B,
327}
328
329/// Configuration for the SAI peripheral. Mainly affects the ACR and BCR registers.
330/// Used for either channel. For details, see documentation of individual structs and fields.
331/// You may be forced into certain settings based on the device used.
332#[derive(Clone)]
333pub struct SaiConfig {
334    pub mode: SaiMode,
335    /// Select protocols between Free, Ac'97, and SPDIF. Defaults to Free.
336    pub protocol: Protocol,
337    /// Select mono or stereo modes. Default to mono.
338    pub mono: Mono,
339    /// An audio subblock can be configured to operate synchronously with the second audio
340    /// subblock in the same SAI. In this case, the bit clock and the frame synchronization signals
341    /// are shared to reduce the number of external pins used for the communication. Default to async.
342    pub sync: SyncMode,
343    /// Used for synchronization with other SAI blocks and peripherals. Set this using the A config.
344    /// Controls which block is the master synchronization signal for other SAI peripherals.
345    #[cfg(not(feature = "l4"))]
346    pub sync_out: SyncOut,
347    /// Used for synchronization with other SAI blocks and peripherals. Set this using the A config.
348    /// Configurd to sync with SAI1, if syncmode is external.
349    #[cfg(not(feature = "l4"))]
350    pub sync_in: SyncIn,
351    /// Clock strobing edge. Defaults to Signals generated by the SAI change on SCK rising edge, while signals received by the SAI are
352    /// sampled on the SCK falling edge
353    pub clock_strobe: ClockStrobe,
354    /// Size of the data in the slot. Defaults to 24-bit.
355    pub datasize: DataSize,
356    /// Size of the slot; can be 16 or 32 bits. Defaults to the same or larger for the data size.
357    pub slotsize: SlotSize,
358    /// Select wheather the master clock out is enabled, eg for syncing external devices. Defaults
359    /// to disabled.
360    pub master_clock: MasterClock,
361    pub first_bit: FirstBit,
362    pub oversampling_ratio: OversamplingRatio,
363    /// Define the audio frame length expressed in number
364    /// of SCK clock cycles: the number of bits in the frame is equal to FRL[7:0] + 1.
365    /// The minimum number of bits to transfer in an audio frame must be equal to 8, otherwise the audio
366    /// block will behaves in an unexpected way. This is the case when the data size is 8 bits and only one
367    /// slot 0 is defined in NBSLOT[4:0] of SAI_xSLOTR register (NBSLOT[3:0] = 0000).
368    /// In master mode, if the master clock (available on MCLK_x pin) is used, the frame length should be
369    /// aligned with a number equal to a power of 2, ranging from 8 to 256. When the master clock is not
370    /// used (NOMCK = 1), it is recommended to program the frame length to an value ranging from 8 to
371    /// 256.
372    pub frame_length: u16, // u16 to allow the value of 256.
373    pub fs_offset: FsOffset,
374    /// Active high, or active low polarity. Defaults to active high.
375    pub fs_polarity: FsPolarity,
376    /// Start of frame. Default to frame and channel.
377    pub fs_signal: FsSignal,
378    /// Number of slots. Defaults to 2.
379    pub num_slots: u8,
380    /// The FIFO threshold configures when the FREQ interrupt is generated based on how full
381    /// the FIFO is.
382    pub fifo_thresh: FifoThresh,
383    /// These bits are set and cleared by software.
384    /// The value set in this bitfield defines the position of the first data transfer bit in the slot. It represents
385    /// an offset value. In transmission mode, the bits outside the data field are forced to 0. In reception
386    /// mode, the extra received bits are discarded.
387    /// These bits must be set when the audio block is disabled.
388    /// They are ignored in AC’97 or SPDIF mode.
389    pub first_bit_offset: u8,
390    /// Enable Pulse Density Modulation (PDM) functionality, eg for digital microphones.
391    /// See the relevant ST Application note: AN5027
392    pub pdm_mode: bool,
393    /// The number of connected PDM mics, if applicable. Defualts to 2.
394    pub num_pdm_mics: NumPdmMics,
395    /// Which PDM CK line to enable. Must be 1-4. Defaults to 1. (CK1 in User manuals)
396    pub pdm_clock_used: u8,
397    /// Master clock divider. Divides the kernel clock input. Defaults to 0, for no division.
398    pub mckdiv: u8,
399}
400
401impl Default for SaiConfig {
402    fn default() -> Self {
403        Self {
404            mode: SaiMode::MasterTransmitter,
405            protocol: Protocol::Free,
406            mono: Mono::Stereo,
407            sync: SyncMode::Async,
408            #[cfg(not(feature = "l4"))]
409            sync_out: SyncOut::NoSync,
410            #[cfg(not(feature = "l4"))]
411            sync_in: SyncIn::Sai1,
412            clock_strobe: ClockStrobe::TransmitRisingEdge,
413            datasize: DataSize::S24,
414            slotsize: SlotSize::DataSize,
415            master_clock: MasterClock::NotUsed,
416            first_bit: FirstBit::MsbFirst,
417            oversampling_ratio: OversamplingRatio::FMul256,
418            frame_length: 64,
419            // fs_level_len: 32, // For now, we always use frame_length / 2, for 50% duty cycle.
420            fs_offset: FsOffset::FirstBit,
421            fs_polarity: FsPolarity::ActiveHigh,
422            fs_signal: FsSignal::FrameAndChannel, // Use FrameAndChannel for I2S.
423            num_slots: 2,
424            first_bit_offset: 0,
425            fifo_thresh: FifoThresh::T1_4,
426            pdm_mode: false,
427            num_pdm_mics: NumPdmMics::N2,
428            pdm_clock_used: 1,
429            mckdiv: 0,
430        }
431    }
432}
433
434// todo: Populate these presets
435impl SaiConfig {
436    /// Default configuration for I2S.
437    pub fn i2s_preset() -> Self {
438        Self {
439            // Use our default of 2 slots, and a frame length of 64 bits, to allow for up
440            // to 32 bits per slot.
441            // We also use our default fifo thresh of 1/4 of the total size of 8 words,
442            // ie 1 word per channel
443            // Note that we include some settings here that are present in default,
444            // for explicitness. (ie required by I2S, but perhaps arbitrary in default)
445            first_bit: FirstBit::MsbFirst,
446            // H743 RM: Frame schronization offset: Depending on the audio protocol targeted in the
447            // application, the Frame synchronization signal can be asserted when transmitting
448            // the last bit or the first bit of the audio frame (this is the case in I2S standard
449            // protocol and in MSB-justified protocol, respectively)
450            fs_offset: FsOffset::BeforeFirstBit,
451            // RM: this bit has to be set for I2S or MSB/LSB-justified protocols.
452            fs_signal: FsSignal::FrameAndChannel,
453            protocol: Protocol::Free,
454            datasize: DataSize::S24,
455            frame_length: 64,
456            num_slots: 2,
457            // From the INMP441 datasheet: The default data format is I²S (two’s complement), MSB-first.
458            // In this format, the MSB of each word is delayed by one SCK cycle from
459            // the start of each half-frame
460            // Note that this is already handled by by `fs_offset`.
461            first_bit_offset: 0,
462            fifo_thresh: FifoThresh::T1_4,
463            pdm_mode: false,
464
465            ..Default::default()
466        }
467    }
468
469    /// Default configuration for TDM. Configures an I2S-style delay of 1 between FS and
470    /// data start. Configures the FS signal to be a pulse indicating frame start. Sets
471    /// window length based on data size and number of slots.
472    pub fn tdm_preset(num_slots: u8, slotsize: SlotSize) -> Self {
473        // let frame_length = match datasize {
474        //     DataSize::S8 => 8 * num_slots as u16,
475        //     DataSize::S10 => 10 * num_slots as u16,
476        //     DataSize::S16 => 16 * num_slots as u16,
477        //     DataSize::S20 => 20 * num_slots as u16,
478        //     DataSize::S24 => 24 * num_slots as u16,
479        //     DataSize::S32 => 32 * num_slots as u16,
480        // };
481
482        let frame_length = match slotsize {
483            SlotSize::S16 => 16 * num_slots as u16,
484            SlotSize::S32 => 32 * num_slots as u16,
485            SlotSize::DataSize => panic!(),
486        };
487
488        Self {
489            first_bit: FirstBit::MsbFirst,
490            fs_offset: FsOffset::BeforeFirstBit,
491            fs_signal: FsSignal::Frame,
492            protocol: Protocol::Free,
493            slotsize,
494            frame_length,
495            num_slots,
496            first_bit_offset: 0,
497            // todo: Smartly set up fifo thresh based on number of slots?
498            fifo_thresh: FifoThresh::Full,
499            pdm_mode: false,
500
501            ..Default::default()
502        }
503    }
504
505    /// Default configuration for PDM microphones. See H743 RM, Table 422. TDM settings.
506    /// See table 423 for how to configure Frame Length, and number of slots.
507    /// This default configures for 48kHz sample rate, assuming 3.072Mhz SAI clock,
508    /// and 1 slots of 16 bits per frame. If using something else, see Table 423, and
509    /// modify as required.
510    pub fn pdm_mic_preset(num_mics: NumPdmMics, clock_used: u8) -> Self {
511        Self {
512            // These first settings (up to `pdm_mode1) are taken directly from Table 422.
513            //Mode must be MASTER receiver
514            mode: SaiMode::MasterReceiver,
515
516            // Free protocol for TDM
517            protocol: Protocol::Free,
518            // Signal transitions occur on the rising edge of the SCK_A bit clock. Signals
519            // are stable on the falling edge of the bit clock.
520            clock_strobe: ClockStrobe::TransmitRisingEdge,
521            mono: Mono::Stereo,
522            // Note: FSALL is set to 0, to set Pulse width is one bit clock cycle.
523            // We handle that directly in `new()`.
524            // FS signal is a start of frame
525            fs_signal: FsSignal::Frame,
526            // FS is active High
527            fs_polarity: FsPolarity::ActiveHigh,
528            // FS is asserted on the first bit of slot 0
529            fs_offset: FsOffset::FirstBit,
530            // No offset on slot
531            first_bit_offset: 0,
532            master_clock: MasterClock::NotUsed,
533
534            // These next 3 settings may depend on master clock speed, sample rate, and number
535            // of mics. See table 423.
536            // This is currently set for 2 mics, 1 slot of 16-bits per frame.
537            // 3.072Mhz SAI freq.
538            // RM: FRL = (16 x (MICNBR + 1)) - 1
539            frame_length: (16 * (num_mics as u8 as u16 + 1)) - 1,
540            // todo: Make these more flexible instead of hard-coded
541            datasize: DataSize::S16,
542            num_slots: 1,
543            fifo_thresh: FifoThresh::Empty,
544
545            pdm_mode: true,
546            num_pdm_mics: num_mics,
547            pdm_clock_used: clock_used,
548            ..Default::default()
549        }
550    }
551
552    /// Default configuration for AC'97
553    pub fn ac97_preset() -> Self {
554        Self {
555            protocol: Protocol::Ac97,
556            // Start of frame, like for instance the PCM/DSP, TDM, AC’97 audio protocols,
557            fs_signal: FsSignal::Frame,
558            // Note that AC97 uses 13 slots, but with the AC97 protocol set, the slots setting is
559            // ignored.
560            ..Default::default()
561        }
562    }
563
564    /// Default configuration for SPDIF
565    pub fn spdif_preset() -> Self {
566        Self {
567            protocol: Protocol::Spdif,
568            ..Default::default()
569        }
570    }
571}
572
573/// Represents the Serial Audio Interface (SAI) peripheral, used for digital audio
574/// input and output.
575pub struct Sai<R> {
576    pub regs: R,
577    config_a: SaiConfig,
578    config_b: SaiConfig,
579}
580
581impl<R> Sai<R>
582where
583    R: Deref<Target = sai::RegisterBlock> + RccPeriph,
584{
585    /// Initialize a SAI peripheral, including  enabling and resetting
586    /// its RCC peripheral clock.
587    pub fn new(regs: R, config_a: SaiConfig, config_b: SaiConfig, _clocks: &Clocks) -> Self {
588        let rcc = unsafe { &(*RCC::ptr()) };
589        R::en_reset(rcc);
590
591        // todo: Do we always want to configure and enable both A and B?
592
593        // Set the master clock divider.
594
595        // See H7 RM, Table 421.
596
597        // mckdiv = SAI clock / (sampling freq * 256) ?? (512 for oversampling?)
598
599        // with NOMCK = 1: (No master clock)
600        // F_SCK = F_sai_ker_ck / MCKDIV
601        // F_FS = F_sai_ker_ck / ((FRL + 1) * MCKDIV)
602
603        // 6-bit fields.
604        assert!(config_a.mckdiv <= 0b111111);
605        assert!(config_b.mckdiv <= 0b111111);
606
607        // For info on modes, reference H743 RM, section 51.4.3: "Configuring and
608        // Enabling SAI modes".
609        regs.cha().cr1.modify(|_, w| unsafe {
610            w.mode().bits(config_a.mode as u8);
611            w.prtcfg().bits(config_a.protocol as u8);
612            w.mono().bit(config_a.mono as u8 != 0);
613            w.syncen().bits(config_a.sync as u8);
614            w.ckstr().bit(config_a.clock_strobe as u8 != 0);
615            // The NOMCK bit of the SAI_xCR1 register is used to define whether the master clock is
616            // generated or not.
617            // Inversed polarity on non-H7 based on how we have `MasterClock` enabled.
618            #[cfg(not(any(feature = "h7", feature = "l4", feature = "l5")))]
619            w.mcken().bit(config_a.master_clock as u8 == 0);
620            #[cfg(feature = "h7")]
621            // Due to an H743v PAC error, xCR bit 19 is called NODIV (Which is how it is on other platforms).
622            // This is actually the NOMCK bit.
623            // todo: NODIV may need to be set by the user and presets - not hard-set like this!
624            w.nodiv().bit(config_a.master_clock as u8 != 0);
625            // The audio frame can target different data sizes by configuring bit DS[2:0] in the SAI_xCR1
626            // register. The data sizes may be 8, 10, 16, 20, 24 or 32 bits. During the transfer, either the
627            // MSB or the LSB of the data are sent first, depending on the configuration of bit LSBFIRST in
628            // the SAI_xCR1 register.
629            w.ds().bits(config_a.datasize as u8);
630            #[cfg(not(feature = "l4"))]
631            w.osr().bit(config_a.oversampling_ratio as u8 != 0);
632            // This bit is set and cleared by software. It must be configured when the audio block is disabled. This
633            // bit has no meaning in AC’97 audio protocol since AC’97 data are always transferred with the MSB
634            // first. This bit has no meaning in SPDIF audio protocol since in SPDIF data are always transferred
635            // with LSB first
636            w.lsbfirst().bit(config_a.first_bit as u8 != 0);
637            w.mckdiv().bits(config_a.mckdiv)
638        });
639        // todo: MCKEN vice NOMCK?? Make sure your enum reflects how you handle it.
640
641        // RM: External synchronization
642        // The audio subblocks can also be configured to operate synchronously with another SAI.
643        // This can be done as follow:
644        // 1. The SAI, which is configured as the source from which the other SAI is synchronized,
645        // has to define which of its audio subblock is supposed to provide the FS and SCK
646        // signals to other SAI. This is done by programming SYNCOUT[1:0] bits.
647        // 2. The SAI which shall receive the synchronization signals has to select which SAI will
648        // provide the synchronization by setting the proper value on SYNCIN[1:0] bits. For each
649        // of the two SAI audio subblocks, the user must then specify if it operates synchronously
650        // with the other SAI via the SYNCEN bit.
651        // Note: SYNCIN[1:0] and SYNCOUT[1:0] bits are located into the SAI_GCR register, and SYNCEN
652        // bits into SAI_xCR1 register.
653        //
654        // If both audio subblocks in a given SAI need to be synchronized with another SAI, it is
655        // possible to choose one of the following configurations:
656        // • Configure each audio block to be synchronous with another SAI block through the
657        // SYNCEN[1:0] bits.
658        // • Configure one audio block to be synchronous with another SAI through the
659        // SYNCEN[1:0] bits. The other audio block is then configured as synchronous with the
660        // second SAI audio block through SYNCEN[1:0] bits.
661
662        // We use config A's settings here, and ignore config B. These must be set with SAI disabled.
663        #[cfg(not(any(feature = "l4", feature = "wb", feature = "g4")))]
664        regs.gcr.modify(|_, w| unsafe {
665            w.syncout().bits(config_a.sync_out as u8);
666            w.syncin().bits(config_a.sync_in as u8)
667        });
668
669        regs.chb().cr1.modify(|_, w| unsafe {
670            w.mode().bits(config_b.mode as u8);
671            w.prtcfg().bits(config_b.protocol as u8);
672            w.mono().bit(config_b.mono as u8 != 0);
673            w.syncen().bits(config_b.sync as u8);
674            w.ckstr().bit(config_b.clock_strobe as u8 != 0);
675            #[cfg(not(any(feature = "h7", feature = "l4", feature = "l5")))]
676            w.mcken().bit(config_b.master_clock as u8 == 0);
677            #[cfg(feature = "h7")]
678            w.nodiv().bit(config_b.master_clock as u8 != 0);
679            w.ds().bits(config_b.datasize as u8);
680            #[cfg(not(feature = "l4"))]
681            w.osr().bit(config_b.oversampling_ratio as u8 != 0);
682            w.lsbfirst().bit(config_b.first_bit as u8 != 0);
683            w.mckdiv().bits(config_a.mckdiv)
684        });
685
686        // todo: Add this to config and don't hard-set.
687        regs.cha().cr2.modify(|_, w| unsafe {
688            w.comp().bits(0);
689            w.cpl().clear_bit();
690            #[cfg(feature = "wb")]
691            w.mutecnt().bits(0); // rec only
692            #[cfg(not(feature = "wb"))]
693            w.muteval().clear_bit(); // xmitter only
694            w.mute().clear_bit(); // xmitter only
695            w.tris().clear_bit(); // xmitter only
696            // The FIFO pointers can be reinitialized when the SAI is disabled by setting bit FFLUSH in the
697            // SAI_xCR2 register. If FFLUSH is set when the SAI is enabled the data present in the FIFO
698            // will be lost automatically.
699            w.fflush().set_bit();
700            // FIFO threshold
701            w.fth().bits(config_a.fifo_thresh as u8)
702        });
703
704        regs.chb().cr2.modify(|_, w| unsafe {
705            w.comp().bits(0);
706            w.cpl().clear_bit();
707            #[cfg(feature = "wb")]
708            w.mutecnt().bits(0); // rec only
709            #[cfg(not(feature = "wb"))]
710            w.muteval().clear_bit(); // xmitter only
711            w.mute().clear_bit(); // xmitter only
712            w.tris().clear_bit(); // xmitter only
713            w.fflush().set_bit();
714            w.fth().bits(config_b.fifo_thresh as u8)
715        });
716
717        // The FS signal can have a different meaning depending on the FS function. FSDEF bit in the
718        // SAI_xFRCR register selects which meaning it will have:
719        // • 0: start of frame, like for instance the PCM/DSP, TDM, AC’97, audio protocols,
720        // • 1: start of frame and channel side identification within the audio frame like for the I2S,
721        // the MSB or LSB-justified protocols.
722        // When the FS signal is considered as a start of frame and channel side identification within
723        // the frame, the number of declared slots must be considered to be half the number for the left
724        // channel and half the number for the right channel. If the number of bit clock cycles on half
725        // audio frame is greater than the number of slots dedicated to a channel side, and TRIS = 0, 0
726        // is sent for transmission for the remaining bit clock cycles in the SAI_xCR2 register.
727        // Otherwise if TRIS = 1, the SD line is released to HI-Z. In reception mode, the remaining bit
728        // clock cycles are not considered until the channel side changes.
729
730        if config_a.frame_length < 8
731            || config_b.frame_length < 8
732            || config_a.frame_length > 256
733            || config_b.frame_length > 256
734        {
735            panic!("Frame length must be bewteen 8 and 256")
736        }
737
738        let fsall_bits_a = if let FsSignal::Frame = config_a.fs_signal {
739            0
740        } else {
741            // Hard-set a 50% duty cycle. Don't think this is a safe assumption? Send in an issue
742            // or PR.
743            (config_a.frame_length / 2) as u8 - 1
744        };
745
746        let fsall_bits_b = if let FsSignal::Frame = config_a.fs_signal {
747            0
748        } else {
749            // Hard-set a 50% duty cycle. Don't think this is a safe assumption? Send in an issue
750            // or PR.
751            (config_a.frame_length / 2) as u8 - 1
752        };
753
754        // The audio frame length can be configured to up to 256 bit clock cycles, by setting
755        // FRL[7:0] field in the SAI_xFRCR register.
756        regs.cha().frcr.modify(|_, w| unsafe {
757            w.fsoff().bit(config_a.fs_offset as u8 != 0);
758            w.fspol().bit(config_a.fs_polarity as u8 != 0);
759            w.fsdef().bit(config_a.fs_signal as u8 != 0);
760            w.fsall().bits(fsall_bits_a);
761            w.frl().bits((config_a.frame_length - 1) as u8)
762        });
763
764        regs.chb().frcr.modify(|_, w| unsafe {
765            w.fsoff().bit(config_a.fs_offset as u8 != 0);
766            w.fspol().bit(config_b.fs_polarity as u8 != 0);
767            w.fsdef().bit(config_b.fs_signal as u8 != 0);
768            w.fsall().bits(fsall_bits_b);
769            w.frl().bits((config_b.frame_length - 1) as u8)
770        });
771
772        assert!(config_a.first_bit_offset <= 0b11111);
773        assert!(config_b.first_bit_offset <= 0b11111);
774
775        // Each SLOTEN bit corresponds to a slot position from 0 to 15 (maximum 16 slots).
776        // So, to enable the first 2 slots, we set 0b11. The code below calculates this.
777        let slot_en_bits = 2_u16.pow(config_a.num_slots as u32) - 1;
778
779        regs.cha().slotr.modify(|_, w| unsafe {
780            w.sloten().bits(slot_en_bits);
781            // The slot is the basic element in the audio frame. The number of slots in the audio frame is
782            // equal to NBSLOT[3:0] + 1.
783            w.nbslot().bits(config_a.num_slots - 1);
784            // The slot size must be higher or equal to the data size. If this condition is not respected, the behavior
785            // of the SAI will be undetermined.
786            w.slotsz().bits(config_a.slotsize as u8);
787            w.fboff().bits(config_a.first_bit_offset)
788        });
789
790        let slot_en_bits = 2_u16.pow(config_b.num_slots as u32) - 1;
791        regs.chb().slotr.modify(|_, w| unsafe {
792            w.sloten().bits(slot_en_bits);
793            w.nbslot().bits(config_b.num_slots - 1);
794            w.slotsz().bits(config_b.slotsize as u8);
795            w.fboff().bits(config_b.first_bit_offset)
796        });
797
798        // The PDM function is intended to be used in conjunction with SAI_A subblock configured in
799        // TDM master mode. It cannot be used with SAI_B subblock. The PDM interface uses the
800        // timing signals provided by the TDM interface of SAI_A and adapts them to generate a
801        // bitstream clock (SAI_CK[m]).
802
803        // AN: The PDM function is intended to be used in conjunction with SAI_A sub-block, configured in
804        // Time Division Multiplexing (TDM) master mode. It cannot be used with SAI_B sub-block
805
806        // Enabling the PDM interface (H743 RM, section 51.4.10)
807        // To enable the PDM interface, follow the sequence below:
808        // 1. Configure SAI_A in TDM master mode (see Table 422).
809        // (Above. Although we don't check this)
810        // 2. Configure the PDM interface as follows:
811        #[cfg(not(feature = "l4"))]
812        if config_a.pdm_mode {
813            assert!(config_a.pdm_clock_used <= 4 && config_a.pdm_clock_used >= 1);
814
815            regs.pdmcr.modify(|_, w| unsafe {
816                // a) Define the number of digital microphones via MICNBR.
817                w.micnbr().bits(config_a.num_pdm_mics as u8);
818                // b) Enable the bitstream clock needed in the application by setting the corresponding
819                // bits on CKEN to 1.
820                w.cken1().bit(config_a.pdm_clock_used == 1);
821                w.cken2().bit(config_a.pdm_clock_used == 2);
822                #[cfg(not(feature = "l5"))]
823                w.cken3().bit(config_a.pdm_clock_used == 3);
824                #[cfg(not(feature = "l5"))]
825                w.cken4().bit(config_a.pdm_clock_used == 4);
826                // 3. Enable the PDM interface, via PDMEN bit.
827                w.pdmen().set_bit()
828            })
829        }
830
831        // 4. Enable the SAI_A.
832        // (Handled with the `enable()` function called by the user.)
833        // Note: Once the PDM interface and SAI_A are enabled, the first 2 TDMA frames received on
834        // SAI_ADR are invalid and shall be dropped.
835
836        // Note that most register fields set in this initialization function must be done with
837        // SAIEN disabled.
838
839        Self {
840            regs,
841            config_a,
842            config_b,
843        }
844    }
845
846    /// Enable an audio subblock (channel).
847    pub fn enable(&mut self, channel: SaiChannel) {
848        // Each of the audio blocks in the SAI are enabled by SAIEN bit in the SAI_xCR1 register. As
849        // soon as this bit is active, the transmitter or the receiver is sensitive to the activity on the
850        // clock line, data line and synchronization line in slave mode.
851        // In master TX mode, enabling the audio block immediately generates the bit clock for the
852        // external slaves even if there is no data in the FIFO, However FS signal generation is
853        // conditioned by the presence of data in the FIFO. After the FIFO receives the first data to
854        // transmit, this data is output to external slaves. If there is no data to transmit in the FIFO, 0
855        // values are then sent in the audio frame with an underrun flag generation.
856        // In slave mode, the audio frame starts when the audio block is enabled and when a start of
857        // frame is detected.
858        // In Slave TX mode, no underrun event is possible on the first frame after the audio block is
859        // enabled, because the mandatory operating sequence in this case is:
860        // 1. Write into the SAI_xDR (by software or by DMA).
861        // 2. Wait until the FIFO threshold (FLH) flag is different from 0b000 (FIFO empty).
862        // 3. Enable the audio block in slave transmitter mode.
863
864        match channel {
865            SaiChannel::A => {
866                // todo: Do we want to flush?
867                self.regs.cha().cr2.modify(|_, w| w.fflush().set_bit());
868                self.regs.cha().cr1.modify(|_, w| w.saien().set_bit());
869
870                // Note: This read check only fires the WCKCFG bit if Master out is enabled.
871
872                if self.regs.cha().sr.read().wckcfg().bit_is_set() {
873                    panic!("Wrong clock configuration. Clock configuration does not respect the rule concerning
874the frame length specification defined in Section 51.4.6: Frame synchronization (configuration of
875FRL[7:0] bit in the SAI_xFRCR register)
876This bit is used only when the audio block operates in master mode (MODE[1] = 0) and NOMCK = 0.
877It can generate an interrupt if WCKCFGIE bit is set in SAI_xIM register");
878                }
879            }
880            SaiChannel::B => {
881                self.regs.chb().cr2.modify(|_, w| w.fflush().set_bit());
882                self.regs.chb().cr1.modify(|_, w| w.saien().set_bit());
883
884                if self.regs.chb().sr.read().wckcfg().bit_is_set() {
885                    panic!("Wrong clock configuration. Clock configuration does not respect the rule concerning the frame length specification defined in
886Section 51.4.6: Frame synchronization (configuration of FRL[7:0] bit in the SAI_xFRCR register)
887This bit is used only when the audio block operates in master mode (MODE[1] = 0) and NOMCK = 0.
888It can generate an interrupt if WCKCFGIE bit is set in SAI_xIM register");
889                }
890            }
891        }
892    }
893
894    /// Disable an audio subblock (channel). See H743 RM, section 51.4.15.
895    /// The SAI audio block can be disabled at any moment by clearing SAIEN bit in the SAI_xCR1
896    /// register. All the already started frames are automatically completed before the SAI is stops
897    /// working. SAIEN bit remains High until the SAI is completely switched-off at the end of the
898    /// current audio frame transfer.
899    /// If an audio block in the SAI operates synchronously with the other one, the one which is the
900    /// master must be disabled first.
901    pub fn disable(&mut self, channel: SaiChannel) {
902        match channel {
903            SaiChannel::A => self.regs.cha().cr1.modify(|_, w| w.saien().clear_bit()),
904            SaiChannel::B => self.regs.chb().cr1.modify(|_, w| w.saien().clear_bit()),
905        }
906    }
907
908    /// Read a word of data.
909    pub fn read(&self, channel: SaiChannel) -> i32 {
910        match channel {
911            SaiChannel::A => self.regs.cha().dr.read().bits() as i32,
912            SaiChannel::B => self.regs.chb().dr.read().bits() as i32,
913        }
914    }
915
916    // /// Read 2 words of data from a channel: Left and Right channel, in that order.
917    // pub fn read(&self, channel: SaiChannel) -> (u32, u32) {
918    //     // // todo TEMP TS!
919    //     let reading = self.regs.cha().dr.read().bits();
920    //     return (reading, reading);
921    //
922    //     match channel {
923    //         SaiChannel::A => (
924    //             self.regs.cha().dr.read().bits(),
925    //             self.regs.cha().dr.read().bits(),
926    //         ),
927    //         SaiChannel::B => (
928    //             self.regs.chb().dr.read().bits(),
929    //             self.regs.chb().dr.read().bits(),
930    //         ),
931    //     }
932    // }
933
934    /// Send 2 words of data to a single channel: Left and right channel, in that order.
935    /// A write to the SR register loads the FIFO provided the FIFO is not full.
936    pub fn write(&mut self, channel: SaiChannel, left_word: i32, right_word: i32) {
937        match channel {
938            SaiChannel::A => self
939                .regs
940                .cha()
941                .dr
942                .write(|w| unsafe { w.bits(left_word as u32).bits(right_word as u32) }),
943            SaiChannel::B => self
944                .regs
945                .chb()
946                .dr
947                .write(|w| unsafe { w.bits(left_word as u32).bits(right_word as u32) }),
948        }
949
950        // todo: Why 2 words?
951        // todo: Check FIFO level?
952
953        // The FIFO is 8 words long. A write consists of 2 words, in stereo mode.
954        // Therefore you need to wait for 3/4s to ensure 2 words are available for writing.
955        // match audio_ch.sr.read().flvl().variant() {
956        //     Val(sr::FLVL_A::FULL) => Err(nb::Error::WouldBlock),
957        //     Val(sr::FLVL_A::QUARTER4) => Err(nb::Error::WouldBlock),
958        //     _ => {
959        //         unsafe {
960        //             audio_ch.dr.write(|w| w.bits(left_word).bits(right_word));
961        //         }
962        //         Ok(())
963        //     }
964        // }
965    }
966
967    /// Send data over SAI with DMA. H743 RM, section 51.4.16: SAI DMA Interface.
968    /// To free the CPU and to optimize bus bandwidth, each SAI audio block has an independent
969    /// DMA interface to read/write from/to the SAI_xDR register (to access the internal FIFO).
970    /// There is one DMA channel per audio subblock supporting basic DMA request/acknowledge
971    /// protocol.
972    /// Before configuring the SAI block, the SAI DMA channel must be disabled.
973    #[cfg(not(any(feature = "f4", feature = "l552")))]
974    pub unsafe fn write_dma<D>(
975        &mut self,
976        buf: &[i32], // todo size?
977        sai_channel: SaiChannel,
978        dma_channel: DmaChannel,
979        channel_cfg: ChannelCfg,
980        dma: &mut Dma<D>,
981    ) where
982        D: Deref<Target = dma_p::RegisterBlock>,
983    {
984        let (ptr, len) = (buf.as_ptr(), buf.len());
985
986        // todo: DMA2 support.
987
988        // L44 RM, Table 41. "DMA1 requests for each channel"
989        #[cfg(any(feature = "f3", feature = "l4"))]
990        let dma_channel = match sai_channel {
991            SaiChannel::A => DmaInput::Sai1A.dma1_channel(),
992            SaiChannel::B => DmaInput::Sai1B.dma1_channel(),
993        };
994
995        #[cfg(feature = "l4")]
996        match sai_channel {
997            SaiChannel::A => dma.channel_select(DmaInput::Sai1A),
998            SaiChannel::B => dma.channel_select(DmaInput::Sai1B),
999        };
1000
1001        // To configure the audio subblock for DMA transfer, set DMAEN bit in the SAI_xCR1 register.
1002        // The DMA request is managed directly by the FIFO controller depending on the FIFO
1003        // threshold level (for more details refer to Section 51.4.9: Internal FIFOs). DMA transfer
1004        // direction is linked to the SAI audio subblock configuration:
1005        // • If the audio block operates as a transmitter, the audio block FIFO controller outputs a
1006        // DMA request to load the FIFO with data written in the SAI_xDR register.
1007        // • If the audio block is operates as a receiver, the DMA request is related to read
1008        // operations from the SAI_xDR register.
1009        match sai_channel {
1010            SaiChannel::A => self.regs.cha().cr1.modify(|_, w| w.dmaen().set_bit()),
1011            SaiChannel::B => self.regs.chb().cr1.modify(|_, w| w.dmaen().set_bit()),
1012        }
1013
1014        // Follow the sequence below to configure the SAI interface in DMA mode:
1015        // 1. Configure SAI and FIFO threshold levels to specify when the DMA request will be
1016        // launched.
1017        // (Set in `new`).
1018        // 2. Configure SAI DMA channel. (handled by `dma.cfg_channel`)
1019        // 3. Enable the DMA. (handled by `dma.cfg_channel`)
1020
1021        let periph_addr = match sai_channel {
1022            SaiChannel::A => &self.regs.cha().dr as *const _ as u32,
1023            SaiChannel::B => &self.regs.chb().dr as *const _ as u32,
1024        };
1025
1026        #[cfg(feature = "h7")]
1027        let len = len as u32;
1028        #[cfg(not(feature = "h7"))]
1029        let len = len as u16;
1030
1031        let cfg_datasize = match sai_channel {
1032            SaiChannel::A => self.config_a.datasize,
1033            SaiChannel::B => self.config_b.datasize,
1034        };
1035
1036        let datasize = match cfg_datasize {
1037            DataSize::S8 => dma::DataSize::S8,
1038            DataSize::S10 => dma::DataSize::S16,
1039            DataSize::S16 => dma::DataSize::S16,
1040            _ => dma::DataSize::S32,
1041        };
1042
1043        dma.cfg_channel(
1044            dma_channel,
1045            periph_addr,
1046            ptr as u32,
1047            len,
1048            dma::Direction::ReadFromMem,
1049            datasize,
1050            datasize,
1051            channel_cfg,
1052        );
1053
1054        // 4. Enable the SAI interface. (handled by `Sai::enable() in user code`.)
1055    }
1056
1057    /// Read data from SAI with DMA. H743 RM, section 51.4.16: SAI DMA Interface.
1058    /// To free the CPU and to optimize bus bandwidth, each SAI audio block has an independent
1059    /// DMA interface to read/write from/to the SAI_xDR register (to access the internal FIFO).
1060    /// There is one DMA channel per audio subblock supporting basic DMA request/acknowledge
1061    /// protocol.
1062    #[cfg(not(any(feature = "f4", feature = "l552")))]
1063    pub unsafe fn read_dma<D>(
1064        &mut self,
1065        buf: &mut [i32], // todo size?
1066        sai_channel: SaiChannel,
1067        dma_channel: DmaChannel,
1068        channel_cfg: ChannelCfg,
1069        dma: &mut Dma<D>,
1070    ) where
1071        D: Deref<Target = dma_p::RegisterBlock>,
1072    {
1073        let (ptr, len) = (buf.as_mut_ptr(), buf.len());
1074
1075        // See commends on `write_dma`.
1076
1077        // L44 RM, Table 41. "DMA1 requests for each channel
1078        // todo: DMA2 support.
1079
1080        #[cfg(any(feature = "f3", feature = "l4"))]
1081        let dma_channel = match sai_channel {
1082            SaiChannel::A => DmaInput::Sai1A.dma1_channel(),
1083            SaiChannel::B => DmaInput::Sai1B.dma1_channel(),
1084        };
1085
1086        #[cfg(feature = "l4")]
1087        match sai_channel {
1088            SaiChannel::A => dma.channel_select(DmaInput::Sai1A),
1089            SaiChannel::B => dma.channel_select(DmaInput::Sai1B),
1090        };
1091
1092        match sai_channel {
1093            SaiChannel::A => self.regs.cha().cr1.modify(|_, w| w.dmaen().set_bit()),
1094            SaiChannel::B => self.regs.chb().cr1.modify(|_, w| w.dmaen().set_bit()),
1095        }
1096
1097        let periph_addr = match sai_channel {
1098            SaiChannel::A => &self.regs.cha().dr as *const _ as u32,
1099            SaiChannel::B => &self.regs.chb().dr as *const _ as u32,
1100        };
1101
1102        #[cfg(feature = "h7")]
1103        let num_data = len as u32;
1104        #[cfg(not(feature = "h7"))]
1105        let num_data = len as u16;
1106
1107        let cfg_datasize = match sai_channel {
1108            SaiChannel::A => self.config_a.datasize,
1109            SaiChannel::B => self.config_b.datasize,
1110        };
1111
1112        let datasize = match cfg_datasize {
1113            DataSize::S8 => dma::DataSize::S8,
1114            DataSize::S10 => dma::DataSize::S16,
1115            DataSize::S16 => dma::DataSize::S16,
1116            _ => dma::DataSize::S32,
1117        };
1118
1119        dma.cfg_channel(
1120            dma_channel,
1121            periph_addr,
1122            ptr as u32,
1123            num_data,
1124            dma::Direction::ReadFromPeriph,
1125            datasize,
1126            datasize,
1127            channel_cfg,
1128        );
1129
1130        // 4. Enable the SAI interface. (handled by `Sai::enable() in user code`.)
1131    }
1132
1133    /// Enable a specific type of interrupt. See L4 RM, Table 220: "SAI interrupt sources".
1134    pub fn enable_interrupt(&mut self, interrupt_type: SaiInterrupt, channel: SaiChannel) {
1135        // Disable the UART to allow writing the `add` and `addm7` bits
1136        // L4 RM: Follow the sequence below to enable an interrupt:
1137        // 1. Disable SAI interrupt.
1138        // 2. Configure SAI.
1139        // 3. Configure SAI interrupt source.
1140        // 4. Enable SAI.
1141
1142        //todo: Does that mean we need to disable and re-enable SAI here?
1143
1144        match channel {
1145            SaiChannel::A => {
1146                self.regs.cha().im.modify(|_, w| match interrupt_type {
1147                    SaiInterrupt::Freq => w.freqie().set_bit(),
1148                    SaiInterrupt::Ovrudr => w.ovrudrie().set_bit(),
1149                    SaiInterrupt::AfsDet => w.afsdetie().set_bit(),
1150                    SaiInterrupt::LfsDet => w.lfsdetie().set_bit(),
1151                    SaiInterrupt::CnRdy => w.cnrdyie().set_bit(),
1152                    SaiInterrupt::MuteDet => w.mutedetie().set_bit(),
1153                    SaiInterrupt::WckCfg => w.wckcfgie().set_bit(),
1154                });
1155            }
1156            SaiChannel::B => {
1157                self.regs.chb().im.modify(|_, w| match interrupt_type {
1158                    SaiInterrupt::Freq => w.freqie().set_bit(),
1159                    SaiInterrupt::Ovrudr => w.ovrudrie().set_bit(),
1160                    SaiInterrupt::AfsDet => w.afsdetie().set_bit(),
1161                    SaiInterrupt::LfsDet => w.lfsdetie().set_bit(),
1162                    SaiInterrupt::CnRdy => w.cnrdyie().set_bit(),
1163                    SaiInterrupt::MuteDet => w.mutedetie().set_bit(),
1164                    SaiInterrupt::WckCfg => w.wckcfgie().set_bit(),
1165                });
1166            }
1167        }
1168    }
1169
1170    /// Clears the interrupt pending flag for a specific type of interrupt.
1171    pub fn clear_interrupt(&mut self, interrupt_type: SaiInterrupt, channel: SaiChannel) {
1172        match channel {
1173            SaiChannel::A => {
1174                self.regs.cha().clrfr.write(|w| match interrupt_type {
1175                    // This Interrupt (FREQ bit in SAI_xSR register) is
1176                    // cleared by hardware when the FIFO becomes empty (FLVL[2:0] bits in SAI_xSR is equal
1177                    // to 0b000) i.e no data are stored in FIFO.
1178                    SaiInterrupt::Freq => w.cmutedet().set_bit(), // There is no Freq flag.
1179                    SaiInterrupt::Ovrudr => w.covrudr().set_bit(),
1180                    SaiInterrupt::AfsDet => w.cafsdet().set_bit(),
1181                    SaiInterrupt::LfsDet => w.clfsdet().set_bit(),
1182                    SaiInterrupt::CnRdy => w.ccnrdy().set_bit(),
1183                    SaiInterrupt::MuteDet => w.cmutedet().set_bit(),
1184                    SaiInterrupt::WckCfg => w.cwckcfg().set_bit(),
1185                });
1186            }
1187            SaiChannel::B => {
1188                self.regs.chb().clrfr.write(|w| match interrupt_type {
1189                    SaiInterrupt::Freq => w.cmutedet().set_bit(),
1190                    SaiInterrupt::Ovrudr => w.covrudr().set_bit(),
1191                    SaiInterrupt::AfsDet => w.cafsdet().set_bit(),
1192                    SaiInterrupt::LfsDet => w.clfsdet().set_bit(),
1193                    SaiInterrupt::CnRdy => w.ccnrdy().set_bit(),
1194                    SaiInterrupt::MuteDet => w.cmutedet().set_bit(),
1195                    SaiInterrupt::WckCfg => w.cwckcfg().set_bit(),
1196                });
1197            }
1198        }
1199    }
1200
1201    /// Print the (raw) contents of the (A and B) status registers.
1202    pub fn read_status(&self) -> (u32, u32) {
1203        unsafe {
1204            (
1205                self.regs.cha().sr.read().bits(),
1206                self.regs.chb().sr.read().bits(),
1207            )
1208        }
1209    }
1210}