arduino-audio-tools
Loading...
Searching...
No Matches
I2SDriverSTM32.h
Go to the documentation of this file.
1#pragma once
2
3#ifdef STM32
6#include "stm32-i2s.h"
7
8#ifdef STM_I2S_PINS
9#define IS_I2S_IMPLEMENTED
10
11namespace audio_tools {
12
25class I2SDriverSTM32 : public I2SDriverBase {
26 friend class I2SStream;
27
28 public:
30 I2SConfigStd defaultConfig(RxTxMode mode = TX_MODE) {
31 I2SConfigStd c(mode);
32 return c;
33 }
34
36 bool setAudioInfo(AudioInfo) { return false;}
37
39 bool begin(RxTxMode mode = TX_MODE) {
40 TRACED();
41 return begin(defaultConfig(mode));
42 }
43
45 bool begin(I2SConfigStd cfg) {
46 // TRACED();
47 this->cfg = cfg;
48 bool result = false;
50 LOGI("buffer_size: %d", cfg.buffer_size);
51 LOGI("buffer_count: %d", cfg.buffer_count);
52
53 if (cfg.channels > 2 || cfg.channels <= 0) {
54 LOGE("Channels not supported: %d", cfg.channels);
55 return false;
56 }
57
59 setupPins();
60 result = use_dma ? startI2SDMA() : startI2S();
61 this->active = result;
62 return result;
63 }
64
66 void end() {
67 // TRACED();
68 i2s.end();
70 active = false;
71 }
72
74 int available() {
75 if (!active) return 0;
76 if (use_dma && p_rx_buffer == nullptr) return 0;
77 return cfg.buffer_size;
78 }
79
81 int availableForWrite() {
82 if (!active) return 0;
83 if (use_dma && p_tx_buffer == nullptr) return 0;
84 return cfg.buffer_size;
85 }
86
88 I2SConfigStd config() { return cfg; }
89
91 size_t writeBytes(const void *src, size_t size_bytes) {
92 TRACED();
93 size_t result = 0;
94 if (!use_dma) {
95 result = i2s.write((uint8_t *)src, size_bytes);
96 } else {
97 // if we have an input stream we do not need to fill the buffer
98 if (p_dma_in != nullptr) {
99 // by calling writeBytes we activate the automatic timeout handling
100 // and expect further writes to continue the output
102 result = size_bytes;
103 } else {
104 // fill buffer
105 result = writeBytesDMA(src, size_bytes);
106 }
107 }
108 return result;
109 }
110
111 size_t readBytes(void *dest, size_t size_bytes) {
112 TRACED();
113 if (!use_dma) {
114 return i2s.readBytes((uint8_t *)dest, size_bytes);
115 } else {
116 if (cfg.channels == 2) {
117 return p_rx_buffer->readArray((uint8_t *)dest, size_bytes);
118 } else {
119 return readBytesDMA(dest, size_bytes);
120 }
121 }
122 }
123
126 static void writeFromReceive(uint8_t *buffer, uint16_t byteCount, void *ref) {
127 I2SDriverSTM32 *self = (I2SDriverSTM32 *)ref;
128 uint16_t written = 0;
129 if (self->p_dma_out != nullptr)
130 written = self->p_dma_out->write(buffer, byteCount);
131 else
132 written = self->p_rx_buffer->writeArray(buffer, byteCount);
133
134 // check for overflow
135 if (written != byteCount) {
136 LOGW("Buffer overflow: written %d of %d", written, byteCount);
137 }
138 }
139
142 static void readToTransmit(uint8_t *buffer, uint16_t byteCount, void *ref) {
143 I2SDriverSTM32 *self = (I2SDriverSTM32 *)ref;
144 static size_t count = 0;
145 size_t read = 0;
146 if (self->p_dma_in != nullptr) {
147 // stop reading if timout is relevant
148 if (self->isWriteTimedOut()) {
149 // we just provide silence
150 read = byteCount;
151 } else {
152 // get data from stream
153 read = self->p_dma_in->readBytes(buffer, byteCount);
154 }
155 } else {
156 // get data from buffer
157 if (self->stm32_write_active) {
158 read = self->p_tx_buffer->readArray(buffer, byteCount);
159 }
160 }
161 memset(buffer+read, 0, byteCount-read);
162
163 // check for underflow
164 count++;
165 if (read != byteCount) {
166 LOGW("Buffer underflow at %lu: %d for %d", count, read, byteCount);
167 }
168 }
169
171 bool isWriteTimedOut() {
172 return last_write_ms != 0 && last_write_ms + 500 < millis();
173 }
174
176 void setDMAActive(bool flag) { use_dma = flag; }
177
179 void setDMAInputStream(Stream &in) {
180 use_dma = true;
181 p_dma_in = &in;
182 }
183
185 void setDMAOutput(Print &out) {
186 use_dma = true;
187 p_dma_out = &out;
188 }
189
190 protected:
191 stm32_i2s::Stm32I2sClass i2s;
192 stm32_i2s::I2SSettingsSTM32 i2s_stm32;
193 I2SConfigStd cfg;
194 bool active = false;
195 bool result = true;
196 BaseBuffer<uint8_t> *p_tx_buffer = nullptr;
197 BaseBuffer<uint8_t> *p_rx_buffer = nullptr;
198 volatile bool stm32_write_active = false;
199 bool use_dma = true;
200 Print *p_dma_out = nullptr;
201 Stream *p_dma_in = nullptr;
203
204 size_t writeBytesDMA(const void *src, size_t size_bytes) {
205 size_t result = 0;
206 // fill the tx buffer
207 int open = size_bytes;
208 while (open > 0) {
209 int actual_written = writeBytesExt(src, size_bytes);
210 result += actual_written;
211 open -= actual_written;
212 if (open > 0) {
213 stm32_write_active = true;
214 //delay(1);
215 }
216 }
217
218 // start output of data only when buffer has been filled
219 if (!stm32_write_active && p_tx_buffer->availableForWrite() == 0) {
220 stm32_write_active = true;
221 LOGI("Buffer is full->starting i2s output");
222 }
223
224 return size_bytes;
225 }
226
227 size_t readBytesDMA(void *dest, size_t size_bytes) {
228 // combine two channels to 1: so request double the amout
229 int req_bytes = size_bytes * 2;
230 uint8_t tmp[req_bytes];
231 int16_t *tmp_16 = (int16_t *)tmp;
232 int eff_bytes = p_rx_buffer->readArray((uint8_t *)tmp, req_bytes);
233 // add 2 channels together
236 int16_t idx = 0;
237 for (int j = 0; j < eff_samples; j += 2) {
238 dest_16[idx++] = static_cast<float>(tmp_16[j]) + tmp_16[j + 1] / 2.0;
239 }
240 return eff_bytes / 2;
241 }
242
243 bool startI2S() {
244 switch (cfg.rx_tx_mode) {
245 case RX_MODE:
246 result = i2s.begin(i2s_stm32, false, true);
247 break;
248 case TX_MODE:
249 result = i2s.begin(i2s_stm32, true, false);
250 break;
251 case RXTX_MODE:
252 default:
253 result = i2s.begin(i2s_stm32, true, true);
254 break;
255 }
256 return result;
257 }
258
259 bool startI2SDMA() {
260 switch (cfg.rx_tx_mode) {
261 case RX_MODE:
262 if (use_dma && p_rx_buffer == nullptr)
263 p_rx_buffer = allocateBuffer();
264 result = i2s.beginReadDMA(i2s_stm32, writeFromReceive);
265 break;
266 case TX_MODE:
267 stm32_write_active = false;
268 if (use_dma && p_tx_buffer == nullptr)
270 result = i2s.beginWriteDMA(i2s_stm32, readToTransmit);
271 break;
272
273 case RXTX_MODE:
274 if (use_dma) {
275 stm32_write_active = false;
276 if (p_rx_buffer == nullptr)
277 p_rx_buffer = allocateBuffer();
278 if (p_tx_buffer == nullptr)
280 }
281 result = i2s.beginReadWriteDMA(
283 break;
284
285 default:
286 LOGE("Unsupported mode");
287 return false;
288 }
289 return result;
290 }
291
292 uint32_t toDataFormat(int bits_per_sample) {
293 switch (bits_per_sample) {
294 case 16:
295 return I2S_DATAFORMAT_16B;
296 case 24:
297 return I2S_DATAFORMAT_24B;
298 case 32:
299 return I2S_DATAFORMAT_32B;
300 }
301 return I2S_DATAFORMAT_16B;
302 }
303
304 void deleteBuffers() {
305 if (p_rx_buffer != nullptr) {
306 delete p_rx_buffer;
307 p_rx_buffer = nullptr;
308 }
309 if (p_tx_buffer != nullptr) {
310 delete p_tx_buffer;
311 p_tx_buffer = nullptr;
312 }
313 }
314
316 i2s_stm32.sample_rate = getSampleRate(cfg);
317 i2s_stm32.data_format = toDataFormat(cfg.bits_per_sample);
318 i2s_stm32.mode = getMode(cfg);
319 i2s_stm32.standard = getStandard(cfg);
320 i2s_stm32.fullduplexmode = cfg.rx_tx_mode == RXTX_MODE
323 i2s_stm32.hardware_config.buffer_size = cfg.buffer_size;
324 // provide ourself as parameter to callback
325 i2s_stm32.ref = this;
326 }
327
328 void setupPins(){
329 if (cfg.pin_bck == -1 || cfg.pin_ws == -1 || cfg.pin_data == -1) {
330 LOGW("pins ignored: used from stm32-i2s");
331 } else {
332 LOGI("setting up pins for stm32-i2s");
333 i2s_stm32.hardware_config.pins[0].function = stm32_i2s::mclk;
334 i2s_stm32.hardware_config.pins[0].pin = digitalPinToPinName(cfg.pin_mck);
335 i2s_stm32.hardware_config.pins[0].altFunction = cfg.pin_alt_function;
336
337 i2s_stm32.hardware_config.pins[1].function = stm32_i2s::bck;
338 i2s_stm32.hardware_config.pins[1].pin = digitalPinToPinName(cfg.pin_bck);
339 i2s_stm32.hardware_config.pins[1].altFunction = cfg.pin_alt_function;
340
341 i2s_stm32.hardware_config.pins[2].function = stm32_i2s::ws;
342 i2s_stm32.hardware_config.pins[2].pin = digitalPinToPinName(cfg.pin_ws);
343 i2s_stm32.hardware_config.pins[2].altFunction = cfg.pin_alt_function;
344
345 switch (cfg.rx_tx_mode) {
346 case TX_MODE:
347 i2s_stm32.hardware_config.pins[3].function = stm32_i2s::data_out;
348 i2s_stm32.hardware_config.pins[3].pin = digitalPinToPinName(cfg.pin_data);
349 i2s_stm32.hardware_config.pins[3].altFunction = cfg.pin_alt_function;
350 break;
351 case RX_MODE:
352 i2s_stm32.hardware_config.pins[4].function = stm32_i2s::data_in;
353 i2s_stm32.hardware_config.pins[4].pin = digitalPinToPinName(cfg.pin_data);
354 i2s_stm32.hardware_config.pins[4].altFunction = cfg.pin_alt_function;
355 break;
356 case RXTX_MODE:
357 i2s_stm32.hardware_config.pins[3].function = stm32_i2s::data_out;
358 i2s_stm32.hardware_config.pins[3].pin = digitalPinToPinName(cfg.pin_data);
359 i2s_stm32.hardware_config.pins[3].altFunction = cfg.pin_alt_function;
360
361 i2s_stm32.hardware_config.pins[4].function = stm32_i2s::data_in;
362 i2s_stm32.hardware_config.pins[4].pin = digitalPinToPinName(cfg.pin_data);
363 i2s_stm32.hardware_config.pins[4].altFunction = cfg.pin_alt_function;
364 break;
365 };
366
367 }
368 }
369
370 uint32_t getMode(I2SConfigStd &cfg) {
371 if (cfg.is_master) {
372 switch (cfg.rx_tx_mode) {
373 case RX_MODE:
374 return I2S_MODE_MASTER_RX;
375 case TX_MODE:
376 return I2S_MODE_MASTER_TX;
377 default:
378 LOGE("RXTX_MODE not supported");
379 return I2S_MODE_MASTER_TX;
380 }
381 } else {
382 switch (cfg.rx_tx_mode) {
383 case RX_MODE:
384 return I2S_MODE_SLAVE_RX;
385 case TX_MODE:
386 return I2S_MODE_SLAVE_TX;
387 default:
388 LOGE("RXTX_MODE not supported");
389 return I2S_MODE_SLAVE_TX;
390 }
391 }
392 }
393
394 uint32_t getStandard(I2SConfigStd &cfg) {
395 uint32_t result;
396 switch (cfg.i2s_format) {
399 case I2S_STD_FORMAT:
400 case I2S_LSB_FORMAT:
402 return I2S_STANDARD_MSB;
403 case I2S_MSB_FORMAT:
405 return I2S_STANDARD_LSB;
406 }
408 }
409
410 uint32_t getSampleRate(I2SConfigStd &cfg) {
411 switch (cfg.sample_rate) {
420 case I2S_AUDIOFREQ_8K:
421 return cfg.sample_rate;
422 default:
423 LOGE("Unsupported sample rate: %u", cfg.sample_rate);
424 return cfg.sample_rate;
425 }
426 }
427
428 size_t writeBytesExt(const void *src, size_t size_bytes) {
429 size_t result = 0;
430 if (cfg.channels == 2) {
431 result = p_tx_buffer->writeArray((uint8_t *)src, size_bytes);
432 } else {
433 // write each sample 2 times
434 int samples = size_bytes / 2;
436 int16_t tmp[2];
437 int result = 0;
438 for (int j = 0; j < samples; j++) {
439 tmp[0] = src_16[j];
440 tmp[1] = src_16[j];
441 if (p_tx_buffer->availableForWrite() >= 4) {
442 p_tx_buffer->writeArray((uint8_t *)tmp, 4);
443 result = j * 2;
444 } else {
445 // abort if the buffer is full
446 break;
447 }
448 }
449 }
450 LOGD("writeBytesExt: %u", result)
451 return result;
452 }
453
454 BaseBuffer<uint8_t>* allocateBuffer() {
455 //return new RingBuffer<uint8_t>(cfg.buffer_size * cfg.buffer_count);
456 return new NBuffer<uint8_t>(cfg.buffer_size, cfg.buffer_count);
457 }
458};
459
461
462} // namespace audio_tools
463
464#endif
465#endif
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Definition Arduino.h:56
Definition Arduino.h:136
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:26
@ RXTX_MODE
Definition AudioTypes.h:26
@ TX_MODE
Definition AudioTypes.h:26
@ RX_MODE
Definition AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
@ I2S_STD_FORMAT
Definition AudioTypes.h:417
@ I2S_PHILIPS_FORMAT
Definition AudioTypes.h:420
@ I2S_LSB_FORMAT
Definition AudioTypes.h:418
@ I2S_LEFT_JUSTIFIED_FORMAT
Definition AudioTypes.h:422
@ I2S_RIGHT_JUSTIFIED_FORMAT
Definition AudioTypes.h:421
@ I2S_MSB_FORMAT
Definition AudioTypes.h:419
I2SDriverESP32 I2SDriver
Definition I2SDriverESP32.h:403
uint32_t millis()
Returns the milliseconds since the start.
Definition Arduino.h:256
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:508
constexpr const _Ep * end(initializer_list< _Ep > __il) noexcept
Definition InitializerList.h:63
constexpr const _Ep * begin(initializer_list< _Ep > __il) noexcept
Definition InitializerList.h:55