2025-07-10 6:40 PM
Hello ST Community,
I'm working on a project using STM32U595 with INMP441 MEMS microphone and NS4168 audio DAC. The system is configured to capture audio data from the microphone using SAI1_Block_A in DMA mode and then output it to the speaker using SAI1_Block_B.
Issue:
I've implemented the SAI RX half-complete and complete callbacks, but they are never triggered. I've added printf statements to display the received data, but nothing is being printed. The DAC seems to be initialized correctly (NS4168_INIT() returns without error), but no audio data is being processed. Code:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : SAI.c
* Description : This file provides code for the configuration
* of the SAI instances.
******************************************************************************
* @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 "sai.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
SAI_HandleTypeDef hsai_BlockA1;
SAI_HandleTypeDef hsai_BlockB1;
DMA_NodeTypeDef Node_GPDMA1_Channel1;
DMA_QListTypeDef List_GPDMA1_Channel1;
DMA_HandleTypeDef handle_GPDMA1_Channel1;
DMA_NodeTypeDef Node_GPDMA1_Channel0;
DMA_QListTypeDef List_GPDMA1_Channel0;
DMA_HandleTypeDef handle_GPDMA1_Channel0;
/* SAI1 init function */
void MX_SAI1_Init(void)
{
/* USER CODE BEGIN SAI1_Init 0 */
/* USER CODE END SAI1_Init 0 */
/* USER CODE BEGIN SAI1_Init 1 */
/* USER CODE END SAI1_Init 1 */
hsai_BlockA1.Instance = SAI1_Block_A;
hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_RX;
hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockA1.Init.MonoStereoMode = SAI_MONOMODE;
hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_MSBJUSTIFIED, SAI_PROTOCOL_DATASIZE_32BIT, 2) != HAL_OK)
{
Error_Handler();
}
hsai_BlockB1.Instance = SAI1_Block_B;
hsai_BlockB1.Init.AudioMode = SAI_MODEMASTER_TX;
hsai_BlockB1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
hsai_BlockB1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
hsai_BlockB1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K;
hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockB1.Init.MckOutput = SAI_MCK_OUTPUT_DISABLE;
hsai_BlockB1.Init.MonoStereoMode = SAI_MONOMODE;
hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
if (HAL_SAI_InitProtocol(&hsai_BlockB1, SAI_I2S_MSBJUSTIFIED, SAI_PROTOCOL_DATASIZE_32BIT, 2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SAI1_Init 2 */
/* USER CODE END SAI1_Init 2 */
}
static uint32_t SAI1_client = 0;
void HAL_SAI_MspInit(SAI_HandleTypeDef *saiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
DMA_NodeConfTypeDef NodeConfig;
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/* SAI1 */
if (saiHandle->Instance == SAI1_Block_A)
{
/* SAI1 clock enable */
/** Initializes the peripherals clock
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLL1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
if (SAI1_client == 0)
{
__HAL_RCC_SAI1_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(SAI1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SAI1_IRQn);
}
SAI1_client++;
/**SAI1_A_Block_A GPIO Configuration
PC3 ------> SAI1_SD_A
PA8 ------> SAI1_SCK_A
PB9 ------> SAI1_FS_A
*/
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral DMA init*/
NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
NodeConfig.Init.Request = GPDMA1_REQUEST_SAI1_A;
NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
NodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
NodeConfig.Init.SrcInc = DMA_SINC_FIXED;
NodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
NodeConfig.Init.SrcBurstLength = 1;
NodeConfig.Init.DestBurstLength = 1;
NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
NodeConfig.Init.Mode = DMA_NORMAL;
NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel0) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel0, NULL, &Node_GPDMA1_Channel0) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel0) != HAL_OK)
{
Error_Handler();
}
handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
handle_GPDMA1_Channel0.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
handle_GPDMA1_Channel0.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA1_Channel0.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel0.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel0.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel0) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel0, &List_GPDMA1_Channel0) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(saiHandle, hdmarx, handle_GPDMA1_Channel0);
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
}
if (saiHandle->Instance == SAI1_Block_B)
{
/* SAI1 clock enable */
/** Initializes the peripherals clock
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLL1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
if (SAI1_client == 0)
{
__HAL_RCC_SAI1_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(SAI1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SAI1_IRQn);
}
SAI1_client++;
/**SAI1_B_Block_B GPIO Configuration
PB3 (JTDO/TRACESWO) ------> SAI1_SCK_B
PB5 ------> SAI1_SD_B
PB6 ------> SAI1_FS_B
*/
GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_5 | GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF13_SAI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral DMA init*/
NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
NodeConfig.Init.Request = GPDMA1_REQUEST_SAI1_B;
NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
NodeConfig.Init.Direction = DMA_MEMORY_TO_PERIPH;
NodeConfig.Init.SrcInc = DMA_SINC_INCREMENTED;
NodeConfig.Init.DestInc = DMA_DINC_FIXED;
NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
NodeConfig.Init.SrcBurstLength = 1;
NodeConfig.Init.DestBurstLength = 1;
NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
NodeConfig.Init.Mode = DMA_NORMAL;
NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel1) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel1, NULL, &Node_GPDMA1_Channel1) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel1) != HAL_OK)
{
Error_Handler();
}
handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
handle_GPDMA1_Channel1.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
handle_GPDMA1_Channel1.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA1_Channel1.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel1.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
handle_GPDMA1_Channel1.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel1) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel1, &List_GPDMA1_Channel1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(saiHandle, hdmatx, handle_GPDMA1_Channel1);
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
}
}
void HAL_SAI_MspDeInit(SAI_HandleTypeDef *saiHandle)
{
/* SAI1 */
if (saiHandle->Instance == SAI1_Block_A)
{
SAI1_client--;
if (SAI1_client == 0)
{
/* Peripheral clock disable */
__HAL_RCC_SAI1_CLK_DISABLE();
HAL_NVIC_DisableIRQ(SAI1_IRQn);
}
/**SAI1_A_Block_A GPIO Configuration
PC3 ------> SAI1_SD_A
PA8 ------> SAI1_SCK_A
PB9 ------> SAI1_FS_A
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_3);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_8);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9);
HAL_DMA_DeInit(saiHandle->hdmarx);
}
if (saiHandle->Instance == SAI1_Block_B)
{
SAI1_client--;
if (SAI1_client == 0)
{
/* Peripheral clock disable */
__HAL_RCC_SAI1_CLK_DISABLE();
HAL_NVIC_DisableIRQ(SAI1_IRQn);
}
/**SAI1_B_Block_B GPIO Configuration
PB3 (JTDO/TRACESWO) ------> SAI1_SCK_B
PB5 ------> SAI1_SD_B
PB6 ------> SAI1_FS_B
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3 | GPIO_PIN_5 | GPIO_PIN_6);
HAL_DMA_DeInit(saiHandle->hdmatx);
}
}
/**
* @}
*/
/**
* @}
*/
#include "sai.h"
#include "INMP441.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "../BSP/NS4168/NS4168.h"
#define AUDIO_BUFFER_FRAME 128
#define AUDIO_BUFFER_CHANEL 2
#define AUDIO_BUFFER_SIZE (AUDIO_BUFFER_FRAME * AUDIO_BUFFER_CHANEL)
int32_t AUDIO_BUFFER[AUDIO_BUFFER_SIZE];
/**
* @brief 初始化INMP441麦克风
* @retval HAL状态
*/
HAL_StatusTypeDef INMP441_Init(void)
{
NS4168_INIT();
// 清空缓冲区
memset(AUDIO_BUFFER, 0, sizeof(AUDIO_BUFFER));
return HAL_OK;
}
/**
* @brief 开始音频采集
* @retval HAL状态
*/
HAL_StatusTypeDef INMP441_Start(void)
{
HAL_StatusTypeDef status;
memset(AUDIO_BUFFER, 0, sizeof(AUDIO_BUFFER));
// 启动DMA接收
status = HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint32_t *)&AUDIO_BUFFER, sizeof(AUDIO_BUFFER));
// 启动DMA发送
status = HAL_SAI_Transmit_DMA(&hsai_BlockB1, (uint32_t *)&AUDIO_BUFFER, sizeof(AUDIO_BUFFER));
return status;
}
/**
* @brief 停止音频采集
* @retval HAL状态
*/
HAL_StatusTypeDef INMP441_Stop(void)
{
// printf("INMP441: Stopping SAI DMA\r\n");
return HAL_SAI_DMAStop(&hsai_BlockA1);
}
/**
* @brief SAI半传输完成回调
* @PAram hsai SAI句柄
* @retval 无
*/
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
if (hsai->Instance == SAI1_Block_A)
{
printf("INMP441 RxHalf Data: ");
for (int i = 0; i < 5; i++)
{
printf("%d ", AUDIO_BUFFER[i]);
}
}
}
/**
* @brief SAI传输完成回调
* @PAram hsai SAI句柄
* @retval 无
*/
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai)
{
if (hsai->Instance == SAI1_Block_A)
{
}
}
/**
* @brief SAI错误回调
* @PAram hsai SAI句柄
* @retval 无
*/
void HAL_SAI_ErrorCallback(SAI_HandleTypeDef *hsai)
{
if (hsai->Instance == SAI1_Block_A)
{
uint32_t error_code = hsai->ErrorCode;
printf("INMP441 SAI Error: 0x%lX\r\n", error_code);
}
}
2025-07-11 2:23 AM
Plese refer to the BSP example below which use SAI with DMA for audio recording and audio playback.
STM32CubeU5/Projects/STM32U575I-EV/Examples/BSP at main · STMicroelectronics/STM32CubeU5 · GitHub