aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-09-05 14:13:10 +0000
committerGitHub <[email protected]>2025-09-05 14:13:10 +0000
commit1c2fa92293e52157582b92766e75381a228bbaf1 (patch)
tree77b065f52abf07298b5f4ed5feaa97ebfb3c345b
parent581594d1f5b403363a109e41b5d354f9364b4724 (diff)
parent683ca6595ff7f4c6f0e70e90d3cdeab13d0b1c07 (diff)
Merge pull request #4620 from embassy-rs/lolsai
stm32: add i2s support for all versions, cleanup spi/i2s/sai versions.
-rw-r--r--embassy-stm32/CHANGELOG.md1
-rw-r--r--embassy-stm32/Cargo.toml8
-rw-r--r--embassy-stm32/build.rs29
-rw-r--r--embassy-stm32/src/adc/mod.rs68
-rw-r--r--embassy-stm32/src/i2s.rs183
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/sai/mod.rs328
-rw-r--r--embassy-stm32/src/spi/mod.rs134
-rw-r--r--examples/stm32h7/src/bin/sai.rs72
-rw-r--r--examples/stm32h723/src/bin/spdifrx.rs2
10 files changed, 269 insertions, 558 deletions
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index fe2fb64f2..50bdc1072 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
12- feat: Derive Clone, Copy for QSPI Config 12- feat: Derive Clone, Copy for QSPI Config
13- fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received 13- fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received
14- feat: stm32/timer: add set_polarity functions for main and complementary outputs in complementary_pwm 14- feat: stm32/timer: add set_polarity functions for main and complementary outputs in complementary_pwm
15- Add I2S support for STM32F1, STM32C0, STM32F0, STM32F3, STM32F7, STM32G0, STM32WL, STM32H5, STM32H7RS
15 16
16## 0.4.0 - 2025-08-26 17## 0.4.0 - 2025-08-26
17 18
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index cdb4e07d1..9c2ba1f53 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -173,8 +173,8 @@ cortex-m = "0.7.6"
173futures-util = { version = "0.3.30", default-features = false } 173futures-util = { version = "0.3.30", default-features = false }
174sdio-host = "0.9.0" 174sdio-host = "0.9.0"
175critical-section = "1.1" 175critical-section = "1.1"
176stm32-metapac = { version = "18" } 176#stm32-metapac = { version = "18" }
177#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ecb93d42a6cbcd9e09cab74873908a2ca22327f7" } 177stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d8432edd0406495adec19d31923584e80b8e03cb" }
178 178
179vcell = "0.1.3" 179vcell = "0.1.3"
180nb = "1.0.0" 180nb = "1.0.0"
@@ -202,8 +202,8 @@ proptest-state-machine = "0.3.0"
202proc-macro2 = "1.0.36" 202proc-macro2 = "1.0.36"
203quote = "1.0.15" 203quote = "1.0.15"
204 204
205stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} 205#stm32-metapac = { version = "18", default-features = false, features = ["metadata"]}
206#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ecb93d42a6cbcd9e09cab74873908a2ca22327f7", default-features = false, features = ["metadata"] } 206stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d8432edd0406495adec19d31923584e80b8e03cb", default-features = false, features = ["metadata"] }
207 207
208[features] 208[features]
209default = ["rt"] 209default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index deefb13c1..b731012c6 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -16,6 +16,27 @@ use stm32_metapac::metadata::{
16#[path = "./build_common.rs"] 16#[path = "./build_common.rs"]
17mod common; 17mod common;
18 18
19/// Helper function to handle peripheral versions with underscores.
20/// For a version like "v1_foo_bar", this generates all prefix combinations:
21/// - "kind_v1"
22/// - "kind_v1_foo"
23/// - "kind_v1_foo_bar"
24fn foreach_version_cfg(
25 cfgs: &mut common::CfgSet,
26 kind: &str,
27 version: &str,
28 mut cfg_fn: impl FnMut(&mut common::CfgSet, &str),
29) {
30 let parts: Vec<&str> = version.split('_').collect();
31
32 // Generate all possible prefix combinations
33 for i in 1..=parts.len() {
34 let partial_version = parts[0..i].join("_");
35 let cfg_name = format!("{}_{}", kind, partial_version);
36 cfg_fn(cfgs, &cfg_name);
37 }
38}
39
19fn main() { 40fn main() {
20 let mut cfgs = common::CfgSet::new(); 41 let mut cfgs = common::CfgSet::new();
21 common::set_target_cfgs(&mut cfgs); 42 common::set_target_cfgs(&mut cfgs);
@@ -38,14 +59,18 @@ fn main() {
38 for p in METADATA.peripherals { 59 for p in METADATA.peripherals {
39 if let Some(r) = &p.registers { 60 if let Some(r) = &p.registers {
40 cfgs.enable(r.kind); 61 cfgs.enable(r.kind);
41 cfgs.enable(format!("{}_{}", r.kind, r.version)); 62 foreach_version_cfg(&mut cfgs, r.kind, r.version, |cfgs, cfg_name| {
63 cfgs.enable(cfg_name);
64 });
42 } 65 }
43 } 66 }
44 67
45 for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() { 68 for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() {
46 cfgs.declare(kind); 69 cfgs.declare(kind);
47 for &version in versions.iter() { 70 for &version in versions.iter() {
48 cfgs.declare(format!("{}_{}", kind, version)); 71 foreach_version_cfg(&mut cfgs, kind, version, |cfgs, cfg_name| {
72 cfgs.declare(cfg_name);
73 });
49 } 74 }
50 } 75 }
51 76
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 778edc6f6..ea986f4cf 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -2,12 +2,12 @@
2 2
3#![macro_use] 3#![macro_use]
4#![allow(missing_docs)] // TODO 4#![allow(missing_docs)] // TODO
5#![cfg_attr(adc_f3_v2, allow(unused))] 5#![cfg_attr(adc_f3v3, allow(unused))]
6 6
7#[cfg(not(any(adc_f3_v2, adc_wba)))] 7#[cfg(not(any(adc_f3v3, adc_wba)))]
8#[cfg_attr(adc_f1, path = "f1.rs")] 8#[cfg_attr(adc_f1, path = "f1.rs")]
9#[cfg_attr(adc_f3, path = "f3.rs")] 9#[cfg_attr(adc_f3v1, path = "f3.rs")]
10#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] 10#[cfg_attr(adc_f3v2, path = "f3_v1_1.rs")]
11#[cfg_attr(adc_v1, path = "v1.rs")] 11#[cfg_attr(adc_v1, path = "v1.rs")]
12#[cfg_attr(adc_l0, path = "v1.rs")] 12#[cfg_attr(adc_l0, path = "v1.rs")]
13#[cfg_attr(adc_v2, path = "v2.rs")] 13#[cfg_attr(adc_v2, path = "v2.rs")]
@@ -20,10 +20,10 @@ mod _version;
20use core::marker::PhantomData; 20use core::marker::PhantomData;
21 21
22#[allow(unused)] 22#[allow(unused)]
23#[cfg(not(any(adc_f3_v2, adc_wba)))] 23#[cfg(not(any(adc_f3v3, adc_wba)))]
24pub use _version::*; 24pub use _version::*;
25use embassy_hal_internal::{impl_peripheral, PeripheralType}; 25use embassy_hal_internal::{impl_peripheral, PeripheralType};
26#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 26#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
27use embassy_sync::waitqueue::AtomicWaker; 27use embassy_sync::waitqueue::AtomicWaker;
28 28
29#[cfg(any(adc_u5, adc_wba))] 29#[cfg(any(adc_u5, adc_wba))]
@@ -31,7 +31,7 @@ use embassy_sync::waitqueue::AtomicWaker;
31pub mod adc4; 31pub mod adc4;
32 32
33pub use crate::pac::adc::vals; 33pub use crate::pac::adc::vals;
34#[cfg(not(any(adc_f1, adc_f3_v2)))] 34#[cfg(not(any(adc_f1, adc_f3v3)))]
35pub use crate::pac::adc::vals::Res as Resolution; 35pub use crate::pac::adc::vals::Res as Resolution;
36pub use crate::pac::adc::vals::SampleTime; 36pub use crate::pac::adc::vals::SampleTime;
37use crate::peripherals; 37use crate::peripherals;
@@ -47,16 +47,16 @@ dma_trait!(RxDma4, adc4::Instance);
47pub struct Adc<'d, T: Instance> { 47pub struct Adc<'d, T: Instance> {
48 #[allow(unused)] 48 #[allow(unused)]
49 adc: crate::Peri<'d, T>, 49 adc: crate::Peri<'d, T>,
50 #[cfg(not(any(adc_f3_v2, adc_f3_v1_1, adc_wba)))] 50 #[cfg(not(any(adc_f3v3, adc_f3v2, adc_wba)))]
51 sample_time: SampleTime, 51 sample_time: SampleTime,
52} 52}
53 53
54#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 54#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
55pub struct State { 55pub struct State {
56 pub waker: AtomicWaker, 56 pub waker: AtomicWaker,
57} 57}
58 58
59#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 59#[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
60impl State { 60impl State {
61 pub const fn new() -> Self { 61 pub const fn new() -> Self {
62 Self { 62 Self {
@@ -69,10 +69,10 @@ trait SealedInstance {
69 #[cfg(not(adc_wba))] 69 #[cfg(not(adc_wba))]
70 #[allow(unused)] 70 #[allow(unused)]
71 fn regs() -> crate::pac::adc::Adc; 71 fn regs() -> crate::pac::adc::Adc;
72 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] 72 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0)))]
73 #[allow(unused)] 73 #[allow(unused)]
74 fn common_regs() -> crate::pac::adccommon::AdcCommon; 74 fn common_regs() -> crate::pac::adccommon::AdcCommon;
75 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 75 #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
76 fn state() -> &'static State; 76 fn state() -> &'static State;
77} 77}
78 78
@@ -100,22 +100,8 @@ pub(crate) fn blocking_delay_us(us: u32) {
100 100
101/// ADC instance. 101/// ADC instance.
102#[cfg(not(any( 102#[cfg(not(any(
103 adc_f1, 103 adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs,
104 adc_v1, 104 adc_u5, adc_c0, adc_wba,
105 adc_l0,
106 adc_v2,
107 adc_v3,
108 adc_v4,
109 adc_g4,
110 adc_f3,
111 adc_f3_v1_1,
112 adc_g0,
113 adc_u0,
114 adc_h5,
115 adc_h7rs,
116 adc_u5,
117 adc_c0,
118 adc_wba,
119)))] 105)))]
120#[allow(private_bounds)] 106#[allow(private_bounds)]
121pub trait Instance: SealedInstance + crate::PeripheralType { 107pub trait Instance: SealedInstance + crate::PeripheralType {
@@ -123,22 +109,8 @@ pub trait Instance: SealedInstance + crate::PeripheralType {
123} 109}
124/// ADC instance. 110/// ADC instance.
125#[cfg(any( 111#[cfg(any(
126 adc_f1, 112 adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs,
127 adc_v1, 113 adc_u5, adc_c0, adc_wba,
128 adc_l0,
129 adc_v2,
130 adc_v3,
131 adc_v4,
132 adc_g4,
133 adc_f3,
134 adc_f3_v1_1,
135 adc_g0,
136 adc_u0,
137 adc_h5,
138 adc_h7rs,
139 adc_u5,
140 adc_c0,
141 adc_wba,
142))] 114))]
143#[allow(private_bounds)] 115#[allow(private_bounds)]
144pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral { 116pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeripheral {
@@ -258,12 +230,12 @@ foreach_adc!(
258 crate::pac::$inst 230 crate::pac::$inst
259 } 231 }
260 232
261 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0, adc_u5, adc_wba)))] 233 #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3v3, adc_f3v2, adc_g0, adc_u5, adc_wba)))]
262 fn common_regs() -> crate::pac::adccommon::AdcCommon { 234 fn common_regs() -> crate::pac::adccommon::AdcCommon {
263 return crate::pac::$common_inst 235 return crate::pac::$common_inst
264 } 236 }
265 237
266 #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] 238 #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))]
267 fn state() -> &'static State { 239 fn state() -> &'static State {
268 static STATE: State = State::new(); 240 static STATE: State = State::new();
269 &STATE 241 &STATE
@@ -295,7 +267,7 @@ macro_rules! impl_adc_pin {
295/// Get the maximum reading value for this resolution. 267/// Get the maximum reading value for this resolution.
296/// 268///
297/// This is `2**n - 1`. 269/// This is `2**n - 1`.
298#[cfg(not(any(adc_f1, adc_f3_v2)))] 270#[cfg(not(any(adc_f1, adc_f3v3)))]
299pub const fn resolution_to_max_count(res: Resolution) -> u32 { 271pub const fn resolution_to_max_count(res: Resolution) -> u32 {
300 match res { 272 match res {
301 #[cfg(adc_v4)] 273 #[cfg(adc_v4)]
@@ -309,7 +281,7 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 {
309 Resolution::BITS12 => (1 << 12) - 1, 281 Resolution::BITS12 => (1 << 12) - 1,
310 Resolution::BITS10 => (1 << 10) - 1, 282 Resolution::BITS10 => (1 << 10) - 1,
311 Resolution::BITS8 => (1 << 8) - 1, 283 Resolution::BITS8 => (1 << 8) - 1,
312 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3, adc_f3_v1_1, adc_h5))] 284 #[cfg(any(adc_v1, adc_v2, adc_v3, adc_l0, adc_c0, adc_g0, adc_f3v1, adc_f3v2, adc_h5))]
313 Resolution::BITS6 => (1 << 6) - 1, 285 Resolution::BITS6 => (1 << 6) - 1,
314 #[allow(unreachable_patterns)] 286 #[allow(unreachable_patterns)]
315 _ => core::unreachable!(), 287 _ => core::unreachable!(),
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs
index a51d21bb0..0c4ab56e3 100644
--- a/embassy-stm32/src/i2s.rs
+++ b/embassy-stm32/src/i2s.rs
@@ -27,7 +27,8 @@ enum Function {
27 Transmit, 27 Transmit,
28 /// Receive audio data 28 /// Receive audio data
29 Receive, 29 Receive,
30 #[cfg(spi_v3)] 30 #[cfg(any(spi_v4, spi_v5))]
31
31 /// Transmit and Receive audio data 32 /// Transmit and Receive audio data
32 FullDuplex, 33 FullDuplex,
33} 34}
@@ -72,7 +73,6 @@ impl From<ringbuffer::Error> for Error {
72} 73}
73 74
74impl Standard { 75impl Standard {
75 #[cfg(any(spi_v1, spi_v3, spi_f1))]
76 const fn i2sstd(&self) -> vals::I2sstd { 76 const fn i2sstd(&self) -> vals::I2sstd {
77 match self { 77 match self {
78 Standard::Philips => vals::I2sstd::PHILIPS, 78 Standard::Philips => vals::I2sstd::PHILIPS,
@@ -83,7 +83,6 @@ impl Standard {
83 } 83 }
84 } 84 }
85 85
86 #[cfg(any(spi_v1, spi_v3, spi_f1))]
87 const fn pcmsync(&self) -> vals::Pcmsync { 86 const fn pcmsync(&self) -> vals::Pcmsync {
88 match self { 87 match self {
89 Standard::PcmLongSync => vals::Pcmsync::LONG, 88 Standard::PcmLongSync => vals::Pcmsync::LONG,
@@ -106,7 +105,6 @@ pub enum Format {
106} 105}
107 106
108impl Format { 107impl Format {
109 #[cfg(any(spi_v1, spi_v3, spi_f1))]
110 const fn datlen(&self) -> vals::Datlen { 108 const fn datlen(&self) -> vals::Datlen {
111 match self { 109 match self {
112 Format::Data16Channel16 => vals::Datlen::BITS16, 110 Format::Data16Channel16 => vals::Datlen::BITS16,
@@ -116,7 +114,6 @@ impl Format {
116 } 114 }
117 } 115 }
118 116
119 #[cfg(any(spi_v1, spi_v3, spi_f1))]
120 const fn chlen(&self) -> vals::Chlen { 117 const fn chlen(&self) -> vals::Chlen {
121 match self { 118 match self {
122 Format::Data16Channel16 => vals::Chlen::BITS16, 119 Format::Data16Channel16 => vals::Chlen::BITS16,
@@ -137,7 +134,6 @@ pub enum ClockPolarity {
137} 134}
138 135
139impl ClockPolarity { 136impl ClockPolarity {
140 #[cfg(any(spi_v1, spi_v3, spi_f1))]
141 const fn ckpol(&self) -> vals::Ckpol { 137 const fn ckpol(&self) -> vals::Ckpol {
142 match self { 138 match self {
143 ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH, 139 ClockPolarity::IdleHigh => vals::Ckpol::IDLE_HIGH,
@@ -314,7 +310,8 @@ impl<'d, W: Word> I2S<'d, W> {
314 ) 310 )
315 } 311 }
316 312
317 #[cfg(spi_v3)] 313 #[cfg(any(spi_v4, spi_v5))]
314
318 /// Create a full duplex driver. 315 /// Create a full duplex driver.
319 pub fn new_full_duplex<T: Instance>( 316 pub fn new_full_duplex<T: Instance>(
320 peri: Peri<'d, T>, 317 peri: Peri<'d, T>,
@@ -357,7 +354,7 @@ impl<'d, W: Word> I2S<'d, W> {
357 if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { 354 if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer {
358 rx_ring_buffer.start(); 355 rx_ring_buffer.start();
359 // SPIv3 clears rxfifo on SPE=0 356 // SPIv3 clears rxfifo on SPE=0
360 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 357 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
361 flush_rx_fifo(self.spi.info.regs); 358 flush_rx_fifo(self.spi.info.regs);
362 359
363 set_rxdmaen(self.spi.info.regs, true); 360 set_rxdmaen(self.spi.info.regs, true);
@@ -365,7 +362,7 @@ impl<'d, W: Word> I2S<'d, W> {
365 self.spi.info.regs.cr1().modify(|w| { 362 self.spi.info.regs.cr1().modify(|w| {
366 w.set_spe(true); 363 w.set_spe(true);
367 }); 364 });
368 #[cfg(any(spi_v3, spi_v4, spi_v5))] 365 #[cfg(any(spi_v4, spi_v5, spi_v6))]
369 self.spi.info.regs.cr1().modify(|w| { 366 self.spi.info.regs.cr1().modify(|w| {
370 w.set_cstart(true); 367 w.set_cstart(true);
371 }); 368 });
@@ -404,7 +401,7 @@ impl<'d, W: Word> I2S<'d, W> {
404 401
405 join(rx_f, tx_f).await; 402 join(rx_f, tx_f).await;
406 403
407 #[cfg(any(spi_v3, spi_v4, spi_v5))] 404 #[cfg(any(spi_v4, spi_v5, spi_v6))]
408 { 405 {
409 if let Mode::Master = self.mode { 406 if let Mode::Master = self.mode {
410 regs.cr1().modify(|w| { 407 regs.cr1().modify(|w| {
@@ -492,103 +489,98 @@ impl<'d, W: Word> I2S<'d, W> {
492 489
493 let (odd, div) = compute_baud_rate(pclk, config.frequency, config.master_clock, config.format); 490 let (odd, div) = compute_baud_rate(pclk, config.frequency, config.master_clock, config.format);
494 491
495 #[cfg(any(spi_v1, spi_v3, spi_f1))] 492 #[cfg(any(spi_v4, spi_v5))]
496 { 493 {
497 #[cfg(spi_v3)] 494 regs.cr1().modify(|w| w.set_spe(false));
498 {
499 regs.cr1().modify(|w| w.set_spe(false));
500 495
501 reset_incompatible_bitfields::<T>(); 496 reset_incompatible_bitfields::<T>();
502 } 497 }
503 498
504 use stm32_metapac::spi::vals::{I2scfg, Odd}; 499 use stm32_metapac::spi::vals::{I2scfg, Odd};
505 500
506 // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud 501 // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud
507 // rate to reach the proper audio sample frequency. The ODD bit in the 502 // rate to reach the proper audio sample frequency. The ODD bit in the
508 // SPI_I2SPR/SPI_I2SCFGR register also has to be defined. 503 // SPI_I2SPR/SPI_I2SCFGR register also has to be defined.
509
510 // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
511 // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to
512 // the external DAC/ADC audio component (the I2SDIV and ODD values should be
513 // computed depending on the state of the MCK output, for more details refer to
514 // Section 28.4.4: Clock generator).
515
516 // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the
517 // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the
518 // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.
519 // Select also the I2S master mode and direction (Transmitter or Receiver) through the
520 // I2SCFG[1:0] bits in the SPI_I2SCFGR register.
521
522 // 4. If needed, select all the potential interruption sources and the DMA capabilities by
523 // writing the SPI_CR2 register.
524
525 // 5. The I2SE bit in SPI_I2SCFGR register must be set.
526
527 let clk_reg = {
528 #[cfg(any(spi_v1, spi_f1))]
529 {
530 regs.i2spr()
531 }
532 #[cfg(spi_v3)]
533 {
534 regs.i2scfgr()
535 }
536 };
537
538 clk_reg.modify(|w| {
539 w.set_i2sdiv(div);
540 w.set_odd(match odd {
541 true => Odd::ODD,
542 false => Odd::EVEN,
543 });
544 504
545 w.set_mckoe(config.master_clock); 505 // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the
506 // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to
507 // the external DAC/ADC audio component (the I2SDIV and ODD values should be
508 // computed depending on the state of the MCK output, for more details refer to
509 // Section 28.4.4: Clock generator).
510
511 // 3. Set the I2SMOD bit in SPI_I2SCFGR to activate the I2S functionalities and choose the
512 // I2S standard through the I2SSTD[1:0] and PCMSYNC bits, the data length through the
513 // DATLEN[1:0] bits and the number of bits per channel by configuring the CHLEN bit.
514 // Select also the I2S master mode and direction (Transmitter or Receiver) through the
515 // I2SCFG[1:0] bits in the SPI_I2SCFGR register.
516
517 // 4. If needed, select all the potential interruption sources and the DMA capabilities by
518 // writing the SPI_CR2 register.
519
520 // 5. The I2SE bit in SPI_I2SCFGR register must be set.
521
522 let clk_reg = {
523 #[cfg(any(spi_v1, spi_v2, spi_v3))]
524 {
525 regs.i2spr()
526 }
527 #[cfg(any(spi_v4, spi_v5))]
528 {
529 regs.i2scfgr()
530 }
531 };
532
533 clk_reg.modify(|w| {
534 w.set_i2sdiv(div);
535 w.set_odd(match odd {
536 true => Odd::ODD,
537 false => Odd::EVEN,
546 }); 538 });
547 539
548 regs.i2scfgr().modify(|w| { 540 w.set_mckoe(config.master_clock);
549 w.set_ckpol(config.clock_polarity.ckpol()); 541 });
550 542
551 w.set_i2smod(true); 543 regs.i2scfgr().modify(|w| {
544 w.set_ckpol(config.clock_polarity.ckpol());
552 545
553 w.set_i2sstd(config.standard.i2sstd()); 546 w.set_i2smod(true);
554 w.set_pcmsync(config.standard.pcmsync());
555 547
556 w.set_datlen(config.format.datlen()); 548 w.set_i2sstd(config.standard.i2sstd());
557 w.set_chlen(config.format.chlen()); 549 w.set_pcmsync(config.standard.pcmsync());
558 550
559 w.set_i2scfg(match (config.mode, function) { 551 w.set_datlen(config.format.datlen());
560 (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX, 552 w.set_chlen(config.format.chlen());
561 (Mode::Master, Function::Receive) => I2scfg::MASTER_RX,
562 #[cfg(spi_v3)]
563 (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX,
564 (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX,
565 (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX,
566 #[cfg(spi_v3)]
567 (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX,
568 });
569 553
570 #[cfg(any(spi_v1, spi_f1))] 554 w.set_i2scfg(match (config.mode, function) {
571 w.set_i2se(true); 555 (Mode::Master, Function::Transmit) => I2scfg::MASTER_TX,
556 (Mode::Master, Function::Receive) => I2scfg::MASTER_RX,
557 #[cfg(any(spi_v4, spi_v5))]
558 (Mode::Master, Function::FullDuplex) => I2scfg::MASTER_FULL_DUPLEX,
559 (Mode::Slave, Function::Transmit) => I2scfg::SLAVE_TX,
560 (Mode::Slave, Function::Receive) => I2scfg::SLAVE_RX,
561 #[cfg(any(spi_v4, spi_v5))]
562 (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVE_FULL_DUPLEX,
572 }); 563 });
573 564
574 let mut opts = TransferOptions::default(); 565 #[cfg(any(spi_v1, spi_v2, spi_v3))]
575 opts.half_transfer_ir = true; 566 w.set_i2se(true);
576 567 });
577 Self { 568
578 mode: config.mode, 569 let mut opts = TransferOptions::default();
579 spi, 570 opts.half_transfer_ir = true;
580 txsd: txsd.map(|w| w.into()), 571
581 rxsd: rxsd.map(|w| w.into()), 572 Self {
582 ws: Some(ws.into()), 573 mode: config.mode,
583 ck: Some(ck.into()), 574 spi,
584 mck: mck.map(|w| w.into()), 575 txsd: txsd.map(|w| w.into()),
585 tx_ring_buffer: txdma.map(|(ch, buf)| unsafe { 576 rxsd: rxsd.map(|w| w.into()),
586 WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) 577 ws: Some(ws.into()),
587 }), 578 ck: Some(ck.into()),
588 rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe { 579 mck: mck.map(|w| w.into()),
589 ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) 580 tx_ring_buffer: txdma
590 }), 581 .map(|(ch, buf)| unsafe { WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) }),
591 } 582 rx_ring_buffer: rxdma
583 .map(|(ch, buf)| unsafe { ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) }),
592 } 584 }
593 } 585 }
594} 586}
@@ -639,7 +631,8 @@ fn compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_for
639 } 631 }
640} 632}
641 633
642#[cfg(spi_v3)] 634#[cfg(any(spi_v4, spi_v5))]
635
643// The STM32H7 reference manual specifies that any incompatible bitfields should be reset 636// The STM32H7 reference manual specifies that any incompatible bitfields should be reset
644// to their reset values while operating in I2S mode. 637// to their reset values while operating in I2S mode.
645fn reset_incompatible_bitfields<T: Instance>() { 638fn reset_incompatible_bitfields<T: Instance>() {
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 3be98c462..7e0f7884e 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -87,7 +87,7 @@ pub mod hsem;
87pub mod hspi; 87pub mod hspi;
88#[cfg(i2c)] 88#[cfg(i2c)]
89pub mod i2c; 89pub mod i2c;
90#[cfg(any(all(spi_v1, rcc_f4), spi_v3))] 90#[cfg(any(spi_v1_i2s, spi_v2_i2s, spi_v3_i2s, spi_v4_i2s, spi_v5_i2s))]
91pub mod i2s; 91pub mod i2s;
92#[cfg(stm32wb)] 92#[cfg(stm32wb)]
93pub mod ipcc; 93pub mod ipcc;
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs
index 88cc225dd..cde2a56c2 100644
--- a/embassy-stm32/src/sai/mod.rs
+++ b/embassy-stm32/src/sai/mod.rs
@@ -8,6 +8,7 @@ use embassy_hal_internal::PeripheralType;
8pub use crate::dma::word; 8pub use crate::dma::word;
9use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; 9use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer};
10use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; 10use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
11pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider;
11use crate::pac::sai::{vals, Sai as Regs}; 12use crate::pac::sai::{vals, Sai as Regs};
12use crate::rcc::{self, RccPeripheral}; 13use crate::rcc::{self, RccPeripheral};
13use crate::{peripherals, Peri}; 14use crate::{peripherals, Peri};
@@ -45,7 +46,6 @@ pub enum Mode {
45} 46}
46 47
47impl Mode { 48impl Mode {
48 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
49 const fn mode(&self, tx_rx: TxRx) -> vals::Mode { 49 const fn mode(&self, tx_rx: TxRx) -> vals::Mode {
50 match tx_rx { 50 match tx_rx {
51 TxRx::Transmitter => match self { 51 TxRx::Transmitter => match self {
@@ -80,7 +80,6 @@ pub enum SlotSize {
80} 80}
81 81
82impl SlotSize { 82impl SlotSize {
83 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
84 const fn slotsz(&self) -> vals::Slotsz { 83 const fn slotsz(&self) -> vals::Slotsz {
85 match self { 84 match self {
86 SlotSize::DataSize => vals::Slotsz::DATA_SIZE, 85 SlotSize::DataSize => vals::Slotsz::DATA_SIZE,
@@ -103,7 +102,6 @@ pub enum DataSize {
103} 102}
104 103
105impl DataSize { 104impl DataSize {
106 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
107 const fn ds(&self) -> vals::Ds { 105 const fn ds(&self) -> vals::Ds {
108 match self { 106 match self {
109 DataSize::Data8 => vals::Ds::BIT8, 107 DataSize::Data8 => vals::Ds::BIT8,
@@ -128,7 +126,6 @@ pub enum FifoThreshold {
128} 126}
129 127
130impl FifoThreshold { 128impl FifoThreshold {
131 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
132 const fn fth(&self) -> vals::Fth { 129 const fn fth(&self) -> vals::Fth {
133 match self { 130 match self {
134 FifoThreshold::Empty => vals::Fth::EMPTY, 131 FifoThreshold::Empty => vals::Fth::EMPTY,
@@ -149,7 +146,6 @@ pub enum MuteValue {
149} 146}
150 147
151impl MuteValue { 148impl MuteValue {
152 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
153 const fn muteval(&self) -> vals::Muteval { 149 const fn muteval(&self) -> vals::Muteval {
154 match self { 150 match self {
155 MuteValue::Zero => vals::Muteval::SEND_ZERO, 151 MuteValue::Zero => vals::Muteval::SEND_ZERO,
@@ -168,7 +164,6 @@ pub enum Protocol {
168} 164}
169 165
170impl Protocol { 166impl Protocol {
171 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
172 const fn prtcfg(&self) -> vals::Prtcfg { 167 const fn prtcfg(&self) -> vals::Prtcfg {
173 match self { 168 match self {
174 Protocol::Free => vals::Prtcfg::FREE, 169 Protocol::Free => vals::Prtcfg::FREE,
@@ -187,7 +182,7 @@ pub enum SyncInput {
187 /// Syncs with the other A/B sub-block within the SAI unit 182 /// Syncs with the other A/B sub-block within the SAI unit
188 Internal, 183 Internal,
189 /// Syncs with a sub-block in the other SAI unit 184 /// Syncs with a sub-block in the other SAI unit
190 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 185 #[cfg(any(sai_v3, sai_v4))]
191 External(SyncInputInstance), 186 External(SyncInputInstance),
192} 187}
193 188
@@ -196,14 +191,14 @@ impl SyncInput {
196 match self { 191 match self {
197 SyncInput::None => vals::Syncen::ASYNCHRONOUS, 192 SyncInput::None => vals::Syncen::ASYNCHRONOUS,
198 SyncInput::Internal => vals::Syncen::INTERNAL, 193 SyncInput::Internal => vals::Syncen::INTERNAL,
199 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 194 #[cfg(any(sai_v3, sai_v4))]
200 SyncInput::External(_) => vals::Syncen::EXTERNAL, 195 SyncInput::External(_) => vals::Syncen::EXTERNAL,
201 } 196 }
202 } 197 }
203} 198}
204 199
205/// SAI instance to sync from. 200/// SAI instance to sync from.
206#[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 201#[cfg(any(sai_v3, sai_v4))]
207#[derive(Copy, Clone, PartialEq)] 202#[derive(Copy, Clone, PartialEq)]
208#[allow(missing_docs)] 203#[allow(missing_docs)]
209pub enum SyncInputInstance { 204pub enum SyncInputInstance {
@@ -226,7 +221,6 @@ pub enum StereoMono {
226} 221}
227 222
228impl StereoMono { 223impl StereoMono {
229 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
230 const fn mono(&self) -> vals::Mono { 224 const fn mono(&self) -> vals::Mono {
231 match self { 225 match self {
232 StereoMono::Stereo => vals::Mono::STEREO, 226 StereoMono::Stereo => vals::Mono::STEREO,
@@ -245,7 +239,6 @@ pub enum BitOrder {
245} 239}
246 240
247impl BitOrder { 241impl BitOrder {
248 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
249 const fn lsbfirst(&self) -> vals::Lsbfirst { 242 const fn lsbfirst(&self) -> vals::Lsbfirst {
250 match self { 243 match self {
251 BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST, 244 BitOrder::LsbFirst => vals::Lsbfirst::LSB_FIRST,
@@ -264,7 +257,6 @@ pub enum FrameSyncOffset {
264} 257}
265 258
266impl FrameSyncOffset { 259impl FrameSyncOffset {
267 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
268 const fn fsoff(&self) -> vals::Fsoff { 260 const fn fsoff(&self) -> vals::Fsoff {
269 match self { 261 match self {
270 FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST, 262 FrameSyncOffset::OnFirstBit => vals::Fsoff::ON_FIRST,
@@ -283,7 +275,6 @@ pub enum FrameSyncPolarity {
283} 275}
284 276
285impl FrameSyncPolarity { 277impl FrameSyncPolarity {
286 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
287 const fn fspol(&self) -> vals::Fspol { 278 const fn fspol(&self) -> vals::Fspol {
288 match self { 279 match self {
289 FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE, 280 FrameSyncPolarity::ActiveLow => vals::Fspol::FALLING_EDGE,
@@ -301,7 +292,6 @@ pub enum FrameSyncDefinition {
301} 292}
302 293
303impl FrameSyncDefinition { 294impl FrameSyncDefinition {
304 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
305 const fn fsdef(&self) -> bool { 295 const fn fsdef(&self) -> bool {
306 match self { 296 match self {
307 FrameSyncDefinition::StartOfFrame => false, 297 FrameSyncDefinition::StartOfFrame => false,
@@ -319,7 +309,6 @@ pub enum ClockStrobe {
319} 309}
320 310
321impl ClockStrobe { 311impl ClockStrobe {
322 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
323 const fn ckstr(&self) -> vals::Ckstr { 312 const fn ckstr(&self) -> vals::Ckstr {
324 match self { 313 match self {
325 ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE, 314 ClockStrobe::Falling => vals::Ckstr::FALLING_EDGE,
@@ -337,7 +326,6 @@ pub enum ComplementFormat {
337} 326}
338 327
339impl ComplementFormat { 328impl ComplementFormat {
340 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
341 const fn cpl(&self) -> vals::Cpl { 329 const fn cpl(&self) -> vals::Cpl {
342 match self { 330 match self {
343 ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT, 331 ComplementFormat::OnesComplement => vals::Cpl::ONES_COMPLEMENT,
@@ -356,7 +344,6 @@ pub enum Companding {
356} 344}
357 345
358impl Companding { 346impl Companding {
359 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
360 const fn comp(&self) -> vals::Comp { 347 const fn comp(&self) -> vals::Comp {
361 match self { 348 match self {
362 Companding::None => vals::Comp::NO_COMPANDING, 349 Companding::None => vals::Comp::NO_COMPANDING,
@@ -375,7 +362,6 @@ pub enum OutputDrive {
375} 362}
376 363
377impl OutputDrive { 364impl OutputDrive {
378 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
379 const fn outdriv(&self) -> vals::Outdriv { 365 const fn outdriv(&self) -> vals::Outdriv {
380 match self { 366 match self {
381 OutputDrive::OnStart => vals::Outdriv::ON_START, 367 OutputDrive::OnStart => vals::Outdriv::ON_START,
@@ -384,196 +370,6 @@ impl OutputDrive {
384 } 370 }
385} 371}
386 372
387/// Master clock divider.
388#[derive(Copy, Clone, PartialEq)]
389#[allow(missing_docs)]
390#[cfg(any(sai_v1, sai_v2))]
391pub enum MasterClockDivider {
392 MasterClockDisabled,
393 Div1,
394 Div2,
395 Div4,
396 Div6,
397 Div8,
398 Div10,
399 Div12,
400 Div14,
401 Div16,
402 Div18,
403 Div20,
404 Div22,
405 Div24,
406 Div26,
407 Div28,
408 Div30,
409}
410
411/// Master clock divider.
412#[derive(Copy, Clone, PartialEq)]
413#[allow(missing_docs)]
414#[cfg(any(sai_v1_4pdm, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
415pub enum MasterClockDivider {
416 MasterClockDisabled,
417 Div1,
418 Div2,
419 Div3,
420 Div4,
421 Div5,
422 Div6,
423 Div7,
424 Div8,
425 Div9,
426 Div10,
427 Div11,
428 Div12,
429 Div13,
430 Div14,
431 Div15,
432 Div16,
433 Div17,
434 Div18,
435 Div19,
436 Div20,
437 Div21,
438 Div22,
439 Div23,
440 Div24,
441 Div25,
442 Div26,
443 Div27,
444 Div28,
445 Div29,
446 Div30,
447 Div31,
448 Div32,
449 Div33,
450 Div34,
451 Div35,
452 Div36,
453 Div37,
454 Div38,
455 Div39,
456 Div40,
457 Div41,
458 Div42,
459 Div43,
460 Div44,
461 Div45,
462 Div46,
463 Div47,
464 Div48,
465 Div49,
466 Div50,
467 Div51,
468 Div52,
469 Div53,
470 Div54,
471 Div55,
472 Div56,
473 Div57,
474 Div58,
475 Div59,
476 Div60,
477 Div61,
478 Div62,
479 Div63,
480}
481
482impl MasterClockDivider {
483 #[cfg(any(sai_v1, sai_v2))]
484 const fn mckdiv(&self) -> u8 {
485 match self {
486 MasterClockDivider::MasterClockDisabled => 0,
487 MasterClockDivider::Div1 => 0,
488 MasterClockDivider::Div2 => 1,
489 MasterClockDivider::Div4 => 2,
490 MasterClockDivider::Div6 => 3,
491 MasterClockDivider::Div8 => 4,
492 MasterClockDivider::Div10 => 5,
493 MasterClockDivider::Div12 => 6,
494 MasterClockDivider::Div14 => 7,
495 MasterClockDivider::Div16 => 8,
496 MasterClockDivider::Div18 => 9,
497 MasterClockDivider::Div20 => 10,
498 MasterClockDivider::Div22 => 11,
499 MasterClockDivider::Div24 => 12,
500 MasterClockDivider::Div26 => 13,
501 MasterClockDivider::Div28 => 14,
502 MasterClockDivider::Div30 => 15,
503 }
504 }
505
506 #[cfg(any(sai_v1_4pdm, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))]
507 const fn mckdiv(&self) -> u8 {
508 match self {
509 MasterClockDivider::MasterClockDisabled => 0,
510 MasterClockDivider::Div1 => 1,
511 MasterClockDivider::Div2 => 2,
512 MasterClockDivider::Div3 => 3,
513 MasterClockDivider::Div4 => 4,
514 MasterClockDivider::Div5 => 5,
515 MasterClockDivider::Div6 => 6,
516 MasterClockDivider::Div7 => 7,
517 MasterClockDivider::Div8 => 8,
518 MasterClockDivider::Div9 => 9,
519 MasterClockDivider::Div10 => 10,
520 MasterClockDivider::Div11 => 11,
521 MasterClockDivider::Div12 => 12,
522 MasterClockDivider::Div13 => 13,
523 MasterClockDivider::Div14 => 14,
524 MasterClockDivider::Div15 => 15,
525 MasterClockDivider::Div16 => 16,
526 MasterClockDivider::Div17 => 17,
527 MasterClockDivider::Div18 => 18,
528 MasterClockDivider::Div19 => 19,
529 MasterClockDivider::Div20 => 20,
530 MasterClockDivider::Div21 => 21,
531 MasterClockDivider::Div22 => 22,
532 MasterClockDivider::Div23 => 23,
533 MasterClockDivider::Div24 => 24,
534 MasterClockDivider::Div25 => 25,
535 MasterClockDivider::Div26 => 26,
536 MasterClockDivider::Div27 => 27,
537 MasterClockDivider::Div28 => 28,
538 MasterClockDivider::Div29 => 29,
539 MasterClockDivider::Div30 => 30,
540 MasterClockDivider::Div31 => 31,
541 MasterClockDivider::Div32 => 32,
542 MasterClockDivider::Div33 => 33,
543 MasterClockDivider::Div34 => 34,
544 MasterClockDivider::Div35 => 35,
545 MasterClockDivider::Div36 => 36,
546 MasterClockDivider::Div37 => 37,
547 MasterClockDivider::Div38 => 38,
548 MasterClockDivider::Div39 => 39,
549 MasterClockDivider::Div40 => 40,
550 MasterClockDivider::Div41 => 41,
551 MasterClockDivider::Div42 => 42,
552 MasterClockDivider::Div43 => 43,
553 MasterClockDivider::Div44 => 44,
554 MasterClockDivider::Div45 => 45,
555 MasterClockDivider::Div46 => 46,
556 MasterClockDivider::Div47 => 47,
557 MasterClockDivider::Div48 => 48,
558 MasterClockDivider::Div49 => 49,
559 MasterClockDivider::Div50 => 50,
560 MasterClockDivider::Div51 => 51,
561 MasterClockDivider::Div52 => 52,
562 MasterClockDivider::Div53 => 53,
563 MasterClockDivider::Div54 => 54,
564 MasterClockDivider::Div55 => 55,
565 MasterClockDivider::Div56 => 56,
566 MasterClockDivider::Div57 => 57,
567 MasterClockDivider::Div58 => 58,
568 MasterClockDivider::Div59 => 59,
569 MasterClockDivider::Div60 => 60,
570 MasterClockDivider::Div61 => 61,
571 MasterClockDivider::Div62 => 62,
572 MasterClockDivider::Div63 => 63,
573 }
574 }
575}
576
577/// [`SAI`] configuration. 373/// [`SAI`] configuration.
578#[allow(missing_docs)] 374#[allow(missing_docs)]
579#[non_exhaustive] 375#[non_exhaustive]
@@ -598,8 +394,7 @@ pub struct Config {
598 pub frame_length: u8, 394 pub frame_length: u8,
599 pub clock_strobe: ClockStrobe, 395 pub clock_strobe: ClockStrobe,
600 pub output_drive: OutputDrive, 396 pub output_drive: OutputDrive,
601 pub master_clock_divider: MasterClockDivider, 397 pub master_clock_divider: Option<MasterClockDivider>,
602 pub nodiv: bool,
603 pub is_high_impedance_on_inactive_slot: bool, 398 pub is_high_impedance_on_inactive_slot: bool,
604 pub fifo_threshold: FifoThreshold, 399 pub fifo_threshold: FifoThreshold,
605 pub companding: Companding, 400 pub companding: Companding,
@@ -628,8 +423,7 @@ impl Default for Config {
628 frame_sync_active_level_length: word::U7(16), 423 frame_sync_active_level_length: word::U7(16),
629 frame_sync_definition: FrameSyncDefinition::ChannelIdentification, 424 frame_sync_definition: FrameSyncDefinition::ChannelIdentification,
630 frame_length: 32, 425 frame_length: 32,
631 master_clock_divider: MasterClockDivider::MasterClockDisabled, 426 master_clock_divider: None,
632 nodiv: false,
633 clock_strobe: ClockStrobe::Rising, 427 clock_strobe: ClockStrobe::Rising,
634 output_drive: OutputDrive::Immediately, 428 output_drive: OutputDrive::Immediately,
635 is_high_impedance_on_inactive_slot: false, 429 is_high_impedance_on_inactive_slot: false,
@@ -706,7 +500,7 @@ fn update_synchronous_config(config: &mut Config) {
706 config.sync_input = SyncInput::Internal; 500 config.sync_input = SyncInput::Internal;
707 } 501 }
708 502
709 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 503 #[cfg(any(sai_v3, sai_v4))]
710 { 504 {
711 //this must either be Internal or External 505 //this must either be Internal or External
712 //The asynchronous sub-block on the same SAI needs to enable sync_output 506 //The asynchronous sub-block on the same SAI needs to enable sync_output
@@ -761,15 +555,11 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
761 mclk: Peri<'d, impl MclkPin<T, S>>, 555 mclk: Peri<'d, impl MclkPin<T, S>>,
762 dma: Peri<'d, impl Channel + Dma<T, S>>, 556 dma: Peri<'d, impl Channel + Dma<T, S>>,
763 dma_buf: &'d mut [W], 557 dma_buf: &'d mut [W],
764 mut config: Config, 558 config: Config,
765 ) -> Self { 559 ) -> Self {
766 let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); 560 let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
767 mclk.set_as_af(mclk.af_num(), ck_af_type); 561 mclk.set_as_af(mclk.af_num(), ck_af_type);
768 562
769 if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
770 config.master_clock_divider = MasterClockDivider::Div1;
771 }
772
773 Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config) 563 Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config)
774 } 564 }
775 565
@@ -851,14 +641,11 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
851 ) -> Self { 641 ) -> Self {
852 let ch = T::REGS.ch(sub_block as usize); 642 let ch = T::REGS.ch(sub_block as usize);
853 643
854 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 644 ch.cr1().modify(|w| w.set_saien(false));
855 {
856 ch.cr1().modify(|w| w.set_saien(false));
857 }
858 645
859 ch.cr2().modify(|w| w.set_fflush(true)); 646 ch.cr2().modify(|w| w.set_fflush(true));
860 647
861 #[cfg(any(sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 648 #[cfg(any(sai_v3, sai_v4))]
862 { 649 {
863 if let SyncInput::External(i) = config.sync_input { 650 if let SyncInput::External(i) = config.sync_input {
864 T::REGS.gcr().modify(|w| { 651 T::REGS.gcr().modify(|w| {
@@ -877,55 +664,52 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
877 } 664 }
878 } 665 }
879 666
880 #[cfg(any(sai_v1, sai_v1_4pdm, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] 667 ch.cr1().modify(|w| {
881 { 668 w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) {
882 ch.cr1().modify(|w| { 669 TxRx::Transmitter
883 w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { 670 } else {
884 TxRx::Transmitter 671 TxRx::Receiver
885 } else { 672 }));
886 TxRx::Receiver 673 w.set_prtcfg(config.protocol.prtcfg());
887 })); 674 w.set_ds(config.data_size.ds());
888 w.set_prtcfg(config.protocol.prtcfg()); 675 w.set_lsbfirst(config.bit_order.lsbfirst());
889 w.set_ds(config.data_size.ds()); 676 w.set_ckstr(config.clock_strobe.ckstr());
890 w.set_lsbfirst(config.bit_order.lsbfirst()); 677 w.set_syncen(config.sync_input.syncen());
891 w.set_ckstr(config.clock_strobe.ckstr()); 678 w.set_mono(config.stereo_mono.mono());
892 w.set_syncen(config.sync_input.syncen()); 679 w.set_outdriv(config.output_drive.outdriv());
893 w.set_mono(config.stereo_mono.mono()); 680 w.set_mckdiv(config.master_clock_divider.unwrap_or(MasterClockDivider::DIV1));
894 w.set_outdriv(config.output_drive.outdriv()); 681 w.set_nodiv(config.master_clock_divider.is_none());
895 w.set_mckdiv(config.master_clock_divider.mckdiv().into()); 682 w.set_dmaen(true);
896 w.set_nodiv(config.nodiv); 683 });
897 w.set_dmaen(true); 684
898 }); 685 ch.cr2().modify(|w| {
899 686 w.set_fth(config.fifo_threshold.fth());
900 ch.cr2().modify(|w| { 687 w.set_comp(config.companding.comp());
901 w.set_fth(config.fifo_threshold.fth()); 688 w.set_cpl(config.complement_format.cpl());
902 w.set_comp(config.companding.comp()); 689 w.set_muteval(config.mute_value.muteval());
903 w.set_cpl(config.complement_format.cpl()); 690 w.set_mutecnt(config.mute_detection_counter.0 as u8);
904 w.set_muteval(config.mute_value.muteval()); 691 w.set_tris(config.is_high_impedance_on_inactive_slot);
905 w.set_mutecnt(config.mute_detection_counter.0 as u8); 692 });
906 w.set_tris(config.is_high_impedance_on_inactive_slot); 693
907 }); 694 ch.frcr().modify(|w| {
908 695 w.set_fsoff(config.frame_sync_offset.fsoff());
909 ch.frcr().modify(|w| { 696 w.set_fspol(config.frame_sync_polarity.fspol());
910 w.set_fsoff(config.frame_sync_offset.fsoff()); 697 w.set_fsdef(config.frame_sync_definition.fsdef());
911 w.set_fspol(config.frame_sync_polarity.fspol()); 698 w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1);
912 w.set_fsdef(config.frame_sync_definition.fsdef()); 699 w.set_frl(config.frame_length - 1);
913 w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); 700 });
914 w.set_frl(config.frame_length - 1); 701
915 }); 702 ch.slotr().modify(|w| {
916 703 w.set_nbslot(config.slot_count.0 as u8 - 1);
917 ch.slotr().modify(|w| { 704 w.set_slotsz(config.slot_size.slotsz());
918 w.set_nbslot(config.slot_count.0 as u8 - 1); 705 w.set_fboff(config.first_bit_offset.0 as u8);
919 w.set_slotsz(config.slot_size.slotsz()); 706 w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16));
920 w.set_fboff(config.first_bit_offset.0 as u8); 707 });
921 w.set_sloten(vals::Sloten::from_bits(config.slot_enable as u16)); 708
922 }); 709 ch.cr1().modify(|w| w.set_saien(true));
923 710
924 ch.cr1().modify(|w| w.set_saien(true)); 711 if ch.cr1().read().saien() == false {
925 712 panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)");
926 if ch.cr1().read().saien() == false {
927 panic!("SAI failed to enable. Check that config is valid (frame length, slot count, etc)");
928 }
929 } 713 }
930 714
931 Self { 715 Self {
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 4c5308eba..a49ebcbee 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -174,7 +174,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
174 self.info.rcc.enable_and_reset(); 174 self.info.rcc.enable_and_reset();
175 175
176 let regs = self.info.regs; 176 let regs = self.info.regs;
177 #[cfg(any(spi_v1, spi_f1))] 177 #[cfg(any(spi_v1, spi_v2))]
178 { 178 {
179 regs.cr2().modify(|w| { 179 regs.cr2().modify(|w| {
180 w.set_ssoe(false); 180 w.set_ssoe(false);
@@ -198,7 +198,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
198 w.set_dff(<u8 as SealedWord>::CONFIG) 198 w.set_dff(<u8 as SealedWord>::CONFIG)
199 }); 199 });
200 } 200 }
201 #[cfg(spi_v2)] 201 #[cfg(spi_v3)]
202 { 202 {
203 regs.cr2().modify(|w| { 203 regs.cr2().modify(|w| {
204 let (ds, frxth) = <u8 as SealedWord>::CONFIG; 204 let (ds, frxth) = <u8 as SealedWord>::CONFIG;
@@ -220,7 +220,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
220 w.set_spe(true); 220 w.set_spe(true);
221 }); 221 });
222 } 222 }
223 #[cfg(any(spi_v3, spi_v4, spi_v5))] 223 #[cfg(any(spi_v4, spi_v5, spi_v6))]
224 { 224 {
225 regs.ifcr().write(|w| w.0 = 0xffff_ffff); 225 regs.ifcr().write(|w| w.0 = 0xffff_ffff);
226 regs.cfg2().modify(|w| { 226 regs.cfg2().modify(|w| {
@@ -274,7 +274,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
274 } 274 }
275 } 275 }
276 276
277 #[cfg(any(spi_v1, spi_f1, spi_v2))] 277 #[cfg(any(spi_v1, spi_v2, spi_v3))]
278 self.info.regs.cr1().modify(|w| { 278 self.info.regs.cr1().modify(|w| {
279 w.set_cpha(cpha); 279 w.set_cpha(cpha);
280 w.set_cpol(cpol); 280 w.set_cpol(cpol);
@@ -282,7 +282,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
282 w.set_lsbfirst(lsbfirst); 282 w.set_lsbfirst(lsbfirst);
283 }); 283 });
284 284
285 #[cfg(any(spi_v3, spi_v4, spi_v5))] 285 #[cfg(any(spi_v4, spi_v5, spi_v6))]
286 { 286 {
287 self.info.regs.cr1().modify(|w| { 287 self.info.regs.cr1().modify(|w| {
288 w.set_spe(false); 288 w.set_spe(false);
@@ -306,11 +306,11 @@ impl<'d, M: PeriMode> Spi<'d, M> {
306 306
307 /// Get current SPI configuration. 307 /// Get current SPI configuration.
308 pub fn get_current_config(&self) -> Config { 308 pub fn get_current_config(&self) -> Config {
309 #[cfg(any(spi_v1, spi_f1, spi_v2))] 309 #[cfg(any(spi_v1, spi_v2, spi_v3))]
310 let cfg = self.info.regs.cr1().read(); 310 let cfg = self.info.regs.cr1().read();
311 #[cfg(any(spi_v3, spi_v4, spi_v5))] 311 #[cfg(any(spi_v4, spi_v5, spi_v6))]
312 let cfg = self.info.regs.cfg2().read(); 312 let cfg = self.info.regs.cfg2().read();
313 #[cfg(any(spi_v3, spi_v4, spi_v5))] 313 #[cfg(any(spi_v4, spi_v5, spi_v6))]
314 let cfg1 = self.info.regs.cfg1().read(); 314 let cfg1 = self.info.regs.cfg1().read();
315 315
316 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { 316 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW {
@@ -335,9 +335,9 @@ impl<'d, M: PeriMode> Spi<'d, M> {
335 Some(pin) => pin.pull(), 335 Some(pin) => pin.pull(),
336 }; 336 };
337 337
338 #[cfg(any(spi_v1, spi_f1, spi_v2))] 338 #[cfg(any(spi_v1, spi_v2, spi_v3))]
339 let br = cfg.br(); 339 let br = cfg.br();
340 #[cfg(any(spi_v3, spi_v4, spi_v5))] 340 #[cfg(any(spi_v4, spi_v5, spi_v6))]
341 let br = cfg1.mbr(); 341 let br = cfg1.mbr();
342 342
343 let frequency = compute_frequency(self.kernel_clock, br); 343 let frequency = compute_frequency(self.kernel_clock, br);
@@ -360,16 +360,16 @@ impl<'d, M: PeriMode> Spi<'d, M> {
360 w.set_spe(false); 360 w.set_spe(false);
361 }); 361 });
362 362
363 #[cfg(any(spi_v1, spi_f1))] 363 #[cfg(any(spi_v1, spi_v2))]
364 self.info.regs.cr1().modify(|reg| { 364 self.info.regs.cr1().modify(|reg| {
365 reg.set_dff(word_size); 365 reg.set_dff(word_size);
366 }); 366 });
367 #[cfg(spi_v2)] 367 #[cfg(spi_v3)]
368 self.info.regs.cr2().modify(|w| { 368 self.info.regs.cr2().modify(|w| {
369 w.set_frxth(word_size.1); 369 w.set_frxth(word_size.1);
370 w.set_ds(word_size.0); 370 w.set_ds(word_size.0);
371 }); 371 });
372 #[cfg(any(spi_v3, spi_v4, spi_v5))] 372 #[cfg(any(spi_v4, spi_v5, spi_v6))]
373 self.info.regs.cfg1().modify(|w| { 373 self.info.regs.cfg1().modify(|w| {
374 w.set_dsize(word_size); 374 w.set_dsize(word_size);
375 }); 375 });
@@ -380,7 +380,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
380 /// Blocking write. 380 /// Blocking write.
381 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { 381 pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
382 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 382 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
383 #[cfg(any(spi_v3, spi_v4, spi_v5))] 383 #[cfg(any(spi_v4, spi_v5, spi_v6))]
384 self.info.regs.cr1().modify(|w| w.set_spe(false)); 384 self.info.regs.cr1().modify(|w| w.set_spe(false));
385 self.set_word_size(W::CONFIG); 385 self.set_word_size(W::CONFIG);
386 self.info.regs.cr1().modify(|w| w.set_spe(true)); 386 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -391,7 +391,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
391 // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`. 391 // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`.
392 // See https://github.com/embassy-rs/embassy/issues/2902 392 // See https://github.com/embassy-rs/embassy/issues/2902
393 // This is not documented as an errata by ST, and I've been unable to find anything online... 393 // This is not documented as an errata by ST, and I've been unable to find anything online...
394 #[cfg(not(any(spi_v1, spi_f1)))] 394 #[cfg(not(any(spi_v1, spi_v2)))]
395 write_word(self.info.regs, *word)?; 395 write_word(self.info.regs, *word)?;
396 396
397 // if we're doing tx only, after writing the last byte to FIFO we have to wait 397 // if we're doing tx only, after writing the last byte to FIFO we have to wait
@@ -401,14 +401,14 @@ impl<'d, M: PeriMode> Spi<'d, M> {
401 // Luckily this doesn't affect SPIv2+. 401 // Luckily this doesn't affect SPIv2+.
402 // See http://efton.sk/STM32/gotcha/g68.html 402 // See http://efton.sk/STM32/gotcha/g68.html
403 // ST doesn't seem to document this in errata sheets (?) 403 // ST doesn't seem to document this in errata sheets (?)
404 #[cfg(any(spi_v1, spi_f1))] 404 #[cfg(any(spi_v1, spi_v2))]
405 transfer_word(self.info.regs, *word)?; 405 transfer_word(self.info.regs, *word)?;
406 } 406 }
407 407
408 // wait until last word is transmitted. (except on v1, see above) 408 // wait until last word is transmitted. (except on v1, see above)
409 #[cfg(not(any(spi_v1, spi_f1, spi_v2)))] 409 #[cfg(not(any(spi_v1, spi_v2, spi_v3)))]
410 while !self.info.regs.sr().read().txc() {} 410 while !self.info.regs.sr().read().txc() {}
411 #[cfg(spi_v2)] 411 #[cfg(spi_v3)]
412 while self.info.regs.sr().read().bsy() {} 412 while self.info.regs.sr().read().bsy() {}
413 413
414 Ok(()) 414 Ok(())
@@ -417,7 +417,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
417 /// Blocking read. 417 /// Blocking read.
418 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 418 pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
419 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 419 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
420 #[cfg(any(spi_v3, spi_v4, spi_v5))] 420 #[cfg(any(spi_v4, spi_v5, spi_v6))]
421 self.info.regs.cr1().modify(|w| w.set_spe(false)); 421 self.info.regs.cr1().modify(|w| w.set_spe(false));
422 self.set_word_size(W::CONFIG); 422 self.set_word_size(W::CONFIG);
423 self.info.regs.cr1().modify(|w| w.set_spe(true)); 423 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -433,7 +433,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
433 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. 433 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
434 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { 434 pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
435 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 435 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
436 #[cfg(any(spi_v3, spi_v4, spi_v5))] 436 #[cfg(any(spi_v4, spi_v5, spi_v6))]
437 self.info.regs.cr1().modify(|w| w.set_spe(false)); 437 self.info.regs.cr1().modify(|w| w.set_spe(false));
438 self.set_word_size(W::CONFIG); 438 self.set_word_size(W::CONFIG);
439 self.info.regs.cr1().modify(|w| w.set_spe(true)); 439 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -452,7 +452,7 @@ impl<'d, M: PeriMode> Spi<'d, M> {
452 /// If `write` is shorter it is padded with zero bytes. 452 /// If `write` is shorter it is padded with zero bytes.
453 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { 453 pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
454 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? 454 // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...?
455 #[cfg(any(spi_v3, spi_v4, spi_v5))] 455 #[cfg(any(spi_v4, spi_v5, spi_v6))]
456 self.info.regs.cr1().modify(|w| w.set_spe(false)); 456 self.info.regs.cr1().modify(|w| w.set_spe(false));
457 self.set_word_size(W::CONFIG); 457 self.set_word_size(W::CONFIG);
458 self.info.regs.cr1().modify(|w| w.set_spe(true)); 458 self.info.regs.cr1().modify(|w| w.set_spe(true));
@@ -572,7 +572,7 @@ impl<'d> Spi<'d, Async> {
572 peri: Peri<'d, T>, 572 peri: Peri<'d, T>,
573 sck: Peri<'d, impl SckPin<T>>, 573 sck: Peri<'d, impl SckPin<T>>,
574 miso: Peri<'d, impl MisoPin<T>>, 574 miso: Peri<'d, impl MisoPin<T>>,
575 #[cfg(any(spi_v1, spi_f1, spi_v2))] tx_dma: Peri<'d, impl TxDma<T>>, 575 #[cfg(any(spi_v1, spi_v2, spi_v3))] tx_dma: Peri<'d, impl TxDma<T>>,
576 rx_dma: Peri<'d, impl RxDma<T>>, 576 rx_dma: Peri<'d, impl RxDma<T>>,
577 config: Config, 577 config: Config,
578 ) -> Self { 578 ) -> Self {
@@ -581,9 +581,9 @@ impl<'d> Spi<'d, Async> {
581 new_pin!(sck, config.sck_af()), 581 new_pin!(sck, config.sck_af()),
582 None, 582 None,
583 new_pin!(miso, AfType::input(config.miso_pull)), 583 new_pin!(miso, AfType::input(config.miso_pull)),
584 #[cfg(any(spi_v1, spi_f1, spi_v2))] 584 #[cfg(any(spi_v1, spi_v2, spi_v3))]
585 new_dma!(tx_dma), 585 new_dma!(tx_dma),
586 #[cfg(any(spi_v3, spi_v4, spi_v5))] 586 #[cfg(any(spi_v4, spi_v5, spi_v6))]
587 None, 587 None,
588 new_dma!(rx_dma), 588 new_dma!(rx_dma),
589 config, 589 config,
@@ -677,7 +677,7 @@ impl<'d> Spi<'d, Async> {
677 self.info.regs.cr1().modify(|w| { 677 self.info.regs.cr1().modify(|w| {
678 w.set_spe(true); 678 w.set_spe(true);
679 }); 679 });
680 #[cfg(any(spi_v3, spi_v4, spi_v5))] 680 #[cfg(any(spi_v4, spi_v5, spi_v6))]
681 self.info.regs.cr1().modify(|w| { 681 self.info.regs.cr1().modify(|w| {
682 w.set_cstart(true); 682 w.set_cstart(true);
683 }); 683 });
@@ -690,7 +690,7 @@ impl<'d> Spi<'d, Async> {
690 } 690 }
691 691
692 /// SPI read, using DMA. 692 /// SPI read, using DMA.
693 #[cfg(any(spi_v3, spi_v4, spi_v5))] 693 #[cfg(any(spi_v4, spi_v5, spi_v6))]
694 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 694 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
695 if data.is_empty() { 695 if data.is_empty() {
696 return Ok(()); 696 return Ok(());
@@ -710,7 +710,7 @@ impl<'d> Spi<'d, Async> {
710 prev 710 prev
711 }); 711 });
712 712
713 #[cfg(spi_v3)] 713 #[cfg(spi_v4)]
714 let i2scfg = regs.i2scfgr().modify(|w| { 714 let i2scfg = regs.i2scfgr().modify(|w| {
715 w.i2smod().then(|| { 715 w.i2smod().then(|| {
716 let prev = w.i2scfg(); 716 let prev = w.i2scfg();
@@ -766,7 +766,7 @@ impl<'d> Spi<'d, Async> {
766 w.set_tsize(0); 766 w.set_tsize(0);
767 }); 767 });
768 768
769 #[cfg(spi_v3)] 769 #[cfg(spi_v4)]
770 if let Some(i2scfg) = i2scfg { 770 if let Some(i2scfg) = i2scfg {
771 regs.i2scfgr().modify(|w| { 771 regs.i2scfgr().modify(|w| {
772 w.set_i2scfg(i2scfg); 772 w.set_i2scfg(i2scfg);
@@ -777,7 +777,7 @@ impl<'d> Spi<'d, Async> {
777 } 777 }
778 778
779 /// SPI read, using DMA. 779 /// SPI read, using DMA.
780 #[cfg(any(spi_v1, spi_f1, spi_v2))] 780 #[cfg(any(spi_v1, spi_v2, spi_v3))]
781 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 781 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
782 if data.is_empty() { 782 if data.is_empty() {
783 return Ok(()); 783 return Ok(());
@@ -790,7 +790,7 @@ impl<'d> Spi<'d, Async> {
790 self.set_word_size(W::CONFIG); 790 self.set_word_size(W::CONFIG);
791 791
792 // SPIv3 clears rxfifo on SPE=0 792 // SPIv3 clears rxfifo on SPE=0
793 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 793 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
794 flush_rx_fifo(self.info.regs); 794 flush_rx_fifo(self.info.regs);
795 795
796 set_rxdmaen(self.info.regs, true); 796 set_rxdmaen(self.info.regs, true);
@@ -813,7 +813,7 @@ impl<'d> Spi<'d, Async> {
813 self.info.regs.cr1().modify(|w| { 813 self.info.regs.cr1().modify(|w| {
814 w.set_spe(true); 814 w.set_spe(true);
815 }); 815 });
816 #[cfg(any(spi_v3, spi_v4, spi_v5))] 816 #[cfg(any(spi_v4, spi_v5, spi_v6))]
817 self.info.regs.cr1().modify(|w| { 817 self.info.regs.cr1().modify(|w| {
818 w.set_cstart(true); 818 w.set_cstart(true);
819 }); 819 });
@@ -838,7 +838,7 @@ impl<'d> Spi<'d, Async> {
838 self.set_word_size(W::CONFIG); 838 self.set_word_size(W::CONFIG);
839 839
840 // SPIv3 clears rxfifo on SPE=0 840 // SPIv3 clears rxfifo on SPE=0
841 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 841 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
842 flush_rx_fifo(self.info.regs); 842 flush_rx_fifo(self.info.regs);
843 843
844 set_rxdmaen(self.info.regs, true); 844 set_rxdmaen(self.info.regs, true);
@@ -858,7 +858,7 @@ impl<'d> Spi<'d, Async> {
858 self.info.regs.cr1().modify(|w| { 858 self.info.regs.cr1().modify(|w| {
859 w.set_spe(true); 859 w.set_spe(true);
860 }); 860 });
861 #[cfg(any(spi_v3, spi_v4, spi_v5))] 861 #[cfg(any(spi_v4, spi_v5, spi_v6))]
862 self.info.regs.cr1().modify(|w| { 862 self.info.regs.cr1().modify(|w| {
863 w.set_cstart(true); 863 w.set_cstart(true);
864 }); 864 });
@@ -898,9 +898,9 @@ impl<'d, M: PeriMode> Drop for Spi<'d, M> {
898 } 898 }
899} 899}
900 900
901#[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 901#[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
902use vals::Br; 902use vals::Br;
903#[cfg(any(spi_v3, spi_v4, spi_v5))] 903#[cfg(any(spi_v4, spi_v5, spi_v6))]
904use vals::Mbr as Br; 904use vals::Mbr as Br;
905 905
906fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br { 906fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br {
@@ -941,21 +941,21 @@ pub(crate) trait RegsExt {
941 941
942impl RegsExt for Regs { 942impl RegsExt for Regs {
943 fn tx_ptr<W>(&self) -> *mut W { 943 fn tx_ptr<W>(&self) -> *mut W {
944 #[cfg(any(spi_v1, spi_f1))] 944 #[cfg(any(spi_v1, spi_v2))]
945 let dr = self.dr(); 945 let dr = self.dr();
946 #[cfg(spi_v2)] 946 #[cfg(spi_v3)]
947 let dr = self.dr16(); 947 let dr = self.dr16();
948 #[cfg(any(spi_v3, spi_v4, spi_v5))] 948 #[cfg(any(spi_v4, spi_v5, spi_v6))]
949 let dr = self.txdr32(); 949 let dr = self.txdr32();
950 dr.as_ptr() as *mut W 950 dr.as_ptr() as *mut W
951 } 951 }
952 952
953 fn rx_ptr<W>(&self) -> *mut W { 953 fn rx_ptr<W>(&self) -> *mut W {
954 #[cfg(any(spi_v1, spi_f1))] 954 #[cfg(any(spi_v1, spi_v2))]
955 let dr = self.dr(); 955 let dr = self.dr();
956 #[cfg(spi_v2)] 956 #[cfg(spi_v3)]
957 let dr = self.dr16(); 957 let dr = self.dr16();
958 #[cfg(any(spi_v3, spi_v4, spi_v5))] 958 #[cfg(any(spi_v4, spi_v5, spi_v6))]
959 let dr = self.rxdr32(); 959 let dr = self.rxdr32();
960 dr.as_ptr() as *mut W 960 dr.as_ptr() as *mut W
961 } 961 }
@@ -965,22 +965,22 @@ fn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> {
965 if sr.ovr() && ovr { 965 if sr.ovr() && ovr {
966 return Err(Error::Overrun); 966 return Err(Error::Overrun);
967 } 967 }
968 #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] 968 #[cfg(not(any(spi_v1, spi_v4, spi_v5, spi_v6)))]
969 if sr.fre() { 969 if sr.fre() {
970 return Err(Error::Framing); 970 return Err(Error::Framing);
971 } 971 }
972 #[cfg(any(spi_v3, spi_v4, spi_v5))] 972 #[cfg(any(spi_v4, spi_v5, spi_v6))]
973 if sr.tifre() { 973 if sr.tifre() {
974 return Err(Error::Framing); 974 return Err(Error::Framing);
975 } 975 }
976 if sr.modf() { 976 if sr.modf() {
977 return Err(Error::ModeFault); 977 return Err(Error::ModeFault);
978 } 978 }
979 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 979 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
980 if sr.crcerr() { 980 if sr.crcerr() {
981 return Err(Error::Crc); 981 return Err(Error::Crc);
982 } 982 }
983 #[cfg(any(spi_v3, spi_v4, spi_v5))] 983 #[cfg(any(spi_v4, spi_v5, spi_v6))]
984 if sr.crce() { 984 if sr.crce() {
985 return Err(Error::Crc); 985 return Err(Error::Crc);
986 } 986 }
@@ -994,11 +994,11 @@ fn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> {
994 994
995 check_error_flags(sr, ovr)?; 995 check_error_flags(sr, ovr)?;
996 996
997 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 997 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
998 if sr.txe() { 998 if sr.txe() {
999 return Ok(()); 999 return Ok(());
1000 } 1000 }
1001 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1001 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1002 if sr.txp() { 1002 if sr.txp() {
1003 return Ok(()); 1003 return Ok(());
1004 } 1004 }
@@ -1011,11 +1011,11 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
1011 1011
1012 check_error_flags(sr, true)?; 1012 check_error_flags(sr, true)?;
1013 1013
1014 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1014 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1015 if sr.rxne() { 1015 if sr.rxne() {
1016 return Ok(()); 1016 return Ok(());
1017 } 1017 }
1018 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1018 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1019 if sr.rxp() { 1019 if sr.rxp() {
1020 return Ok(()); 1020 return Ok(());
1021 } 1021 }
@@ -1023,46 +1023,46 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
1023} 1023}
1024 1024
1025pub(crate) fn flush_rx_fifo(regs: Regs) { 1025pub(crate) fn flush_rx_fifo(regs: Regs) {
1026 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1026 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1027 while regs.sr().read().rxne() { 1027 while regs.sr().read().rxne() {
1028 #[cfg(not(spi_v2))] 1028 #[cfg(not(spi_v3))]
1029 let _ = regs.dr().read(); 1029 let _ = regs.dr().read();
1030 #[cfg(spi_v2)] 1030 #[cfg(spi_v3)]
1031 let _ = regs.dr16().read(); 1031 let _ = regs.dr16().read();
1032 } 1032 }
1033 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1033 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1034 while regs.sr().read().rxp() { 1034 while regs.sr().read().rxp() {
1035 let _ = regs.rxdr32().read(); 1035 let _ = regs.rxdr32().read();
1036 } 1036 }
1037} 1037}
1038 1038
1039pub(crate) fn set_txdmaen(regs: Regs, val: bool) { 1039pub(crate) fn set_txdmaen(regs: Regs, val: bool) {
1040 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1040 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1041 regs.cr2().modify(|reg| { 1041 regs.cr2().modify(|reg| {
1042 reg.set_txdmaen(val); 1042 reg.set_txdmaen(val);
1043 }); 1043 });
1044 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1044 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1045 regs.cfg1().modify(|reg| { 1045 regs.cfg1().modify(|reg| {
1046 reg.set_txdmaen(val); 1046 reg.set_txdmaen(val);
1047 }); 1047 });
1048} 1048}
1049 1049
1050pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { 1050pub(crate) fn set_rxdmaen(regs: Regs, val: bool) {
1051 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1051 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1052 regs.cr2().modify(|reg| { 1052 regs.cr2().modify(|reg| {
1053 reg.set_rxdmaen(val); 1053 reg.set_rxdmaen(val);
1054 }); 1054 });
1055 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1055 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1056 regs.cfg1().modify(|reg| { 1056 regs.cfg1().modify(|reg| {
1057 reg.set_rxdmaen(val); 1057 reg.set_rxdmaen(val);
1058 }); 1058 });
1059} 1059}
1060 1060
1061fn finish_dma(regs: Regs) { 1061fn finish_dma(regs: Regs) {
1062 #[cfg(spi_v2)] 1062 #[cfg(spi_v3)]
1063 while regs.sr().read().ftlvl().to_bits() > 0 {} 1063 while regs.sr().read().ftlvl().to_bits() > 0 {}
1064 1064
1065 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1065 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1066 { 1066 {
1067 if regs.cr2().read().tsize() == 0 { 1067 if regs.cr2().read().tsize() == 0 {
1068 while !regs.sr().read().txc() {} 1068 while !regs.sr().read().txc() {}
@@ -1070,7 +1070,7 @@ fn finish_dma(regs: Regs) {
1070 while !regs.sr().read().eot() {} 1070 while !regs.sr().read().eot() {}
1071 } 1071 }
1072 } 1072 }
1073 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1073 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1074 while regs.sr().read().bsy() {} 1074 while regs.sr().read().bsy() {}
1075 1075
1076 // Disable the spi peripheral 1076 // Disable the spi peripheral
@@ -1080,12 +1080,12 @@ fn finish_dma(regs: Regs) {
1080 1080
1081 // The peripheral automatically disables the DMA stream on completion without error, 1081 // The peripheral automatically disables the DMA stream on completion without error,
1082 // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. 1082 // but it does not clear the RXDMAEN/TXDMAEN flag in CR2.
1083 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] 1083 #[cfg(not(any(spi_v4, spi_v5, spi_v6)))]
1084 regs.cr2().modify(|reg| { 1084 regs.cr2().modify(|reg| {
1085 reg.set_txdmaen(false); 1085 reg.set_txdmaen(false);
1086 reg.set_rxdmaen(false); 1086 reg.set_rxdmaen(false);
1087 }); 1087 });
1088 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1088 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1089 regs.cfg1().modify(|reg| { 1089 regs.cfg1().modify(|reg| {
1090 reg.set_txdmaen(false); 1090 reg.set_txdmaen(false);
1091 reg.set_rxdmaen(false); 1091 reg.set_rxdmaen(false);
@@ -1098,7 +1098,7 @@ fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
1098 unsafe { 1098 unsafe {
1099 ptr::write_volatile(regs.tx_ptr(), tx_word); 1099 ptr::write_volatile(regs.tx_ptr(), tx_word);
1100 1100
1101 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1101 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1102 regs.cr1().modify(|reg| reg.set_cstart(true)); 1102 regs.cr1().modify(|reg| reg.set_cstart(true));
1103 } 1103 }
1104 1104
@@ -1117,7 +1117,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> {
1117 unsafe { 1117 unsafe {
1118 ptr::write_volatile(regs.tx_ptr(), tx_word); 1118 ptr::write_volatile(regs.tx_ptr(), tx_word);
1119 1119
1120 #[cfg(any(spi_v3, spi_v4, spi_v5))] 1120 #[cfg(any(spi_v4, spi_v5, spi_v6))]
1121 regs.cr1().modify(|reg| reg.set_cstart(true)); 1121 regs.cr1().modify(|reg| reg.set_cstart(true));
1122 } 1122 }
1123 Ok(()) 1123 Ok(())
@@ -1225,7 +1225,7 @@ macro_rules! impl_word {
1225 }; 1225 };
1226} 1226}
1227 1227
1228#[cfg(any(spi_v1, spi_f1))] 1228#[cfg(any(spi_v1, spi_v2))]
1229mod word_impl { 1229mod word_impl {
1230 use super::*; 1230 use super::*;
1231 1231
@@ -1235,7 +1235,7 @@ mod word_impl {
1235 impl_word!(u16, vals::Dff::BITS16); 1235 impl_word!(u16, vals::Dff::BITS16);
1236} 1236}
1237 1237
1238#[cfg(spi_v2)] 1238#[cfg(spi_v3)]
1239mod word_impl { 1239mod word_impl {
1240 use super::*; 1240 use super::*;
1241 1241
@@ -1256,7 +1256,7 @@ mod word_impl {
1256 impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF)); 1256 impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF));
1257} 1257}
1258 1258
1259#[cfg(any(spi_v3, spi_v4, spi_v5))] 1259#[cfg(any(spi_v4, spi_v5, spi_v6))]
1260mod word_impl { 1260mod word_impl {
1261 use super::*; 1261 use super::*;
1262 1262
diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs
index 01937593a..847b70c85 100644
--- a/examples/stm32h7/src/bin/sai.rs
+++ b/examples/stm32h7/src/bin/sai.rs
@@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) {
63 tx_config.tx_rx = TxRx::Transmitter; 63 tx_config.tx_rx = TxRx::Transmitter;
64 tx_config.sync_output = true; 64 tx_config.sync_output = true;
65 tx_config.clock_strobe = ClockStrobe::Falling; 65 tx_config.clock_strobe = ClockStrobe::Falling;
66 tx_config.master_clock_divider = mclk_div; 66 tx_config.master_clock_divider = Some(mclk_div);
67 tx_config.stereo_mono = StereoMono::Stereo; 67 tx_config.stereo_mono = StereoMono::Stereo;
68 tx_config.data_size = DataSize::Data24; 68 tx_config.data_size = DataSize::Data24;
69 tx_config.bit_order = BitOrder::MsbFirst; 69 tx_config.bit_order = BitOrder::MsbFirst;
@@ -119,71 +119,7 @@ async fn main(_spawner: Spawner) {
119 } 119 }
120} 120}
121 121
122const fn mclk_div_from_u8(v: u8) -> MasterClockDivider { 122fn mclk_div_from_u8(v: u8) -> MasterClockDivider {
123 match v { 123 assert!((1..=63).contains(&v));
124 1 => MasterClockDivider::Div1, 124 MasterClockDivider::from_bits(v)
125 2 => MasterClockDivider::Div2,
126 3 => MasterClockDivider::Div3,
127 4 => MasterClockDivider::Div4,
128 5 => MasterClockDivider::Div5,
129 6 => MasterClockDivider::Div6,
130 7 => MasterClockDivider::Div7,
131 8 => MasterClockDivider::Div8,
132 9 => MasterClockDivider::Div9,
133 10 => MasterClockDivider::Div10,
134 11 => MasterClockDivider::Div11,
135 12 => MasterClockDivider::Div12,
136 13 => MasterClockDivider::Div13,
137 14 => MasterClockDivider::Div14,
138 15 => MasterClockDivider::Div15,
139 16 => MasterClockDivider::Div16,
140 17 => MasterClockDivider::Div17,
141 18 => MasterClockDivider::Div18,
142 19 => MasterClockDivider::Div19,
143 20 => MasterClockDivider::Div20,
144 21 => MasterClockDivider::Div21,
145 22 => MasterClockDivider::Div22,
146 23 => MasterClockDivider::Div23,
147 24 => MasterClockDivider::Div24,
148 25 => MasterClockDivider::Div25,
149 26 => MasterClockDivider::Div26,
150 27 => MasterClockDivider::Div27,
151 28 => MasterClockDivider::Div28,
152 29 => MasterClockDivider::Div29,
153 30 => MasterClockDivider::Div30,
154 31 => MasterClockDivider::Div31,
155 32 => MasterClockDivider::Div32,
156 33 => MasterClockDivider::Div33,
157 34 => MasterClockDivider::Div34,
158 35 => MasterClockDivider::Div35,
159 36 => MasterClockDivider::Div36,
160 37 => MasterClockDivider::Div37,
161 38 => MasterClockDivider::Div38,
162 39 => MasterClockDivider::Div39,
163 40 => MasterClockDivider::Div40,
164 41 => MasterClockDivider::Div41,
165 42 => MasterClockDivider::Div42,
166 43 => MasterClockDivider::Div43,
167 44 => MasterClockDivider::Div44,
168 45 => MasterClockDivider::Div45,
169 46 => MasterClockDivider::Div46,
170 47 => MasterClockDivider::Div47,
171 48 => MasterClockDivider::Div48,
172 49 => MasterClockDivider::Div49,
173 50 => MasterClockDivider::Div50,
174 51 => MasterClockDivider::Div51,
175 52 => MasterClockDivider::Div52,
176 53 => MasterClockDivider::Div53,
177 54 => MasterClockDivider::Div54,
178 55 => MasterClockDivider::Div55,
179 56 => MasterClockDivider::Div56,
180 57 => MasterClockDivider::Div57,
181 58 => MasterClockDivider::Div58,
182 59 => MasterClockDivider::Div59,
183 60 => MasterClockDivider::Div60,
184 61 => MasterClockDivider::Div61,
185 62 => MasterClockDivider::Div62,
186 63 => MasterClockDivider::Div63,
187 _ => panic!(),
188 }
189} 125}
diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs
index 6d29e8a4d..b75a03ae8 100644
--- a/examples/stm32h723/src/bin/spdifrx.rs
+++ b/examples/stm32h723/src/bin/spdifrx.rs
@@ -168,7 +168,7 @@ fn new_sai_transmitter<'d>(
168 sai_config.slot_enable = 0xFFFF; // All slots 168 sai_config.slot_enable = 0xFFFF; // All slots
169 sai_config.data_size = sai::DataSize::Data32; 169 sai_config.data_size = sai::DataSize::Data32;
170 sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; 170 sai_config.frame_length = (CHANNEL_COUNT * 32) as u8;
171 sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; 171 sai_config.master_clock_divider = None;
172 172
173 let (sub_block_tx, _) = hal::sai::split_subblocks(sai); 173 let (sub_block_tx, _) = hal::sai::split_subblocks(sai);
174 Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) 174 Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config)