aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorelagil <[email protected]>2024-11-18 20:51:22 +0100
committerelagil <[email protected]>2024-11-18 20:51:22 +0100
commit62dbdcd45adfa7f3b74b377f0b4ef7eafaef78fd (patch)
treec8417e738a1eb6eb46a7409ba5d8a15a69847a78
parent050d3d1a092eca69b14bd07cd5ca8496a0f09f2d (diff)
feat: add SPDIFRX driver
-rw-r--r--embassy-stm32/build.rs12
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/spdifrx/mod.rs336
3 files changed, 350 insertions, 0 deletions
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 71bfb3747..348d48b9b 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1220,6 +1220,17 @@ fn main() {
1220 impl_dac_pin!( #peri, #pin_name, #ch); 1220 impl_dac_pin!( #peri, #pin_name, #ch);
1221 }) 1221 })
1222 } 1222 }
1223
1224 if regs.kind == "spdifrx" {
1225 let peri = format_ident!("{}", p.name);
1226 let pin_name = format_ident!("{}", pin.pin);
1227 let af = pin.af.unwrap_or(0);
1228 let sel: u8 = pin.signal.strip_prefix("IN").unwrap().parse().unwrap();
1229
1230 g.extend(quote! {
1231 impl_spdifrx_pin!( #peri, #pin_name, #af, #sel);
1232 })
1233 }
1223 } 1234 }
1224 } 1235 }
1225 } 1236 }
@@ -1244,6 +1255,7 @@ fn main() {
1244 (("sai", "B"), quote!(crate::sai::Dma<B>)), 1255 (("sai", "B"), quote!(crate::sai::Dma<B>)),
1245 (("spi", "RX"), quote!(crate::spi::RxDma)), 1256 (("spi", "RX"), quote!(crate::spi::RxDma)),
1246 (("spi", "TX"), quote!(crate::spi::TxDma)), 1257 (("spi", "TX"), quote!(crate::spi::TxDma)),
1258 (("spdifrx", "RX"), quote!(crate::spdifrx::Dma)),
1247 (("i2c", "RX"), quote!(crate::i2c::RxDma)), 1259 (("i2c", "RX"), quote!(crate::i2c::RxDma)),
1248 (("i2c", "TX"), quote!(crate::i2c::TxDma)), 1260 (("i2c", "TX"), quote!(crate::i2c::TxDma)),
1249 (("dcmi", "DCMI"), quote!(crate::dcmi::FrameDma)), 1261 (("dcmi", "DCMI"), quote!(crate::dcmi::FrameDma)),
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 286a18da2..e189351d1 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -107,6 +107,8 @@ pub mod rtc;
107pub mod sai; 107pub mod sai;
108#[cfg(sdmmc)] 108#[cfg(sdmmc)]
109pub mod sdmmc; 109pub mod sdmmc;
110#[cfg(spdifrx)]
111pub mod spdifrx;
110#[cfg(spi)] 112#[cfg(spi)]
111pub mod spi; 113pub mod spi;
112#[cfg(tsc)] 114#[cfg(tsc)]
diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs
new file mode 100644
index 000000000..bfc8bff29
--- /dev/null
+++ b/embassy-stm32/src/spdifrx/mod.rs
@@ -0,0 +1,336 @@
1//! S/PDIF receiver
2#![macro_use]
3#![cfg_attr(gpdma, allow(unused))]
4
5use core::marker::PhantomData;
6
7use embassy_hal_internal::{into_ref, PeripheralRef};
8use embassy_sync::waitqueue::AtomicWaker;
9
10use crate::dma::ringbuffer::Error as RingbufferError;
11pub use crate::dma::word;
12#[cfg(not(gpdma))]
13use crate::dma::ReadableRingBuffer;
14use crate::dma::{Channel, TransferOptions};
15use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _};
16use crate::interrupt::typelevel::Interrupt;
17use crate::pac::spdifrx::Spdifrx as Regs;
18use crate::rcc::{RccInfo, SealedRccPeripheral};
19use crate::{interrupt, peripherals, Peripheral};
20
21/// Possible S/PDIF preamble types.
22#[allow(dead_code)]
23#[repr(u8)]
24enum PreambleType {
25 Unused = 0x00,
26 /// The preamble changes to preamble “B” once every 192 frames to identify the start of the block structure used to
27 /// organize the channel status and user information.
28 B = 0x01,
29 /// The first sub-frame (left or “A” channel in stereophonic operation and primary channel in monophonic operation)
30 /// normally starts with preamble “M”
31 M = 0x02,
32 /// The second sub-frame (right or “B” channel in stereophonic operation and secondary channel in monophonic
33 /// operation) always starts with preamble “W”.
34 W = 0x03,
35}
36
37macro_rules! new_spdifrx_pin {
38 ($name:ident, $af_type:expr) => {{
39 let pin = $name.into_ref();
40 let input_sel = pin.input_sel();
41 pin.set_as_af(pin.af_num(), $af_type);
42 (Some(pin.map_into()), input_sel)
43 }};
44}
45
46macro_rules! impl_spdifrx_pin {
47 ($inst:ident, $pin:ident, $af:expr, $sel:expr) => {
48 impl crate::spdifrx::InPin<peripherals::$inst> for crate::peripherals::$pin {
49 fn af_num(&self) -> u8 {
50 $af
51 }
52 fn input_sel(&self) -> u8 {
53 $sel
54 }
55 }
56 };
57}
58
59/// Ring-buffered SPDIFRX driver.
60///
61/// Data is read by DMAs and stored in a ring buffer.
62#[cfg(not(gpdma))]
63pub struct Spdifrx<'d, T: Instance> {
64 _peri: PeripheralRef<'d, T>,
65 spdifrx_in: Option<PeripheralRef<'d, AnyPin>>,
66 data_ring_buffer: ReadableRingBuffer<'d, u32>,
67}
68
69/// Gives the address of the data register.
70fn dr_address(r: Regs) -> *mut u32 {
71 #[cfg(spdifrx_v1)]
72 let address = r.dr().as_ptr() as _;
73 #[cfg(spdifrx_h7)]
74 let address = r.fmt0_dr().as_ptr() as _; // All fmtx_dr() implementations have the same address.
75
76 return address;
77}
78
79/// Gives the address of the channel status register.
80#[allow(unused)]
81fn csr_address(r: Regs) -> *mut u32 {
82 r.csr().as_ptr() as _
83}
84
85/// Select the channel for capturing control information.
86pub enum ControlChannelSelection {
87 /// Capture control info from channel A.
88 A,
89 /// Capture control info from channel B.
90 B,
91}
92
93/// Configuration options for the SPDIFRX driver.
94pub struct Config {
95 /// Select the channel for capturing control information.
96 pub control_channel_selection: ControlChannelSelection,
97}
98
99/// S/PDIF errors.
100#[derive(Debug)]
101pub enum Error {
102 /// DMA overrun error.
103 RingbufferError(RingbufferError),
104 /// Left/right channel synchronization error.
105 ChannelSyncError,
106}
107
108impl From<RingbufferError> for Error {
109 fn from(error: RingbufferError) -> Self {
110 Self::RingbufferError(error)
111 }
112}
113
114impl Default for Config {
115 fn default() -> Self {
116 Self {
117 control_channel_selection: ControlChannelSelection::A,
118 }
119 }
120}
121
122#[cfg(not(gpdma))]
123impl<'d, T: Instance> Spdifrx<'d, T> {
124 fn dma_opts() -> TransferOptions {
125 TransferOptions {
126 half_transfer_ir: true,
127 // new_write() and new_read() always use circular mode
128 ..Default::default()
129 }
130 }
131
132 /// Create a new `Spdifrx` instance.
133 pub fn new(
134 peri: impl Peripheral<P = T> + 'd,
135 _irq: impl interrupt::typelevel::Binding<T::GlobalInterrupt, GlobalInterruptHandler<T>> + 'd,
136 config: Config,
137 spdifrx_in: impl Peripheral<P = impl InPin<T>> + 'd,
138 data_dma: impl Peripheral<P = impl Channel + Dma<T>> + 'd,
139 data_dma_buf: &'d mut [u32],
140 ) -> Self {
141 let (spdifrx_in, input_sel) = new_spdifrx_pin!(spdifrx_in, AfType::input(Pull::None));
142 Self::setup(config, input_sel);
143
144 into_ref!(peri, data_dma);
145
146 let regs = T::info().regs;
147 let dr_request = data_dma.request();
148 let dr_ring_buffer =
149 unsafe { ReadableRingBuffer::new(data_dma, dr_request, dr_address(regs), data_dma_buf, Self::dma_opts()) };
150
151 Self {
152 _peri: peri,
153 spdifrx_in,
154 data_ring_buffer: dr_ring_buffer,
155 }
156 }
157
158 fn setup(config: Config, input_sel: u8) {
159 T::info().rcc.enable_and_reset();
160 T::GlobalInterrupt::unpend();
161 unsafe { T::GlobalInterrupt::enable() };
162
163 let regs = T::info().regs;
164
165 regs.imr().write(|imr| {
166 imr.set_ifeie(true); // Enables interrupts for TERR, SERR, FERR.
167 imr.set_syncdie(true); // Enables SYNCD interrupt.
168 });
169
170 regs.cr().write(|cr| {
171 cr.set_spdifen(0x00); // Disable SPDIF receiver synchronization.
172 cr.set_rxdmaen(true); // Use RX DMA for data. Enabled on `read`.
173 cr.set_cbdmaen(false); // Do not capture channel info.
174 cr.set_rxsteo(true); // Operate in stereo mode.
175 cr.set_drfmt(0x01); // Data is left-aligned (MSB).
176
177 // Disable all status fields in the data register.
178 // Status can be obtained directly with the status register DMA.
179 cr.set_pmsk(false); // Write parity bit to the data register. FIXME: Add parity check.
180 cr.set_vmsk(false); // Write validity to the data register.
181 cr.set_cumsk(true); // Do not write C and U bits to the data register.
182 cr.set_ptmsk(false); // Write preamble bits to the data register.
183
184 cr.set_chsel(match config.control_channel_selection {
185 ControlChannelSelection::A => false,
186 ControlChannelSelection::B => true,
187 }); // Select channel status source.
188
189 cr.set_nbtr(0x02); // 16 attempts are allowed.
190 cr.set_wfa(true); // Wait for activity before going to synchronization phase.
191 cr.set_insel(input_sel); // Input pin selection.
192
193 #[cfg(stm32h7)]
194 cr.set_cksen(true); // Generate a symbol clock.
195
196 #[cfg(stm32h7)]
197 cr.set_cksbkpen(false); // Do not generate a backup symbol clock.
198 });
199 }
200
201 /// Start the SPDIFRX driver.
202 pub fn start(&mut self) {
203 self.data_ring_buffer.start();
204
205 T::info().regs.cr().modify(|cr| {
206 cr.set_spdifen(0x03); // Enable S/PDIF receiver.
207 });
208 }
209
210 /// Read from the SPDIFRX data ring buffer.
211 ///
212 /// SPDIFRX is always receiving data in the background. This function pops already-received
213 /// data from the buffer.
214 ///
215 /// If there's less than `data.len()` data in the buffer, this waits until there is.
216 pub async fn read(&mut self, data: &mut [u32]) -> Result<(), Error> {
217 self.data_ring_buffer.read_exact(data).await?;
218
219 let first_preamble = (data[0] >> 4) & 0b11_u32;
220 if (first_preamble as u8) == (PreambleType::W as u8) {
221 trace!("S/PDIF left/right mismatch");
222
223 // Resynchronize until the first sample is for the left channel.
224 self.data_ring_buffer.clear();
225 return Err(Error::ChannelSyncError);
226 };
227
228 for sample in data.as_mut() {
229 if (*sample & (0x0002_u32)) == 0x0001 {
230 // Discard invalid samples, setting them to mute level.
231 *sample = 0;
232 } else {
233 // Discard status information in the lowest byte.
234 *sample &= 0xFFFFFF00;
235 }
236 }
237
238 Ok(())
239 }
240}
241
242#[cfg(not(gpdma))]
243impl<'d, T: Instance> Drop for Spdifrx<'d, T> {
244 fn drop(&mut self) {
245 T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00));
246 self.spdifrx_in.as_ref().map(|x| x.set_as_disconnected());
247 }
248}
249
250struct State {
251 #[allow(unused)]
252 waker: AtomicWaker,
253}
254
255impl State {
256 const fn new() -> Self {
257 Self {
258 waker: AtomicWaker::new(),
259 }
260 }
261}
262
263struct Info {
264 regs: crate::pac::spdifrx::Spdifrx,
265 rcc: RccInfo,
266}
267
268peri_trait!(
269 irqs: [GlobalInterrupt],
270);
271
272/// SPIDFRX pin trait
273pub trait InPin<T: Instance>: crate::gpio::Pin {
274 /// Get the GPIO AF number needed to use this pin.
275 fn af_num(&self) -> u8;
276 /// Get the SPIDFRX INSEL number needed to use this pin.
277 fn input_sel(&self) -> u8;
278}
279
280dma_trait!(Dma, Instance);
281
282/// Global interrupt handler.
283pub struct GlobalInterruptHandler<T: Instance> {
284 _phantom: PhantomData<T>,
285}
286
287impl<T: Instance> interrupt::typelevel::Handler<T::GlobalInterrupt> for GlobalInterruptHandler<T> {
288 unsafe fn on_interrupt() {
289 T::state().waker.wake();
290
291 let regs = T::info().regs;
292 let sr = regs.sr().read();
293
294 if sr.serr() || sr.terr() || sr.ferr() {
295 trace!("SPDIFRX error, resync");
296
297 // Clear errors by disabling SPDIFRX, then reenable.
298 regs.cr().modify(|cr| cr.set_spdifen(0x00));
299 regs.cr().modify(|cr| cr.set_spdifen(0x03));
300 } else if sr.syncd() {
301 // Synchronization was successful.
302 trace!("SPDIFRX sync success");
303 }
304
305 // Clear interrupt flags.
306 regs.ifcr().write(|ifcr| {
307 ifcr.set_perrcf(true); // Clears parity error flag.
308 ifcr.set_ovrcf(true); // Clears overrun error flag.
309 ifcr.set_sbdcf(true); // Clears synchronization block detected flag.
310 ifcr.set_syncdcf(true); // Clears SYNCD from SR (was read above).
311 });
312 }
313}
314
315foreach_peripheral!(
316 (spdifrx, $inst:ident) => {
317 #[allow(private_interfaces)]
318 impl SealedInstance for peripherals::$inst {
319 fn info() -> &'static Info {
320 static INFO: Info = Info{
321 regs: crate::pac::$inst,
322 rcc: crate::peripherals::$inst::RCC_INFO,
323 };
324 &INFO
325 }
326 fn state() -> &'static State {
327 static STATE: State = State::new();
328 &STATE
329 }
330 }
331
332 impl Instance for peripherals::$inst {
333 type GlobalInterrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;
334 }
335 };
336);