2026-05-11 5:15 AM
Hello,
I am writing this because I am unable to enable the SAI on this development board. The strange thing is that I actually managed to enable it before and it was working correctly. However, while implementing another feature, I switched branches and worked on a different GitHub branch for some time. When I came back, I noticed that the SAI was no longer starting.
At first, I thought I had made a mistake in a commit, so I created a completely new project where the only goal was to start the SAI. Even though I believe all the steps are configured correctly, the MCK clock never starts, and the MCKEN register bit never becomes enabled. I am attaching screenshots of the SAI registers below.
As shown in the screenshot, the SAI is enabled, but I cannot see any clock output on the oscilloscope. As I mentioned before, what confuses me the most is that two weeks ago, using the exact same development board, I was able to make it work correctly.
I tried adding the following line inside the MX_SAI2_Init function
hsai_BlockB2.Init.MckOutput = SAI_MCK_OUTPUT_ENABLE;After doing this, the MCKEN bit appeared as 0x01, but the SAI still did not start.
I also created a small test program to verify that all the pins used by the SAI are working correctly. In this test, I toggle the pins at a very high speed, and all of them behave as expected on the oscilloscope. Because of this, I would rule out a hardware issue as the cause of the problem.
I am using the CN14 and CN15 headers of the development board.
When probing the pins with the oscilloscope, I observe the following:
Additionally, I have completely disabled the cache in order to rule it out as a possible source of the issue.
The first time I configured the SAI, I used the following post as a reference:
STM32H7RS GPDMA Circular w/ Linked List issue
I am also including the code from the main, sai, linked_list, and gpdma files in case someone can spot where my mistake is. I will also upload a ZIP file containing the complete project.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 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"
#include "gpdma.h"
#include "sai.h"
#include "usb_otg.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#include <stdint.h>
#include <string.h>
#include "linked_list.h"
extern DMA_QListTypeDef SAI_DMA_queue;
int32_t buf[100] __attribute__ ((aligned(32)));
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Update SystemCoreClock variable according to RCC registers values. */
SystemCoreClockUpdate();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_USB_OTG_HS_PCD_Init();
MX_SAI2_Init();
/* USER CODE BEGIN 2 */
memset(&SAI_DMA_queue, 0, sizeof(SAI_DMA_queue));
MX_SAI_DMA_queue_Config();
// D: check for correct linkage, it was fine
if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel0, &SAI_DMA_queue) != HAL_OK)
{
Error_Handler();
}
// D: attempt to link the DMA handle, and be seen as linked list
// D: now below the linkq, to associate SAI with handle that has gotten parameters
__HAL_LINKDMA(&hsai_BlockB2, hdmarx, handle_GPDMA1_Channel0);
// D: does all necessary calls, 256 is half-word samples
if (HAL_SAI_Receive_DMA(&hsai_BlockB2, (uint8_t*) buf, 100) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : SAI.c
* Description : This file provides code for the configuration
* of the SAI instances.
******************************************************************************
* @attention
*
* Copyright (c) 2026 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_BlockB2;
/* SAI2 init function */
void MX_SAI2_Init(void)
{
/* USER CODE BEGIN SAI2_Init 0 */
/* USER CODE END SAI2_Init 0 */
/* USER CODE BEGIN SAI2_Init 1 */
/* USER CODE END SAI2_Init 1 */
hsai_BlockB2.Instance = SAI2_Block_B;
hsai_BlockB2.Init.AudioMode = SAI_MODEMASTER_RX;
hsai_BlockB2.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockB2.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockB2.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockB2.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockB2.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;
hsai_BlockB2.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockB2.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockB2.Init.CompandingMode = SAI_NOCOMPANDING;
if (HAL_SAI_InitProtocol(&hsai_BlockB2, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_24BIT, 2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SAI2_Init 2 */
/* USER CODE END SAI2_Init 2 */
}
static uint32_t SAI2_client =0;
void HAL_SAI_MspInit(SAI_HandleTypeDef* saiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/* SAI2 */
if(saiHandle->Instance==SAI2_Block_B)
{
/* SAI2 clock enable */
/** Initializes the peripherals clock
*/
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI2;
PeriphClkInit.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLL3P;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
if (SAI2_client == 0)
{
__HAL_RCC_SAI2_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(SAI2_B_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SAI2_B_IRQn);
}
SAI2_client ++;
/**SAI2_B_Block_B GPIO Configuration
PE14 ------> SAI2_MCLK_B
PE13 ------> SAI2_FS_B
PE11 ------> SAI2_SD_B
PE12 ------> SAI2_SCK_B
*/
GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_13|GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF10_SAI2;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}
}
void HAL_SAI_MspDeInit(SAI_HandleTypeDef* saiHandle)
{
/* SAI2 */
if(saiHandle->Instance==SAI2_Block_B)
{
SAI2_client --;
if (SAI2_client == 0)
{
/* Peripheral clock disable */
__HAL_RCC_SAI2_CLK_DISABLE();
HAL_NVIC_DisableIRQ(SAI2_B_IRQn);
}
/**SAI2_B_Block_B GPIO Configuration
PE14 ------> SAI2_MCLK_B
PE13 ------> SAI2_FS_B
PE11 ------> SAI2_SD_B
PE12 ------> SAI2_SCK_B
*/
HAL_GPIO_DeInit(GPIOE, GPIO_PIN_14|GPIO_PIN_13|GPIO_PIN_11|GPIO_PIN_12);
}
}
/**
* @}
*/
/**
* @}
*//* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : linked_list.c
* Description : This file provides code for the configuration
* of the LinkedList.
******************************************************************************
* @attention
*
* Copyright (c) 2026 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 "linked_list.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
DMA_NodeTypeDef SAI_DMA_Node __attribute__((section("noncacheable_buffer")));
DMA_QListTypeDef SAI_DMA_queue;
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/**
* @brief DMA Linked-list SAI_DMA_queue configuration
* @PAram None
* @retval None
*/
HAL_StatusTypeDef MX_SAI_DMA_queue_Config(void)
{
HAL_StatusTypeDef ret = HAL_OK;
/* DMA node configuration declaration */
DMA_NodeConfTypeDef pNodeConfig;
/* Set node configuration ################################################*/
pNodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
pNodeConfig.Init.Request = GPDMA1_REQUEST_SAI2_B;
pNodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
pNodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
pNodeConfig.Init.SrcInc = DMA_SINC_FIXED;
pNodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
pNodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD;
pNodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD;
pNodeConfig.Init.SrcBurstLength = 1;
pNodeConfig.Init.DestBurstLength = 1;
pNodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
pNodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
pNodeConfig.Init.Mode = DMA_NORMAL;
pNodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
pNodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
pNodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
pNodeConfig.SrcAddress = 0;
pNodeConfig.DstAddress = 0;
pNodeConfig.DataSize = 0;
/* Build SAI_DMA_Node Node */
ret |= HAL_DMAEx_List_BuildNode(&pNodeConfig, &SAI_DMA_Node);
/* Insert SAI_DMA_Node to Queue */
ret |= HAL_DMAEx_List_InsertNode_Tail(&SAI_DMA_queue, &SAI_DMA_Node);
ret |= HAL_DMAEx_List_SetCircularMode(&SAI_DMA_queue);
return ret;
}/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file gpdma.c
* @brief This file provides code for the configuration
* of the GPDMA instances.
******************************************************************************
* @attention
*
* Copyright (c) 2026 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 "gpdma.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
DMA_HandleTypeDef handle_GPDMA1_Channel0;
/* GPDMA1 init function */
void MX_GPDMA1_Init(void)
{
/* USER CODE BEGIN GPDMA1_Init 0 */
/* USER CODE END GPDMA1_Init 0 */
/* 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);
/* USER CODE BEGIN GPDMA1_Init 1 */
/* USER CODE END GPDMA1_Init 1 */
handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
handle_GPDMA1_Channel0.InitLinkedList.Priority = DMA_HIGH_PRIORITY;
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_LAST_LL_ITEM_TRANSFER;
handle_GPDMA1_Channel0.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel0) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN GPDMA1_Init 2 */
/* USER CODE END GPDMA1_Init 2 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
2026-05-11 5:28 AM
I forgot to upload the project ZIP file.
Also, I wanted to ask a question. Is it possible to damage the SAI peripheral while the pins still continue working as GPIOs? Because the last time I worked with SAI, when I wrote __HAL_SAI_ENABLE();, the peripheral started despite having a bad configuration.
2026-05-11 5:37 AM
One last comment I would like to add: if someone could try to run my code and let me know whether it works or not, it would help me a lot, since it would tell me whether the issue is related to software or hardware.
2026-05-11 7:58 AM
Hello @esxu
@esxu wrote:I am using the CN14 and CN15 headers of the development board.
When probing the pins with the oscilloscope, I observe the following:
- D13 remains at 0V
- D12 remains at 3.3V
- D11 remains at 0V
Pin name D13, D12 and D11 are not related to SAI2.
I tested your code, and the DMA TC and HTC interrupts are triggered correctly. However, the buffer remains empty and contains only zeros.