stm32f4xx_hal/
sai.rs

1//! Serial Audio Interface.
2//!
3//! Each SAI instance consists of two sub-blocks (SAIA and SAIB), each of which can be configured
4//! completely independently. Optionally, one block can be configured to be synchronous to the
5//! other, in which no clock and word select pins are required.
6//!
7//! This implementation supports PCM audio with short and long frame synchronization signals as
8//! well as I2S and its left-justified and right-justified variations.
9//!
10//! # Usage Examples
11//!
12//! See examples/sai-duplex.rs
13//!
14//! # Clock Selection
15//!
16//! TODO
17//!
18
19// TODO: Unify capitalization of template parameters.
20// TODO: Synchronization of multiple SAIs.
21
22use core::ops::Deref;
23
24use crate::gpio::alt::SaiChannel;
25use crate::pac::RCC;
26#[cfg(feature = "sai2")]
27use crate::pac::SAI2;
28#[cfg(any(
29    feature = "gpio-f413",
30    feature = "gpio-f469",
31    feature = "stm32f429",
32    feature = "stm32f439"
33))]
34use crate::pac::{sai, SAI as SAI1};
35#[cfg(any(feature = "stm32f427", feature = "stm32f437", feature = "stm32f446"))]
36use crate::pac::{sai1 as sai, SAI1};
37use crate::rcc;
38use crate::time::Hertz;
39
40pub use sai::ch::{cr1::DS as WordSize, slotr::SLOTSZ as SlotSize};
41
42fn word_size(ws: WordSize) -> u8 {
43    match ws {
44        WordSize::Bit8 => 8,
45        WordSize::Bit10 => 10,
46        WordSize::Bit16 => 16,
47        WordSize::Bit20 => 20,
48        WordSize::Bit24 => 24,
49        WordSize::Bit32 => 32,
50    }
51}
52
53fn slot_size(sz: SlotSize, ws: u8) -> u8 {
54    match sz {
55        SlotSize::DataSize => ws,
56        SlotSize::Bit16 => 16,
57        SlotSize::Bit32 => 32,
58    }
59}
60
61/// Serial audio protocol.
62#[derive(Clone, Copy)]
63pub struct Protocol {
64    /// Synchronization scheme.
65    pub sync: Synchronization,
66    /// Number of bits filled with audio data.
67    ///
68    /// The only values allowed are 8, 10, 16, 20, 24, and 32.
69    pub word_size: WordSize,
70    /// Number of bits transmitted per word.
71    ///
72    /// If a master clock is generated, the slot size should be a power of two if an integer ratio
73    /// between the master clock and the bit clock is required.
74    ///
75    /// If the value does not equal the word size, the only other values allowed are 16 and 32. In
76    /// any case, the value has to be equal or larger than the word size. If the slot size does not
77    /// match the word size, the data is padded according to the synchronization scheme.
78    pub slot_size: SlotSize,
79    /// Number of slots (i.e., audio channels) per frame.
80    ///
81    /// For everything but PCM audio, the value needs to be 2 (stereo).
82    pub num_slots: u8,
83}
84
85/// Synchronization mode.
86#[derive(Clone, Copy)]
87pub enum Synchronization {
88    /// I2S standard synchronization.
89    ///
90    /// The frame-select signal is low during data for the left channel and high for the right
91    /// channel. The frame-select signal is activated one SCK cycle before the first bit for the
92    /// corresponding channel is available.
93    ///
94    /// Data is followed by zeros if the configured word size does not match the frame size.
95    I2S,
96    /// MSB-justified synchronization.
97    ///
98    /// Like I2S, but the frame-select signal is activated when the first bit for the corresponding
99    /// channel is available.
100    MSBJustified,
101    /// LSB-justified synchronization.
102    ///
103    /// Like I2S, but the frame-select signal is activated when the first bit for the corresponding
104    /// channel is available, and the leading bits are set to zero if the word size does not match
105    /// the frame size.
106    LSBJustified,
107    /// PCM data with short-frame synchronization.
108    ///
109    /// The frame-select signal is asserted for one bit before the start of the data.
110    PCMShortFrame,
111    /// PCM data with long-frame synchronization.
112    ///
113    /// The frame-select signal is asserted at the same time as the start of the data and is held
114    /// high for 13 bits.
115    PCMLongFrame,
116}
117
118/// SAI sub-block.
119pub struct SAICH<SAI, const C: bool> {
120    sai: SAI,
121}
122
123impl<SAI: Instance, const C: bool> SAICH<SAI, C> {
124    fn ch(&self) -> &sai::CH {
125        self.sai.ch(usize::from(C))
126    }
127}
128
129/// SAI A sub-block.
130pub type SAIA<SAI> = SAICH<SAI, false>;
131
132/// SAI B sub-block.
133pub type SAIB<SAI> = SAICH<SAI, true>;
134
135impl<SAI> crate::Sealed for SAIA<SAI> {}
136
137/// Asynchronous SAI sub-block which has not yet been configured.
138///
139/// Asynchronous means that the sub-block has its own set of clock pins.
140pub struct Asynchronous;
141
142/// Asynchronous SAI sub-block which as been configured as a master.
143pub struct AsyncMaster<Ch: SaiChannel> {
144    // TODO: Remove attribute when destroy function is implemented.
145    #[allow(unused)]
146    pins: (Option<Ch::Mclk>, Ch::Fs, Ch::Sck, Ch::Sd),
147}
148
149impl<Ch: SaiChannel> AsyncMaster<Ch> {
150    fn new(
151        pins: (
152            Option<impl Into<Ch::Mclk>>,
153            impl Into<Ch::Fs>,
154            impl Into<Ch::Sck>,
155            impl Into<Ch::Sd>,
156        ),
157    ) -> Self {
158        Self {
159            pins: (
160                pins.0.map(Into::into),
161                pins.1.into(),
162                pins.2.into(),
163                pins.3.into(),
164            ),
165        }
166    }
167}
168
169/// Asynchronous SAI sub-block which as been configured as a slave.
170pub struct AsyncSlave<Ch: SaiChannel> {
171    // TODO: Remove attribute when destroy function is implemented.
172    #[allow(unused)]
173    pins: (Option<Ch::Mclk>, Ch::Fs, Ch::Sck, Ch::Sd),
174}
175
176impl<Ch: SaiChannel> AsyncSlave<Ch> {
177    fn new(
178        pins: (
179            Option<impl Into<Ch::Mclk>>,
180            impl Into<Ch::Fs>,
181            impl Into<Ch::Sck>,
182            impl Into<Ch::Sd>,
183        ),
184    ) -> Self {
185        Self {
186            pins: (
187                pins.0.map(Into::into),
188                pins.1.into(),
189                pins.2.into(),
190                pins.3.into(),
191            ),
192        }
193    }
194}
195
196/// Synchronous SAI sub-block.
197///
198/// Synchronous sub-blocks are always configured as slaves.
199pub struct Synchronous;
200
201/// Synchronous SAI sub-block which as been configured as a slave.
202pub struct SyncSlave<Ch: SaiChannel> {
203    // TODO: Remove attribute when destroy function is implemented.
204    #[allow(unused)]
205    sd: Ch::Sd,
206}
207
208impl<Ch: SaiChannel> SyncSlave<Ch> {
209    fn new(sd: impl Into<Ch::Sd>) -> Self {
210        Self { sd: sd.into() }
211    }
212}
213
214/// SAI sub-block which has neither been configured as a receiver nor as a transmitter.
215pub struct NoDir;
216
217/// SAI sub-block which has been configured as a receiver.
218pub struct Receive;
219
220/// SAI sub-block which has been configured as a transmitter.
221pub struct Transmit;
222
223pub trait Instance:
224    crate::Sealed
225    + crate::Ptr<RB = sai::RegisterBlock>
226    + crate::Steal
227    + Deref<Target = Self::RB>
228    + rcc::Enable
229    + rcc::Reset
230    + rcc::BusClock
231{
232}
233
234impl<SAI: Instance, const C: bool> SAICH<SAI, C> {
235    fn new(sai: SAI) -> Self {
236        Self { sai }
237    }
238    fn new_steal() -> Self {
239        Self {
240            sai: unsafe { SAI::steal() },
241        }
242    }
243}
244
245macro_rules! sai_impl {
246    ($SAI:ty, $sai:ident, $SAIA:ident, $SAIB:ident) => {
247        pub type $SAIA = SAIA<$SAI>;
248        pub type $SAIB = SAIB<$SAI>;
249
250        impl Instance for $SAI {}
251    };
252}
253
254sai_impl!(SAI1, sai1, SAI1A, SAI1B);
255#[cfg(feature = "sai2")]
256sai_impl!(SAI2, sai2, SAI2A, SAI2B);
257
258impl<SAI, const C: bool> Deref for SAICH<SAI, C>
259where
260    SAI: Instance,
261{
262    type Target = sai::CH;
263
264    fn deref(&self) -> &Self::Target {
265        self.ch()
266    }
267}
268
269pub trait ChannelClocks {
270    fn get_clk_frequency(clocks: &rcc::Clocks) -> Option<Hertz>;
271}
272
273pub trait Channel: ChannelClocks {
274    fn set_master_tx(&self);
275    fn set_master_rx(&self);
276    fn set_slave_tx(&self);
277    fn set_slave_rx(&self);
278
279    fn is_slave(&self) -> bool;
280    fn set_clock_gen(&self, sample_freq: Hertz, clocks: &rcc::Clocks);
281    fn set_protocol(&self, protocol: Protocol, tx: bool);
282
283    fn start(&self);
284    fn stop(&self);
285
286    fn fifo_full(&self) -> bool;
287    fn fifo_empty(&self) -> bool;
288
289    fn write(&self, word: u32);
290    fn read(&self) -> u32;
291
292    // TODO: DMA information?
293    // TODO: Interrupt bits?
294    // TODO: SAIA is on Channel 0, Stream 1 and 3.
295    // TODO: SAIB is on Channel 0, Stream 5 and Channel 1, Stream 4.
296}
297
298#[cfg(not(feature = "sai2"))]
299impl<SAI, const C: bool> ChannelClocks for SAICH<SAI, C> {
300    fn get_clk_frequency(clocks: &rcc::Clocks) -> Option<Hertz> {
301        if C {
302            clocks.saib_clk()
303        } else {
304            clocks.saia_clk()
305        }
306    }
307}
308
309#[cfg(feature = "sai2")]
310impl<const C: bool> ChannelClocks for SAICH<SAI1, C> {
311    fn get_clk_frequency(clocks: &rcc::Clocks) -> Option<Hertz> {
312        clocks.sai1_clk()
313    }
314}
315
316#[cfg(feature = "sai2")]
317impl<const C: bool> ChannelClocks for SAICH<SAI2, C> {
318    fn get_clk_frequency(clocks: &rcc::Clocks) -> Option<Hertz> {
319        clocks.sai2_clk()
320    }
321}
322
323impl<Ch> Channel for Ch
324where
325    Ch: Deref<Target = sai::CH> + ChannelClocks,
326{
327    fn set_master_tx(&self) {
328        self.cr1().modify(|_, w| w.mode().master_tx());
329    }
330
331    fn set_master_rx(&self) {
332        self.cr1().modify(|_, w| w.mode().master_rx());
333    }
334
335    fn set_slave_tx(&self) {
336        self.cr1().modify(|_, w| w.mode().slave_tx());
337    }
338
339    fn set_slave_rx(&self) {
340        self.cr1().modify(|_, w| w.mode().slave_rx());
341    }
342
343    fn is_slave(&self) -> bool {
344        let mode = self.cr1().read().mode();
345        mode.is_slave_tx() || mode.is_slave_rx()
346    }
347
348    fn set_clock_gen(&self, sample_freq: Hertz, clocks: &rcc::Clocks) {
349        let mclk = sample_freq.raw() * 256;
350        let sai_clock = Self::get_clk_frequency(clocks)
351            .expect("no SAI clock available")
352            .raw();
353        if (sai_clock + (mclk >> 1)) / mclk == 1 {
354            self.cr1().modify(|_, w| unsafe { w.mckdiv().bits(0) });
355        } else {
356            let best_divider = (sai_clock + mclk) / (mclk << 1);
357            assert!(best_divider < 16);
358            self.cr1()
359                .modify(|_, w| unsafe { w.mckdiv().bits(best_divider as u8) });
360        }
361    }
362
363    fn set_protocol(&self, protocol: Protocol, tx: bool) {
364        // According to the reference manual (I2S section), PCM has an inverted bit clock by
365        // default. Other sources sometimes disagree.
366        let pcm = match protocol.sync {
367            Synchronization::I2S => false,
368            Synchronization::MSBJustified => false,
369            Synchronization::LSBJustified => false,
370            Synchronization::PCMLongFrame => true,
371            Synchronization::PCMShortFrame => true,
372        };
373        if !pcm && protocol.num_slots != 2 {
374            panic!("only stereo I2S supported");
375        }
376        assert!(protocol.num_slots > 0);
377        let word_size = word_size(protocol.word_size);
378        let slot_size = slot_size(protocol.slot_size, word_size);
379        assert!(slot_size >= word_size, "slot size smaller than word size");
380        self.cr1().modify(|_, w| {
381            w.ds().variant(protocol.word_size);
382            if (pcm && tx) || (!pcm && !tx) {
383                w.ckstr().rising_edge()
384            } else {
385                w.ckstr().falling_edge()
386            }
387        });
388        self.frcr().modify(|_, w| {
389            match protocol.sync {
390                Synchronization::PCMShortFrame => w.fsoff().before_first(),
391                _ => w.fsoff().on_first(),
392            };
393            if pcm {
394                w.fspol().rising_edge();
395                w.fsdef().clear_bit();
396                unsafe {
397                    // The long frame sync signal is fixed and has a length of 13 bits.
398                    match protocol.sync {
399                        Synchronization::PCMShortFrame => w.fsall().bits(0),
400                        Synchronization::PCMLongFrame => w.fsall().bits(12),
401                        _ => unreachable!(),
402                    };
403                    w.frl().bits((slot_size * protocol.num_slots) - 1);
404                }
405            } else {
406                w.fspol().falling_edge();
407                w.fsdef().set_bit();
408                unsafe {
409                    w.fsall().bits(slot_size - 1);
410                    w.frl().bits((slot_size << 1) - 1);
411                }
412            }
413            w
414        });
415        self.slotr().modify(|_, w| unsafe {
416            if pcm {
417                w.sloten().bits((1 << protocol.num_slots as u32) - 1);
418                w.nbslot().bits(protocol.num_slots - 1);
419            } else {
420                w.sloten().bits(0x3);
421                w.nbslot().bits(1);
422            }
423            w.slotsz().variant(protocol.slot_size);
424            match protocol.sync {
425                Synchronization::LSBJustified => w.fboff().bits(slot_size - word_size),
426                _ => w.fboff().bits(0),
427            };
428            w
429        });
430    }
431
432    fn start(&self) {
433        self.clrfr().write(|w| {
434            w.clfsdet().set_bit();
435            w.cafsdet().set_bit();
436            w.ccnrdy().set_bit();
437            w.cwckcfg().set_bit();
438            w.cmutedet().set_bit();
439            w.covrudr().set_bit();
440            w
441        });
442        self.cr2().modify(|_, w| w.fflush().flush());
443        self.cr1().modify(|_, w| w.saien().enabled());
444    }
445
446    fn stop(&self) {
447        self.cr1().modify(|_, w| w.saien().disabled());
448        while self.cr1().read().saien().bit_is_set() {}
449    }
450
451    fn fifo_full(&self) -> bool {
452        // We usually write at least two words (stereo data).
453        let sr = self.sr().read();
454        sr.flvl().is_full() || sr.flvl().is_quarter4()
455    }
456    fn fifo_empty(&self) -> bool {
457        // We usually readat least two words (stereo data).(stereo data).
458        let sr = self.sr().read();
459        sr.flvl().is_empty() || sr.flvl().is_quarter1()
460    }
461
462    fn write(&self, word: u32) {
463        self.dr().write(|w| unsafe { w.data().bits(word) });
464    }
465    fn read(&self) -> u32 {
466        self.dr().read().data().bits()
467    }
468}
469
470/// Wrapper for a single channel of the SAI and its configuration.
471pub struct SubBlock<Channel, Config, Direction = NoDir> {
472    channel: Channel,
473    #[allow(unused)]
474    config: Config,
475    #[allow(unused)]
476    direction: Direction,
477}
478
479/// Functions to configure the two sub-blocks of an SAI instance.
480///
481/// For the two sub-blocks of a single SAI instance, only specific combinations of modes are valid.
482/// This trait has one method for each such combination.
483pub trait SAIExt
484where
485    Self: Instance,
486{
487    /// Splits the SAI instance into two asynchronous sub-blocks.
488    fn split(
489        self,
490        rcc: &mut RCC,
491    ) -> (
492        SubBlock<SAIA<Self>, Asynchronous>,
493        SubBlock<SAIB<Self>, Asynchronous>,
494    )
495    where
496        Self: Sized;
497
498    /// Splits the SAI instance so that the A block uses the synchronization signals of the B
499    /// block.
500    fn split_sync_a(
501        self,
502        rcc: &mut RCC,
503    ) -> (
504        SubBlock<SAIA<Self>, Synchronous>,
505        SubBlock<SAIB<Self>, Asynchronous>,
506    )
507    where
508        Self: Sized;
509
510    /// Splits the SAI instance so that the B block uses the synchronization signals of the A
511    /// block.
512    fn split_sync_b(
513        self,
514        rcc: &mut RCC,
515    ) -> (
516        SubBlock<SAIA<Self>, Asynchronous>,
517        SubBlock<SAIB<Self>, Synchronous>,
518    )
519    where
520        Self: Sized;
521
522    /*/// Un-splits the two sub-blocks and resets the SAI.
523    fn uninit<ConfigA, ConfigB>(a: SubBlock<SAIA, ConfigA>, b: SubBlock<SAIB, ConfigB>) -> Self
524    where
525        Self: Sized;*/
526}
527
528impl<SAI> SAIExt for SAI
529where
530    SAI: Instance,
531{
532    fn split(
533        self,
534        rcc: &mut RCC,
535    ) -> (
536        SubBlock<SAIA<Self>, Asynchronous>,
537        SubBlock<SAIB<Self>, Asynchronous>,
538    )
539    where
540        Self: Sized,
541    {
542        SAI::enable(rcc);
543        SAI::reset(rcc);
544        (
545            SubBlock {
546                channel: SAIA::new(self),
547                config: Asynchronous,
548                direction: NoDir,
549            },
550            SubBlock {
551                channel: SAIB::new_steal(),
552                config: Asynchronous,
553                direction: NoDir,
554            },
555        )
556    }
557
558    fn split_sync_a(
559        self,
560        rcc: &mut RCC,
561    ) -> (
562        SubBlock<SAIA<Self>, Synchronous>,
563        SubBlock<SAIB<Self>, Asynchronous>,
564    )
565    where
566        Self: Sized,
567    {
568        SAI::enable(rcc);
569        SAI::reset(rcc);
570        (
571            SubBlock {
572                channel: SAIA::new(self),
573                config: Synchronous,
574                direction: NoDir,
575            },
576            SubBlock {
577                channel: SAIB::new_steal(),
578                config: Asynchronous,
579                direction: NoDir,
580            },
581        )
582    }
583
584    fn split_sync_b(
585        self,
586        rcc: &mut RCC,
587    ) -> (
588        SubBlock<SAIA<Self>, Asynchronous>,
589        SubBlock<SAIB<Self>, Synchronous>,
590    )
591    where
592        Self: Sized,
593    {
594        SAI::enable(rcc);
595        SAI::reset(rcc);
596        (
597            SubBlock {
598                channel: SAIA::new(self),
599                config: Asynchronous,
600                direction: NoDir,
601            },
602            SubBlock {
603                channel: SAIB::new_steal(),
604                config: Synchronous,
605                direction: NoDir,
606            },
607        )
608    }
609
610    /*fn uninit<ConfigA, ConfigB>(a: SubBlock<SAIA, ConfigA>, b: SubBlock<SAIB, ConfigB>) -> Self {
611        // TODO
612    }*/
613}
614
615impl<Ch> SubBlock<Ch, Asynchronous>
616where
617    Ch: Channel + SaiChannel,
618{
619    /// Configures the channel as a master and a receiver.
620    pub fn master_rx(
621        self,
622        pins: (
623            Option<impl Into<Ch::Mclk>>,
624            impl Into<Ch::Fs>,
625            impl Into<Ch::Sck>,
626            impl Into<Ch::Sd>,
627        ),
628        protocol: Protocol,
629        sample_freq: impl Into<Hertz>,
630        clocks: &rcc::Clocks,
631    ) -> SubBlock<Ch, AsyncMaster<Ch>, Receive> {
632        self.channel.set_clock_gen(sample_freq.into(), clocks);
633        self.channel.set_master_rx();
634        self.channel.set_protocol(protocol, false);
635
636        SubBlock {
637            channel: self.channel,
638            config: AsyncMaster::new(pins),
639            direction: Receive,
640        }
641    }
642
643    /// Configures the channel as a master and a transmitter.
644    pub fn master_tx(
645        self,
646        pins: (
647            Option<impl Into<Ch::Mclk>>,
648            impl Into<Ch::Fs>,
649            impl Into<Ch::Sck>,
650            impl Into<Ch::Sd>,
651        ),
652        protocol: Protocol,
653        sample_freq: impl Into<Hertz>,
654        clocks: &rcc::Clocks,
655    ) -> SubBlock<Ch, AsyncMaster<Ch>, Transmit> {
656        self.channel.set_clock_gen(sample_freq.into(), clocks);
657        self.channel.set_master_tx();
658        self.channel.set_protocol(protocol, true);
659
660        SubBlock {
661            channel: self.channel,
662            config: AsyncMaster::new(pins),
663            direction: Transmit,
664        }
665    }
666
667    /// Configures the channel as a slave and a receiver.
668    pub fn slave_rx(
669        self,
670        pins: (
671            Option<impl Into<Ch::Mclk>>,
672            impl Into<Ch::Fs>,
673            impl Into<Ch::Sck>,
674            impl Into<Ch::Sd>,
675        ),
676        protocol: Protocol,
677    ) -> SubBlock<Ch, AsyncSlave<Ch>, Receive> {
678        self.channel.set_slave_rx();
679        self.channel.set_protocol(protocol, false);
680
681        SubBlock {
682            channel: self.channel,
683            config: AsyncSlave::new(pins),
684            direction: Receive,
685        }
686    }
687
688    /// Configures the channel as a master and a transmitter.
689    pub fn slave_tx(
690        self,
691        pins: (
692            Option<impl Into<Ch::Mclk>>,
693            impl Into<Ch::Fs>,
694            impl Into<Ch::Sck>,
695            impl Into<Ch::Sd>,
696        ),
697        protocol: Protocol,
698    ) -> SubBlock<Ch, AsyncSlave<Ch>, Transmit> {
699        self.channel.set_slave_tx();
700        self.channel.set_protocol(protocol, true);
701
702        SubBlock {
703            channel: self.channel,
704            config: AsyncSlave::new(pins),
705            direction: Transmit,
706        }
707    }
708}
709
710impl<Ch> SubBlock<Ch, Synchronous>
711where
712    Ch: Channel + SaiChannel,
713{
714    /// Configures the channel as a slave and a receiver.
715    pub fn slave_rx(
716        self,
717        sd: impl Into<Ch::Sd>,
718        protocol: Protocol,
719    ) -> SubBlock<Ch, SyncSlave<Ch>, Receive> {
720        self.channel.set_slave_rx();
721        self.channel.set_protocol(protocol, false);
722
723        SubBlock {
724            channel: self.channel,
725            config: SyncSlave::new(sd),
726            direction: Receive,
727        }
728    }
729
730    /// Configures the channel as a slave and a transmitter.
731    pub fn slave_tx(
732        self,
733        sd: impl Into<Ch::Sd>,
734        protocol: Protocol,
735    ) -> SubBlock<Ch, SyncSlave<Ch>, Transmit> {
736        self.channel.set_slave_tx();
737        self.channel.set_protocol(protocol, true);
738
739        SubBlock {
740            channel: self.channel,
741            config: SyncSlave::new(sd),
742            direction: Transmit,
743        }
744    }
745}
746
747impl<Ch, Config> SubBlock<Ch, Config, Receive>
748where
749    Ch: Channel,
750{
751    pub fn start(&mut self) {
752        self.channel.start();
753    }
754
755    pub fn stop(&mut self) {
756        self.channel.stop();
757    }
758
759    // TODO: Mono functions, DMA.
760
761    pub fn try_read(&mut self) -> nb::Result<(u32, u32), ()> {
762        if !self.channel.fifo_empty() {
763            // Note that fifo_empty() actually checks for at least two words of data.
764            Ok((self.channel.read(), self.channel.read()))
765        } else {
766            Err(nb::Error::WouldBlock)
767        }
768    }
769}
770
771impl<Ch, Config> SubBlock<Ch, Config, Transmit>
772where
773    Ch: Channel,
774{
775    pub fn start(&mut self) {
776        self.channel.start();
777    }
778
779    pub fn stop(&mut self) {
780        self.channel.stop();
781    }
782
783    // TODO: Mono functions, DMA.
784
785    pub fn try_send(&mut self, left: u32, right: u32) -> nb::Result<(), ()> {
786        if !self.channel.fifo_full() {
787            // Note that fifo_full) actually checks for at least two words of space.
788            self.channel.write(left);
789            self.channel.write(right);
790            Ok(())
791        } else {
792            Err(nb::Error::WouldBlock)
793        }
794    }
795}
796
797/// Wrapper around `Receive` and `Transmit` blocks to provide full-duplex I2S transfers.
798pub struct Duplex<Ch1, Config1, Ch2, Config2> {
799    rx: SubBlock<Ch1, Config1, Receive>,
800    tx: SubBlock<Ch2, Config2, Transmit>,
801}
802
803impl<Ch1, Config1, Ch2, Config2> Duplex<Ch1, Config1, Ch2, Config2>
804where
805    Ch1: Channel,
806    Ch2: Channel,
807{
808    /// Wraps the specified receiver/transmitter objects.
809    pub fn new(rx: SubBlock<Ch1, Config1, Receive>, tx: SubBlock<Ch2, Config2, Transmit>) -> Self {
810        Self { rx, tx }
811    }
812
813    pub fn start(&mut self) {
814        // When the two channels are synchronized (internally or externally), we need to start the
815        // slave first.
816        if self.rx.channel.is_slave() {
817            self.rx.start();
818        }
819        if self.tx.channel.is_slave() {
820            self.tx.start();
821        }
822        if !self.rx.channel.is_slave() {
823            self.rx.start();
824        }
825        if !self.tx.channel.is_slave() {
826            self.tx.start();
827        }
828    }
829
830    pub fn stop(&mut self) {
831        self.rx.stop();
832        self.tx.stop();
833    }
834
835    pub fn try_read(&mut self) -> nb::Result<(u32, u32), ()> {
836        self.rx.try_read()
837    }
838
839    pub fn try_send(&mut self, left: u32, right: u32) -> nb::Result<(), ()> {
840        self.tx.try_send(left, right)
841    }
842
843    // TODO: Implement embedded-hal I2S traits for Duplex.
844}
845
846unsafe impl<SAI: Instance, const C: bool> crate::dma::traits::PeriAddress for SAICH<SAI, C> {
847    #[inline(always)]
848    fn address(&self) -> u32 {
849        self.ch().dr().as_ptr() as u32
850    }
851
852    type MemSize = u32;
853}