1 | //! Serial Audio Interface (SAI) |
2 | #![macro_use ] |
3 | #![cfg_attr (gpdma, allow(unused))] |
4 | |
5 | use core::marker::PhantomData; |
6 | |
7 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
8 | |
9 | pub use crate::dma::word; |
10 | #[cfg (not(gpdma))] |
11 | use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; |
12 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
13 | use crate::pac::sai::{vals, Sai as Regs}; |
14 | use crate::rcc::{self, RccPeripheral}; |
15 | use crate::{peripherals, Peripheral}; |
16 | |
17 | /// SAI error |
18 | #[derive (Debug, PartialEq, Eq, Clone, Copy)] |
19 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
20 | pub enum Error { |
21 | /// `write` called on a SAI in receive mode. |
22 | NotATransmitter, |
23 | /// `read` called on a SAI in transmit mode. |
24 | NotAReceiver, |
25 | /// Overrun |
26 | Overrun, |
27 | } |
28 | |
29 | #[cfg (not(gpdma))] |
30 | impl From<ringbuffer::Error> for Error { |
31 | fn from(#[allow (unused)] err: ringbuffer::Error) -> Self { |
32 | #[cfg (feature = "defmt" )] |
33 | { |
34 | if err == ringbuffer::Error::DmaUnsynced { |
35 | defmt::error!("Ringbuffer broken invariants detected!" ); |
36 | } |
37 | } |
38 | Self::Overrun |
39 | } |
40 | } |
41 | |
42 | /// Master/slave mode. |
43 | #[derive (Copy, Clone)] |
44 | #[allow (missing_docs)] |
45 | pub enum Mode { |
46 | Master, |
47 | Slave, |
48 | } |
49 | |
50 | impl Mode { |
51 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
52 | const fn mode(&self, tx_rx: TxRx) -> vals::Mode { |
53 | match tx_rx { |
54 | TxRx::Transmitter => match self { |
55 | Mode::Master => vals::Mode::MASTER_TX, |
56 | Mode::Slave => vals::Mode::SLAVE_TX, |
57 | }, |
58 | TxRx::Receiver => match self { |
59 | Mode::Master => vals::Mode::MASTER_RX, |
60 | Mode::Slave => vals::Mode::SLAVE_RX, |
61 | }, |
62 | } |
63 | } |
64 | } |
65 | |
66 | /// Direction: transmit or receive |
67 | #[derive (Copy, Clone)] |
68 | #[allow (missing_docs)] |
69 | pub enum TxRx { |
70 | Transmitter, |
71 | Receiver, |
72 | } |
73 | |
74 | /// Data slot size. |
75 | #[derive (Copy, Clone)] |
76 | #[allow (missing_docs)] |
77 | pub enum SlotSize { |
78 | DataSize, |
79 | /// 16 bit data length on 16 bit wide channel |
80 | Channel16, |
81 | /// 16 bit data length on 32 bit wide channel |
82 | Channel32, |
83 | } |
84 | |
85 | impl SlotSize { |
86 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
87 | const fn slotsz(&self) -> vals::Slotsz { |
88 | match self { |
89 | SlotSize::DataSize => vals::Slotsz::DATA_SIZE, |
90 | SlotSize::Channel16 => vals::Slotsz::BIT16, |
91 | SlotSize::Channel32 => vals::Slotsz::BIT32, |
92 | } |
93 | } |
94 | } |
95 | |
96 | /// Data size. |
97 | #[derive (Copy, Clone)] |
98 | #[allow (missing_docs)] |
99 | pub enum DataSize { |
100 | Data8, |
101 | Data10, |
102 | Data16, |
103 | Data20, |
104 | Data24, |
105 | Data32, |
106 | } |
107 | |
108 | impl DataSize { |
109 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
110 | const fn ds(&self) -> vals::Ds { |
111 | match self { |
112 | DataSize::Data8 => vals::Ds::BIT8, |
113 | DataSize::Data10 => vals::Ds::BIT10, |
114 | DataSize::Data16 => vals::Ds::BIT16, |
115 | DataSize::Data20 => vals::Ds::BIT20, |
116 | DataSize::Data24 => vals::Ds::BIT24, |
117 | DataSize::Data32 => vals::Ds::BIT32, |
118 | } |
119 | } |
120 | } |
121 | |
122 | /// FIFO threshold level. |
123 | #[derive (Copy, Clone)] |
124 | #[allow (missing_docs)] |
125 | pub enum FifoThreshold { |
126 | Empty, |
127 | Quarter, |
128 | Half, |
129 | ThreeQuarters, |
130 | Full, |
131 | } |
132 | |
133 | impl FifoThreshold { |
134 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
135 | const fn fth(&self) -> vals::Fth { |
136 | match self { |
137 | FifoThreshold::Empty => vals::Fth::EMPTY, |
138 | FifoThreshold::Quarter => vals::Fth::QUARTER1, |
139 | FifoThreshold::Half => vals::Fth::QUARTER2, |
140 | FifoThreshold::ThreeQuarters => vals::Fth::QUARTER3, |
141 | FifoThreshold::Full => vals::Fth::FULL, |
142 | } |
143 | } |
144 | } |
145 | |
146 | /// Output value on mute. |
147 | #[derive (Copy, Clone)] |
148 | #[allow (missing_docs)] |
149 | pub enum MuteValue { |
150 | Zero, |
151 | LastValue, |
152 | } |
153 | |
154 | impl MuteValue { |
155 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
156 | const fn muteval(&self) -> vals::Muteval { |
157 | match self { |
158 | MuteValue::Zero => vals::Muteval::SEND_ZERO, |
159 | MuteValue::LastValue => vals::Muteval::SEND_LAST, |
160 | } |
161 | } |
162 | } |
163 | |
164 | /// Protocol variant to use. |
165 | #[derive (Copy, Clone)] |
166 | #[allow (missing_docs)] |
167 | pub enum Protocol { |
168 | Free, |
169 | Spdif, |
170 | Ac97, |
171 | } |
172 | |
173 | impl Protocol { |
174 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
175 | const fn prtcfg(&self) -> vals::Prtcfg { |
176 | match self { |
177 | Protocol::Free => vals::Prtcfg::FREE, |
178 | Protocol::Spdif => vals::Prtcfg::SPDIF, |
179 | Protocol::Ac97 => vals::Prtcfg::AC97, |
180 | } |
181 | } |
182 | } |
183 | |
184 | /// Sync input between SAI units/blocks. |
185 | #[derive (Copy, Clone, PartialEq)] |
186 | #[allow (missing_docs)] |
187 | pub enum SyncInput { |
188 | /// Not synced to any other SAI unit. |
189 | None, |
190 | /// Syncs with the other A/B sub-block within the SAI unit |
191 | Internal, |
192 | /// Syncs with a sub-block in the other SAI unit |
193 | #[cfg (any(sai_v4_2pdm, sai_v4_4pdm))] |
194 | External(SyncInputInstance), |
195 | } |
196 | |
197 | impl SyncInput { |
198 | const fn syncen(&self) -> vals::Syncen { |
199 | match self { |
200 | SyncInput::None => vals::Syncen::ASYNCHRONOUS, |
201 | SyncInput::Internal => vals::Syncen::INTERNAL, |
202 | #[cfg (any(sai_v4_2pdm, sai_v4_4pdm))] |
203 | SyncInput::External(_) => vals::Syncen::EXTERNAL, |
204 | } |
205 | } |
206 | } |
207 | |
208 | /// SAI instance to sync from. |
209 | #[cfg (any(sai_v4_2pdm, sai_v4_4pdm))] |
210 | #[derive (Copy, Clone, PartialEq)] |
211 | #[allow (missing_docs)] |
212 | pub enum SyncInputInstance { |
213 | #[cfg (peri_sai1)] |
214 | Sai1 = 0, |
215 | #[cfg (peri_sai2)] |
216 | Sai2 = 1, |
217 | #[cfg (peri_sai3)] |
218 | Sai3 = 2, |
219 | #[cfg (peri_sai4)] |
220 | Sai4 = 3, |
221 | } |
222 | |
223 | /// Channels (stereo or mono). |
224 | #[derive (Copy, Clone, PartialEq)] |
225 | #[allow (missing_docs)] |
226 | pub enum StereoMono { |
227 | Stereo, |
228 | Mono, |
229 | } |
230 | |
231 | impl StereoMono { |
232 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
233 | const fn mono(&self) -> vals::Mono { |
234 | match self { |
235 | StereoMono::Stereo => vals::Mono::STEREO, |
236 | StereoMono::Mono => vals::Mono::MONO, |
237 | } |
238 | } |
239 | } |
240 | |
241 | /// Bit order |
242 | #[derive (Copy, Clone)] |
243 | pub enum BitOrder { |
244 | /// Least significant bit first. |
245 | LsbFirst, |
246 | /// Most significant bit first. |
247 | MsbFirst, |
248 | } |
249 | |
250 | impl BitOrder { |
251 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
252 | const fn lsbfirst(&self) -> vals::Lsbfirst { |
253 | match self { |
254 | BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST, |
255 | BitOrder::MsbFirst => vals::Lsbfirst::MSB_FIRST, |
256 | } |
257 | } |
258 | } |
259 | |
260 | /// Frame sync offset. |
261 | #[derive (Copy, Clone)] |
262 | pub enum FrameSyncOffset { |
263 | /// This is used in modes other than standard I2S phillips mode |
264 | OnFirstBit, |
265 | /// This is used in standard I2S phillips mode |
266 | BeforeFirstBit, |
267 | } |
268 | |
269 | impl FrameSyncOffset { |
270 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
271 | const fn fsoff(&self) -> vals::Fsoff { |
272 | match self { |
273 | FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST, |
274 | FrameSyncOffset::BeforeFirstBit => vals::Fsoff::BEFORE_FIRST, |
275 | } |
276 | } |
277 | } |
278 | |
279 | /// Frame sync polarity |
280 | #[derive (Copy, Clone)] |
281 | pub enum FrameSyncPolarity { |
282 | /// Sync signal is active low. |
283 | ActiveLow, |
284 | /// Sync signal is active high |
285 | ActiveHigh, |
286 | } |
287 | |
288 | impl FrameSyncPolarity { |
289 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
290 | const fn fspol(&self) -> vals::Fspol { |
291 | match self { |
292 | FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE, |
293 | FrameSyncPolarity::ActiveHigh => vals::Fspol::RISING_EDGE, |
294 | } |
295 | } |
296 | } |
297 | |
298 | /// Sync definition. |
299 | #[derive (Copy, Clone)] |
300 | #[allow (missing_docs)] |
301 | pub enum FrameSyncDefinition { |
302 | StartOfFrame, |
303 | ChannelIdentification, |
304 | } |
305 | |
306 | impl FrameSyncDefinition { |
307 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
308 | const fn fsdef(&self) -> bool { |
309 | match self { |
310 | FrameSyncDefinition::StartOfFrame => false, |
311 | FrameSyncDefinition::ChannelIdentification => true, |
312 | } |
313 | } |
314 | } |
315 | |
316 | /// Clock strobe. |
317 | #[derive (Copy, Clone)] |
318 | #[allow (missing_docs)] |
319 | pub enum ClockStrobe { |
320 | Falling, |
321 | Rising, |
322 | } |
323 | |
324 | impl ClockStrobe { |
325 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
326 | const fn ckstr(&self) -> vals::Ckstr { |
327 | match self { |
328 | ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE, |
329 | ClockStrobe::Rising => vals::Ckstr::RISING_EDGE, |
330 | } |
331 | } |
332 | } |
333 | |
334 | /// Complements format for negative samples. |
335 | #[derive (Copy, Clone)] |
336 | #[allow (missing_docs)] |
337 | pub enum ComplementFormat { |
338 | OnesComplement, |
339 | TwosComplement, |
340 | } |
341 | |
342 | impl ComplementFormat { |
343 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
344 | const fn cpl(&self) -> vals::Cpl { |
345 | match self { |
346 | ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT, |
347 | ComplementFormat::TwosComplement => vals::Cpl::TWOS_COMPLEMENT, |
348 | } |
349 | } |
350 | } |
351 | |
352 | /// Companding setting. |
353 | #[derive (Copy, Clone)] |
354 | #[allow (missing_docs)] |
355 | pub enum Companding { |
356 | None, |
357 | MuLaw, |
358 | ALaw, |
359 | } |
360 | |
361 | impl Companding { |
362 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
363 | const fn comp(&self) -> vals::Comp { |
364 | match self { |
365 | Companding::None => vals::Comp::NO_COMPANDING, |
366 | Companding::MuLaw => vals::Comp::MU_LAW, |
367 | Companding::ALaw => vals::Comp::ALAW, |
368 | } |
369 | } |
370 | } |
371 | |
372 | /// Output drive |
373 | #[derive (Copy, Clone)] |
374 | #[allow (missing_docs)] |
375 | pub enum OutputDrive { |
376 | OnStart, |
377 | Immediately, |
378 | } |
379 | |
380 | impl OutputDrive { |
381 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
382 | const fn outdriv(&self) -> vals::Outdriv { |
383 | match self { |
384 | OutputDrive::OnStart => vals::Outdriv::ON_START, |
385 | OutputDrive::Immediately => vals::Outdriv::IMMEDIATELY, |
386 | } |
387 | } |
388 | } |
389 | |
390 | /// Master clock divider. |
391 | #[derive (Copy, Clone, PartialEq)] |
392 | #[allow (missing_docs)] |
393 | #[cfg (any(sai_v1, sai_v2))] |
394 | pub enum MasterClockDivider { |
395 | MasterClockDisabled, |
396 | Div1, |
397 | Div2, |
398 | Div4, |
399 | Div6, |
400 | Div8, |
401 | Div10, |
402 | Div12, |
403 | Div14, |
404 | Div16, |
405 | Div18, |
406 | Div20, |
407 | Div22, |
408 | Div24, |
409 | Div26, |
410 | Div28, |
411 | Div30, |
412 | } |
413 | |
414 | /// Master clock divider. |
415 | #[derive (Copy, Clone, PartialEq)] |
416 | #[allow (missing_docs)] |
417 | #[cfg (any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
418 | pub enum MasterClockDivider { |
419 | MasterClockDisabled, |
420 | Div1, |
421 | Div2, |
422 | Div3, |
423 | Div4, |
424 | Div5, |
425 | Div6, |
426 | Div7, |
427 | Div8, |
428 | Div9, |
429 | Div10, |
430 | Div11, |
431 | Div12, |
432 | Div13, |
433 | Div14, |
434 | Div15, |
435 | Div16, |
436 | Div17, |
437 | Div18, |
438 | Div19, |
439 | Div20, |
440 | Div21, |
441 | Div22, |
442 | Div23, |
443 | Div24, |
444 | Div25, |
445 | Div26, |
446 | Div27, |
447 | Div28, |
448 | Div29, |
449 | Div30, |
450 | Div31, |
451 | Div32, |
452 | Div33, |
453 | Div34, |
454 | Div35, |
455 | Div36, |
456 | Div37, |
457 | Div38, |
458 | Div39, |
459 | Div40, |
460 | Div41, |
461 | Div42, |
462 | Div43, |
463 | Div44, |
464 | Div45, |
465 | Div46, |
466 | Div47, |
467 | Div48, |
468 | Div49, |
469 | Div50, |
470 | Div51, |
471 | Div52, |
472 | Div53, |
473 | Div54, |
474 | Div55, |
475 | Div56, |
476 | Div57, |
477 | Div58, |
478 | Div59, |
479 | Div60, |
480 | Div61, |
481 | Div62, |
482 | Div63, |
483 | } |
484 | |
485 | impl MasterClockDivider { |
486 | #[cfg (any(sai_v1, sai_v2))] |
487 | const fn mckdiv(&self) -> u8 { |
488 | match self { |
489 | MasterClockDivider::MasterClockDisabled => 0, |
490 | MasterClockDivider::Div1 => 0, |
491 | MasterClockDivider::Div2 => 1, |
492 | MasterClockDivider::Div4 => 2, |
493 | MasterClockDivider::Div6 => 3, |
494 | MasterClockDivider::Div8 => 4, |
495 | MasterClockDivider::Div10 => 5, |
496 | MasterClockDivider::Div12 => 6, |
497 | MasterClockDivider::Div14 => 7, |
498 | MasterClockDivider::Div16 => 8, |
499 | MasterClockDivider::Div18 => 9, |
500 | MasterClockDivider::Div20 => 10, |
501 | MasterClockDivider::Div22 => 11, |
502 | MasterClockDivider::Div24 => 12, |
503 | MasterClockDivider::Div26 => 13, |
504 | MasterClockDivider::Div28 => 14, |
505 | MasterClockDivider::Div30 => 15, |
506 | } |
507 | } |
508 | |
509 | #[cfg (any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
510 | const fn mckdiv(&self) -> u8 { |
511 | match self { |
512 | MasterClockDivider::MasterClockDisabled => 0, |
513 | MasterClockDivider::Div1 => 1, |
514 | MasterClockDivider::Div2 => 2, |
515 | MasterClockDivider::Div3 => 3, |
516 | MasterClockDivider::Div4 => 4, |
517 | MasterClockDivider::Div5 => 5, |
518 | MasterClockDivider::Div6 => 6, |
519 | MasterClockDivider::Div7 => 7, |
520 | MasterClockDivider::Div8 => 8, |
521 | MasterClockDivider::Div9 => 9, |
522 | MasterClockDivider::Div10 => 10, |
523 | MasterClockDivider::Div11 => 11, |
524 | MasterClockDivider::Div12 => 12, |
525 | MasterClockDivider::Div13 => 13, |
526 | MasterClockDivider::Div14 => 14, |
527 | MasterClockDivider::Div15 => 15, |
528 | MasterClockDivider::Div16 => 16, |
529 | MasterClockDivider::Div17 => 17, |
530 | MasterClockDivider::Div18 => 18, |
531 | MasterClockDivider::Div19 => 19, |
532 | MasterClockDivider::Div20 => 20, |
533 | MasterClockDivider::Div21 => 21, |
534 | MasterClockDivider::Div22 => 22, |
535 | MasterClockDivider::Div23 => 23, |
536 | MasterClockDivider::Div24 => 24, |
537 | MasterClockDivider::Div25 => 25, |
538 | MasterClockDivider::Div26 => 26, |
539 | MasterClockDivider::Div27 => 27, |
540 | MasterClockDivider::Div28 => 28, |
541 | MasterClockDivider::Div29 => 29, |
542 | MasterClockDivider::Div30 => 30, |
543 | MasterClockDivider::Div31 => 31, |
544 | MasterClockDivider::Div32 => 32, |
545 | MasterClockDivider::Div33 => 33, |
546 | MasterClockDivider::Div34 => 34, |
547 | MasterClockDivider::Div35 => 35, |
548 | MasterClockDivider::Div36 => 36, |
549 | MasterClockDivider::Div37 => 37, |
550 | MasterClockDivider::Div38 => 38, |
551 | MasterClockDivider::Div39 => 39, |
552 | MasterClockDivider::Div40 => 40, |
553 | MasterClockDivider::Div41 => 41, |
554 | MasterClockDivider::Div42 => 42, |
555 | MasterClockDivider::Div43 => 43, |
556 | MasterClockDivider::Div44 => 44, |
557 | MasterClockDivider::Div45 => 45, |
558 | MasterClockDivider::Div46 => 46, |
559 | MasterClockDivider::Div47 => 47, |
560 | MasterClockDivider::Div48 => 48, |
561 | MasterClockDivider::Div49 => 49, |
562 | MasterClockDivider::Div50 => 50, |
563 | MasterClockDivider::Div51 => 51, |
564 | MasterClockDivider::Div52 => 52, |
565 | MasterClockDivider::Div53 => 53, |
566 | MasterClockDivider::Div54 => 54, |
567 | MasterClockDivider::Div55 => 55, |
568 | MasterClockDivider::Div56 => 56, |
569 | MasterClockDivider::Div57 => 57, |
570 | MasterClockDivider::Div58 => 58, |
571 | MasterClockDivider::Div59 => 59, |
572 | MasterClockDivider::Div60 => 60, |
573 | MasterClockDivider::Div61 => 61, |
574 | MasterClockDivider::Div62 => 62, |
575 | MasterClockDivider::Div63 => 63, |
576 | } |
577 | } |
578 | } |
579 | |
580 | /// [`SAI`] configuration. |
581 | #[allow (missing_docs)] |
582 | #[non_exhaustive ] |
583 | #[derive (Copy, Clone)] |
584 | pub struct Config { |
585 | pub mode: Mode, |
586 | pub tx_rx: TxRx, |
587 | pub sync_input: SyncInput, |
588 | pub sync_output: bool, |
589 | pub protocol: Protocol, |
590 | pub slot_size: SlotSize, |
591 | pub slot_count: word::U4, |
592 | pub slot_enable: u16, |
593 | pub first_bit_offset: word::U5, |
594 | pub data_size: DataSize, |
595 | pub stereo_mono: StereoMono, |
596 | pub bit_order: BitOrder, |
597 | pub frame_sync_offset: FrameSyncOffset, |
598 | pub frame_sync_polarity: FrameSyncPolarity, |
599 | pub frame_sync_active_level_length: word::U7, |
600 | pub frame_sync_definition: FrameSyncDefinition, |
601 | pub frame_length: u8, |
602 | pub clock_strobe: ClockStrobe, |
603 | pub output_drive: OutputDrive, |
604 | pub master_clock_divider: MasterClockDivider, |
605 | pub is_high_impedance_on_inactive_slot: bool, |
606 | pub fifo_threshold: FifoThreshold, |
607 | pub companding: Companding, |
608 | pub complement_format: ComplementFormat, |
609 | pub mute_value: MuteValue, |
610 | pub mute_detection_counter: word::U5, |
611 | } |
612 | |
613 | impl Default for Config { |
614 | fn default() -> Self { |
615 | Self { |
616 | mode: Mode::Master, |
617 | tx_rx: TxRx::Transmitter, |
618 | sync_output: false, |
619 | sync_input: SyncInput::None, |
620 | protocol: Protocol::Free, |
621 | slot_size: SlotSize::DataSize, |
622 | slot_count: word::U4(2), |
623 | first_bit_offset: word::U5(0), |
624 | slot_enable: 0b11, |
625 | data_size: DataSize::Data16, |
626 | stereo_mono: StereoMono::Stereo, |
627 | bit_order: BitOrder::LsbFirst, |
628 | frame_sync_offset: FrameSyncOffset::BeforeFirstBit, |
629 | frame_sync_polarity: FrameSyncPolarity::ActiveLow, |
630 | frame_sync_active_level_length: word::U7(16), |
631 | frame_sync_definition: FrameSyncDefinition::ChannelIdentification, |
632 | frame_length: 32, |
633 | master_clock_divider: MasterClockDivider::MasterClockDisabled, |
634 | clock_strobe: ClockStrobe::Rising, |
635 | output_drive: OutputDrive::Immediately, |
636 | is_high_impedance_on_inactive_slot: false, |
637 | fifo_threshold: FifoThreshold::ThreeQuarters, |
638 | companding: Companding::None, |
639 | complement_format: ComplementFormat::TwosComplement, |
640 | mute_value: MuteValue::Zero, |
641 | mute_detection_counter: word::U5(4), |
642 | } |
643 | } |
644 | } |
645 | |
646 | impl Config { |
647 | /// Create a new config with all default values. |
648 | pub fn new() -> Self { |
649 | return Default::default(); |
650 | } |
651 | } |
652 | |
653 | #[cfg (not(gpdma))] |
654 | enum RingBuffer<'d, W: word::Word> { |
655 | Writable(WritableRingBuffer<'d, W>), |
656 | Readable(ReadableRingBuffer<'d, W>), |
657 | } |
658 | |
659 | fn dr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: WhichSubBlock) -> *mut W { |
660 | let ch: Ch = w.ch(sub_block as usize); |
661 | ch.dr().as_ptr() as _ |
662 | } |
663 | |
664 | // return the type for (sd, sck) |
665 | fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) { |
666 | ( |
667 | //sd is defined by tx/rx mode |
668 | match tx_rx { |
669 | TxRx::Transmitter => AfType::output(OutputType::PushPull, Speed::VeryHigh), |
670 | TxRx::Receiver => AfType::input(Pull::Down), // Ensure mute level when no input is connected. |
671 | }, |
672 | //clocks (mclk, sck and fs) are defined by master/slave |
673 | match mode { |
674 | Mode::Master => AfType::output(OutputType::PushPull, Speed::VeryHigh), |
675 | Mode::Slave => AfType::input(Pull::Down), // Ensure no clocks when no input is connected. |
676 | }, |
677 | ) |
678 | } |
679 | |
680 | #[cfg (not(gpdma))] |
681 | fn get_ring_buffer<'d, T: Instance, W: word::Word>( |
682 | dma: impl Peripheral<P = impl Channel> + 'd, |
683 | dma_buf: &'d mut [W], |
684 | request: Request, |
685 | sub_block: WhichSubBlock, |
686 | tx_rx: TxRx, |
687 | ) -> RingBuffer<'d, W> { |
688 | let opts = TransferOptions { |
689 | half_transfer_ir: true, |
690 | //the new_write() and new_read() always use circular mode |
691 | ..Default::default() |
692 | }; |
693 | match tx_rx { |
694 | TxRx::Transmitter => RingBuffer::Writable(unsafe { |
695 | WritableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts) |
696 | }), |
697 | TxRx::Receiver => RingBuffer::Readable(unsafe { |
698 | ReadableRingBuffer::new(dma, request, dr(T::REGS, sub_block), dma_buf, opts) |
699 | }), |
700 | } |
701 | } |
702 | |
703 | fn update_synchronous_config(config: &mut Config) { |
704 | config.mode = Mode::Slave; |
705 | config.sync_output = false; |
706 | |
707 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm))] |
708 | { |
709 | config.sync_input = SyncInput::Internal; |
710 | } |
711 | |
712 | #[cfg (any(sai_v4_2pdm, sai_v4_4pdm))] |
713 | { |
714 | //this must either be Internal or External |
715 | //The asynchronous sub-block on the same SAI needs to enable sync_output |
716 | assert!(config.sync_input != SyncInput::None); |
717 | } |
718 | } |
719 | |
720 | /// SAI subblock instance. |
721 | pub struct SubBlock<'d, T, S: SubBlockInstance> { |
722 | peri: PeripheralRef<'d, T>, |
723 | _phantom: PhantomData<S>, |
724 | } |
725 | |
726 | /// Split the main SAIx peripheral into the two subblocks. |
727 | /// |
728 | /// You can then create a [`Sai`] driver for each each half. |
729 | pub fn split_subblocks<'d, T: Instance>(peri: impl Peripheral<P = T> + 'd) -> (SubBlock<'d, T, A>, SubBlock<'d, T, B>) { |
730 | into_ref!(peri); |
731 | rcc::enable_and_reset::<T>(); |
732 | |
733 | ( |
734 | SubBlock { |
735 | peri: unsafe { peri.clone_unchecked() }, |
736 | _phantom: PhantomData, |
737 | }, |
738 | SubBlock { |
739 | peri, |
740 | _phantom: PhantomData, |
741 | }, |
742 | ) |
743 | } |
744 | |
745 | /// SAI sub-block driver. |
746 | pub struct Sai<'d, T: Instance, W: word::Word> { |
747 | _peri: PeripheralRef<'d, T>, |
748 | sd: Option<PeripheralRef<'d, AnyPin>>, |
749 | fs: Option<PeripheralRef<'d, AnyPin>>, |
750 | sck: Option<PeripheralRef<'d, AnyPin>>, |
751 | mclk: Option<PeripheralRef<'d, AnyPin>>, |
752 | #[cfg (gpdma)] |
753 | ring_buffer: PhantomData<W>, |
754 | #[cfg (not(gpdma))] |
755 | ring_buffer: RingBuffer<'d, W>, |
756 | sub_block: WhichSubBlock, |
757 | } |
758 | |
759 | #[cfg (not(gpdma))] |
760 | impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { |
761 | /// Create a new SAI driver in asynchronous mode with MCLK. |
762 | /// |
763 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. |
764 | pub fn new_asynchronous_with_mclk<S: SubBlockInstance>( |
765 | peri: SubBlock<'d, T, S>, |
766 | sck: impl Peripheral<P = impl SckPin<T, S>> + 'd, |
767 | sd: impl Peripheral<P = impl SdPin<T, S>> + 'd, |
768 | fs: impl Peripheral<P = impl FsPin<T, S>> + 'd, |
769 | mclk: impl Peripheral<P = impl MclkPin<T, S>> + 'd, |
770 | dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd, |
771 | dma_buf: &'d mut [W], |
772 | mut config: Config, |
773 | ) -> Self { |
774 | into_ref!(mclk); |
775 | |
776 | let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); |
777 | mclk.set_as_af(mclk.af_num(), ck_af_type); |
778 | |
779 | if config.master_clock_divider == MasterClockDivider::MasterClockDisabled { |
780 | config.master_clock_divider = MasterClockDivider::Div1; |
781 | } |
782 | |
783 | Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config) |
784 | } |
785 | |
786 | /// Create a new SAI driver in asynchronous mode without MCLK. |
787 | /// |
788 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. |
789 | pub fn new_asynchronous<S: SubBlockInstance>( |
790 | peri: SubBlock<'d, T, S>, |
791 | sck: impl Peripheral<P = impl SckPin<T, S>> + 'd, |
792 | sd: impl Peripheral<P = impl SdPin<T, S>> + 'd, |
793 | fs: impl Peripheral<P = impl FsPin<T, S>> + 'd, |
794 | dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd, |
795 | dma_buf: &'d mut [W], |
796 | config: Config, |
797 | ) -> Self { |
798 | let peri = peri.peri; |
799 | into_ref!(peri, dma, sck, sd, fs); |
800 | |
801 | let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); |
802 | sd.set_as_af(sd.af_num(), sd_af_type); |
803 | sck.set_as_af(sck.af_num(), ck_af_type); |
804 | fs.set_as_af(fs.af_num(), ck_af_type); |
805 | |
806 | let sub_block = S::WHICH; |
807 | let request = dma.request(); |
808 | |
809 | Self::new_inner( |
810 | peri, |
811 | sub_block, |
812 | Some(sck.map_into()), |
813 | None, |
814 | Some(sd.map_into()), |
815 | Some(fs.map_into()), |
816 | get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx), |
817 | config, |
818 | ) |
819 | } |
820 | |
821 | /// Create a new SAI driver in synchronous mode. |
822 | /// |
823 | /// You can obtain the [`SubBlock`] with [`split_subblocks`]. |
824 | pub fn new_synchronous<S: SubBlockInstance>( |
825 | peri: SubBlock<'d, T, S>, |
826 | sd: impl Peripheral<P = impl SdPin<T, S>> + 'd, |
827 | dma: impl Peripheral<P = impl Channel + Dma<T, S>> + 'd, |
828 | dma_buf: &'d mut [W], |
829 | mut config: Config, |
830 | ) -> Self { |
831 | update_synchronous_config(&mut config); |
832 | |
833 | let peri = peri.peri; |
834 | into_ref!(dma, peri, sd); |
835 | |
836 | let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx); |
837 | sd.set_as_af(sd.af_num(), sd_af_type); |
838 | |
839 | let sub_block = S::WHICH; |
840 | let request = dma.request(); |
841 | |
842 | Self::new_inner( |
843 | peri, |
844 | sub_block, |
845 | None, |
846 | None, |
847 | Some(sd.map_into()), |
848 | None, |
849 | get_ring_buffer::<T, W>(dma, dma_buf, request, sub_block, config.tx_rx), |
850 | config, |
851 | ) |
852 | } |
853 | |
854 | fn new_inner( |
855 | peri: impl Peripheral<P = T> + 'd, |
856 | sub_block: WhichSubBlock, |
857 | sck: Option<PeripheralRef<'d, AnyPin>>, |
858 | mclk: Option<PeripheralRef<'d, AnyPin>>, |
859 | sd: Option<PeripheralRef<'d, AnyPin>>, |
860 | fs: Option<PeripheralRef<'d, AnyPin>>, |
861 | ring_buffer: RingBuffer<'d, W>, |
862 | config: Config, |
863 | ) -> Self { |
864 | let ch = T::REGS.ch(sub_block as usize); |
865 | |
866 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
867 | { |
868 | ch.cr1().modify(|w| w.set_saien(false)); |
869 | } |
870 | |
871 | ch.cr2().modify(|w| w.set_fflush(true)); |
872 | |
873 | #[cfg (any(sai_v4_2pdm, sai_v4_4pdm))] |
874 | { |
875 | if let SyncInput::External(i) = config.sync_input { |
876 | T::REGS.gcr().modify(|w| { |
877 | w.set_syncin(i as u8); |
878 | }); |
879 | } |
880 | |
881 | if config.sync_output { |
882 | let syncout: u8 = match sub_block { |
883 | WhichSubBlock::A => 0b01, |
884 | WhichSubBlock::B => 0b10, |
885 | }; |
886 | T::REGS.gcr().modify(|w| { |
887 | w.set_syncout(syncout); |
888 | }); |
889 | } |
890 | } |
891 | |
892 | #[cfg (any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] |
893 | { |
894 | ch.cr1().modify(|w| { |
895 | w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { |
896 | TxRx::Transmitter |
897 | } else { |
898 | TxRx::Receiver |
899 | })); |
900 | w.set_prtcfg(config.protocol.prtcfg()); |
901 | w.set_ds(config.data_size.ds()); |
902 | w.set_lsbfirst(config.bit_order.lsbfirst()); |
903 | w.set_ckstr(config.clock_strobe.ckstr()); |
904 | w.set_syncen(config.sync_input.syncen()); |
905 | w.set_mono(config.stereo_mono.mono()); |
906 | w.set_outdriv(config.output_drive.outdriv()); |
907 | w.set_mckdiv(config.master_clock_divider.mckdiv()); |
908 | w.set_nodiv( |
909 | if config.master_clock_divider == MasterClockDivider::MasterClockDisabled { |
910 | vals::Nodiv::NO_DIV |
911 | } else { |
912 | vals::Nodiv::MASTER_CLOCK |
913 | }, |
914 | ); |
915 | w.set_dmaen(true); |
916 | }); |
917 | |
918 | ch.cr2().modify(|w| { |
919 | w.set_fth(config.fifo_threshold.fth()); |
920 | w.set_comp(config.companding.comp()); |
921 | w.set_cpl(config.complement_format.cpl()); |
922 | w.set_muteval(config.mute_value.muteval()); |
923 | w.set_mutecnt(config.mute_detection_counter.0 as u8); |
924 | w.set_tris(config.is_high_impedance_on_inactive_slot); |
925 | }); |
926 | |
927 | ch.frcr().modify(|w| { |
928 | w.set_fsoff(config.frame_sync_offset.fsoff()); |
929 | w.set_fspol(config.frame_sync_polarity.fspol()); |
930 | w.set_fsdef(config.frame_sync_definition.fsdef()); |
931 | w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); |
932 | w.set_frl(config.frame_length - 1); |
933 | }); |
934 | |
935 | ch.slotr().modify(|w| { |
936 | w.set_nbslot(config.slot_count.0 as u8 - 1); |
937 | w.set_slotsz(config.slot_size.slotsz()); |
938 | w.set_fboff(config.first_bit_offset.0 as u8); |
939 | w.set_sloten(vals::Sloten(config.slot_enable as u16)); |
940 | }); |
941 | |
942 | ch.cr1().modify(|w| w.set_saien(true)); |
943 | |
944 | if ch.cr1().read().saien() == false { |
945 | panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)" ); |
946 | } |
947 | } |
948 | |
949 | Self { |
950 | _peri: peri.into_ref(), |
951 | sub_block, |
952 | sck, |
953 | mclk, |
954 | sd, |
955 | fs, |
956 | ring_buffer, |
957 | } |
958 | } |
959 | |
960 | /// Start the SAI driver. |
961 | /// |
962 | /// Only receivers can be started. Transmitters are started on the first writing operation. |
963 | pub fn start(&mut self) -> Result<(), Error> { |
964 | match self.ring_buffer { |
965 | RingBuffer::Writable(_) => Err(Error::NotAReceiver), |
966 | RingBuffer::Readable(ref mut rb) => { |
967 | rb.start(); |
968 | Ok(()) |
969 | } |
970 | } |
971 | } |
972 | |
973 | fn is_transmitter(ring_buffer: &RingBuffer<W>) -> bool { |
974 | match ring_buffer { |
975 | RingBuffer::Writable(_) => true, |
976 | _ => false, |
977 | } |
978 | } |
979 | |
980 | /// Reset SAI operation. |
981 | pub fn reset() { |
982 | rcc::enable_and_reset::<T>(); |
983 | } |
984 | |
985 | /// Enable or disable mute. |
986 | pub fn set_mute(&mut self, value: bool) { |
987 | let ch = T::REGS.ch(self.sub_block as usize); |
988 | ch.cr2().modify(|w| w.set_mute(value)); |
989 | } |
990 | |
991 | /// Determine the mute state of the receiver. |
992 | /// |
993 | /// Clears the mute state flag in the status register. |
994 | pub fn is_muted(&self) -> Result<bool, Error> { |
995 | match &self.ring_buffer { |
996 | RingBuffer::Readable(_) => { |
997 | let ch = T::REGS.ch(self.sub_block as usize); |
998 | let mute_state = ch.sr().read().mutedet(); |
999 | ch.clrfr().write(|w| w.set_cmutedet(true)); |
1000 | Ok(mute_state) |
1001 | } |
1002 | _ => Err(Error::NotAReceiver), |
1003 | } |
1004 | } |
1005 | |
1006 | /// Wait until any SAI write error occurs. |
1007 | /// |
1008 | /// One useful application for this is stopping playback as soon as the SAI |
1009 | /// experiences an overrun of the ring buffer. Then, instead of letting |
1010 | /// the SAI peripheral play the last written buffer over and over again, SAI |
1011 | /// can be muted or dropped instead. |
1012 | pub async fn wait_write_error(&mut self) -> Result<(), Error> { |
1013 | match &mut self.ring_buffer { |
1014 | RingBuffer::Writable(buffer) => { |
1015 | buffer.wait_write_error().await?; |
1016 | Ok(()) |
1017 | } |
1018 | _ => return Err(Error::NotATransmitter), |
1019 | } |
1020 | } |
1021 | |
1022 | /// Write data to the SAI ringbuffer. |
1023 | /// |
1024 | /// The first write starts the DMA after filling the ring buffer with the provided data. |
1025 | /// This ensures that the DMA does not run before data is available in the ring buffer. |
1026 | /// |
1027 | /// This appends the data to the buffer and returns immediately. The |
1028 | /// data will be transmitted in the background. |
1029 | /// |
1030 | /// If there's no space in the buffer, this waits until there is. |
1031 | pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { |
1032 | match &mut self.ring_buffer { |
1033 | RingBuffer::Writable(buffer) => { |
1034 | if buffer.is_running() { |
1035 | buffer.write_exact(data).await?; |
1036 | } else { |
1037 | buffer.write_immediate(data)?; |
1038 | buffer.start(); |
1039 | } |
1040 | Ok(()) |
1041 | } |
1042 | _ => return Err(Error::NotATransmitter), |
1043 | } |
1044 | } |
1045 | |
1046 | /// Read data from the SAI ringbuffer. |
1047 | /// |
1048 | /// SAI is always receiving data in the background. This function pops already-received |
1049 | /// data from the buffer. |
1050 | /// |
1051 | /// If there's less than `data.len()` data in the buffer, this waits until there is. |
1052 | pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { |
1053 | match &mut self.ring_buffer { |
1054 | RingBuffer::Readable(buffer) => { |
1055 | buffer.read_exact(data).await?; |
1056 | Ok(()) |
1057 | } |
1058 | _ => Err(Error::NotAReceiver), |
1059 | } |
1060 | } |
1061 | } |
1062 | |
1063 | impl<'d, T: Instance, W: word::Word> Drop for Sai<'d, T, W> { |
1064 | fn drop(&mut self) { |
1065 | let ch: Ch = T::REGS.ch(self.sub_block as usize); |
1066 | ch.cr1().modify(|w: &mut Cr1| w.set_saien(val:false)); |
1067 | ch.cr2().modify(|w: &mut Cr2| w.set_fflush(val:true)); |
1068 | self.fs.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected()); |
1069 | self.sd.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected()); |
1070 | self.sck.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected()); |
1071 | self.mclk.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected()); |
1072 | } |
1073 | } |
1074 | |
1075 | trait SealedInstance { |
1076 | const REGS: Regs; |
1077 | } |
1078 | |
1079 | #[derive (Copy, Clone)] |
1080 | enum WhichSubBlock { |
1081 | A = 0, |
1082 | B = 1, |
1083 | } |
1084 | |
1085 | trait SealedSubBlock { |
1086 | const WHICH: WhichSubBlock; |
1087 | } |
1088 | |
1089 | /// Sub-block instance trait. |
1090 | #[allow (private_bounds)] |
1091 | pub trait SubBlockInstance: SealedSubBlock {} |
1092 | |
1093 | /// Sub-block A. |
1094 | pub enum A {} |
1095 | impl SealedSubBlock for A { |
1096 | const WHICH: WhichSubBlock = WhichSubBlock::A; |
1097 | } |
1098 | impl SubBlockInstance for A {} |
1099 | |
1100 | /// Sub-block B. |
1101 | pub enum B {} |
1102 | impl SealedSubBlock for B { |
1103 | const WHICH: WhichSubBlock = WhichSubBlock::B; |
1104 | } |
1105 | impl SubBlockInstance for B {} |
1106 | |
1107 | /// SAI instance trait. |
1108 | #[allow (private_bounds)] |
1109 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} |
1110 | |
1111 | pin_trait!(SckPin, Instance, SubBlockInstance); |
1112 | pin_trait!(FsPin, Instance, SubBlockInstance); |
1113 | pin_trait!(SdPin, Instance, SubBlockInstance); |
1114 | pin_trait!(MclkPin, Instance, SubBlockInstance); |
1115 | |
1116 | dma_trait!(Dma, Instance, SubBlockInstance); |
1117 | |
1118 | foreach_peripheral!( |
1119 | (sai, $inst:ident) => { |
1120 | impl SealedInstance for peripherals::$inst { |
1121 | const REGS: Regs = crate::pac::$inst; |
1122 | } |
1123 | |
1124 | impl Instance for peripherals::$inst {} |
1125 | }; |
1126 | ); |
1127 | |