2025-11-17 8:05 AM
I'm working with an STM32F411 and trying to run two I2S master-receiver interfaces (I2S1 and I2S2) at the same time, each receiving data from a pair of PDM microphones using DMA circular transfers.
However, I found a very strange and extremely hard-to-debug issue:
When only one DMA is enabled, both I2S1 and I2S2 work correctly.
When both I2S + DMA are enabled at the same time, the following happens:
If I2S1 has no microphone connected, it correctly receives all zeros.
If I2S2 does have a microphone connected, then I2S1 suddenly starts receiving non-zero data, even though no microphone is connected to I2S1.
It looks like I2S1 is receiving data that actually belongs to I2S2’s DMA buffer.
Sometimes the behavior changes depending on the order of calling HAL_I2S_Receive_DMA().
Both DMA buffers are allocated as global arrays (4 × 512-word buffers).
I also tried disabling JTAG on PA15 (I2S1_WS), but the issue still exists.
I2S1 RX → DMA2 Stream2
I2S2 RX → DMA1 Stream3
Both in circular mode
FIFO disabled
24-bit Philips I2S mode
I2S1’s DMA interrupt triggers normally
But the content of I2S1’s buffer seems partly “contaminated” by I2S2 data
Only the first element of each buffer is non-zero; the rest is zero
If I2S2 microphone is unplugged, I2S1 buffer becomes all zero again
This makes me suspect:
hidden DMA conflict?
wrong memory alignment?
a silicon errata related to STM32F4 DMA + I2S?
some CubeMX-generated initialization order bug?
I’ve been debugging this for a week and still cannot find the root cause.
Has anyone experienced cross-talk between two DMA streams on STM32F4 when using dual I2S master RX?
Any guidance or similar cases would be appreciated.
main.c:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "dios_ssp_api.h"
#include "malloc.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define Number 512
uint32_t DMA[Number]; //original
uint32_t DMADMA[Number];
uint32_t DMAC[Number];
uint32_t DMADMAC[Number];
int micBuf[Number/2]; //256
int micBufBuf[Number/2];
bool isI2S2Over = false;
bool isI2S1Over = false;
int micNumber = 2;
float mic_coord[6] = {-0.05,0,0,0.05,0,0};
float loc_phi = 0;
volatile bool isCallbackBusy = false;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2S_HandleTypeDef hi2s1;
I2S_HandleTypeDef hi2s2;
DMA_HandleTypeDef hdma_spi1_rx;
DMA_HandleTypeDef hdma_spi2_rx;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_I2S2_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_I2S1_Init(void);
/* USER CODE BEGIN PFP */
void processData(uint32_t DMAA[Number],int micBuff[Number/2])
{
uint32_t leftValue24[Number/4]; //
uint32_t rightValue24[Number/4];
int leftValue32[Number/4];
int rightValue32[Number/4];
for(int i = 0;i<Number/4;i++)
{
leftValue24[i] = (DMAA[4*i]<<8)+(DMAA[4*i+1]>>8);
rightValue24[i] = (DMAA[4*i+2]<<8)+(DMAA[4*i+3]>>8);
if(leftValue24[i] & 0x800000) //negative
{
leftValue32[i]=0xff000000 | leftValue24[i];
}
else //positive
{
leftValue32[i]= leftValue24[i];
}
if(rightValue24[i] & 0x800000) //negative
{
rightValue32[i]=0xff000000 | rightValue24[i];
}
else //positive
{
rightValue32[i]= rightValue24[i];
}
micBuff[i] = rightValue32[i];
micBuff[Number/4+i] = leftValue32[i];
//printf("%d %d\r\n",leftValue32[i],rightValue32[i]);
}
}
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
//printf("555\r\n");
if (isCallbackBusy) return;
isCallbackBusy = true;
if(hi2s==&hi2s2)
{
if(isI2S2Over == false)
{
//memcpy(DMAC, DMA, Number * sizeof(uint32_t)); // 512 words ≈ 2KB
for(int i = 0; i<Number ; i++)
{
DMAC[i] = DMA[i];
}
isI2S2Over = true;
}
}
if(hi2s == &hi2s1)
{
if(isI2S1Over == false)
{
//memcpy(DMADMAC, DMADMA, Number * sizeof(uint32_t));
for(int i = 0; i<Number ; i++)
{
DMADMAC[i] = DMADMA[i];
}
isI2S1Over = true;
}
}
isCallbackBusy = false;
}
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#if 1
#include <stdio.h>
int _write(int fd, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 0xFFFF);
return len;
}
#endif
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
void* st1;
void* dios_ssp_init_api(int mic_num,float loc_phi,float *mic_coord);
st1 = dios_ssp_init_api(micNumber,loc_phi,mic_coord);
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* Configure the peripherals common clocks */
PeriphCommonClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_I2S1_Init();
MX_I2S2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_I2S_Receive_DMA(&hi2s1,(uint16_t*)DMADMA,Number);
HAL_I2S_Receive_DMA(&hi2s2,(uint16_t*)DMA,Number);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(isI2S2Over == true && isI2S2Over == true)
{
processData(DMAC,micBuf);
processData(DMADMAC,micBufBuf);
dios_ssp_processDouble_api(st1,micBufBuf);
printf("angle2:%f\r\n",***Angle);
dios_ssp_process_api(st1,micBuf);
printf("angle1:%f\r\n\r\n",shitAngle);
isI2S2Over = false;
isI2S1Over = false;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 12;
RCC_OscInitStruct.PLL.PLLN = 96;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief Peripherals Common Clock Configuration
* @retval None
*/
void PeriphCommonClock_Config(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
PeriphClkInitStruct.PLLI2S.PLLI2SN = 192;
PeriphClkInitStruct.PLLI2S.PLLI2SM = 16;
PeriphClkInitStruct.PLLI2S.PLLI2SR = 3;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2S1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_I2S1_Init(void)
{
/* USER CODE BEGIN I2S1_Init 0 */
/* USER CODE END I2S1_Init 0 */
/* USER CODE BEGIN I2S1_Init 1 */
/* USER CODE END I2S1_Init 1 */
hi2s1.Instance = SPI1;
hi2s1.Init.Mode = I2S_MODE_MASTER_RX;
hi2s1.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s1.Init.DataFormat = I2S_DATAFORMAT_24B;
hi2s1.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
hi2s1.Init.AudioFreq = I2S_AUDIOFREQ_16K;
hi2s1.Init.CPOL = I2S_CPOL_LOW;
hi2s1.Init.ClockSource = I2S_CLOCK_PLL;
hi2s1.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
if (HAL_I2S_Init(&hi2s1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2S1_Init 2 */
/* USER CODE END I2S1_Init 2 */
}
/**
* @brief I2S2 Initialization Function
* @PAram None
* @retval None
*/
static void MX_I2S2_Init(void)
{
/* USER CODE BEGIN I2S2_Init 0 */
/* USER CODE END I2S2_Init 0 */
/* USER CODE BEGIN I2S2_Init 1 */
/* USER CODE END I2S2_Init 1 */
hi2s2.Instance = SPI2;
hi2s2.Init.Mode = I2S_MODE_MASTER_RX;
hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_16K;
hi2s2.Init.CPOL = I2S_CPOL_LOW;
hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
if (HAL_I2S_Init(&hi2s2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2S2_Init 2 */
/* USER CODE END I2S2_Init 2 */
}
/**
* @brief USART1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Stream3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
/* DMA2_Stream2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
}
/**
* @brief GPIO Initialization Function
* @PAram None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @PAram file: pointer to the source file name
* @PAram line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
2025-11-17 9:34 AM - edited 2025-11-17 9:34 AM
If an I2S input is not connected (i.e. floating), ascribe no significance you get to the numbers it returns. You should not expect to get all 0s, or all 1s, or any particular input whatsoever.
Use a pullup/pulldown to get a default state when no input is connected.