aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/build.rs11
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rcc/f4.rs60
-rw-r--r--embassy-stm32/src/sai/mod.rs894
4 files changed, 962 insertions, 5 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index b36af4795..ccc9210df 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -463,6 +463,15 @@ fn main() {
463 (("lpuart", "RTS"), quote!(crate::usart::RtsPin)), 463 (("lpuart", "RTS"), quote!(crate::usart::RtsPin)),
464 (("lpuart", "CK"), quote!(crate::usart::CkPin)), 464 (("lpuart", "CK"), quote!(crate::usart::CkPin)),
465 (("lpuart", "DE"), quote!(crate::usart::DePin)), 465 (("lpuart", "DE"), quote!(crate::usart::DePin)),
466 (("sai", "SCK_A"), quote!(crate::sai::SckAPin)),
467 (("sai", "SCK_B"), quote!(crate::sai::SckBPin)),
468 (("sai", "FS_A"), quote!(crate::sai::FsAPin)),
469 (("sai", "FS_B"), quote!(crate::sai::FsBPin)),
470 (("sai", "SD_A"), quote!(crate::sai::SdAPin)),
471 (("sai", "SD_B"), quote!(crate::sai::SdBPin)),
472 (("sai", "MCLK_A"), quote!(crate::sai::MclkAPin)),
473 (("sai", "MCLK_B"), quote!(crate::sai::MclkBPin)),
474 (("sai", "WS"), quote!(crate::sai::WsPin)),
466 (("spi", "SCK"), quote!(crate::spi::SckPin)), 475 (("spi", "SCK"), quote!(crate::spi::SckPin)),
467 (("spi", "MOSI"), quote!(crate::spi::MosiPin)), 476 (("spi", "MOSI"), quote!(crate::spi::MosiPin)),
468 (("spi", "MISO"), quote!(crate::spi::MisoPin)), 477 (("spi", "MISO"), quote!(crate::spi::MisoPin)),
@@ -750,6 +759,8 @@ fn main() {
750 (("usart", "TX"), quote!(crate::usart::TxDma)), 759 (("usart", "TX"), quote!(crate::usart::TxDma)),
751 (("lpuart", "RX"), quote!(crate::usart::RxDma)), 760 (("lpuart", "RX"), quote!(crate::usart::RxDma)),
752 (("lpuart", "TX"), quote!(crate::usart::TxDma)), 761 (("lpuart", "TX"), quote!(crate::usart::TxDma)),
762 (("sai", "A"), quote!(crate::sai::DmaA)),
763 (("sai", "B"), quote!(crate::sai::DmaB)),
753 (("spi", "RX"), quote!(crate::spi::RxDma)), 764 (("spi", "RX"), quote!(crate::spi::RxDma)),
754 (("spi", "TX"), quote!(crate::spi::TxDma)), 765 (("spi", "TX"), quote!(crate::spi::TxDma)),
755 (("i2c", "RX"), quote!(crate::i2c::RxDma)), 766 (("i2c", "RX"), quote!(crate::i2c::RxDma)),
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 1e184f9d7..2718c96da 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -55,6 +55,8 @@ pub mod qspi;
55pub mod rng; 55pub mod rng;
56#[cfg(all(rtc, not(rtc_v1)))] 56#[cfg(all(rtc, not(rtc_v1)))]
57pub mod rtc; 57pub mod rtc;
58#[cfg(sai)]
59pub mod sai;
58#[cfg(sdmmc)] 60#[cfg(sdmmc)]
59pub mod sdmmc; 61pub mod sdmmc;
60#[cfg(spi)] 62#[cfg(spi)]
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index 5914c926a..ebf78d0e2 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -32,6 +32,9 @@ pub struct Config {
32 #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] 32 #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
33 pub plli2s: Option<Hertz>, 33 pub plli2s: Option<Hertz>,
34 34
35 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
36 pub pllsai: Option<Hertz>,
37
35 pub pll48: bool, 38 pub pll48: bool,
36 pub rtc: Option<RtcClockSource>, 39 pub rtc: Option<RtcClockSource>,
37 pub lsi: bool, 40 pub lsi: bool,
@@ -50,10 +53,9 @@ fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
50} 53}
51 54
52#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] 55#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
53fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> { 56fn calculate_sai_i2s_pll_values(vco_in: u32, max_div: u32, target: Option<u32>) -> Option<(u32, u32, u32)> {
54 let min_div = 2; 57 let min_div = 2;
55 let max_div = 7; 58 let target = match target {
56 let target = match plli2s {
57 Some(target) => target, 59 Some(target) => target,
58 None => return None, 60 None => return None,
59 }; 61 };
@@ -77,15 +79,48 @@ fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
77 }) 79 })
78 .min_by_key(|(_, _, _, error)| *error)?; 80 .min_by_key(|(_, _, _, error)| *error)?;
79 81
82 Some((n, outdiv, output))
83}
84
85#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
86fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
87 let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 7, plli2s)?;
88
80 RCC.plli2scfgr().modify(|w| { 89 RCC.plli2scfgr().modify(|w| {
81 w.set_plli2sn(n as u16); 90 w.set_plli2sn(n as u16);
82 w.set_plli2sr(outdiv as u8); 91 w.set_plli2sr(outdiv as u8);
92 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
93 w.set_plli2sq(outdiv as u8); //set sai divider same as i2s
83 }); 94 });
84 95
85 Some(output) 96 Some(output)
86} 97}
87 98
88fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Option<u32>, pll48clk: bool) -> PllResults { 99#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))]
100fn setup_sai_pll(_vco_in: u32, _pllsai: Option<u32>) -> Option<u32> {
101 None
102}
103
104#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
105fn setup_sai_pll(vco_in: u32, pllsai: Option<u32>) -> Option<u32> {
106 let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 15, pllsai)?;
107
108 RCC.pllsaicfgr().modify(|w| {
109 w.set_pllsain(n as u16);
110 w.set_pllsaiq(outdiv as u8);
111 });
112
113 Some(output)
114}
115
116fn setup_pll(
117 pllsrcclk: u32,
118 use_hse: bool,
119 pllsysclk: Option<u32>,
120 plli2s: Option<u32>,
121 pllsai: Option<u32>,
122 pll48clk: bool,
123) -> PllResults {
89 use crate::pac::rcc::vals::{Pllp, Pllsrc}; 124 use crate::pac::rcc::vals::{Pllp, Pllsrc};
90 125
91 let sysclk = pllsysclk.unwrap_or(pllsrcclk); 126 let sysclk = pllsysclk.unwrap_or(pllsrcclk);
@@ -97,6 +132,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
97 pllsysclk: None, 132 pllsysclk: None,
98 pll48clk: None, 133 pll48clk: None,
99 plli2sclk: None, 134 plli2sclk: None,
135 pllsaiclk: None,
100 }; 136 };
101 } 137 }
102 // Input divisor from PLL source clock, must result to frequency in 138 // Input divisor from PLL source clock, must result to frequency in
@@ -147,6 +183,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
147 w.set_pllp(Pllp::from_bits(pllp as u8)); 183 w.set_pllp(Pllp::from_bits(pllp as u8));
148 w.set_pllq(pllq as u8); 184 w.set_pllq(pllq as u8);
149 w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)); 185 w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
186 w.set_pllr(0);
150 }); 187 });
151 188
152 let real_pllsysclk = vco_in * plln / sysclk_div; 189 let real_pllsysclk = vco_in * plln / sysclk_div;
@@ -156,6 +193,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
156 pllsysclk: Some(real_pllsysclk), 193 pllsysclk: Some(real_pllsysclk),
157 pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, 194 pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
158 plli2sclk: setup_i2s_pll(vco_in, plli2s), 195 plli2sclk: setup_i2s_pll(vco_in, plli2s),
196 pllsaiclk: setup_sai_pll(vco_in, pllsai),
159 } 197 }
160} 198}
161 199
@@ -343,6 +381,10 @@ pub(crate) unsafe fn init(config: Config) {
343 config.plli2s.map(|i2s| i2s.0), 381 config.plli2s.map(|i2s| i2s.0),
344 #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] 382 #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
345 None, 383 None,
384 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
385 config.pllsai.map(|sai| sai.0),
386 #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))]
387 None,
346 config.pll48, 388 config.pll48,
347 ); 389 );
348 390
@@ -440,6 +482,12 @@ pub(crate) unsafe fn init(config: Config) {
440 while !RCC.cr().read().plli2srdy() {} 482 while !RCC.cr().read().plli2srdy() {}
441 } 483 }
442 484
485 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
486 if plls.pllsaiclk.is_some() {
487 RCC.cr().modify(|w| w.set_pllsaion(true));
488 while !RCC.cr().read().pllsairdy() {}
489 }
490
443 RCC.cfgr().modify(|w| { 491 RCC.cfgr().modify(|w| {
444 w.set_ppre2(Ppre::from_bits(ppre2_bits)); 492 w.set_ppre2(Ppre::from_bits(ppre2_bits));
445 w.set_ppre1(Ppre::from_bits(ppre1_bits)); 493 w.set_ppre1(Ppre::from_bits(ppre1_bits));
@@ -490,7 +538,7 @@ pub(crate) unsafe fn init(config: Config) {
490 plli2s: plls.plli2sclk.map(Hertz), 538 plli2s: plls.plli2sclk.map(Hertz),
491 539
492 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] 540 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
493 pllsai: None, 541 pllsai: plls.pllsaiclk.map(Hertz),
494 542
495 rtc: rtc, 543 rtc: rtc,
496 rtc_hse: None, 544 rtc_hse: None,
@@ -503,6 +551,8 @@ struct PllResults {
503 pll48clk: Option<u32>, 551 pll48clk: Option<u32>,
504 #[allow(dead_code)] 552 #[allow(dead_code)]
505 plli2sclk: Option<u32>, 553 plli2sclk: Option<u32>,
554 #[allow(dead_code)]
555 pllsaiclk: Option<u32>,
506} 556}
507 557
508mod max { 558mod max {
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs
new file mode 100644
index 000000000..4ffa6e9ce
--- /dev/null
+++ b/embassy-stm32/src/sai/mod.rs
@@ -0,0 +1,894 @@
1#![macro_use]
2
3use embassy_embedded_hal::SetConfig;
4use embassy_hal_internal::{into_ref, PeripheralRef};
5
6pub use crate::dma::word;
7use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, TransferOptions, WritableRingBuffer};
8use crate::gpio::sealed::{AFType, Pin as _};
9use crate::gpio::AnyPin;
10use crate::pac::sai::{vals, Sai as Regs};
11use crate::rcc::RccPeripheral;
12use crate::{peripherals, Peripheral};
13
14#[derive(Debug)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub enum Error {
17 NotATransmitter,
18 NotAReceiver,
19 OverrunError,
20}
21
22impl From<ringbuffer::OverrunError> for Error {
23 fn from(_: ringbuffer::OverrunError) -> Self {
24 Self::OverrunError
25 }
26}
27
28#[derive(Copy, Clone)]
29pub enum SyncBlock {
30 None,
31 Sai1BlockA,
32 Sai1BlockB,
33 Sai2BlockA,
34 Sai2BlockB,
35}
36
37#[derive(Copy, Clone)]
38pub enum SyncIn {
39 None,
40 ChannelZero,
41 ChannelOne,
42}
43
44#[derive(Copy, Clone)]
45pub enum Mode {
46 Master,
47 Slave,
48}
49
50#[derive(Copy, Clone)]
51enum TxRx {
52 Transmiter,
53 Receiver,
54}
55
56impl Mode {
57 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
58 const fn mode(&self, tx_rx: TxRx) -> vals::Mode {
59 match tx_rx {
60 TxRx::Transmiter => match self {
61 Mode::Master => vals::Mode::MASTERTX,
62 Mode::Slave => vals::Mode::SLAVETX,
63 },
64 TxRx::Receiver => match self {
65 Mode::Master => vals::Mode::MASTERRX,
66 Mode::Slave => vals::Mode::SLAVERX,
67 },
68 }
69 }
70}
71
72#[derive(Copy, Clone)]
73pub enum SlotSize {
74 DataSize,
75 /// 16 bit data length on 16 bit wide channel
76 Channel16,
77 /// 16 bit data length on 32 bit wide channel
78 Channel32,
79}
80
81impl SlotSize {
82 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
83 pub const fn slotsz(&self) -> vals::Slotsz {
84 match self {
85 SlotSize::DataSize => vals::Slotsz::DATASIZE,
86 SlotSize::Channel16 => vals::Slotsz::BIT16,
87 SlotSize::Channel32 => vals::Slotsz::BIT32,
88 }
89 }
90}
91
92#[derive(Copy, Clone)]
93pub enum DataSize {
94 Data8,
95 Data10,
96 Data16,
97 Data20,
98 Data24,
99 Data32,
100}
101
102impl DataSize {
103 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
104 pub const fn ds(&self) -> vals::Ds {
105 match self {
106 DataSize::Data8 => vals::Ds::BIT8,
107 DataSize::Data10 => vals::Ds::BIT10,
108 DataSize::Data16 => vals::Ds::BIT16,
109 DataSize::Data20 => vals::Ds::BIT20,
110 DataSize::Data24 => vals::Ds::BIT24,
111 DataSize::Data32 => vals::Ds::BIT32,
112 }
113 }
114}
115
116#[derive(Copy, Clone)]
117pub enum FifoThreshold {
118 Empty,
119 Quarter,
120 Half,
121 ThreeQuarters,
122 Full,
123}
124
125impl FifoThreshold {
126 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
127 pub const fn fth(&self) -> vals::Fth {
128 match self {
129 FifoThreshold::Empty => vals::Fth::EMPTY,
130 FifoThreshold::Quarter => vals::Fth::QUARTER1,
131 FifoThreshold::Half => vals::Fth::QUARTER2,
132 FifoThreshold::ThreeQuarters => vals::Fth::QUARTER3,
133 FifoThreshold::Full => vals::Fth::FULL,
134 }
135 }
136}
137
138#[derive(Copy, Clone)]
139pub enum FifoLevel {
140 Empty,
141 FirstQuarter,
142 SecondQuarter,
143 ThirdQuarter,
144 FourthQuarter,
145 Full,
146}
147
148#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
149impl From<vals::Flvl> for FifoLevel {
150 fn from(flvl: vals::Flvl) -> Self {
151 match flvl {
152 vals::Flvl::EMPTY => FifoLevel::Empty,
153 vals::Flvl::QUARTER1 => FifoLevel::FirstQuarter,
154 vals::Flvl::QUARTER2 => FifoLevel::SecondQuarter,
155 vals::Flvl::QUARTER3 => FifoLevel::ThirdQuarter,
156 vals::Flvl::QUARTER4 => FifoLevel::FourthQuarter,
157 vals::Flvl::FULL => FifoLevel::Full,
158 _ => FifoLevel::Empty,
159 }
160 }
161}
162
163#[derive(Copy, Clone)]
164pub enum MuteDetection {
165 NoMute,
166 Mute,
167}
168
169#[derive(Copy, Clone)]
170pub enum MuteValue {
171 Zero,
172 LastValue,
173}
174
175impl MuteValue {
176 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
177 pub const fn muteval(&self) -> vals::Muteval {
178 match self {
179 MuteValue::Zero => vals::Muteval::SENDZERO,
180 MuteValue::LastValue => vals::Muteval::SENDLAST,
181 }
182 }
183}
184
185#[derive(Copy, Clone)]
186pub enum OverUnderStatus {
187 NoError,
188 OverUnderRunDetected,
189}
190
191#[derive(Copy, Clone)]
192pub enum Protocol {
193 Free,
194 Spdif,
195 Ac97,
196}
197
198impl Protocol {
199 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
200 pub const fn prtcfg(&self) -> vals::Prtcfg {
201 match self {
202 Protocol::Free => vals::Prtcfg::FREE,
203 Protocol::Spdif => vals::Prtcfg::SPDIF,
204 Protocol::Ac97 => vals::Prtcfg::AC97,
205 }
206 }
207}
208
209#[derive(Copy, Clone)]
210pub enum SyncEnable {
211 Asynchronous,
212 /// Syncs with the other A/B sub-block within the SAI unit
213 Internal,
214 /// Syncs with a sub-block in the other SAI unit - use set_sync_output() and set_sync_input()
215 External,
216}
217
218impl SyncEnable {
219 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
220 pub const fn syncen(&self) -> vals::Syncen {
221 match self {
222 SyncEnable::Asynchronous => vals::Syncen::ASYNCHRONOUS,
223 SyncEnable::Internal => vals::Syncen::INTERNAL,
224 SyncEnable::External => vals::Syncen::EXTERNAL,
225 }
226 }
227}
228
229#[derive(Copy, Clone, PartialEq)]
230pub enum StereoMono {
231 Stereo,
232 Mono,
233}
234
235impl StereoMono {
236 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
237 pub const fn mono(&self) -> vals::Mono {
238 match self {
239 StereoMono::Stereo => vals::Mono::STEREO,
240 StereoMono::Mono => vals::Mono::MONO,
241 }
242 }
243}
244
245#[derive(Copy, Clone)]
246pub enum BitOrder {
247 LsbFirst,
248 MsbFirst,
249}
250
251impl BitOrder {
252 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
253 pub const fn lsbfirst(&self) -> vals::Lsbfirst {
254 match self {
255 BitOrder::LsbFirst => vals::Lsbfirst::LSBFIRST,
256 BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST,
257 }
258 }
259}
260
261#[derive(Copy, Clone)]
262pub 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
269impl FrameSyncOffset {
270 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
271 pub const fn fsoff(&self) -> vals::Fsoff {
272 match self {
273 FrameSyncOffset::OnFirstBit => vals::Fsoff::ONFIRST,
274 FrameSyncOffset::BeforeFirstBit => vals::Fsoff::BEFOREFIRST,
275 }
276 }
277}
278
279#[derive(Copy, Clone)]
280pub enum FrameSyncPolarity {
281 ActiveLow,
282 ActiveHigh,
283}
284
285impl FrameSyncPolarity {
286 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
287 pub const fn fspol(&self) -> vals::Fspol {
288 match self {
289 FrameSyncPolarity::ActiveLow => vals::Fspol::FALLINGEDGE,
290 FrameSyncPolarity::ActiveHigh => vals::Fspol::RISINGEDGE,
291 }
292 }
293}
294
295#[derive(Copy, Clone)]
296pub enum FrameSyncDefinition {
297 StartOfFrame,
298 ChannelIdentification,
299}
300
301impl FrameSyncDefinition {
302 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
303 pub const fn fsdef(&self) -> bool {
304 match self {
305 FrameSyncDefinition::StartOfFrame => false,
306 FrameSyncDefinition::ChannelIdentification => true,
307 }
308 }
309}
310
311#[derive(Copy, Clone)]
312pub enum ClockStrobe {
313 Falling,
314 Rising,
315}
316
317impl ClockStrobe {
318 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
319 pub const fn ckstr(&self) -> vals::Ckstr {
320 match self {
321 ClockStrobe::Falling => vals::Ckstr::FALLINGEDGE,
322 ClockStrobe::Rising => vals::Ckstr::RISINGEDGE,
323 }
324 }
325}
326
327#[derive(Copy, Clone)]
328pub enum ComplementFormat {
329 OnesComplement,
330 TwosComplement,
331}
332
333impl ComplementFormat {
334 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
335 pub const fn cpl(&self) -> vals::Cpl {
336 match self {
337 ComplementFormat::OnesComplement => vals::Cpl::ONESCOMPLEMENT,
338 ComplementFormat::TwosComplement => vals::Cpl::TWOSCOMPLEMENT,
339 }
340 }
341}
342
343#[derive(Copy, Clone)]
344pub enum Companding {
345 None,
346 MuLaw,
347 ALaw,
348}
349
350impl Companding {
351 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
352 pub const fn comp(&self) -> vals::Comp {
353 match self {
354 Companding::None => vals::Comp::NOCOMPANDING,
355 Companding::MuLaw => vals::Comp::MULAW,
356 Companding::ALaw => vals::Comp::ALAW,
357 }
358 }
359}
360
361#[derive(Copy, Clone)]
362pub enum OutputDrive {
363 OnStart,
364 Immediately,
365}
366
367impl OutputDrive {
368 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
369 pub const fn outdriv(&self) -> vals::Outdriv {
370 match self {
371 OutputDrive::OnStart => vals::Outdriv::ONSTART,
372 OutputDrive::Immediately => vals::Outdriv::IMMEDIATELY,
373 }
374 }
375}
376
377#[derive(Copy, Clone, PartialEq)]
378pub enum MasterClockDivider {
379 MasterClockDisabled,
380 Div1,
381 Div2,
382 Div4,
383 Div6,
384 Div8,
385 Div10,
386 Div12,
387 Div14,
388 Div16,
389 Div18,
390 Div20,
391 Div22,
392 Div24,
393 Div26,
394 Div28,
395 Div30,
396}
397
398impl MasterClockDivider {
399 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
400 pub const fn mckdiv(&self) -> u8 {
401 match self {
402 MasterClockDivider::MasterClockDisabled => 0,
403 MasterClockDivider::Div1 => 0,
404 MasterClockDivider::Div2 => 1,
405 MasterClockDivider::Div4 => 2,
406 MasterClockDivider::Div6 => 3,
407 MasterClockDivider::Div8 => 4,
408 MasterClockDivider::Div10 => 5,
409 MasterClockDivider::Div12 => 6,
410 MasterClockDivider::Div14 => 7,
411 MasterClockDivider::Div16 => 8,
412 MasterClockDivider::Div18 => 9,
413 MasterClockDivider::Div20 => 10,
414 MasterClockDivider::Div22 => 11,
415 MasterClockDivider::Div24 => 12,
416 MasterClockDivider::Div26 => 13,
417 MasterClockDivider::Div28 => 14,
418 MasterClockDivider::Div30 => 15,
419 }
420 }
421}
422
423/// [`SAI`] configuration.
424#[non_exhaustive]
425#[derive(Copy, Clone)]
426pub struct Config {
427 pub mode: Mode,
428 pub sync_enable: SyncEnable,
429 pub is_sync_output: bool,
430 pub protocol: Protocol,
431 pub slot_size: SlotSize,
432 pub slot_count: word::U4,
433 pub slot_enable: u16,
434 pub first_bit_offset: word::U5,
435 pub data_size: DataSize,
436 pub stereo_mono: StereoMono,
437 pub bit_order: BitOrder,
438 pub frame_sync_offset: FrameSyncOffset,
439 pub frame_sync_polarity: FrameSyncPolarity,
440 pub frame_sync_active_level_length: word::U7,
441 pub frame_sync_definition: FrameSyncDefinition,
442 pub frame_length: u8,
443 pub clock_strobe: ClockStrobe,
444 pub output_drive: OutputDrive,
445 pub master_clock_divider: MasterClockDivider,
446 pub is_high_impedenane_on_inactive_slot: bool,
447 pub fifo_threshold: FifoThreshold,
448 pub companding: Companding,
449 pub complement_format: ComplementFormat,
450 pub mute_value: MuteValue,
451 pub mute_detection_counter: word::U5,
452}
453
454impl Default for Config {
455 fn default() -> Self {
456 Self {
457 mode: Mode::Master,
458 is_sync_output: false,
459 sync_enable: SyncEnable::Asynchronous,
460 protocol: Protocol::Free,
461 slot_size: SlotSize::DataSize,
462 slot_count: word::U4(2),
463 first_bit_offset: word::U5(0),
464 slot_enable: 0b11,
465 data_size: DataSize::Data16,
466 stereo_mono: StereoMono::Stereo,
467 bit_order: BitOrder::LsbFirst,
468 frame_sync_offset: FrameSyncOffset::BeforeFirstBit,
469 frame_sync_polarity: FrameSyncPolarity::ActiveLow,
470 frame_sync_active_level_length: word::U7(16),
471 frame_sync_definition: FrameSyncDefinition::ChannelIdentification,
472 frame_length: 32,
473 master_clock_divider: MasterClockDivider::MasterClockDisabled,
474 clock_strobe: ClockStrobe::Rising,
475 output_drive: OutputDrive::Immediately,
476 is_high_impedenane_on_inactive_slot: false,
477 fifo_threshold: FifoThreshold::ThreeQuarters,
478 companding: Companding::None,
479 complement_format: ComplementFormat::TwosComplement,
480 mute_value: MuteValue::Zero,
481 mute_detection_counter: word::U5(4),
482 }
483 }
484}
485
486impl Config {
487 pub fn new_i2s() -> Self {
488 return Default::default();
489 }
490
491 pub fn new_msb_first() -> Self {
492 Self {
493 bit_order: BitOrder::MsbFirst,
494 frame_sync_offset: FrameSyncOffset::OnFirstBit,
495 ..Default::default()
496 }
497 }
498}
499
500#[derive(Copy, Clone)]
501pub enum SubBlock {
502 A = 0,
503 B = 1,
504}
505
506enum RingBuffer<'d, C: Channel, W: word::Word> {
507 Writable(WritableRingBuffer<'d, C, W>),
508 #[allow(dead_code)] // remove this after implementing new_* functions for receiver
509 Readable(ReadableRingBuffer<'d, C, W>),
510}
511
512#[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
513fn wdr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: SubBlock) -> *mut W {
514 let ch = w.ch(sub_block as usize);
515 ch.dr().as_ptr() as _
516}
517
518pub struct Sai<'d, T: Instance, C: Channel, W: word::Word> {
519 _peri: PeripheralRef<'d, T>,
520 sd: Option<PeripheralRef<'d, AnyPin>>,
521 fs: Option<PeripheralRef<'d, AnyPin>>,
522 sck: Option<PeripheralRef<'d, AnyPin>>,
523 mclk: Option<PeripheralRef<'d, AnyPin>>,
524 ring_buffer: RingBuffer<'d, C, W>,
525 sub_block: SubBlock,
526}
527
528impl<'d, T: Instance, C: Channel, W: word::Word> Sai<'d, T, C, W> {
529 fn get_transmitter_af_types(mode: Mode) -> (AFType, AFType) {
530 match mode {
531 Mode::Master => (AFType::OutputPushPull, AFType::OutputPushPull),
532 Mode::Slave => (AFType::OutputPushPull, AFType::Input),
533 }
534 }
535
536 pub fn new_asynchronous_transmitter_with_mclk_a(
537 peri: impl Peripheral<P = T> + 'd,
538 sck: impl Peripheral<P = impl SckAPin<T>> + 'd,
539 sd: impl Peripheral<P = impl SdAPin<T>> + 'd,
540 fs: impl Peripheral<P = impl FsAPin<T>> + 'd,
541 mclk: impl Peripheral<P = impl MclkAPin<T>> + 'd,
542 dma: impl Peripheral<P = C> + 'd,
543 dma_buf: &'d mut [W],
544 mut config: Config,
545 ) -> Self
546 where
547 C: Channel + DmaA<T>,
548 {
549 into_ref!(mclk);
550
551 mclk.set_as_af(mclk.af_num(), AFType::OutputPushPull);
552 mclk.set_speed(crate::gpio::Speed::VeryHigh);
553
554 if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
555 config.master_clock_divider = MasterClockDivider::Div1;
556 }
557
558 Self::new_asynchronous_transmitter_a(peri, sck, sd, fs, dma, dma_buf, config)
559 }
560
561 pub fn new_asynchronous_transmitter_a(
562 peri: impl Peripheral<P = T> + 'd,
563 sck: impl Peripheral<P = impl SckAPin<T>> + 'd,
564 sd: impl Peripheral<P = impl SdAPin<T>> + 'd,
565 fs: impl Peripheral<P = impl FsAPin<T>> + 'd,
566 dma: impl Peripheral<P = C> + 'd,
567 dma_buf: &'d mut [W],
568 config: Config,
569 ) -> Self
570 where
571 C: Channel + DmaA<T>,
572 {
573 into_ref!(peri, dma, sck, sd, fs);
574
575 let (sd_af_type, ck_af_type) = Self::get_transmitter_af_types(config.mode);
576 sd.set_as_af(sd.af_num(), sd_af_type);
577 sd.set_speed(crate::gpio::Speed::VeryHigh);
578
579 sck.set_as_af(sck.af_num(), ck_af_type);
580 sck.set_speed(crate::gpio::Speed::VeryHigh);
581 fs.set_as_af(fs.af_num(), ck_af_type);
582 fs.set_speed(crate::gpio::Speed::VeryHigh);
583
584 let request = dma.request();
585 let opts = TransferOptions {
586 half_transfer_ir: true,
587 circular: true,
588 ..Default::default()
589 };
590
591 let sub_block = SubBlock::A;
592
593 Self::new_inner(
594 peri,
595 sub_block,
596 Some(sck.map_into()),
597 None,
598 Some(sd.map_into()),
599 Some(fs.map_into()),
600 RingBuffer::Writable(unsafe {
601 WritableRingBuffer::new_write(dma, request, wdr(T::REGS, sub_block), dma_buf, opts)
602 }),
603 config,
604 )
605 }
606
607 pub fn new_asynchronous_transmitter_with_mclk_b(
608 peri: impl Peripheral<P = T> + 'd,
609 sck: impl Peripheral<P = impl SckBPin<T>> + 'd,
610 sd: impl Peripheral<P = impl SdBPin<T>> + 'd,
611 fs: impl Peripheral<P = impl FsBPin<T>> + 'd,
612 mclk: impl Peripheral<P = impl MclkBPin<T>> + 'd,
613 dma: impl Peripheral<P = C> + 'd,
614 dma_buf: &'d mut [W],
615 mut config: Config,
616 ) -> Self
617 where
618 C: Channel + DmaB<T>,
619 {
620 into_ref!(mclk);
621
622 mclk.set_as_af(mclk.af_num(), AFType::OutputPushPull);
623 mclk.set_speed(crate::gpio::Speed::VeryHigh);
624
625 if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
626 config.master_clock_divider = MasterClockDivider::Div1;
627 }
628
629 Self::new_asynchronous_transmitter_b(peri, sck, sd, fs, dma, dma_buf, config)
630 }
631
632 pub fn new_asynchronous_transmitter_b(
633 peri: impl Peripheral<P = T> + 'd,
634 sck: impl Peripheral<P = impl SckBPin<T>> + 'd,
635 sd: impl Peripheral<P = impl SdBPin<T>> + 'd,
636 fs: impl Peripheral<P = impl FsBPin<T>> + 'd,
637 dma: impl Peripheral<P = C> + 'd,
638 dma_buf: &'d mut [W],
639 config: Config,
640 ) -> Self
641 where
642 C: Channel + DmaB<T>,
643 {
644 into_ref!(dma, peri, sck, sd, fs);
645
646 let (sd_af_type, ck_af_type) = Self::get_transmitter_af_types(config.mode);
647
648 sd.set_as_af(sd.af_num(), sd_af_type);
649 sd.set_speed(crate::gpio::Speed::VeryHigh);
650
651 sck.set_as_af(sck.af_num(), ck_af_type);
652 sck.set_speed(crate::gpio::Speed::VeryHigh);
653 fs.set_as_af(fs.af_num(), ck_af_type);
654 fs.set_speed(crate::gpio::Speed::VeryHigh);
655
656 let request = dma.request();
657 let opts = TransferOptions {
658 half_transfer_ir: true,
659 ..Default::default()
660 };
661
662 let sub_block = SubBlock::B;
663
664 Self::new_inner(
665 peri,
666 sub_block,
667 Some(sck.map_into()),
668 None,
669 Some(sd.map_into()),
670 Some(fs.map_into()),
671 RingBuffer::Writable(unsafe {
672 WritableRingBuffer::new_write(dma, request, wdr(T::REGS, sub_block), dma_buf, opts)
673 }),
674 config,
675 )
676 }
677
678 pub fn start(self: &mut Self) {
679 match self.ring_buffer {
680 RingBuffer::Writable(ref mut rb) => {
681 rb.start();
682 }
683 RingBuffer::Readable(ref mut rb) => {
684 rb.start();
685 }
686 }
687 }
688
689 fn is_transmitter(ring_buffer: &RingBuffer<C, W>) -> bool {
690 match ring_buffer {
691 RingBuffer::Writable(_) => true,
692 _ => false,
693 }
694 }
695
696 fn new_inner(
697 peri: impl Peripheral<P = T> + 'd,
698 sub_block: SubBlock,
699 sck: Option<PeripheralRef<'d, AnyPin>>,
700 mclk: Option<PeripheralRef<'d, AnyPin>>,
701 sd: Option<PeripheralRef<'d, AnyPin>>,
702 fs: Option<PeripheralRef<'d, AnyPin>>,
703 ring_buffer: RingBuffer<'d, C, W>,
704 config: Config,
705 ) -> Self {
706 T::enable();
707 T::reset();
708
709 #[cfg(any(sai_v4))]
710 {
711 // Not totally clear from the datasheet if this is right
712 // This is only used if using SyncEnable::External
713 let value: u8 = if T::REGS.as_ptr() == stm32_metapac::SAI1.as_ptr() {
714 1 //this is SAI1, so sync with SAI2
715 } else {
716 0 //this is SAI2, so sync with SAI1
717 };
718 T::REGS.gcr().modify(|w| {
719 w.set_syncin(value);
720 });
721
722 if config.is_sync_output {
723 let syncout: u8 = match sub_block {
724 SubBlock::A => 0b01,
725 SubBlock::B => 0b10,
726 };
727 T::REGS.gcr().modify(|w| {
728 w.set_syncout(syncout);
729 });
730 }
731 }
732
733 #[cfg(any(sai_v1, sai_v2, sai_v3, sai_v4))]
734 {
735 let ch = T::REGS.ch(sub_block as usize);
736 ch.cr1().modify(|w| {
737 w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) {
738 TxRx::Transmiter
739 } else {
740 TxRx::Receiver
741 }));
742 w.set_prtcfg(config.protocol.prtcfg());
743 w.set_ds(config.data_size.ds());
744 w.set_lsbfirst(config.bit_order.lsbfirst());
745 w.set_ckstr(config.clock_strobe.ckstr());
746 w.set_syncen(config.sync_enable.syncen());
747 w.set_mono(config.stereo_mono.mono());
748 w.set_outdriv(config.output_drive.outdriv());
749 w.set_mckdiv(config.master_clock_divider.mckdiv());
750 w.set_nodiv(
751 if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
752 vals::Nodiv::NODIV
753 } else {
754 vals::Nodiv::MASTERCLOCK
755 },
756 );
757 w.set_dmaen(true);
758 });
759
760 ch.cr2().modify(|w| {
761 w.set_fth(config.fifo_threshold.fth());
762 w.set_comp(config.companding.comp());
763 w.set_cpl(config.complement_format.cpl());
764 w.set_muteval(config.mute_value.muteval());
765 w.set_mutecnt(config.mute_detection_counter.0 as u8);
766 w.set_tris(config.is_high_impedenane_on_inactive_slot);
767 });
768
769 ch.frcr().modify(|w| {
770 w.set_fsoff(config.frame_sync_offset.fsoff());
771 w.set_fspol(config.frame_sync_polarity.fspol());
772 w.set_fsdef(config.frame_sync_definition.fsdef());
773 w.set_fsall(config.frame_sync_active_level_length.0 as u8);
774 w.set_frl(config.frame_length - 1);
775 });
776
777 ch.slotr().modify(|w| {
778 w.set_nbslot(config.slot_count.0 as u8 - 1);
779 w.set_slotsz(config.slot_size.slotsz());
780 w.set_fboff(config.first_bit_offset.0 as u8);
781 w.set_sloten(vals::Sloten(config.slot_enable as u16));
782 });
783
784 ch.cr1().modify(|w| w.set_saien(true));
785 }
786
787 Self {
788 _peri: peri.into_ref(),
789 sub_block,
790 sck,
791 mclk,
792 sd,
793 fs,
794 ring_buffer,
795 }
796 }
797
798 pub fn flush(&mut self) {
799 let ch = T::REGS.ch(self.sub_block as usize);
800 ch.cr1().modify(|w| w.set_saien(false));
801 #[cfg(any(sai_v1, sai_v2))]
802 {
803 ch.cr2().modify(|w| w.set_fflush(vals::Fflush::FLUSH));
804 }
805 #[cfg(any(sai_v3, sai_v4))]
806 {
807 ch.cr2().modify(|w| w.set_fflush(true));
808 }
809 ch.cr1().modify(|w| w.set_saien(true));
810 }
811
812 pub fn set_mute(&mut self, value: bool) {
813 let ch = T::REGS.ch(self.sub_block as usize);
814 ch.cr2().modify(|w| w.set_mute(value));
815 }
816
817 /// Reconfigures it with the supplied config.
818 pub fn reconfigure(&mut self, _config: Config) {}
819
820 pub fn get_current_config(&self) -> Config {
821 Config::default()
822 }
823
824 pub async fn write(&mut self, data: &[W]) -> Result<(), Error> {
825 match &mut self.ring_buffer {
826 RingBuffer::Writable(buffer) => {
827 buffer.write_exact(data).await?;
828 Ok(())
829 }
830 _ => return Err(Error::NotATransmitter),
831 }
832 }
833
834 pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> {
835 match &mut self.ring_buffer {
836 RingBuffer::Readable(buffer) => {
837 buffer.read_exact(data).await?;
838 Ok(())
839 }
840 _ => Err(Error::NotAReceiver),
841 }
842 }
843}
844
845impl<'d, T: Instance, C: Channel, W: word::Word> Drop for Sai<'d, T, C, W> {
846 fn drop(&mut self) {
847 let ch = T::REGS.ch(self.sub_block as usize);
848 ch.cr1().modify(|w| w.set_saien(false));
849 self.fs.as_ref().map(|x| x.set_as_disconnected());
850 self.sd.as_ref().map(|x| x.set_as_disconnected());
851 self.sck.as_ref().map(|x| x.set_as_disconnected());
852 self.mclk.as_ref().map(|x| x.set_as_disconnected());
853 }
854}
855
856pub(crate) mod sealed {
857 use super::*;
858
859 pub trait Instance {
860 const REGS: Regs;
861 }
862}
863
864pub trait Word: word::Word {}
865
866pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
867pin_trait!(SckAPin, Instance);
868pin_trait!(SckBPin, Instance);
869pin_trait!(FsAPin, Instance);
870pin_trait!(FsBPin, Instance);
871pin_trait!(SdAPin, Instance);
872pin_trait!(SdBPin, Instance);
873pin_trait!(MclkAPin, Instance);
874pin_trait!(MclkBPin, Instance);
875
876dma_trait!(DmaA, Instance);
877dma_trait!(DmaB, Instance);
878
879foreach_peripheral!(
880 (sai, $inst:ident) => {
881 impl sealed::Instance for peripherals::$inst {
882 const REGS: Regs = crate::pac::$inst;
883 }
884
885 impl Instance for peripherals::$inst {}
886 };
887);
888
889impl<'d, T: Instance, C: Channel, W: word::Word> SetConfig for Sai<'d, T, C, W> {
890 type Config = Config;
891 fn set_config(&mut self, config: &Self::Config) {
892 self.reconfigure(*config);
893 }
894}