embassy_stm32/spdifrx/
mod.rs

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(true); // 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);