forked from esp8266/Arduino
-
Notifications
You must be signed in to change notification settings - Fork 0
/
core_esp8266_i2s.cpp
577 lines (491 loc) · 17.1 KB
/
core_esp8266_i2s.cpp
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
/*
i2s.c - Software I2S library for esp8266
Code taken and reworked from espessif's I2S example
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Arduino.h"
#include "osapi.h"
#include "ets_sys.h"
#include "i2s_reg.h"
#include "i2s.h"
extern "C" {
#define SLC_BUF_CNT (8) // Number of buffers in the I2S circular buffer
#define SLC_BUF_LEN (64) // Length of one buffer, in 32-bit words.
// We use a queue to keep track of the DMA buffers that are empty. The ISR
// will push buffers to the back of the queue, the I2S transmitter will pull
// them from the front and fill them. For ease, the queue will contain
// *pointers* to the DMA buffers, not the data itself. The queue depth is
// one smaller than the amount of buffers we have, because there's always a
// buffer that is being used by the DMA subsystem *right now* and we don't
// want to be able to write to that simultaneously.
// For RX, it's a little different. The buffers in i2s_slc_queue are
// placed onto the list when they're filled by DMA
typedef struct slc_queue_item {
uint32_t blocksize : 12;
uint32_t datalen : 12;
uint32_t unused : 5;
uint32_t sub_sof : 1;
uint32_t eof : 1;
volatile uint32_t owner : 1; // DMA can change this value
uint32_t * buf_ptr;
struct slc_queue_item * next_link_ptr;
} slc_queue_item_t;
typedef struct i2s_state {
uint32_t * slc_queue[SLC_BUF_CNT];
volatile uint8_t slc_queue_len;
uint32_t * slc_buf_pntr[SLC_BUF_CNT]; // Pointer to the I2S DMA buffer data
slc_queue_item_t slc_items[SLC_BUF_CNT]; // I2S DMA buffer descriptors
uint32_t * curr_slc_buf; // Current buffer for writing
uint32_t curr_slc_buf_pos; // Position in the current buffer
void (*callback) (void);
// Callback function should be defined as 'void ICACHE_RAM_ATTR function_name()',
// and be placed in IRAM for faster execution. Avoid long computational tasks in this
// function, use it to set flags and process later.
} i2s_state_t;
// RX = I2S receive (i.e. microphone), TX = I2S transmit (i.e. DAC)
static i2s_state_t *rx = NULL;
static i2s_state_t *tx = NULL;
// Last I2S sample rate requested
static uint32_t _i2s_sample_rate;
// IOs used for I2S. Not defined in i2s.h, unfortunately.
// Note these are internal GPIO numbers and not pins on an
// Arduino board. Users need to verify their particular wiring.
#define I2SO_DATA 3
#define I2SO_BCK 15
#define I2SO_WS 2
#define I2SI_DATA 12
#define I2SI_BCK 13
#define I2SI_WS 14
static bool _i2s_is_full(const i2s_state_t *ch) {
if (!ch) {
return false;
}
return (ch->curr_slc_buf_pos==SLC_BUF_LEN || ch->curr_slc_buf==NULL) && (ch->slc_queue_len == 0);
}
bool i2s_is_full() {
return _i2s_is_full( tx );
}
bool i2s_rx_is_full() {
return _i2s_is_full( rx );
}
static bool _i2s_is_empty(const i2s_state_t *ch) {
if (!ch) {
return false;
}
return (ch->slc_queue_len >= SLC_BUF_CNT-1);
}
bool i2s_is_empty() {
return _i2s_is_empty( tx );
}
bool i2s_rx_is_empty() {
return _i2s_is_empty( rx );
}
static uint16_t _i2s_available(const i2s_state_t *ch) {
if (!ch) {
return 0;
}
return (SLC_BUF_CNT - ch->slc_queue_len) * SLC_BUF_LEN;
}
uint16_t i2s_available(){
return _i2s_available( tx );
}
uint16_t i2s_rx_available(){
return _i2s_available( rx );
}
// Pop the top off of the queue and return it
static uint32_t * ICACHE_RAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) {
uint8_t i;
uint32_t *item = ch->slc_queue[0];
ch->slc_queue_len--;
for ( i = 0; i < ch->slc_queue_len; i++) {
ch->slc_queue[i] = ch->slc_queue[i+1];
}
return item;
}
// Append an item to the end of the queue from receive
static void ICACHE_RAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t *item) {
// Shift everything up, except for the one corresponding to this item
for (int i=0, dest=0; i < ch->slc_queue_len; i++) {
if (ch->slc_queue[i] != item) {
ch->slc_queue[dest++] = ch->slc_queue[i];
}
}
if (ch->slc_queue_len < SLC_BUF_CNT - 1) {
ch->slc_queue[ch->slc_queue_len++] = item;
} else {
ch->slc_queue[ch->slc_queue_len] = item;
}
}
static void ICACHE_RAM_ATTR i2s_slc_isr(void) {
ETS_SLC_INTR_DISABLE();
uint32_t slc_intr_status = SLCIS;
SLCIC = 0xFFFFFFFF;
if (slc_intr_status & SLCIRXEOF) {
slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCRXEDA;
// Zero the buffer so it is mute in case of underflow
ets_memset((void *)finished_item->buf_ptr, 0x00, SLC_BUF_LEN * 4);
if (tx->slc_queue_len >= SLC_BUF_CNT-1) {
// All buffers are empty. This means we have an underflow
i2s_slc_queue_next_item(tx); // Free space for finished_item
}
tx->slc_queue[tx->slc_queue_len++] = finished_item->buf_ptr;
if (tx->callback) {
tx->callback();
}
}
if (slc_intr_status & SLCITXEOF) {
slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCTXEDA;
// Set owner back to 1 (SW) or else RX stops. TX has no such restriction.
finished_item->owner = 1;
i2s_slc_queue_append_item(rx, finished_item->buf_ptr);
if (rx->callback) {
rx->callback();
}
}
ETS_SLC_INTR_ENABLE();
}
void i2s_set_callback(void (*callback) (void)) {
tx->callback = callback;
}
void i2s_rx_set_callback(void (*callback) (void)) {
rx->callback = callback;
}
static bool _alloc_channel(i2s_state_t *ch) {
ch->slc_queue_len = 0;
for (int x=0; x<SLC_BUF_CNT; x++) {
ch->slc_buf_pntr[x] = (uint32_t *)malloc(SLC_BUF_LEN * sizeof(ch->slc_buf_pntr[0][0]));
if (!ch->slc_buf_pntr[x]) {
// OOM, the upper layer will free up any partially allocated channels.
return false;
}
memset(ch->slc_buf_pntr[x], 0, SLC_BUF_LEN * sizeof(ch->slc_buf_pntr[x][0]));
ch->slc_items[x].unused = 0;
ch->slc_items[x].owner = 1;
ch->slc_items[x].eof = 1;
ch->slc_items[x].sub_sof = 0;
ch->slc_items[x].datalen = SLC_BUF_LEN * 4;
ch->slc_items[x].blocksize = SLC_BUF_LEN * 4;
ch->slc_items[x].buf_ptr = (uint32_t*)&ch->slc_buf_pntr[x][0];
ch->slc_items[x].next_link_ptr = (x<(SLC_BUF_CNT-1))?(&ch->slc_items[x+1]):(&ch->slc_items[0]);
}
return true;
}
static bool i2s_slc_begin() {
if (tx) {
if (!_alloc_channel(tx)) {
return false;
}
}
if (rx) {
if (!_alloc_channel(rx)) {
return false;
}
}
ETS_SLC_INTR_DISABLE();
SLCC0 |= SLCRXLR | SLCTXLR;
SLCC0 &= ~(SLCRXLR | SLCTXLR);
SLCIC = 0xFFFFFFFF;
// Configure DMA
SLCC0 &= ~(SLCMM << SLCM); // Clear DMA MODE
SLCC0 |= (1 << SLCM); // Set DMA MODE to 1
SLCRXDC |= SLCBINR | SLCBTNR; // Enable INFOR_NO_REPLACE and TOKEN_NO_REPLACE
SLCRXDC &= ~(SLCBRXFE | SLCBRXEM | SLCBRXFM); // Disable RX_FILL, RX_EOF_MODE and RX_FILL_MODE
//Feed DMA the 1st buffer desc addr
//To send data to the I2S subsystem, counter-intuitively we use the RXLINK part, not the TXLINK as you might
//expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw
//an error at us otherwise. Just feed it any random descriptor.
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
if (!rx) {
SLCTXL |= (uint32)&tx->slc_items[1] << SLCTXLA; // Set fake (unused) RX descriptor address
} else {
SLCTXL |= (uint32)&rx->slc_items[0] << SLCTXLA; // Set real RX address
}
if (!tx) {
SLCRXL |= (uint32)&rx->slc_items[1] << SLCRXLA; // Set fake (unused) TX descriptor address
} else {
SLCRXL |= (uint32)&tx->slc_items[0] << SLCRXLA; // Set real TX address
}
ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL);
SLCIE = (tx?SLCIRXEOF:0) | (rx?SLCITXEOF:0); // Enable appropriate EOF IRQ
ETS_SLC_INTR_ENABLE();
// Start transmission ("TX" DMA always needed to be enabled)
SLCTXL |= SLCTXLS;
if (tx) {
SLCRXL |= SLCRXLS;
}
return true;
}
static void i2s_slc_end(){
ETS_SLC_INTR_DISABLE();
SLCIC = 0xFFFFFFFF;
SLCIE = 0;
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
for (int x = 0; x<SLC_BUF_CNT; x++) {
if (tx) {
free(tx->slc_buf_pntr[x]);
tx->slc_buf_pntr[x] = NULL;
}
if (rx) {
free(rx->slc_buf_pntr[x]);
rx->slc_buf_pntr[x] = NULL;
}
}
}
// These routines push a single, 32-bit sample to the I2S buffers. Call at (on average)
// at least the current sample rate.
static bool _i2s_write_sample(uint32_t sample, bool nb) {
if (!tx) {
return false;
}
if (tx->curr_slc_buf_pos==SLC_BUF_LEN || tx->curr_slc_buf==NULL) {
if (tx->slc_queue_len == 0) {
if (nb) {
// Don't wait if nonblocking, just notify upper levels
return false;
}
while (1) {
if (tx->slc_queue_len > 0) {
break;
} else {
optimistic_yield(10000);
}
}
}
ETS_SLC_INTR_DISABLE();
tx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx);
ETS_SLC_INTR_ENABLE();
tx->curr_slc_buf_pos=0;
}
tx->curr_slc_buf[tx->curr_slc_buf_pos++]=sample;
return true;
}
bool i2s_write_sample(uint32_t sample) {
return _i2s_write_sample(sample, false);
}
bool i2s_write_sample_nb(uint32_t sample) {
return _i2s_write_sample(sample, true);
}
bool i2s_write_lr(int16_t left, int16_t right){
int sample = right & 0xFFFF;
sample = sample << 16;
sample |= left & 0xFFFF;
return i2s_write_sample(sample);
}
// writes a buffer of frames into the DMA memory, returns the amount of frames written
// A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel.
static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb) {
uint16_t frames_written=0;
while(frame_count>0) {
// make sure we have room in the current buffer
if (tx->curr_slc_buf_pos==SLC_BUF_LEN || tx->curr_slc_buf==NULL) {
// no room in the current buffer? if there are no buffers available then exit
if (tx->slc_queue_len == 0)
{
if (nb) {
// if nonblocking just return the number of frames written so far
break;
}
else {
while (1) {
if (tx->slc_queue_len > 0) {
break;
} else {
optimistic_yield(10000);
}
}
}
}
// get a new buffer
ETS_SLC_INTR_DISABLE();
tx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx);
ETS_SLC_INTR_ENABLE();
tx->curr_slc_buf_pos=0;
}
//space available in the current buffer
uint16_t available = SLC_BUF_LEN - tx->curr_slc_buf_pos;
uint16_t fc = (available < frame_count) ? available : frame_count;
if (mono) {
for(uint16_t i=0;i<fc;i++){
uint16_t v = (uint16_t)(*frames++);
tx->curr_slc_buf[tx->curr_slc_buf_pos++] = (v << 16) | v;
}
}
else
{
for(uint16_t i=0;i<fc;i++){
uint16_t v1 = (uint16_t)(*frames++);
uint16_t v2 = (uint16_t)(*frames++);
tx->curr_slc_buf[tx->curr_slc_buf_pos++] = (v1 << 16) | v2;
}
}
frame_count -= fc;
frames_written += fc;
}
return frames_written;
}
uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); }
uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); }
uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); }
uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); }
bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) {
if (!rx) {
return false;
}
if (rx->curr_slc_buf_pos==SLC_BUF_LEN || rx->curr_slc_buf==NULL) {
if (rx->slc_queue_len == 0) {
if (!blocking) {
return false;
}
while (1) {
if (rx->slc_queue_len > 0){
break;
} else {
optimistic_yield(10000);
}
}
}
ETS_SLC_INTR_DISABLE();
rx->curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(rx);
ETS_SLC_INTR_ENABLE();
rx->curr_slc_buf_pos=0;
}
uint32_t sample = rx->curr_slc_buf[rx->curr_slc_buf_pos++];
if (left) {
*left = sample & 0xffff;
}
if (right) {
*right = sample >> 16;
}
return true;
}
void i2s_set_rate(uint32_t rate) { //Rate in HZ
if (rate == _i2s_sample_rate) {
return;
}
_i2s_sample_rate = rate;
uint32_t scaled_base_freq = I2SBASEFREQ/32;
float delta_best = scaled_base_freq;
uint8_t sbd_div_best=1;
uint8_t scd_div_best=1;
for (uint8_t i=1; i<64; i++) {
for (uint8_t j=i; j<64; j++) {
float new_delta = fabs(((float)scaled_base_freq/i/j) - rate);
if (new_delta < delta_best){
delta_best = new_delta;
sbd_div_best = i;
scd_div_best = j;
}
}
}
i2s_set_dividers( sbd_div_best, scd_div_best );
}
void i2s_set_dividers(uint8_t div1, uint8_t div2) {
// Ensure dividers fit in bit fields
div1 &= I2SBDM;
div2 &= I2SCDM;
// trans master(active low), recv master(active_low), !bits mod(==16 bits/chanel), clear clock dividers
I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
// I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left)
// I2SMR = MSB recv/xmit first
// I2SRMS, I2STMS = 1-bit delay from WS to MSB (I2S format)
// div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this
I2SC |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD);
}
float i2s_get_real_rate(){
return (float)I2SBASEFREQ/32/((I2SC>>I2SBD) & I2SBDM)/((I2SC >> I2SCD) & I2SCDM);
}
bool i2s_rxtx_begin(bool enableRx, bool enableTx) {
if (tx || rx) {
i2s_end(); // Stop and free any ongoing stuff
}
if (enableTx) {
tx = (i2s_state_t*)calloc(1, sizeof(*tx));
if (!tx) {
// Nothing to clean up yet
return false; // OOM Error!
}
pinMode(I2SO_WS, FUNCTION_1);
pinMode(I2SO_DATA, FUNCTION_1);
pinMode(I2SO_BCK, FUNCTION_1);
}
if (enableRx) {
rx = (i2s_state_t*)calloc(1, sizeof(*rx));
if (!rx) {
i2s_end(); // Clean up any TX or pin changes
return false; // OOM error!
}
pinMode(I2SI_WS, OUTPUT);
pinMode(I2SI_BCK, OUTPUT);
pinMode(I2SI_DATA, INPUT);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_I2SI_DATA);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_I2SI_BCK);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_I2SI_WS);
}
_i2s_sample_rate = 0;
if (!i2s_slc_begin()) {
// OOM in SLC memory allocations, tear it all down and abort!
i2s_end();
return false;
}
I2S_CLK_ENABLE();
I2SIC = 0x3F;
I2SIE = 0;
// Reset I2S
I2SC &= ~(I2SRST);
I2SC |= I2SRST;
I2SC &= ~(I2SRST);
// I2STXFMM, I2SRXFMM=0 => 16-bit, dual channel data shifted in/out
I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only)
I2SFC |= I2SDE; // Enable DMA
// I2STXCMM, I2SRXCMM=0 => Dual channel mode
I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); // Set RX/TX CHAN_MOD=0
i2s_set_rate(44100);
if (rx) {
// Need to prime the # of samples to receive in the engine
I2SRXEN = SLC_BUF_LEN;
}
I2SC |= (rx?I2SRXS:0) | (tx?I2STXS:0); // Start transmission/reception
return true;
}
void i2s_begin() {
i2s_rxtx_begin(false, true);
}
void i2s_end() {
// Disable any I2S send or receive
// ? Maybe not needed since we're resetting on the next line...
I2SC &= ~(I2STXS | I2SRXS);
// Reset I2S
I2SC &= ~(I2SRST);
I2SC |= I2SRST;
I2SC &= ~(I2SRST);
i2s_slc_end();
if (tx) {
pinMode(I2SO_DATA, INPUT);
pinMode(I2SO_BCK, INPUT);
pinMode(I2SO_WS, INPUT);
free(tx);
tx = NULL;
}
if (rx) {
pinMode(I2SI_DATA, INPUT);
pinMode(I2SI_BCK, INPUT);
pinMode(I2SI_WS, INPUT);
free(rx);
rx = NULL;
}
}
};