Filling a Queue with Data from 4 Microphones from 2 different I2S Interfaces
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-06-12 2:25 AM
Platform: STM32 Nucleo-H7A3ZI-Q
Goal: Read audio from four microphones in stereo mode (two mics per I²S interface) via DMA, then enqueue complete stereo frames for processing.
1. Hardware & DMA Setup
I2S1 handles Mic 1 (left) and Mic 2 (right).
I2S2 handles Mic 3 (left) and Mic 4 (right).
Each I2S interface uses DMA to fill its receive buffer (data_i2s[] and data_i2s_2[]).
2. Current Callback Code
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s){
if(hi2s==&hi2s1){
if(x1Flag == 0){
valX1 = 0;
valX1 = sample_left = ((int32_t)data_i2s[0]) >> 8;
x1Flag = 1;
}else if(x2Flag == 0){
valX2 = 0;
valX2 = sample_right = ((int32_t)data_i2s[1]) >> 8;
x2Flag = 1;
/*if(x1Flag != 0 && x2Flag != 0){
enqueueData();
}*/
}
}else if (hi2s == &hi2s2){
if(y1Flag == 0){
valY1 = 0;
valY1 = sample_left_2 = ((int32_t)data_i2s_2[0]) >> 8;
y1Flag = 1;
}else if (y2Flag == 0){
valY2 = 0;
valY2 = sample_right_2 = ((int32_t)data_i2s_2[1]) >> 8;
y2Flag = 1;
/*if(y1Flag != 0 && y2Flag != 0){
enqueueData();
}*/
}
}
if(x1Flag != 0 && x2Flag != 0 && y1Flag != 0 && y2Flag != 0){
enqueueData();
}
}
3. Observed Behavior
Often the callback only ever enters the hi2s1 branch and never the hi2s2 branch.
Other times, I see data from three mics but never from the fourth, so enqueueData() either never runs or runs with incomplete data.
4. What I’ve Already Checked
DMA handles are correctly linked to hi2s1 and hi2s2.
Interrupt priorities: I’ve ensured DMA and I2S IRQs don’t mask each other.
Buffer sizes: The receive arrays are distinct and large enough.
Flag reset: Flags are cleared immediately after calling enqueueData().
5. My Questions
How can I guarantee that both I²S interfaces complete their DMA transfer before enqueueData() fires?
Why might the hi2s2 branch be skipped or only partially served?
Is there a cleaner or more reliable pattern in FreeRTOS for gathering four DMA callbacks and pushing full stereo frames into a queue?
Thanks in advance for any suggestions!
- Labels:
-
DMA
-
I2S
-
SPI
-
STM32H7 series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-06-16 3:50 AM
Hello @chikenJoe1
Check the DMA transfer complete callback function for the two I2S before calling enqueueData().
Saket_Om
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-06-16 7:09 AM
Hello,
thanks for your Reply!
What do mean by Checking, I call the DMA in main, for both I2S.
I now try to solve this via SAI in I2S Mode so all the Mics Share the same clock, with SAI A and B.
What is the better Option, i need the Data from the 4 Microphones at the same time to do Processing, i've red that with 2 master I2S to sync the timings is harder then to solve it via SAI with A and B in master Slave mode
Thanks in Advance
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-06-16 7:31 AM
Hello @chikenJoe1
You make flag in the HAL_I2S_RxCpltCallback() and then check the flag in the main.
#include "stm32f4xx_hal.h"
// DMA buffer for I2S data
uint16_t i2s_rx_buffer[128]; // Example buffer size
// Flag to indicate data reception is complete
volatile uint8_t i2s_rx_complete_flag = 0;
int main(void)
{
// HAL initialization
HAL_Init();
// System clock configuration
SystemClock_Config();
// I2S handle and initialization
I2S_HandleTypeDef hi2s;
// (Assume hi2s is properly initialized here)
// Start I2S receive in DMA mode
HAL_I2S_Receive_DMA(&hi2s, i2s_rx_buffer, sizeof(i2s_rx_buffer) / sizeof(uint16_t));
while (1)
{
// Check if the flag is set
if (i2s_rx_complete_flag)
{
// Clear the flag
i2s_rx_complete_flag = 0;
// Process the received data
ProcessI2SData(i2s_rx_buffer, sizeof(i2s_rx_buffer) / sizeof(uint16_t));
// Restart I2S receive
HAL_I2S_Receive_DMA(&hi2s, i2s_rx_buffer, sizeof(i2s_rx_buffer) / sizeof(uint16_t));
}
// Other main loop tasks
}
}
Saket_Om
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2025-06-17 7:51 AM - edited ‎2025-06-17 7:56 AM
I have played a bit with I2S and SAI on H723 - H735, and I can confirm that syncing the SAI blocks is much easier / more reliable.
I use DMA in double buffer mode ("DBM"), and DMA's transfer half-complete and complete interrupts for buffer switching (external HyperRAM), that works perfectly with a audio sampling rate of 200 kHz and 8 channels at 32-bit.
Make sure the data buffers are big enough, so you have enough time for operations between the DMA transfer (half-) complete interrupts.
No HAL used though (except for basic DMA setup), when I started that HAL didn't really support DBM mode.
Edit: problem is that you'll have several buffers with interleaved stereo channels, like:
u32BufferSai1A[] = { L1A_0, R1A_0, L1A_1, R1A_1, L1A_2, R1A_2, ...}
u32BufferSai1B[] = { L1B_0, R1B_0, L1B_1, R1B_1, L1B_2, R1B_2, ...}
But you probably want:
u32BufferSai1AnB[] = { L1A_0, L1B_0, R1A_0, R1B_0, L1A_1, L1B_1, R1A_1, R1B_1, ...}
which calls for some copying - which takes CPU time...
Luckily, I can let our PC app handle this. :)
