arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
I2SNanoSenseBLE.h
1#pragma once
2
3#include "AudioToolsConfig.h"
4
5#if defined(USE_NANO33BLE)
6
7#include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h"
8#include "AudioTools/CoreAudio/AudioLogger.h"
9#include "AudioTools/CoreAudio/AudioTypes.h"
10#include "AudioTools/CoreAudio/Buffers.h"
11
12#define IS_I2S_IMPLEMENTED
13
14namespace audio_tools {
15
16static int i2s_buffer_size = 0;
17static BaseBuffer<uint8_t> *p_i2s_buffer = nullptr;
18static uint8_t *p_i2s_array = nullptr; // current array
19static uint8_t *p_i2s_array_1 = nullptr; // array 1
20static uint8_t *p_i2s_array_2 = nullptr; // array 2
21static uint32_t i2s_underflow_count = 0;
22// alternative API
23static Stream *p_nano_ble_stream = nullptr;
24
29 int id;
30 float freq; // in mhz
31};
32
33static const Nano_BLE_freq_info freq_table[] = {
34 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV8, 32.0 / 8},
35 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV10, 32 / 10},
36 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV11, 32.0 / 11},
37 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV15, 32.0 / 15},
38 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV16, 32.0 / 16},
39 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV21, 32.0 / 21},
40 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV23, 32.0 / 23},
41 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV30, 32.0 / 30},
42 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV31, 32.0 / 31},
43 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV32, 32.0 / 32},
44 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV42, 32.0 / 42},
45 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV63, 32.0 / 63},
46 {I2S_CONFIG_MCKFREQ_MCKFREQ_32MDIV125, 32.0 / 125}};
47
52 int id;
53 float ratio;
54};
55
56static const Nano_BLE_ratio_info ratio_table[] = {
57 {I2S_CONFIG_RATIO_RATIO_32X, 32.0}, {I2S_CONFIG_RATIO_RATIO_48X, 48.0},
58 {I2S_CONFIG_RATIO_RATIO_64X, 64.0}, {I2S_CONFIG_RATIO_RATIO_96X, 96.0},
59 {I2S_CONFIG_RATIO_RATIO_128X, 128.0}, {I2S_CONFIG_RATIO_RATIO_192X, 192.0},
60 {I2S_CONFIG_RATIO_RATIO_256X, 256.0}, {I2S_CONFIG_RATIO_RATIO_384X, 384.0},
61 {I2S_CONFIG_RATIO_RATIO_512X, 512.0}};
62
63void I2S_IRQWrite(void) {
64 // Handle Wrtie
65 if (NRF_I2S->EVENTS_TXPTRUPD == 1) {
66 size_t eff_read = 0;
67
68 // toggle arrays to avoid noise
69 p_i2s_array = p_i2s_array == p_i2s_array_1 ? p_i2s_array_2 : p_i2s_array_1;
70
71 if (p_nano_ble_stream != nullptr) {
72 // Alternative API via Stream
73 eff_read = p_nano_ble_stream->readBytes(p_i2s_array, i2s_buffer_size);
74 } else {
75 // Using readArray
76 eff_read = p_i2s_buffer->readArray(p_i2s_array, i2s_buffer_size);
77 }
78 // if we did not get any valid data we provide silence
79 if (eff_read < i2s_buffer_size) {
80 memset(p_i2s_array, 0, i2s_buffer_size);
81 // allow checking for underflows
82 i2s_underflow_count++;
83 }
84 NRF_I2S->TXD.PTR = (uint32_t)p_i2s_array;
85 NRF_I2S->EVENTS_TXPTRUPD = 0;
86 }
87}
88
89void I2S_IRQRead(void) {
90 // Handle Read
91 if (NRF_I2S->EVENTS_RXPTRUPD == 1) {
92 // reading from pins writing to buffer - overwrite oldest data on overflow
93 p_i2s_buffer->writeArrayOverwrite(p_i2s_array, i2s_buffer_size);
94 // switch buffer assuming that this is necessary like in the write case
95 p_i2s_array = p_i2s_array == p_i2s_array_1 ? p_i2s_array_2 : p_i2s_array_1;
96 NRF_I2S->RXD.PTR = (uint32_t)p_i2s_array;
97 NRF_I2S->EVENTS_RXPTRUPD = 0;
98 }
99}
100
104void I2S_IRQHandler(void) {
105 // prevent NPE
106 if (p_i2s_buffer == nullptr || p_i2s_array == 0) {
107 NRF_I2S->EVENTS_TXPTRUPD = 0;
108 NRF_I2S->EVENTS_RXPTRUPD = 0;
109 return;
110 }
111
112 I2S_IRQWrite();
113 I2S_IRQRead();
114}
115
126 friend class I2SStream;
127
128 public:
129 I2SDriverNanoBLE() = default;
130
133 I2SConfigStd c(mode);
134 return c;
135 }
137 bool setAudioInfo(AudioInfo) { return false; }
138
140 bool begin(RxTxMode mode = TX_MODE) { return begin(defaultConfig(mode)); }
141
143 bool begin(I2SConfigStd cfg) {
144 TRACEI();
145 cfg.logInfo();
146 this->cfg = cfg;
147
148 if (cfg.bits_per_sample == 32) {
149 LOGE("32 bits not supported");
150 return false;
151 }
152
153 if (!setupBuffers()) {
154 LOGE("out of memory");
155 return false;
156 }
157
158 // setup IRQ
159 NVIC_SetVector(I2S_IRQn, (uint32_t)I2S_IRQHandler);
160 NVIC_EnableIRQ(I2S_IRQn);
161
162 if (!setupRxTx(cfg)) {
163 return false;
164 }
165 setupClock(cfg);
166 setupBitWidth(cfg);
167 setupMode(cfg);
168 setupPins(cfg);
169
170 // TX_MODE is started with first write
171 if (cfg.rx_tx_mode == RX_MODE || p_nano_ble_stream != nullptr) {
173 }
174
175 return true;
176 }
177
178 int available() {
179 if (cfg.rx_tx_mode == TX_MODE) return 0;
180 return p_i2s_buffer->available();
181 }
182
183 int availableForWrite() {
184 if (cfg.rx_tx_mode == RX_MODE) return 0;
185 return max(i2s_buffer_size, p_i2s_buffer->availableForWrite());
186 }
187
189 void end() {
190 LOGD(__func__);
191 // stop task
192 NRF_I2S->TASKS_START = 0;
193 // disble I2S
194 NRF_I2S->ENABLE = 0;
195
197
198 is_active = false;
199 }
200
202 I2SConfigStd config() { return cfg; }
203
205 size_t writeBytes(const void *src, size_t size_bytes) {
206 size_t result = p_i2s_buffer->writeArray((uint8_t *)src, size_bytes);
207
208 // activate I2S when the buffer is full
209 if (!is_active && result < size_bytes) {
211 }
212 return result;
213 }
214
216 size_t readBytes(void *dest, size_t size_bytes) {
217 size_t result = p_i2s_buffer->readArray((uint8_t *)dest, size_bytes);
218 return result;
219 }
220
222 void setStream(Stream &stream) { p_nano_ble_stream = &stream; }
223
225 void clearStream() { p_nano_ble_stream = nullptr; }
226
227 void setBufferSize(int size) { i2s_buffer_size = size; }
228
229 protected:
230 I2SConfigStd cfg;
231 bool is_active = false;
232
235 TRACED();
236 switch (cfg.rx_tx_mode) {
237 case TX_MODE:
238 // Enable transmission
239 NRF_I2S->CONFIG.TXEN =
240 (I2S_CONFIG_TXEN_TXEN_Enabled << I2S_CONFIG_TXEN_TXEN_Pos);
241 return true;
242 case RX_MODE:
243 // Enable reception
244 NRF_I2S->CONFIG.RXEN =
245 (I2S_CONFIG_RXEN_RXEN_Enabled << I2S_CONFIG_RXEN_RXEN_Pos);
246 return true;
247 default:
248 LOGE("rx_tx_mode not supported");
249 return false;
250 }
251 }
252
255 TRACED();
256
257 // Enable MCK generator if in master mode
258 if (cfg.is_master) {
259 NRF_I2S->CONFIG.MCKEN =
260 (I2S_CONFIG_MCKEN_MCKEN_Enabled << I2S_CONFIG_MCKEN_MCKEN_Pos);
261 }
262
263 // find closest frequency for requested sample_rate
264 float freq_requested = cfg.sample_rate; // * cfg.bits_per_sample ;
265 float selected_freq = 0;
266 for (auto freq : freq_table) {
267 for (auto div : ratio_table) {
268 float freq_value = freq.freq * 1000000 / div.ratio;
269 if (abs(freq_value - freq_requested) <
270 abs(selected_freq - freq_requested)) {
271 // MCKFREQ
272 NRF_I2S->CONFIG.MCKFREQ = freq.id << I2S_CONFIG_MCKFREQ_MCKFREQ_Pos;
273 // Ratio
274 NRF_I2S->CONFIG.RATIO = div.id << I2S_CONFIG_RATIO_RATIO_Pos;
275 selected_freq = freq_value;
276 LOGD("frequency requested %f vs %f", freq_requested, selected_freq);
277 }
278 }
279 }
280 LOGI("Frequency req. %f vs eff. %f", freq_requested, selected_freq);
281 }
282
285 TRACED();
286 uint16_t swidth = I2S_CONFIG_SWIDTH_SWIDTH_16Bit;
287 switch (cfg.bits_per_sample) {
288 case 8:
289 NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_8Bit
290 << I2S_CONFIG_SWIDTH_SWIDTH_Pos;
291 break;
292 case 16:
293 NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_16Bit
294 << I2S_CONFIG_SWIDTH_SWIDTH_Pos;
295 break;
296 case 24:
297 NRF_I2S->CONFIG.SWIDTH = I2S_CONFIG_SWIDTH_SWIDTH_24Bit
298 << I2S_CONFIG_SWIDTH_SWIDTH_Pos;
299 break;
300 default:
301 LOGE("Unsupported bit width: %d", cfg.bits_per_sample);
302 }
303 }
304
307 TRACED();
308 // setup mode
309 switch (cfg.i2s_format) {
310 case I2S_STD_FORMAT:
311 case I2S_PHILIPS_FORMAT:
312 case I2S_MSB_FORMAT:
313 case I2S_LEFT_JUSTIFIED_FORMAT:
314 NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S
315 << I2S_CONFIG_FORMAT_FORMAT_Pos;
316 NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Left
317 << I2S_CONFIG_ALIGN_ALIGN_Pos;
318 ;
319 break;
320 case I2S_LSB_FORMAT:
321 case I2S_RIGHT_JUSTIFIED_FORMAT:
322 NRF_I2S->CONFIG.FORMAT = I2S_CONFIG_FORMAT_FORMAT_I2S
323 << I2S_CONFIG_FORMAT_FORMAT_Pos;
324 NRF_I2S->CONFIG.ALIGN = I2S_CONFIG_ALIGN_ALIGN_Right
325 << I2S_CONFIG_ALIGN_ALIGN_Pos;
326 ;
327 break;
328 default:
329 LOGW("i2s_format not supported");
330 }
331 }
332#ifdef IS_ZEPHYR
333 int digitalPinToPinName(int pin) {return pin;}
334#endif
336 int getPinName(int pin) {
337#if defined(USE_ALT_PIN_SUPPORT)
338 return cfg.is_arduino_pin_numbers ? digitalPinToPinName(pin) : pin;
339#else
340 return digitalPinToPinName(pin);
341#endif
342 }
343
346 TRACED();
347
348 // MCK
349 if (cfg.is_master && cfg.pin_mck >= 0) {
350 NRF_I2S->PSEL.MCK = getPinName(cfg.pin_mck) << I2S_PSEL_MCK_PIN_Pos;
351 }
352 // SCK - bit clock
353 NRF_I2S->PSEL.SCK = getPinName(cfg.pin_bck) << I2S_PSEL_SCK_PIN_Pos;
354 // LRCK
355 NRF_I2S->PSEL.LRCK = getPinName(cfg.pin_ws) << I2S_PSEL_LRCK_PIN_Pos;
356 // i2s Data Pins
357 switch (cfg.rx_tx_mode) {
358 case TX_MODE:
359 NRF_I2S->PSEL.SDOUT = getPinName(cfg.pin_data)
360 << I2S_PSEL_SDOUT_PIN_Pos;
361 break;
362 case RX_MODE:
363 NRF_I2S->PSEL.SDIN = getPinName(cfg.pin_data) << I2S_PSEL_SDIN_PIN_Pos;
364 break;
365 default:
366 TRACEW();
367 }
368 }
369
371 unsigned long getINTENSET() {
372 unsigned long result = 0;
373 switch (cfg.rx_tx_mode) {
374 case TX_MODE:
375 result = I2S_INTENSET_TXPTRUPD_Enabled << I2S_INTENSET_TXPTRUPD_Pos;
376 break;
377 case RX_MODE:
378 result = I2S_INTENSET_RXPTRUPD_Enabled << I2S_INTENSET_RXPTRUPD_Pos;
379 break;
380 default:
381 TRACEE();
382 }
383 return result;
384 }
385
388 TRACED();
389 // Use stereo
390 NRF_I2S->CONFIG.CHANNELS = I2S_CONFIG_CHANNELS_CHANNELS_Stereo
391 << I2S_CONFIG_CHANNELS_CHANNELS_Pos;
392 // Setup master or slave mode
393 NRF_I2S->CONFIG.MODE =
394 cfg.is_master ? I2S_CONFIG_MODE_MODE_MASTER << I2S_CONFIG_MODE_MODE_Pos
395 : I2S_CONFIG_MODE_MODE_Slave << I2S_CONFIG_MODE_MODE_Pos;
396
397 // initial empty buffer
398 NRF_I2S->TXD.PTR = (uint32_t)p_i2s_array;
399 NRF_I2S->RXD.PTR = (uint32_t)p_i2s_array;
400 // define copy size (always defined as number of 32 bits)
401 NRF_I2S->RXTXD.MAXCNT = i2s_buffer_size / 4;
402
403 NRF_I2S->INTENSET = getINTENSET();
404
405 // ensble I2S
406 NRF_I2S->ENABLE = 1;
407 // start task
408 NRF_I2S->TASKS_START = 1;
409
410 is_active = true;
411 }
412
415 TRACED();
416 i2s_buffer_size = cfg.buffer_size;
417
418 if (p_i2s_array == nullptr) {
419 p_i2s_array_1 = new uint8_t[i2s_buffer_size]{0};
420 p_i2s_array_2 = new uint8_t[i2s_buffer_size]{0};
421 p_i2s_array = p_i2s_array_1;
422 } else {
423 memset(p_i2s_array_1, 0, i2s_buffer_size);
424 memset(p_i2s_array_2, 0, i2s_buffer_size);
425 }
426
427 // allocate buffer only when needed
428 if (p_i2s_buffer == nullptr && p_nano_ble_stream == nullptr) {
429 p_i2s_buffer = new NBuffer<uint8_t>(cfg.buffer_size, i2s_buffer_size);
430 }
431
432 // on stream option we only need to have the arrays allocated
433 if (p_nano_ble_stream != nullptr) {
434 return p_i2s_array_1 != nullptr && p_i2s_array_2 != nullptr;
435 }
436 return p_i2s_array_1 != nullptr && p_i2s_array_2 != nullptr &&
437 p_i2s_buffer != nullptr;
438 }
439
442 TRACED();
443 i2s_buffer_size = 0;
444
445 p_i2s_array = nullptr;
446 delete p_i2s_array_1;
447 p_i2s_array_1 = nullptr;
448 delete p_i2s_array_2;
449 p_i2s_array_2 = nullptr;
450
451 delete p_i2s_buffer;
452 p_i2s_buffer = nullptr;
453 }
454};
455
456using I2SDriver = I2SDriverNanoBLE;
457
458} // namespace audio_tools
459
460#endif
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:37
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:59
virtual int writeArrayOverwrite(const T data[], int len)
Fills the buffer data and overwrites the oldest data if the buffer is full.
Definition Buffers.h:76
virtual int availableForWrite()=0
provides the number of entries that are available to write
virtual int available()=0
provides the number of entries that are available to read
Configuration for i2s.
Definition I2SConfigStd.h:17
RxTxMode rx_tx_mode
public settings
Definition I2SConfigStd.h:48
Basic I2S API - for the Arduino Nano BLE Sense See https://content.arduino.cc/assets/Nano_BLE_MCU-nRF...
Definition I2SNanoSenseBLE.h:125
bool setupBuffers()
dynamic buffer management
Definition I2SNanoSenseBLE.h:414
bool setAudioInfo(AudioInfo)
Potentially updates the sample rate (if supported)
Definition I2SNanoSenseBLE.h:137
void setupPins(I2SConfigStd cfg)
setup pins
Definition I2SNanoSenseBLE.h:345
I2SConfigStd config()
provides the actual configuration
Definition I2SNanoSenseBLE.h:202
void clearStream()
Deactivate alternative API: don't forget to call begin()
Definition I2SNanoSenseBLE.h:225
void setStream(Stream &stream)
alternative API which provides the data directly via a Stream
Definition I2SNanoSenseBLE.h:222
I2SConfigStd defaultConfig(RxTxMode mode)
Provides the default configuration.
Definition I2SNanoSenseBLE.h:132
unsigned long getINTENSET()
Determine the INTENSET value.
Definition I2SNanoSenseBLE.h:371
void setupBitWidth(I2SConfigStd cfg)
setup SWIDTH
Definition I2SNanoSenseBLE.h:284
int getPinName(int pin)
Provides the arduino or unconverted pin name.
Definition I2SNanoSenseBLE.h:336
bool begin(I2SConfigStd cfg)
starts the I2S
Definition I2SNanoSenseBLE.h:143
void end()
stops the I2S
Definition I2SNanoSenseBLE.h:189
void setupMode(I2SConfigStd cfg)
setup format and align
Definition I2SNanoSenseBLE.h:306
size_t writeBytes(const void *src, size_t size_bytes)
writes the data to the I2S buffer
Definition I2SNanoSenseBLE.h:205
bool setupRxTx(I2SConfigStd cfg)
setup TXEN or RXEN
Definition I2SNanoSenseBLE.h:234
size_t readBytes(void *dest, size_t size_bytes)
reads the data from the I2S buffer
Definition I2SNanoSenseBLE.h:216
bool begin(RxTxMode mode=TX_MODE)
starts the I2S with the default config in TX Mode
Definition I2SNanoSenseBLE.h:140
void releaseBuffers()
Release buffers.
Definition I2SNanoSenseBLE.h:441
void setupClock(I2SConfigStd cfg)
setup MCKFREQ and RATIO
Definition I2SNanoSenseBLE.h:254
void startI2SActive()
Start IRQ and I2S.
Definition I2SNanoSenseBLE.h:387
We support the Stream interface for the I2S access. In addition we allow a separate mute pin which mi...
Definition I2SStream.h:33
A lock free N buffer. If count=2 we create a DoubleBuffer, if count=3 a TripleBuffer etc.
Definition Buffers.h:616
Definition NoArduino.h:142
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
void I2S_IRQHandler(void)
Definition I2SNanoSenseBLE.h:104
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59
Mapping Frequency constants to available frequencies.
Definition I2SNanoSenseBLE.h:28
Mapping from Ratio Constants to frequency ratios.
Definition I2SNanoSenseBLE.h:51