Skip to content

Commit 4baf0d4

Browse files
committed
AdvancedI2S: Add I2S input/output support.
Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
1 parent 171a789 commit 4baf0d4

File tree

5 files changed

+503
-0
lines changed

5 files changed

+503
-0
lines changed

src/AdvancedI2S.cpp

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
/*
2+
This file is part of the Arduino_AdvancedAnalog library.
3+
Copyright (c) 2024 Arduino SA. All rights reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
#include "Arduino.h"
21+
#include "HALConfig.h"
22+
#include "AdvancedI2S.h"
23+
24+
struct i2s_descr_t {
25+
I2S_HandleTypeDef i2s;
26+
DMA_HandleTypeDef dmatx;
27+
IRQn_Type dmatx_irqn;
28+
DMA_HandleTypeDef dmarx;
29+
IRQn_Type dmarx_irqn;
30+
DMABufferPool<Sample> *pool;
31+
DMABuffer<Sample> *dmatx_buf[2];
32+
DMABuffer<Sample> *dmarx_buf[2];
33+
};
34+
35+
static i2s_descr_t i2s_descr_all[] = {
36+
{
37+
{SPI1},
38+
{DMA2_Stream1, {DMA_REQUEST_SPI1_TX}}, DMA2_Stream1_IRQn,
39+
{DMA2_Stream2, {DMA_REQUEST_SPI1_RX}}, DMA2_Stream2_IRQn,
40+
nullptr, {nullptr, nullptr}, {nullptr, nullptr},
41+
},
42+
{
43+
{SPI2},
44+
{DMA2_Stream3, {DMA_REQUEST_SPI2_TX}}, DMA2_Stream3_IRQn,
45+
{DMA2_Stream4, {DMA_REQUEST_SPI2_RX}}, DMA2_Stream4_IRQn,
46+
nullptr, {nullptr, nullptr}, {nullptr, nullptr},
47+
},
48+
{
49+
{SPI3},
50+
{DMA2_Stream5, {DMA_REQUEST_SPI3_TX}}, DMA2_Stream5_IRQn,
51+
{DMA2_Stream6, {DMA_REQUEST_SPI3_RX}}, DMA2_Stream6_IRQn,
52+
nullptr, {nullptr, nullptr}, {nullptr, nullptr},
53+
},
54+
};
55+
56+
static const PinMap PinMap_SPI_MCK[] = {
57+
{PC_4, SPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF5_SPI1)},
58+
{PC_6, SPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF5_SPI2)},
59+
{PC_7, SPI_3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF6_SPI3)},
60+
{NC, NC, 0}
61+
};
62+
63+
extern "C" {
64+
65+
void DMA2_Stream1_IRQHandler() {
66+
HAL_DMA_IRQHandler(&i2s_descr_all[0].dmatx);
67+
}
68+
69+
void DMA2_Stream2_IRQHandler() {
70+
HAL_DMA_IRQHandler(&i2s_descr_all[0].dmarx);
71+
}
72+
73+
void DMA2_Stream3_IRQHandler() {
74+
HAL_DMA_IRQHandler(&i2s_descr_all[1].dmatx);
75+
}
76+
77+
void DMA2_Stream4_IRQHandler() {
78+
HAL_DMA_IRQHandler(&i2s_descr_all[1].dmarx);
79+
}
80+
81+
void DMA2_Stream5_IRQHandler() {
82+
HAL_DMA_IRQHandler(&i2s_descr_all[2].dmatx);
83+
}
84+
85+
void DMA2_Stream6_IRQHandler() {
86+
HAL_DMA_IRQHandler(&i2s_descr_all[2].dmarx);
87+
}
88+
89+
} // extern C
90+
91+
static uint32_t i2s_hal_mode(i2s_mode_t i2s_mode) {
92+
if (i2s_mode == I2S_OUT) {
93+
return I2S_MODE_MASTER_TX;
94+
} else if (i2s_mode == I2S_IN) {
95+
return I2S_MODE_MASTER_RX;
96+
} else {
97+
return I2S_MODE_MASTER_FULLDUPLEX;
98+
}
99+
}
100+
101+
static i2s_descr_t *i2s_descr_get(SPI_TypeDef *i2s) {
102+
if (i2s == SPI1) {
103+
return &i2s_descr_all[0];
104+
} else if (i2s == SPI2) {
105+
return &i2s_descr_all[1];
106+
} else if (i2s == SPI3) {
107+
return &i2s_descr_all[2];
108+
}
109+
return NULL;
110+
}
111+
112+
static void i2s_descr_deinit(i2s_descr_t *descr, bool dealloc_pool) {
113+
if (descr != nullptr) {
114+
HAL_I2S_DMAStop(&descr->i2s);
115+
116+
for (size_t i=0; i<AN_ARRAY_SIZE(descr->dmatx_buf); i++) {
117+
if (descr->dmatx_buf[i]) {
118+
descr->dmatx_buf[i]->release();
119+
descr->dmatx_buf[i] = nullptr;
120+
}
121+
}
122+
123+
for (size_t i=0; i<AN_ARRAY_SIZE(descr->dmarx_buf); i++) {
124+
if (descr->dmatx_buf[i]) {
125+
descr->dmatx_buf[i]->release();
126+
descr->dmatx_buf[i] = nullptr;
127+
}
128+
}
129+
130+
if (dealloc_pool) {
131+
if (descr->pool) {
132+
delete descr->pool;
133+
}
134+
descr->pool = nullptr;
135+
} else {
136+
descr->pool->flush();
137+
}
138+
}
139+
}
140+
141+
static int i2s_start_dma_transfer(i2s_descr_t *descr, i2s_mode_t i2s_mode) {
142+
uint16_t *tx_buf = NULL;
143+
uint16_t *rx_buf = NULL;
144+
uint16_t buf_size = 0;
145+
146+
if (i2s_mode & I2S_IN) {
147+
// Start I2S DMA.
148+
descr->dmarx_buf[0] = descr->pool->allocate();
149+
descr->dmarx_buf[1] = descr->pool->allocate();
150+
rx_buf = (uint16_t *) descr->dmarx_buf[0]->data();
151+
buf_size = descr->dmarx_buf[0]->size();
152+
HAL_NVIC_DisableIRQ(descr->dmarx_irqn);
153+
}
154+
155+
if (i2s_mode & I2S_OUT) {
156+
descr->dmatx_buf[0] = descr->pool->dequeue();
157+
descr->dmatx_buf[1] = descr->pool->dequeue();
158+
tx_buf = (uint16_t *) descr->dmatx_buf[0]->data();
159+
buf_size = descr->dmatx_buf[0]->size();
160+
HAL_NVIC_DisableIRQ(descr->dmatx_irqn);
161+
}
162+
163+
// Start I2S DMA.
164+
if (i2s_mode == I2S_IN) {
165+
if (HAL_I2S_Receive_DMA(&descr->i2s, rx_buf, buf_size) != HAL_OK) {
166+
return 0;
167+
}
168+
} else if (i2s_mode == I2S_OUT) {
169+
if (HAL_I2S_Transmit_DMA(&descr->i2s, tx_buf, buf_size) != HAL_OK) {
170+
return 0;
171+
}
172+
} else {
173+
if (HAL_I2SEx_TransmitReceive_DMA(&descr->i2s, tx_buf, rx_buf, buf_size) != HAL_OK) {
174+
return 0;
175+
}
176+
}
177+
178+
// Re/enable DMA double buffer mode.
179+
if (i2s_mode & I2S_IN) {
180+
hal_dma_enable_dbm(&descr->dmarx, descr->dmarx_buf[0]->data(), descr->dmarx_buf[1]->data());
181+
HAL_NVIC_EnableIRQ(descr->dmarx_irqn);
182+
}
183+
184+
if (i2s_mode & I2S_OUT) {
185+
hal_dma_enable_dbm(&descr->dmatx, descr->dmatx_buf[0]->data(), descr->dmatx_buf[1]->data());
186+
HAL_NVIC_EnableIRQ(descr->dmatx_irqn);
187+
}
188+
return 1;
189+
}
190+
191+
bool AdvancedI2S::available() {
192+
if (descr != nullptr) {
193+
if (i2s_mode == I2S_IN) {
194+
return descr->pool->readable();
195+
} else if (i2s_mode == I2S_OUT) {
196+
return descr->pool->writable();
197+
} else {
198+
return descr->pool->readable() && descr->pool->writable();
199+
}
200+
}
201+
return false;
202+
}
203+
204+
DMABuffer<Sample> &AdvancedI2S::read() {
205+
static DMABuffer<Sample> NULLBUF;
206+
if (descr != nullptr) {
207+
while (!descr->pool->readable()) {
208+
__WFI();
209+
}
210+
return *descr->pool->dequeue();
211+
}
212+
return NULLBUF;
213+
}
214+
215+
DMABuffer<Sample> &AdvancedI2S::dequeue() {
216+
static DMABuffer<Sample> NULLBUF;
217+
if (descr != nullptr) {
218+
while (!descr->pool->writable()) {
219+
__WFI();
220+
}
221+
return *descr->pool->allocate();
222+
}
223+
return NULLBUF;
224+
}
225+
226+
void AdvancedI2S::write(DMABuffer<Sample> &dmabuf) {
227+
static uint32_t buf_count = 0;
228+
229+
if (descr == nullptr) {
230+
return;
231+
}
232+
233+
// Make sure any cached data is flushed.
234+
dmabuf.flush();
235+
descr->pool->enqueue(&dmabuf);
236+
237+
if (descr->dmatx_buf[0] == nullptr && (++buf_count % 3) == 0) {
238+
i2s_start_dma_transfer(descr, i2s_mode);
239+
}
240+
}
241+
242+
int AdvancedI2S::begin(i2s_mode_t i2s_mode, uint32_t sample_rate, size_t n_samples, size_t n_buffers) {
243+
// Sanity checks.
244+
if (sample_rate < 8000 || sample_rate > 192000 || descr != nullptr) {
245+
return 0;
246+
}
247+
248+
// Configure I2S pins.
249+
uint32_t i2s = NC;
250+
const PinMap *i2s_pins_map[] = {
251+
PinMap_SPI_SSEL, PinMap_SPI_SCLK, PinMap_SPI_MISO, PinMap_SPI_MOSI, PinMap_SPI_MCK
252+
};
253+
254+
for (size_t i=0; i<i2s_pins_count; i++) {
255+
uint32_t per;
256+
if (i2s_pins[i] == NC) {
257+
continue;
258+
}
259+
per = pinmap_find_peripheral(i2s_pins[i], i2s_pins_map[i]);
260+
if (per == NC) {
261+
return 0;
262+
} else if (i2s == NC) {
263+
i2s = per;
264+
} else if (i2s != per) {
265+
return 0;
266+
}
267+
pinmap_pinout(i2s_pins[i], i2s_pins_map[i]);
268+
}
269+
270+
descr = i2s_descr_get((SPI_TypeDef *) i2s);
271+
if (descr == nullptr) {
272+
return 0;
273+
}
274+
275+
// Allocate DMA buffer pool.
276+
descr->pool = new DMABufferPool<Sample>(n_samples, 2, n_buffers);
277+
if (descr->pool == nullptr) {
278+
descr = nullptr;
279+
return 0;
280+
}
281+
282+
this->i2s_mode = i2s_mode;
283+
284+
// Init and config DMA.
285+
if (i2s_mode & I2S_IN) {
286+
if (hal_dma_config(&descr->dmarx, descr->dmarx_irqn, DMA_PERIPH_TO_MEMORY) != 0) {
287+
return 0;
288+
}
289+
__HAL_LINKDMA(&descr->i2s, hdmarx, descr->dmarx);
290+
}
291+
292+
if (i2s_mode & I2S_OUT) {
293+
if (hal_dma_config(&descr->dmatx, descr->dmatx_irqn, DMA_MEMORY_TO_PERIPH) != 0) {
294+
return 0;
295+
}
296+
__HAL_LINKDMA(&descr->i2s, hdmatx, descr->dmatx);
297+
}
298+
299+
// Init and config I2S.
300+
if (hal_i2s_config(&descr->i2s, sample_rate, i2s_hal_mode(i2s_mode)) != 0) {
301+
return 0;
302+
}
303+
304+
if (i2s_mode == I2S_INOUT) {
305+
for (int i=0; i<3; i++) {
306+
SampleBuffer outbuf = dequeue();
307+
memset(outbuf.data(), 0, outbuf.bytes());
308+
write(outbuf);
309+
}
310+
} else {
311+
// Link channel's DMA handle to I2S handle
312+
if (i2s_mode & I2S_IN) {
313+
return i2s_start_dma_transfer(descr, i2s_mode);
314+
}
315+
}
316+
return 1;
317+
}
318+
319+
int AdvancedI2S::stop() {
320+
i2s_descr_deinit(descr, true);
321+
descr = nullptr;
322+
return 1;
323+
}
324+
325+
AdvancedI2S::~AdvancedI2S() {
326+
i2s_descr_deinit(descr, true);
327+
}
328+
329+
extern "C" {
330+
331+
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *i2s) {
332+
i2s_descr_t *descr = i2s_descr_get(i2s->Instance);
333+
334+
if (descr == nullptr) {
335+
return;
336+
}
337+
338+
// NOTE: CT bit is inverted, to get the DMA buffer that's Not currently in use.
339+
size_t ct = ! hal_dma_get_ct(&descr->dmatx);
340+
341+
// Release the DMA buffer that was just used, dequeue the next one, and update
342+
// the next DMA memory address target.
343+
if (descr->pool->readable()) {
344+
descr->dmatx_buf[ct]->release();
345+
descr->dmatx_buf[ct] = descr->pool->dequeue();
346+
hal_dma_update_memory(&descr->dmatx, descr->dmatx_buf[ct]->data());
347+
} else {
348+
i2s_descr_deinit(descr, false);
349+
}
350+
}
351+
352+
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *i2s) {
353+
i2s_descr_t *descr = i2s_descr_get(i2s->Instance);
354+
355+
if (descr == nullptr) {
356+
return;
357+
}
358+
359+
// NOTE: CT bit is inverted, to get the DMA buffer that's Not currently in use.
360+
size_t ct = ! hal_dma_get_ct(&descr->dmarx);
361+
362+
// Update the buffer's timestamp.
363+
descr->dmarx_buf[ct]->timestamp(us_ticker_read());
364+
365+
// Flush the DMA buffer that was just used, move it to the ready queue, and
366+
// allocate a new one.
367+
if (descr->pool->writable()) {
368+
// Make sure any cached data is discarded.
369+
descr->dmarx_buf[ct]->invalidate();
370+
// Move current DMA buffer to ready queue.
371+
descr->pool->enqueue(descr->dmarx_buf[ct]);
372+
// Allocate a new free buffer.
373+
descr->dmarx_buf[ct] = descr->pool->allocate();
374+
// Currently, all multi-channel buffers are interleaved.
375+
if (descr->dmarx_buf[ct]->channels() > 1) {
376+
descr->dmarx_buf[ct]->setflags(DMA_BUFFER_INTRLVD);
377+
}
378+
} else {
379+
descr->dmarx_buf[ct]->setflags(DMA_BUFFER_DISCONT);
380+
}
381+
382+
// Update the next DMA target pointer.
383+
// NOTE: If the pool was empty, the same buffer is reused.
384+
hal_dma_update_memory(&descr->dmarx, descr->dmarx_buf[ct]->data());
385+
}
386+
387+
void HAL_I2SEx_TxRxCpltCallback(I2S_HandleTypeDef *i2s) {
388+
HAL_I2S_RxCpltCallback(i2s);
389+
HAL_I2S_TxCpltCallback(i2s);
390+
}
391+
392+
} // extern C

0 commit comments

Comments
 (0)