|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+//! I2S interface with DMA data transfer.
|
|
|
2
|
+
|
|
|
3
|
+use cortex_m::interrupt;
|
|
|
4
|
+use stm32f4xx_hal::dma;
|
|
|
5
|
+use stm32f4xx_hal::dma::traits::Stream;
|
|
|
6
|
+use stm32f4xx_hal::gpio::gpiob::{PB12, PB13, PB14, PB15};
|
|
|
7
|
+use stm32f4xx_hal::gpio::gpioc::PC6;
|
|
|
8
|
+use stm32f4xx_hal::gpio::{Alternate, AF5, AF6};
|
|
|
9
|
+use stm32f4xx_hal::stm32::{DMA1, I2S2EXT, RCC, SPI2};
|
|
|
10
|
+
|
|
|
11
|
+use crate::audio_buffer::{AudioBuffer, ReadPacket, WritePacket, CHANNELS, SAMPLES_PER_PACKET};
|
|
|
12
|
+
|
|
|
13
|
+/// Clocks for the I2S peripheral.
|
|
|
14
|
+#[derive(Clone, Copy)]
|
|
|
15
|
+pub struct I2SClocks {
|
|
|
16
|
+ plli2sclk: u32,
|
|
|
17
|
+}
|
|
|
18
|
+
|
|
|
19
|
+impl I2SClocks {
|
|
|
20
|
+ /// Initializes the I2S clocks.
|
|
|
21
|
+ ///
|
|
|
22
|
+ /// This function must only be called once at startup. It assumes that the HSE is already
|
|
|
23
|
+ /// initialized and the HSE frequency is 8MHz.
|
|
|
24
|
+ pub unsafe fn init() -> I2SClocks {
|
|
|
25
|
+ // The following values result in a sample frequency of 47991.07Hz:
|
|
|
26
|
+ // - PLLI2SN = 43
|
|
|
27
|
+ // - PLLI2SR = 4
|
|
|
28
|
+ // - I2SDIV = 3
|
|
|
29
|
+ // - I2SODD = 1
|
|
|
30
|
+
|
|
|
31
|
+ let rcc = &*RCC::ptr();
|
|
|
32
|
+ // Configure PLL.
|
|
|
33
|
+ rcc.plli2scfgr
|
|
|
34
|
+ .modify(|_, w| w.plli2sn().bits(43).plli2sq().bits(4).plli2sr().bits(4));
|
|
|
35
|
+ // Enable PLL.
|
|
|
36
|
+ rcc.cr.modify(|_, w| w.plli2son().set_bit());
|
|
|
37
|
+ // Wait for PLL to stabilise
|
|
|
38
|
+ while rcc.cr.read().plli2srdy().bit_is_clear() {}
|
|
|
39
|
+
|
|
|
40
|
+ // Ensure that the PLL is selected as the I2S clock source.
|
|
|
41
|
+ rcc.cfgr.modify(|_, w| w.i2ssrc().clear_bit());
|
|
|
42
|
+
|
|
|
43
|
+ I2SClocks {
|
|
|
44
|
+ plli2sclk: 86000000,
|
|
|
45
|
+ }
|
|
|
46
|
+ }
|
|
|
47
|
+}
|
|
|
48
|
+
|
|
|
49
|
+/// I2S interface with DMA data transfer.
|
|
|
50
|
+pub struct I2S {
|
|
|
51
|
+ spi: SPI2,
|
|
|
52
|
+ i2sext: I2S2EXT,
|
|
|
53
|
+ rx_dma: dma::Stream3<DMA1>,
|
|
|
54
|
+ tx_dma: dma::Stream4<DMA1>,
|
|
|
55
|
+ in_: &'static AudioBuffer,
|
|
|
56
|
+ in_packets: [Option<WritePacket>; 2],
|
|
|
57
|
+ out: &'static AudioBuffer,
|
|
|
58
|
+ out_packets: [Option<ReadPacket>; 2],
|
|
|
59
|
+}
|
|
|
60
|
+
|
|
|
61
|
+impl I2S {
|
|
|
62
|
+ /// Initializes the I2S interface to read from/write to the specified buffers.
|
|
|
63
|
+ ///
|
|
|
64
|
+ /// The interface is only started when the `start()` function is called.
|
|
|
65
|
+ pub fn new(
|
|
|
66
|
+ spi: SPI2,
|
|
|
67
|
+ i2sext: I2S2EXT,
|
|
|
68
|
+ mut rx_dma: dma::Stream3<DMA1>,
|
|
|
69
|
+ mut tx_dma: dma::Stream4<DMA1>,
|
|
|
70
|
+ mck: PC6<Alternate<AF5>>,
|
|
|
71
|
+ ws: PB12<Alternate<AF5>>,
|
|
|
72
|
+ ck: PB13<Alternate<AF5>>,
|
|
|
73
|
+ sdin: PB14<Alternate<AF6>>,
|
|
|
74
|
+ sdout: PB15<Alternate<AF5>>,
|
|
|
75
|
+ in_: &'static AudioBuffer,
|
|
|
76
|
+ out: &'static AudioBuffer,
|
|
|
77
|
+ _clocks: I2SClocks,
|
|
|
78
|
+ ) -> I2S {
|
|
|
79
|
+ // Enable and reset the peripheral.
|
|
|
80
|
+ let rcc = unsafe { &*RCC::ptr() };
|
|
|
81
|
+ rcc.apb1enr.modify(|_, w| w.spi2en().set_bit());
|
|
|
82
|
+ rcc.apb1rstr.modify(|_, w| w.spi2rst().set_bit());
|
|
|
83
|
+ rcc.apb1rstr.modify(|_, w| w.spi2rst().clear_bit());
|
|
|
84
|
+ //rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit());
|
|
|
85
|
+
|
|
|
86
|
+ // Disable SS output.
|
|
|
87
|
+ spi.cr2.write(|w| w.ssoe().clear_bit());
|
|
|
88
|
+
|
|
|
89
|
+ // Configure the pins.
|
|
|
90
|
+ // TODO
|
|
|
91
|
+
|
|
|
92
|
+ // Configure the clocks.
|
|
|
93
|
+ // For the values, see the comment in I2SClocks::init().
|
|
|
94
|
+ spi.i2spr
|
|
|
95
|
+ .write(|w| unsafe { w.mckoe().set_bit().odd().set_bit().i2sdiv().bits(3) });
|
|
|
96
|
+ i2sext
|
|
|
97
|
+ .i2spr
|
|
|
98
|
+ .write(|w| unsafe { w.mckoe().set_bit().odd().set_bit().i2sdiv().bits(3) });
|
|
|
99
|
+
|
|
|
100
|
+ // Configure I2S.
|
|
|
101
|
+ spi.i2scfgr.modify(|_, w| unsafe {
|
|
|
102
|
+ w.i2smod()
|
|
|
103
|
+ .set_bit()
|
|
|
104
|
+ .i2se()
|
|
|
105
|
+ .clear_bit()
|
|
|
106
|
+ .i2scfg()
|
|
|
107
|
+ .master_tx()
|
|
|
108
|
+ .i2sstd()
|
|
|
109
|
+ .bits(0b01)
|
|
|
110
|
+ .ckpol()
|
|
|
111
|
+ .idle_high()
|
|
|
112
|
+ .datlen()
|
|
|
113
|
+ .bits(0b10)
|
|
|
114
|
+ .chlen()
|
|
|
115
|
+ .set_bit()
|
|
|
116
|
+ });
|
|
|
117
|
+ i2sext.i2scfgr.modify(|_, w| unsafe {
|
|
|
118
|
+ w.i2smod()
|
|
|
119
|
+ .set_bit()
|
|
|
120
|
+ .i2se()
|
|
|
121
|
+ .clear_bit()
|
|
|
122
|
+ .i2scfg()
|
|
|
123
|
+ .slave_rx()
|
|
|
124
|
+ .i2sstd()
|
|
|
125
|
+ .bits(0b01)
|
|
|
126
|
+ .ckpol()
|
|
|
127
|
+ .idle_high()
|
|
|
128
|
+ .datlen()
|
|
|
129
|
+ .bits(0b10)
|
|
|
130
|
+ .chlen()
|
|
|
131
|
+ .set_bit()
|
|
|
132
|
+ });
|
|
|
133
|
+ i2sext.cr2.modify(|_, w| w.rxdmaen().set_bit());
|
|
|
134
|
+ spi.cr2.modify(|_, w| w.txdmaen().set_bit());
|
|
|
135
|
+
|
|
|
136
|
+ rx_dma.set_channel(dma::Channel3);
|
|
|
137
|
+ rx_dma.clear_interrupts();
|
|
|
138
|
+ unsafe {
|
|
|
139
|
+ rx_dma.set_memory_size(1);
|
|
|
140
|
+ rx_dma.set_peripheral_size(1);
|
|
|
141
|
+ }
|
|
|
142
|
+ rx_dma.set_memory_increment(true);
|
|
|
143
|
+ rx_dma.set_direction(dma::PeripheralToMemory);
|
|
|
144
|
+ rx_dma.set_interrupts_enable(true, false, true, true);
|
|
|
145
|
+ rx_dma.set_double_buffer(true);
|
|
|
146
|
+ rx_dma.set_fifo_enable(false);
|
|
|
147
|
+ rx_dma.set_number_of_transfers((SAMPLES_PER_PACKET * CHANNELS * 2) as u16);
|
|
|
148
|
+
|
|
|
149
|
+ tx_dma.set_channel(dma::Channel0);
|
|
|
150
|
+ tx_dma.clear_interrupts();
|
|
|
151
|
+ unsafe {
|
|
|
152
|
+ tx_dma.set_memory_size(1);
|
|
|
153
|
+ tx_dma.set_peripheral_size(1);
|
|
|
154
|
+ }
|
|
|
155
|
+ tx_dma.set_memory_increment(true);
|
|
|
156
|
+ tx_dma.set_direction(dma::MemoryToPeripheral);
|
|
|
157
|
+ tx_dma.set_interrupts_enable(true, false, true, true);
|
|
|
158
|
+ tx_dma.set_double_buffer(true);
|
|
|
159
|
+ tx_dma.set_fifo_enable(false);
|
|
|
160
|
+ tx_dma.set_number_of_transfers((SAMPLES_PER_PACKET * CHANNELS * 2) as u16);
|
|
|
161
|
+
|
|
|
162
|
+ const DR_OFFSET: usize = 0x0c;
|
|
|
163
|
+ let spi_dr = SPI2::ptr() as usize + DR_OFFSET;
|
|
|
164
|
+ let i2sext_dr = I2S2EXT::ptr() as usize + DR_OFFSET;
|
|
|
165
|
+ rx_dma.set_peripheral_address(i2sext_dr as u32);
|
|
|
166
|
+ tx_dma.set_peripheral_address(spi_dr as u32);
|
|
|
167
|
+
|
|
|
168
|
+ I2S {
|
|
|
169
|
+ spi,
|
|
|
170
|
+ i2sext,
|
|
|
171
|
+ rx_dma,
|
|
|
172
|
+ tx_dma,
|
|
|
173
|
+ in_,
|
|
|
174
|
+ in_packets: [None, None],
|
|
|
175
|
+ out,
|
|
|
176
|
+ out_packets: [None, None],
|
|
|
177
|
+ }
|
|
|
178
|
+ }
|
|
|
179
|
+
|
|
|
180
|
+ /// Starts the data stream of the I2S interface.
|
|
|
181
|
+ pub fn start(&mut self) {
|
|
|
182
|
+ interrupt::free(|cs| {
|
|
|
183
|
+ for i in 0..2 {
|
|
|
184
|
+ self.in_packets[i] = self.in_.borrow_write(cs);
|
|
|
185
|
+ }
|
|
|
186
|
+ for i in 0..2 {
|
|
|
187
|
+ self.out_packets[i] = self.out.borrow_read(cs);
|
|
|
188
|
+ }
|
|
|
189
|
+ });
|
|
|
190
|
+
|
|
|
191
|
+ // The system was just started, and some buffers were filled with dummy data, so we can
|
|
|
192
|
+ // assume that borrowing was successful.
|
|
|
193
|
+ let in_0 = self.in_packets[0].as_mut().unwrap().as_mut().as_ptr() as usize as u32;
|
|
|
194
|
+ let in_1 = self.in_packets[1].as_mut().unwrap().as_mut().as_ptr() as usize as u32;
|
|
|
195
|
+ self.rx_dma.set_memory_address(in_0);
|
|
|
196
|
+ self.rx_dma.set_memory_double_buffer_address(in_1);
|
|
|
197
|
+ /*self.dma.st[3].m0ar.write(|w| unsafe { w.bits(in_0) });
|
|
|
198
|
+ self.dma.st[3].m1ar.write(|w| unsafe { w.bits(in_1) });*/
|
|
|
199
|
+ let out_0 = self.out_packets[0].as_ref().unwrap().as_ref().as_ptr() as usize as u32;
|
|
|
200
|
+ let out_1 = self.out_packets[1].as_ref().unwrap().as_ref().as_ptr() as usize as u32;
|
|
|
201
|
+ self.tx_dma.set_memory_address(out_0);
|
|
|
202
|
+ self.tx_dma.set_memory_double_buffer_address(out_1);
|
|
|
203
|
+ /*self.dma.st[4].m0ar.write(|w| unsafe { w.bits(out_0) });
|
|
|
204
|
+ self.dma.st[4].m1ar.write(|w| unsafe { w.bits(out_1) });*/
|
|
|
205
|
+
|
|
|
206
|
+ // Enable DMA and activate the I2S peripherals.
|
|
|
207
|
+ /*self.dma.st[3].cr.modify(|_, w| w.en().set_bit());
|
|
|
208
|
+ self.dma.st[4].cr.modify(|_, w| w.en().set_bit());*/
|
|
|
209
|
+ unsafe {
|
|
|
210
|
+ self.rx_dma.enable();
|
|
|
211
|
+ self.tx_dma.enable();
|
|
|
212
|
+ }
|
|
|
213
|
+ self.i2sext.i2scfgr.modify(|_, w| w.i2se().set_bit());
|
|
|
214
|
+ self.spi.i2scfgr.modify(|_, w| w.i2se().set_bit());
|
|
|
215
|
+
|
|
|
216
|
+ /*loop {
|
|
|
217
|
+ if self.spi.sr.read().txe().bit_is_set() {
|
|
|
218
|
+ self.spi.dr.write(|w| unsafe { w.bits(0xaaaa) });
|
|
|
219
|
+ }
|
|
|
220
|
+ if self.i2sext.sr.read().rxne().bit_is_set() {
|
|
|
221
|
+ self.i2sext.dr.read();
|
|
|
222
|
+ }
|
|
|
223
|
+ }*/
|
|
|
224
|
+ }
|
|
|
225
|
+
|
|
|
226
|
+ /// Reads data from the output audio buffer and writes to the input audio buffer as required.
|
|
|
227
|
+ ///
|
|
|
228
|
+ /// This function needs to be called from the DMA interrupt handler.
|
|
|
229
|
+ pub fn poll(&mut self) {
|
|
|
230
|
+ // TODO
|
|
|
231
|
+ }
|
|
|
232
|
+}
|