Skip to content

Commit a2d93a2

Browse files
Migrate to i2s_struct_t to make I2S RX simpler
Much of the I2S TX infrastructure can be used to handle I2S input, so move the per-channel data structures to a struct for easier changes.
1 parent 6d3f0b4 commit a2d93a2

File tree

1 file changed

+115
-69
lines changed

1 file changed

+115
-69
lines changed

cores/esp8266/core_esp8266_i2s.c

Lines changed: 115 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -39,43 +39,78 @@ extern void ets_wdt_disable(void);
3939
//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
4040
//simultaneously.
4141

42-
struct slc_queue_item {
43-
uint32 blocksize:12;
44-
uint32 datalen:12;
45-
uint32 unused:5;
46-
uint32 sub_sof:1;
47-
uint32 eof:1;
48-
uint32 owner:1;
49-
uint32 buf_ptr;
50-
uint32 next_link_ptr;
51-
};
52-
53-
static uint32_t i2s_slc_queue[SLC_BUF_CNT-1];
54-
static uint8_t i2s_slc_queue_len;
55-
static uint32_t *i2s_slc_buf_pntr[SLC_BUF_CNT]; //Pointer to the I2S DMA buffer data
56-
static struct slc_queue_item i2s_slc_items[SLC_BUF_CNT]; //I2S DMA buffer descriptors
57-
static uint32_t *i2s_curr_slc_buf=NULL;//current buffer for writing
58-
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.
60-
61-
bool ICACHE_FLASH_ATTR i2s_is_full(){
62-
return (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) && (i2s_slc_queue_len == 0);
42+
typedef struct slc_queue_item {
43+
uint32_t blocksize:12;
44+
uint32_t datalen:12;
45+
uint32_t unused:5;
46+
uint32_t sub_sof:1;
47+
uint32_t eof:1;
48+
uint32_t owner:1;
49+
uint32_t buf_ptr;
50+
uint32_t next_link_ptr;
51+
} slc_queue_item_t;
52+
53+
typedef struct i2s_state {
54+
uint32_t i2s_slc_queue[SLC_BUF_CNT-1];
55+
uint8_t i2s_slc_queue_len;
56+
uint32_t * i2s_slc_buf_pntr[SLC_BUF_CNT]; //Pointer to the I2S DMA buffer data
57+
slc_queue_item_t i2s_slc_items[SLC_BUF_CNT]; //I2S DMA buffer descriptors
58+
uint32_t * i2s_curr_slc_buf; // Current buffer for writing
59+
uint32_t i2s_curr_slc_buf_pos; // Position in the current buffer
60+
void (*i2s_callback) (void);
61+
// Callback function should be defined as 'void ICACHE_FLASH_ATTR function_name()',
62+
// and be placed in IRAM for faster execution. Avoid long computational tasks in this
63+
// function, use it to set flags and process later.
64+
} i2s_state_t;
65+
66+
// RX = I2S receive (i.e. microphone), TX = I2S transmit (i.e. DAC)
67+
static i2s_state_t *rx = NULL;
68+
static i2s_state_t *tx = NULL;
69+
70+
static bool ICACHE_FLASH_ATTR _i2s_is_full(const i2s_state_t *ch) {
71+
return (ch->i2s_curr_slc_buf_pos==SLC_BUF_LEN || ch->i2s_curr_slc_buf==NULL) && (ch->i2s_slc_queue_len == 0);
6372
}
6473

65-
bool ICACHE_FLASH_ATTR i2s_is_empty(){
66-
return (i2s_slc_queue_len >= SLC_BUF_CNT-1);
74+
bool ICACHE_FLASH_ATTR i2s_is_full() {
75+
return _i2s_is_full( tx );
76+
}
77+
78+
bool ICACHE_FLASH_ATTR i2s_rx_is_full() {
79+
return _i2s_is_full( rx );
80+
}
81+
82+
static bool ICACHE_FLASH_ATTR _i2s_is_empty(const i2s_state_t *ch) {
83+
return (ch->i2s_slc_queue_len >= SLC_BUF_CNT-1);
84+
}
85+
86+
bool ICACHE_FLASH_ATTR i2s_is_empty() {
87+
return _i2s_is_empty( tx );
88+
}
89+
90+
bool ICACHE_FLASH_ATTR i2s_rx_is_empty() {
91+
return _i2s_is_empty( rx );
92+
}
93+
94+
static int16_t ICACHE_FLASH_ATTR _i2s_available(const i2s_state_t *ch) {
95+
return (SLC_BUF_CNT - ch->i2s_slc_queue_len) * SLC_BUF_LEN;
6796
}
6897

6998
int16_t ICACHE_FLASH_ATTR i2s_available(){
70-
return (SLC_BUF_CNT - i2s_slc_queue_len) * SLC_BUF_LEN;
99+
return _i2s_available( tx );
71100
}
72101

73-
uint32_t ICACHE_FLASH_ATTR i2s_slc_queue_next_item(){ //pop the top off the queue
102+
int16_t ICACHE_FLASH_ATTR i2s_rx_available(){
103+
return _i2s_available( rx );
104+
}
105+
106+
// Pop the top off of the queue and return it
107+
uint32_t ICACHE_FLASH_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) {
74108
uint8_t i;
75-
uint32_t item = i2s_slc_queue[0];
76-
i2s_slc_queue_len--;
77-
for(i=0;i<i2s_slc_queue_len;i++)
78-
i2s_slc_queue[i] = i2s_slc_queue[i+1];
109+
uint32_t item = ch->i2s_slc_queue[0];
110+
ch->i2s_slc_queue_len--;
111+
for ( i = 0; i < ch->i2s_slc_queue_len; i++) {
112+
ch->i2s_slc_queue[i] = ch->i2s_slc_queue[i+1];
113+
}
79114
return item;
80115
}
81116

@@ -87,37 +122,41 @@ void ICACHE_FLASH_ATTR i2s_slc_isr(void) {
87122
SLCIC = 0xFFFFFFFF;
88123
if (slc_intr_status & SLCIRXEOF) {
89124
ETS_SLC_INTR_DISABLE();
90-
struct slc_queue_item *finished_item = (struct slc_queue_item*)SLCRXEDA;
125+
slc_queue_item_t *finished_item = (slc_queue_item_t *)SLCRXEDA;
91126
memset((void *)finished_item->buf_ptr, 0x00, SLC_BUF_LEN * 4);//zero the buffer so it is mute in case of underflow
92-
if (i2s_slc_queue_len >= SLC_BUF_CNT-1) { //All buffers are empty. This means we have an underflow
93-
i2s_slc_queue_next_item(); //free space for finished_item
127+
if (tx->i2s_slc_queue_len >= SLC_BUF_CNT-1) { //All buffers are empty. This means we have an underflow
128+
i2s_slc_queue_next_item(tx); //free space for finished_item
94129
}
95-
i2s_slc_queue[i2s_slc_queue_len++] = finished_item->buf_ptr;
96-
if (i2s_callback) i2s_callback();
130+
tx->i2s_slc_queue[tx->i2s_slc_queue_len++] = finished_item->buf_ptr;
131+
if (tx->i2s_callback) tx->i2s_callback();
97132
ETS_SLC_INTR_ENABLE();
98133
}
99134
}
100135

101136
void i2s_set_callback(void (*callback) (void)){
102-
i2s_callback = callback;
137+
tx->i2s_callback = callback;
138+
}
139+
140+
void i2s_rx_set_callback(void (*callback) (void)){
141+
rx->i2s_callback = callback;
103142
}
104143

105-
void ICACHE_FLASH_ATTR i2s_slc_begin(){
106-
i2s_slc_queue_len = 0;
144+
void ICACHE_FLASH_ATTR i2s_slc_begin() {
145+
tx->i2s_slc_queue_len = 0;
107146
int x, y;
108147

109148
for (x=0; x<SLC_BUF_CNT; x++) {
110-
i2s_slc_buf_pntr[x] = malloc(SLC_BUF_LEN*4);
111-
for (y=0; y<SLC_BUF_LEN; y++) i2s_slc_buf_pntr[x][y] = 0;
112-
113-
i2s_slc_items[x].unused = 0;
114-
i2s_slc_items[x].owner = 1;
115-
i2s_slc_items[x].eof = 1;
116-
i2s_slc_items[x].sub_sof = 0;
117-
i2s_slc_items[x].datalen = SLC_BUF_LEN*4;
118-
i2s_slc_items[x].blocksize = SLC_BUF_LEN*4;
119-
i2s_slc_items[x].buf_ptr = (uint32_t)&i2s_slc_buf_pntr[x][0];
120-
i2s_slc_items[x].next_link_ptr = (int)((x<(SLC_BUF_CNT-1))?(&i2s_slc_items[x+1]):(&i2s_slc_items[0]));
149+
tx->i2s_slc_buf_pntr[x] = malloc(SLC_BUF_LEN*4);
150+
for (y=0; y<SLC_BUF_LEN; y++) tx->i2s_slc_buf_pntr[x][y] = 0;
151+
152+
tx->i2s_slc_items[x].unused = 0;
153+
tx->i2s_slc_items[x].owner = 1;
154+
tx->i2s_slc_items[x].eof = 1;
155+
tx->i2s_slc_items[x].sub_sof = 0;
156+
tx->i2s_slc_items[x].datalen = SLC_BUF_LEN*4;
157+
tx->i2s_slc_items[x].blocksize = SLC_BUF_LEN*4;
158+
tx->i2s_slc_items[x].buf_ptr = (uint32_t)&tx->i2s_slc_buf_pntr[x][0];
159+
tx->i2s_slc_items[x].next_link_ptr = (int)((x<(SLC_BUF_CNT-1))?(&tx->i2s_slc_items[x+1]):(&tx->i2s_slc_items[0]));
121160
}
122161

123162
ETS_SLC_INTR_DISABLE();
@@ -136,9 +175,9 @@ void ICACHE_FLASH_ATTR i2s_slc_begin(){
136175
//expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw
137176
//an error at us otherwise. Just feed it any random descriptor.
138177
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
139-
SLCTXL |= (uint32)&i2s_slc_items[1] << SLCTXLA; //set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid
178+
SLCTXL |= (uint32)&tx->i2s_slc_items[1] << SLCTXLA; //set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid
140179
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
141-
SLCRXL |= (uint32)&i2s_slc_items[0] << SLCRXLA; //set RX descriptor address
180+
SLCRXL |= (uint32)&tx->i2s_slc_items[0] << SLCRXLA; //set RX descriptor address
142181

143182
ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL);
144183
SLCIE = SLCIRXEOF; //Enable only for RX EOF interrupt
@@ -158,19 +197,20 @@ void ICACHE_FLASH_ATTR i2s_slc_end(){
158197
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
159198

160199
for (int x = 0; x<SLC_BUF_CNT; x++) {
161-
free(i2s_slc_buf_pntr[x]);
200+
free(tx->i2s_slc_buf_pntr[x]);
162201
}
163202
}
164203

165204
//This routine pushes a single, 32-bit sample to the I2S buffers. Call this at (on average)
166205
//at least the current sample rate. You can also call it quicker: it will suspend the calling
167206
//thread if the buffer is full and resume when there's room again.
168207

169-
bool ICACHE_FLASH_ATTR i2s_write_sample(uint32_t sample) {
170-
if (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) {
171-
if(i2s_slc_queue_len == 0){
208+
bool ICACHE_FLASH_ATTR _i2s_write_sample(uint32_t sample, bool nb) {
209+
if (tx->i2s_curr_slc_buf_pos==SLC_BUF_LEN || tx->i2s_curr_slc_buf==NULL) {
210+
if (tx->i2s_slc_queue_len == 0) {
211+
if (nb) return false;
172212
while(1){
173-
if(i2s_slc_queue_len > 0){
213+
if(tx->i2s_slc_queue_len > 0){
174214
break;
175215
} else {
176216
ets_wdt_disable();
@@ -179,26 +219,20 @@ bool ICACHE_FLASH_ATTR i2s_write_sample(uint32_t sample) {
179219
}
180220
}
181221
ETS_SLC_INTR_DISABLE();
182-
i2s_curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item();
222+
tx->i2s_curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item(tx);
183223
ETS_SLC_INTR_ENABLE();
184-
i2s_curr_slc_buf_pos=0;
224+
tx->i2s_curr_slc_buf_pos=0;
185225
}
186-
i2s_curr_slc_buf[i2s_curr_slc_buf_pos++]=sample;
226+
tx->i2s_curr_slc_buf[tx->i2s_curr_slc_buf_pos++]=sample;
187227
return true;
188228
}
189229

230+
bool ICACHE_FLASH_ATTR i2s_write_sample(uint32_t sample) {
231+
return _i2s_write_sample(sample, false);
232+
}
233+
190234
bool ICACHE_FLASH_ATTR i2s_write_sample_nb(uint32_t sample) {
191-
if (i2s_curr_slc_buf_pos==SLC_BUF_LEN || i2s_curr_slc_buf==NULL) {
192-
if(i2s_slc_queue_len == 0){
193-
return false;
194-
}
195-
ETS_SLC_INTR_DISABLE();
196-
i2s_curr_slc_buf = (uint32_t *)i2s_slc_queue_next_item();
197-
ETS_SLC_INTR_ENABLE();
198-
i2s_curr_slc_buf_pos=0;
199-
}
200-
i2s_curr_slc_buf[i2s_curr_slc_buf_pos++]=sample;
201-
return true;
235+
return _i2s_write_sample(sample, true);
202236
}
203237

204238
bool ICACHE_FLASH_ATTR i2s_write_lr(int16_t left, int16_t right){
@@ -255,6 +289,13 @@ float ICACHE_FLASH_ATTR i2s_get_real_rate(){
255289
}
256290

257291
void ICACHE_FLASH_ATTR i2s_begin(){
292+
if (tx || rx) {
293+
i2s_end(); // Stop and free any ongoing stuff
294+
}
295+
296+
tx = (i2s_state_t*)calloc(1, sizeof(*tx));
297+
rx = (i2s_state_t*)calloc(1, sizeof(*rx));
298+
258299
_i2s_sample_rate = 0;
259300
i2s_slc_begin();
260301

@@ -291,4 +332,9 @@ void ICACHE_FLASH_ATTR i2s_end(){
291332
pinMode(15, INPUT);
292333

293334
i2s_slc_end();
335+
336+
free(tx);
337+
tx = NULL;
338+
free(rx);
339+
rx = NULL;
294340
}

0 commit comments

Comments
 (0)