@@ -39,43 +39,78 @@ extern void ets_wdt_disable(void);
39
39
//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
40
40
//simultaneously.
41
41
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 );
63
72
}
64
73
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 ;
67
96
}
68
97
69
98
int16_t ICACHE_FLASH_ATTR i2s_available (){
70
- return ( SLC_BUF_CNT - i2s_slc_queue_len ) * SLC_BUF_LEN ;
99
+ return _i2s_available ( tx ) ;
71
100
}
72
101
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 ) {
74
108
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
+ }
79
114
return item ;
80
115
}
81
116
@@ -87,37 +122,41 @@ void ICACHE_FLASH_ATTR i2s_slc_isr(void) {
87
122
SLCIC = 0xFFFFFFFF ;
88
123
if (slc_intr_status & SLCIRXEOF ) {
89
124
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 ;
91
126
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
94
129
}
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 ();
97
132
ETS_SLC_INTR_ENABLE ();
98
133
}
99
134
}
100
135
101
136
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 ;
103
142
}
104
143
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 ;
107
146
int x , y ;
108
147
109
148
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 ]));
121
160
}
122
161
123
162
ETS_SLC_INTR_DISABLE ();
@@ -136,9 +175,9 @@ void ICACHE_FLASH_ATTR i2s_slc_begin(){
136
175
//expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw
137
176
//an error at us otherwise. Just feed it any random descriptor.
138
177
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
140
179
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
142
181
143
182
ETS_SLC_INTR_ATTACH (i2s_slc_isr , NULL );
144
183
SLCIE = SLCIRXEOF ; //Enable only for RX EOF interrupt
@@ -158,19 +197,20 @@ void ICACHE_FLASH_ATTR i2s_slc_end(){
158
197
SLCRXL &= ~(SLCRXLAM << SLCRXLA ); // clear RX descriptor address
159
198
160
199
for (int x = 0 ; x < SLC_BUF_CNT ; x ++ ) {
161
- free (i2s_slc_buf_pntr [x ]);
200
+ free (tx -> i2s_slc_buf_pntr [x ]);
162
201
}
163
202
}
164
203
165
204
//This routine pushes a single, 32-bit sample to the I2S buffers. Call this at (on average)
166
205
//at least the current sample rate. You can also call it quicker: it will suspend the calling
167
206
//thread if the buffer is full and resume when there's room again.
168
207
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;
172
212
while (1 ){
173
- if (i2s_slc_queue_len > 0 ){
213
+ if (tx -> i2s_slc_queue_len > 0 ){
174
214
break ;
175
215
} else {
176
216
ets_wdt_disable ();
@@ -179,26 +219,20 @@ bool ICACHE_FLASH_ATTR i2s_write_sample(uint32_t sample) {
179
219
}
180
220
}
181
221
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 );
183
223
ETS_SLC_INTR_ENABLE ();
184
- i2s_curr_slc_buf_pos = 0 ;
224
+ tx -> i2s_curr_slc_buf_pos = 0 ;
185
225
}
186
- i2s_curr_slc_buf [i2s_curr_slc_buf_pos ++ ]= sample ;
226
+ tx -> i2s_curr_slc_buf [tx -> i2s_curr_slc_buf_pos ++ ]= sample ;
187
227
return true;
188
228
}
189
229
230
+ bool ICACHE_FLASH_ATTR i2s_write_sample (uint32_t sample ) {
231
+ return _i2s_write_sample (sample , false);
232
+ }
233
+
190
234
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);
202
236
}
203
237
204
238
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(){
255
289
}
256
290
257
291
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
+
258
299
_i2s_sample_rate = 0 ;
259
300
i2s_slc_begin ();
260
301
@@ -291,4 +332,9 @@ void ICACHE_FLASH_ATTR i2s_end(){
291
332
pinMode (15 , INPUT );
292
333
293
334
i2s_slc_end ();
335
+
336
+ free (tx );
337
+ tx = NULL ;
338
+ free (rx );
339
+ rx = NULL ;
294
340
}
0 commit comments