2025-08-21 9:05 AM
Hello everyone,
I'm trying to configure the SAI and GPDMA peripherals on an STM32 to acquire audio from an I2S microphone. My goal is to capture a 32-bit, 2-channel audio stream at a 16 kHz sample rate, with the STM32 acting as the clock master. I've followed the steps below, but I'm running into two main issues:
Here's a summary of the steps I've taken:
I've attached part of my code snippets below.
/**
* @brief GPDMA1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_GPDMA1_Init(void)
{
/* Peripheral clock enable */
__HAL_RCC_GPDMA1_CLK_ENABLE();
/* GPDMA1 interrupt Init */
HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);
}
/**
* @brief SAI1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_SAI1_Init(void)
{
/* DMA node configuration declaration */
hsai_BlockB1.Instance = SAI1_Block_B;
hsai_BlockB1.Init.AudioMode = SAI_MODEMASTER_RX;
hsai_BlockB1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockB1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockB1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_16K;
hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockB1.Init.MckOutput = SAI_MCK_OUTPUT_ENABLE;
hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
if (HAL_SAI_InitProtocol(&hsai_BlockB1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_32BIT, 2) != HAL_OK)
{
Error_Handler();
}
}
void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai)
{
GPIO_InitTypeDef GPIO_InitStruct;
DMA_NodeConfTypeDef NodeConfig;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/* SAI1 */
if(hsai->Instance==SAI1_Block_B)
{
/* Peripheral clock enable */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_IC7;
PeriphClkInitStruct.ICSelection[RCC_IC7].ClockSelection = RCC_ICCLKSOURCE_PLL4;
PeriphClkInitStruct.ICSelection[RCC_IC7].ClockDivider = 250;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
if (SAI1_client == 0)
{
__HAL_RCC_SAI1_CLK_ENABLE();
}
SAI1_client ++;
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**SAI1_B_Block_B GPIO Configuration
PG1 ------> SAI1_SCK_B
PA3 ------> SAI1_SD_B
PG2 ------> SAI1_FS_B
*/
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_SAI1;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
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_AF6_SAI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(SAI1_B_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SAI1_B_IRQn);
/* 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_PERIPH_TO_MEMORY;
NodeConfig.Init.SrcInc = DMA_SINC_FIXED;
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.TriggerConfig.TriggerSelection = GPDMA1_TRIGGER_GPDMA1_CH0_TCF;
NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
NodeConfig.SrcSecure = DMA_CHANNEL_SRC_SEC;
NodeConfig.DestSecure = DMA_CHANNEL_DEST_SEC;
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(hsai, hdmarx, handle_GPDMA1_Channel0);
}
}
void main(void)
{
[...]
MX_GPDMA1_Init();
MX_SAI1_Init();
SystemIsolation_Config();
/* set up GPDMA configuration */
/* set GPDMA1 channel 0 used by SAI1 */
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0,DMA_CHANNEL_SEC|DMA_CHANNEL_PRIV|DMA_CHANNEL_SRC_SEC|DMA_CHANNEL_DEST_SEC)!= HAL_OK )
{
Error_Handler();
}
[...]
if (HAL_OK != HAL_SAI_Receive_DMA(&hsai_BlockB1, (uint8_t *) acquisition_buffer, N_BLOCK_SIZE))
{
Error_Handler();
}
printf("SAI1_B DMA configuration done.\n");
}
2025-08-27 6:11 AM
Hello @jweber
Why the DMA DestInc is set to fixed?
NodeConfig.Init.DestInc = DMA_DINC_FIXED;
Please refer to the example below:
2025-09-01 7:39 AM
Hello Saket_Om,
Thank you for pointing out this mistake when I copy pasted example. It appears I try many configuration and was a bit lost with all the parameters to configure.
However, when I changed it to INCREMENTED, it started to work but not every time I run it (in FSBL).
I'm using the CMSIS RTOS middleware and I try to changed the interrupt priority of the GPDMA to be lower (higher value) than the configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY (which is 5 in my case).
Is there any parameter I should pay attention to be sure the DMA interruption can handle a semaphore unlock for a processing task?
Thank you for your time!
2025-09-08 7:27 AM
Hello @Saket_Om ,
I tried working on the configuration using the link you provided as a reference. As I mentioned before, I managed to get it working, but it seems that the GPDMA and/or SAI configuration is still missing something. When I add a simple debug printf function to my project, the GPDMA is no longer initialized, and the clock doesn’t start.
I suspect this might be a side effect related to the length of my code, and that I’m missing some parameter to make the program deterministic. I’ve attached the .ld file to show how I separated the DMA memory from the other RAM space.
Here’s my DMA buffer declaration (as global):
__attribute__((section(".dma_nocache"), aligned(32)))
uint32_t acquisition_buffer[N_DMA_BUFFER];
uint32_t audio_input_buffer[N_SAMPLES_IN_AUDIO_BUFFER];
Are there any other options I should consider since I’m using CMSIS-RTOS V2?
I don’t understand why the behavior of the GPDMA changes when I try to add a simple function such as display debug information using functions like printf AFTER initialization:
#ifdef DEBUG
printf("-------- DEBUG SECTION ---------\r\n");
SAI_debug_configuration();
GPDMA_debug_configuration(handle_GPDMA1_Channel0, hsai_BlockB1);
debug_clocks_configuration();
printf("--------------------------------\r\n");
#endif
Thank you in advance!