Skip to content

Commit decfbdd

Browse files
earlephilhowerdevyte
authored andcommitted
I2S driver fixes for IRQs, protocol, factoring (#4574)
* I2S driver fixes for IRQs, protocol, factoring All redundant ICACHE_FLASH_ATTR decorators were removed, we already do this by default for all routines, anyway, The actual ISR and its called function moved to to IRAM. Used to be in flash due to the decorator, which could lead to crashes. Use ets_memset to mute buffers in ISR. Fix the I2S on-the-wire protocol by enabling the transmit delay I2STMS because I2S is supposed to send the MSB one clock after LRCLK toggles. This was causing I2S to be twice as loud as intended in the best of cases, and causing garbage/noise output when the MSB was set since data was effectively shifted. Refactor the clock divider setting to be done in one function only, as there is no reason to do the same complicated bit setting in two spots. * Comment some add'l registers, use optimstic_yield Comment the known and unknown I2S register settings for posterity, using the ESP32 guide as a basis. Use optimistic_yield() instead of esp_wdt_disable/enable when busy waiting in blocking writes to ensure we don't hog the CPU completely. Move the constant IO pins to #defines for easier understanding.
1 parent a3a3654 commit decfbdd

File tree

1 file changed

+49
-38
lines changed

1 file changed

+49
-38
lines changed

cores/esp8266/core_esp8266_i2s.c

+49-38
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@
2727
#include "i2s_reg.h"
2828
#include "i2s.h"
2929

30-
extern void ets_wdt_enable(void);
31-
extern void ets_wdt_disable(void);
30+
// IOs used for I2S. Not defined in i2s.h, unfortunately.
31+
// Note these are internal IOs numbers and not pins on an
32+
// Arduino board. Users need to verify their particular wiring.
33+
#define I2SO_WS 2
34+
#define I2SO_DATA 3
35+
#define I2SO_BCK 15
3236

3337
#define SLC_BUF_CNT (8) //Number of buffers in the I2S circular buffer
3438
#define SLC_BUF_LEN (64) //Length of one buffer, in 32-bit words.
@@ -56,21 +60,21 @@ static uint32_t *i2s_slc_buf_pntr[SLC_BUF_CNT]; //Pointer to the I2S DMA buffer
5660
static struct slc_queue_item i2s_slc_items[SLC_BUF_CNT]; //I2S DMA buffer descriptors
5761
static uint32_t *i2s_curr_slc_buf=NULL;//current buffer for writing
5862
static int i2s_curr_slc_buf_pos=0; //position in the current buffer
59-
static void (*i2s_callback) (void)=0; //Callback function should be defined as 'void ICACHE_FLASH_ATTR function_name()', placing the function in IRAM for faster execution. Avoid long computational tasks in this function, use it to set flags and process later.
63+
static void (*i2s_callback) (void)=0; //Callback function should be defined as 'void ICACHE_RAM_ATTR function_name()', placing the function in IRAM for faster execution. Avoid long computational tasks in this function, use it to set flags and process later.
6064

61-
bool ICACHE_FLASH_ATTR i2s_is_full(){
65+
bool i2s_is_full(){
6266
return (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) && (i2s_slc_queue_len == 0);
6367
}
6468

65-
bool ICACHE_FLASH_ATTR i2s_is_empty(){
69+
bool i2s_is_empty(){
6670
return (i2s_slc_queue_len >= SLC_BUF_CNT-1);
6771
}
6872

69-
int16_t ICACHE_FLASH_ATTR i2s_available(){
70-
return (SLC_BUF_CNT - i2s_slc_queue_len) * SLC_BUF_LEN;
73+
int16_t i2s_available(){
74+
return (SLC_BUF_CNT - i2s_slc_queue_len) * SLC_BUF_LEN;
7175
}
7276

73-
uint32_t ICACHE_FLASH_ATTR i2s_slc_queue_next_item(){ //pop the top off the queue
77+
uint32_t ICACHE_RAM_ATTR i2s_slc_queue_next_item(){ //pop the top off the queue
7478
uint8_t i;
7579
uint32_t item = i2s_slc_queue[0];
7680
i2s_slc_queue_len--;
@@ -82,13 +86,13 @@ uint32_t ICACHE_FLASH_ATTR i2s_slc_queue_next_item(){ //pop the top off the queu
8286
//This routine is called as soon as the DMA routine has something to tell us. All we
8387
//handle here is the RX_EOF_INT status, which indicate the DMA has sent a buffer whose
8488
//descriptor has the 'EOF' field set to 1.
85-
void ICACHE_FLASH_ATTR i2s_slc_isr(void) {
89+
void ICACHE_RAM_ATTR i2s_slc_isr(void) {
8690
uint32_t slc_intr_status = SLCIS;
8791
SLCIC = 0xFFFFFFFF;
8892
if (slc_intr_status & SLCIRXEOF) {
8993
ETS_SLC_INTR_DISABLE();
9094
struct slc_queue_item *finished_item = (struct slc_queue_item*)SLCRXEDA;
91-
memset((void *)finished_item->buf_ptr, 0x00, SLC_BUF_LEN * 4);//zero the buffer so it is mute in case of underflow
95+
ets_memset((void *)finished_item->buf_ptr, 0x00, SLC_BUF_LEN * 4);//zero the buffer so it is mute in case of underflow
9296
if (i2s_slc_queue_len >= SLC_BUF_CNT-1) { //All buffers are empty. This means we have an underflow
9397
i2s_slc_queue_next_item(); //free space for finished_item
9498
}
@@ -102,7 +106,7 @@ void i2s_set_callback(void (*callback) (void)){
102106
i2s_callback = callback;
103107
}
104108

105-
void ICACHE_FLASH_ATTR i2s_slc_begin(){
109+
void i2s_slc_begin(){
106110
i2s_slc_queue_len = 0;
107111
int x, y;
108112

@@ -150,7 +154,7 @@ void ICACHE_FLASH_ATTR i2s_slc_begin(){
150154
SLCRXL |= SLCRXLS;
151155
}
152156

153-
void ICACHE_FLASH_ATTR i2s_slc_end(){
157+
void i2s_slc_end(){
154158
ETS_SLC_INTR_DISABLE();
155159
SLCIC = 0xFFFFFFFF;
156160
SLCIE = 0;
@@ -166,15 +170,14 @@ void ICACHE_FLASH_ATTR i2s_slc_end(){
166170
//at least the current sample rate. You can also call it quicker: it will suspend the calling
167171
//thread if the buffer is full and resume when there's room again.
168172

169-
bool ICACHE_FLASH_ATTR i2s_write_sample(uint32_t sample) {
173+
bool i2s_write_sample(uint32_t sample) {
170174
if (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) {
171175
if(i2s_slc_queue_len == 0){
172176
while(1){
173177
if(i2s_slc_queue_len > 0){
174178
break;
175179
} else {
176-
ets_wdt_disable();
177-
ets_wdt_enable();
180+
optimistic_yield(10000);
178181
}
179182
}
180183
}
@@ -187,7 +190,7 @@ bool ICACHE_FLASH_ATTR i2s_write_sample(uint32_t sample) {
187190
return true;
188191
}
189192

190-
bool ICACHE_FLASH_ATTR i2s_write_sample_nb(uint32_t sample) {
193+
bool i2s_write_sample_nb(uint32_t sample) {
191194
if (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) {
192195
if(i2s_slc_queue_len == 0){
193196
return false;
@@ -201,7 +204,7 @@ bool ICACHE_FLASH_ATTR i2s_write_sample_nb(uint32_t sample) {
201204
return true;
202205
}
203206

204-
bool ICACHE_FLASH_ATTR i2s_write_lr(int16_t left, int16_t right){
207+
bool i2s_write_lr(int16_t left, int16_t right){
205208
int sample = right & 0xFFFF;
206209
sample = sample << 16;
207210
sample |= left & 0xFFFF;
@@ -215,7 +218,7 @@ bool ICACHE_FLASH_ATTR i2s_write_lr(int16_t left, int16_t right){
215218

216219
static uint32_t _i2s_sample_rate;
217220

218-
void ICACHE_FLASH_ATTR i2s_set_rate(uint32_t rate){ //Rate in HZ
221+
void i2s_set_rate(uint32_t rate){ //Rate in HZ
219222
if(rate == _i2s_sample_rate) return;
220223
_i2s_sample_rate = rate;
221224

@@ -235,60 +238,68 @@ void ICACHE_FLASH_ATTR i2s_set_rate(uint32_t rate){ //Rate in HZ
235238
}
236239
}
237240

238-
//os_printf("Rate %u Div %u Bck %u Frq %u\n", _i2s_sample_rate, i2s_clock_div, i2s_bck_div, I2SBASEFREQ/(i2s_clock_div*i2s_bck_div*2));
239-
240-
//!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right
241-
I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
242-
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | ((sbd_div_best) << I2SBD) | ((scd_div_best) << I2SCD);
241+
i2s_set_dividers( sbd_div_best, scd_div_best );
243242
}
244243

245-
void ICACHE_FLASH_ATTR i2s_set_dividers(uint8_t div1, uint8_t div2){
244+
void i2s_set_dividers(uint8_t div1, uint8_t div2) {
245+
// Ensure dividers fit in bit fields
246246
div1 &= I2SBDM;
247247
div2 &= I2SCDM;
248248

249+
// !trans master(?), !bits mod(==16 bits/chanel), clear clock dividers
249250
I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
250-
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | (div1 << I2SBD) | (div2 << I2SCD);
251+
252+
// I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left)
253+
// I2SMR = MSB recv/xmit first
254+
// I2SRSM = Receive slave mode (?)
255+
// I2SRMS, I2STMS = 1-bit delay from WS to MSB (I2S format)
256+
// div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this
257+
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD);
251258
}
252259

253-
float ICACHE_FLASH_ATTR i2s_get_real_rate(){
260+
float i2s_get_real_rate(){
254261
return (float)I2SBASEFREQ/32/((I2SC>>I2SBD) & I2SBDM)/((I2SC >> I2SCD) & I2SCDM);
255262
}
256263

257-
void ICACHE_FLASH_ATTR i2s_begin(){
264+
void i2s_begin() {
258265
_i2s_sample_rate = 0;
259266
i2s_slc_begin();
260-
261-
pinMode(2, FUNCTION_1); //I2SO_WS (LRCK)
262-
pinMode(3, FUNCTION_1); //I2SO_DATA (SDIN)
263-
pinMode(15, FUNCTION_1); //I2SO_BCK (SCLK)
267+
268+
// Redirect control of IOs to the I2S block
269+
pinMode(I2SO_WS, FUNCTION_1);
270+
pinMode(I2SO_DATA, FUNCTION_1);
271+
pinMode(I2SO_BCK, FUNCTION_1);
264272

265273
I2S_CLK_ENABLE();
266274
I2SIC = 0x3F;
267275
I2SIE = 0;
268276

269-
//Reset I2S
277+
// Reset I2S
270278
I2SC &= ~(I2SRST);
271279
I2SC |= I2SRST;
272280
I2SC &= ~(I2SRST);
273-
274-
I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); //Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only)
281+
282+
// I2STXFMM, I2SRXFMM=0 => 16-bit, dual channel data shifted in/out
283+
I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); //Set RX/TX FIFO_MOD=0 (16-bit) and disable DMA (FIFO only)
275284
I2SFC |= I2SDE; //Enable DMA
285+
// I2STXCMM, I2SRXCMM=0 => Dual channel mode
276286
I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); //Set RX/TX CHAN_MOD=0
277287
i2s_set_rate(44100);
278288
I2SC |= I2STXS; //Start transmission
279289
}
280290

281-
void ICACHE_FLASH_ATTR i2s_end(){
291+
void i2s_end(){
282292
I2SC &= ~I2STXS;
283293

284294
//Reset I2S
285295
I2SC &= ~(I2SRST);
286296
I2SC |= I2SRST;
287297
I2SC &= ~(I2SRST);
288298

289-
pinMode(2, INPUT);
290-
pinMode(3, INPUT);
291-
pinMode(15, INPUT);
299+
// Redirect IOs to user control/GPIO
300+
pinMode(I2SO_WS, INPUT);
301+
pinMode(I2SO_DATA, INPUT);
302+
pinMode(I2SO_BCK, INPUT);
292303

293304
i2s_slc_end();
294305
}

0 commit comments

Comments
 (0)