1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
//! This example receives inputs on SPDIFRX and outputs on SAI4.
//!
//! Only very few controllers connect the SPDIFRX symbol clock to a SAI peripheral's clock input.
//! However, this is necessary for synchronizing the symbol rates and avoiding glitches.
#![no_std]
#![no_main]
use defmt::{info, trace};
use embassy_executor::Spawner;
use embassy_futures::select::{Either, select};
use embassy_stm32::spdifrx::{self, Spdifrx};
use embassy_stm32::{Peri, bind_interrupts, peripherals, sai};
use grounded::uninit::GroundedArrayCell;
use hal::sai::*;
use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _};
bind_interrupts!(struct Irqs {
SPDIF_RX => spdifrx::GlobalInterruptHandler<peripherals::SPDIFRX1>;
});
const CHANNEL_COUNT: usize = 2;
const BLOCK_LENGTH: usize = 64;
const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * CHANNEL_COUNT;
const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks
// DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions
#[unsafe(link_section = ".sram1")]
static SPDIFRX_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
#[unsafe(link_section = ".sram4")]
static SAI_BUFFER: GroundedArrayCell<u32, DMA_BUFFER_LENGTH> = GroundedArrayCell::uninit();
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut peripheral_config = embassy_stm32::Config::default();
{
use embassy_stm32::rcc::*;
peripheral_config.rcc.hsi = Some(HSIPrescaler::DIV1);
peripheral_config.rcc.pll1 = Some(Pll {
source: PllSource::HSI,
prediv: PllPreDiv::DIV16,
mul: PllMul::MUL200,
divp: Some(PllDiv::DIV2), // 400 MHz
divq: Some(PllDiv::DIV2),
divr: Some(PllDiv::DIV2),
});
peripheral_config.rcc.sys = Sysclk::PLL1_P;
peripheral_config.rcc.ahb_pre = AHBPrescaler::DIV2;
peripheral_config.rcc.apb1_pre = APBPrescaler::DIV2;
peripheral_config.rcc.apb2_pre = APBPrescaler::DIV2;
peripheral_config.rcc.apb3_pre = APBPrescaler::DIV2;
peripheral_config.rcc.apb4_pre = APBPrescaler::DIV2;
peripheral_config.rcc.mux.spdifrxsel = mux::Spdifrxsel::PLL1_Q;
}
let mut p = embassy_stm32::init(peripheral_config);
info!("SPDIFRX to SAI4 bridge");
// Use SPDIFRX clock for SAI.
// This ensures equal rates of sample production and consumption.
let clk_source = embassy_stm32::pac::rcc::vals::Saiasel::_RESERVED_5;
embassy_stm32::pac::RCC.d3ccipr().modify(|w| {
w.set_sai4asel(clk_source);
});
let sai_buffer: &mut [u32] = unsafe {
SAI_BUFFER.initialize_all_copied(0);
let (ptr, len) = SAI_BUFFER.get_ptr_len();
core::slice::from_raw_parts_mut(ptr, len)
};
let spdifrx_buffer: &mut [u32] = unsafe {
SPDIFRX_BUFFER.initialize_all_copied(0);
let (ptr, len) = SPDIFRX_BUFFER.get_ptr_len();
core::slice::from_raw_parts_mut(ptr, len)
};
let mut sai_transmitter = new_sai_transmitter(
p.SAI4.reborrow(),
p.PD13.reborrow(),
p.PC1.reborrow(),
p.PD12.reborrow(),
p.BDMA_CH0.reborrow(),
sai_buffer,
);
let mut spdif_receiver = new_spdif_receiver(
p.SPDIFRX1.reborrow(),
p.PD7.reborrow(),
p.DMA2_CH7.reborrow(),
spdifrx_buffer,
);
spdif_receiver.start();
let mut renew_sai = false;
loop {
let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH];
if renew_sai {
renew_sai = false;
trace!("Renew SAI.");
drop(sai_transmitter);
sai_transmitter = new_sai_transmitter(
p.SAI4.reborrow(),
p.PD13.reborrow(),
p.PC1.reborrow(),
p.PD12.reborrow(),
p.BDMA_CH0.reborrow(),
sai_buffer,
);
}
match select(spdif_receiver.read(&mut buf), sai_transmitter.wait_write_error()).await {
Either::First(spdif_read_result) => match spdif_read_result {
Ok(_) => (),
Err(spdifrx::Error::RingbufferError(_)) => {
trace!("SPDIFRX ringbuffer error. Renew.");
drop(spdif_receiver);
spdif_receiver = new_spdif_receiver(
p.SPDIFRX1.reborrow(),
p.PD7.reborrow(),
p.DMA2_CH7.reborrow(),
spdifrx_buffer,
);
spdif_receiver.start();
continue;
}
Err(spdifrx::Error::ChannelSyncError) => {
trace!("SPDIFRX channel sync (left/right assignment) error.");
continue;
}
},
Either::Second(_) => {
renew_sai = true;
continue;
}
};
renew_sai = sai_transmitter.write(&buf).await.is_err();
}
}
/// Creates a new SPDIFRX instance for receiving sample data.
///
/// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect).
fn new_spdif_receiver<'d>(
spdifrx: Peri<'d, peripherals::SPDIFRX1>,
input_pin: Peri<'d, peripherals::PD7>,
dma: Peri<'d, peripherals::DMA2_CH7>,
buf: &'d mut [u32],
) -> Spdifrx<'d, peripherals::SPDIFRX1> {
Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf)
}
/// Creates a new SAI4 instance for transmitting sample data.
///
/// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun).
fn new_sai_transmitter<'d>(
sai: Peri<'d, peripherals::SAI4>,
sck: Peri<'d, peripherals::PD13>,
sd: Peri<'d, peripherals::PC1>,
fs: Peri<'d, peripherals::PD12>,
dma: Peri<'d, peripherals::BDMA_CH0>,
buf: &'d mut [u32],
) -> Sai<'d, peripherals::SAI4, u32> {
let mut sai_config = hal::sai::Config::default();
sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8);
sai_config.slot_enable = 0xFFFF; // All slots
sai_config.data_size = sai::DataSize::Data32;
sai_config.frame_length = (CHANNEL_COUNT * 32) as u16;
let (sub_block_tx, _) = hal::sai::split_subblocks(sai);
Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config)
}
|