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}