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 */
Solved! Go to Solution.
2026-05-12 12:21 AM
I changed the clock source from PLL3P to PLL1Q, and it started working correctly. I’m not sure why PLL3P does not work on my development board, but at least this workaround seems reliable. It also works with PLL2P.
I’ll investigate this a bit further, and if I find a possible explanation, I’ll share it here.
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.
2026-05-11 1:49 PM
Muchas gracias por tomar tu tiempo en mirar si a ti te funciona es que probablemente el circuito de alguna manera se ha dañado a lo mejor por una ESD.
sobre lo que comentas de que no estás relacionadas con el Said sí que lo están, pero no lo pone en el manual del Discovery kit, pero si entras en el .ioc verás que sí.
igualmente, muchas gracias por tu tiempo!
2026-05-11 1:50 PM
Thank you very much for taking the time to check whether it works on your side. If it does, then probably the circuit has somehow been damaged, maybe due to an ESD event.
About what you mentioned regarding the pins not being related to the SAI, they actually are, but it is not stated in the Discovery Kit manual. However, if you open the .ioc file, you will see that they are indeed connected to the SAI peripheral.
And regarding the SD_SAI pin, it is normal for everything to read as zeros if nothing is connected to the SD_SAI pin.
Anyway, thank you very much for your time!
2026-05-12 12:21 AM
I changed the clock source from PLL3P to PLL1Q, and it started working correctly. I’m not sure why PLL3P does not work on my development board, but at least this workaround seems reliable. It also works with PLL2P.
I’ll investigate this a bit further, and if I find a possible explanation, I’ll share it here.
2026-05-12 1:43 AM
I finally found the root cause of this issue! I am sharing the explanation here in case anyone else runs into the same trap when working with the split Boot/Application architecture on the STM32H7S/RS series.
The issue was not a hardware bug with the board or PLL3, but rather a firmware workflow oversight on my end regarding how the system clocks are initialized.
The Root Cause:
In this architecture, the physical hardware clock configuration —the SystemClock_Config() function— is executed inside the Boot project. The Appli (Application) project mainly relies on this pre-configured hardware state and simply calls SystemCoreClockUpdate() to update the software variables with the current clock speeds.
When I changed the SAI clock multiplexer to use PLL3 in STM32CubeMX, the initialization code to turn on and configure PLL3 was generated inside the Boot project. However, during my debugging, I was only recompiling and flashing the Appli project. Because the updated SystemClock_Config() was never flashed to the board, PLL3 was never physically turned on in the hardware.
The "Gotcha" that misled me:
What made this incredibly tricky to debug was that calling HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SAI2); in my Appli code actually returned the correct, expected frequency! I assumed this meant PLL3 was up and running.
Why did PLL1 and PLL2 work as a workaround?
Because PLL1 and PLL2 were already being initialized by the default Bootloader configuration (used for the main CPU clock, memory, etc.). So, routing the SAI to them worked out of the box.
The Solution:
If you make changes to the RCC / Clock Tree in CubeMX (like enabling PLL3 for an audio interface), you must regenerate the code for both projects, and critically, recompile and flash the Boot project before running your Application project. Once I did this, PLL3P worked perfectly for the SAI.
I hope this saves someone else a few hours of debugging!