cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7xx - Bug in SPI Driver HAL in larger transmission (32 bytes transmission in one call)

smati2
Associate II

Hi all. I am having an issue with HAL_SPI_TransmitReceive routine under STM32H743. I am using STM32CubeIDE ver 1.10.1. In our setup for STM32H743, it is bare metal (no RTOS). The SPI is in Slave mode. In particular, I am using SPI4 as follows:

Mode: Slave

PE11 = SPI4_NSS

PE12 = SPI4_SCK

PE13 = SPI4_MISO

PE14 = SPI4_MOSI

Data Size = 8 Bits with first bit as MSB bit

Note that the Master is transmitting at 3M baud.

I am using HAL_SPI_TransmitReceive function. It all works fine until the need for receiving 32 bytes in one call as indicated below:

HAL_SPI_TransmitReceive(&hspi4, aTxBuff, aRxBuff, MAX_NUMBYTES_TO_WRITE_EACH_TIME, 5000)

where MAX_NUMBYTES_TO_WRITE_EACH_TIME = 32

I get about half of them and the rest are all zero and the call gets stuck in HAL_SPI_TransmitReceive and never returns.

Note: I saw a post (https://community.st.com/s/question/0D53W00000MfuaASAR/stm32h7xx-serious-bug-in-spi-driver-hal-version-130-170-and-180?t=1677157489831) where another member (@Dub Bartolec) was having a similar problem. The member had discovered a bug on this routine and had noticed it existed in version 1.3.0, 1.7.0 and 1.8.0 in CubeMX. Having seen that post, I got the latest version (ver 1.11.0 ) from ST GitHub (https://github.com/STMicroelectronics/STM32CubeH7/tree/master/Drivers) and replaced my stm32h7xx_hal_spi.c and stm32h7xx_hal_spi.h with that version. It now actually receives the entire 32 bytes and the bytes are valid. However, it does not come out of the HAL_SPI_TransmitReceive routine. The transmitter thinks it needs to send more but TXP is not set so it gets stuck in that loop. I am doing 8 bytes transfers.

Please assist as we are stuck and the rest of the process cannot continue until I get over this communication issue.

Many thanks in advance.

23 REPLIES 23
Pavel A.
Evangelist III

> and never returns.

You've specified timeout of 5 seconds, so at least if the master does not clock, it should return after 5 seconds. Are you sure that your program is not caught in exception and the HAL timer interrupt is not blocked?

smati2
Associate II

Hi @Pavel A. 

Thank you for your comment. I have changed the Timeout value to 20 sec (20000) and got the same result.

What program exception are you referring? I am basically calling the HAL_SPI_TransmitReceive routine that is provided in the drivers of the STM32CubeIDE and the call never returns. If I pause the debugger, I see it caught in the loop of that routine but TXP and or RXP is not set with more transmit to complete or EOT is set but the count shows more bytes to transmit. Yet the TXP is not set and it basically is stuck in that loop.

Also it might be the case that when I push pause, the SPI hardware may be continuing even though I am on a pause/break....

This is really mind bugging as I am stuck on this issue for multiple days and can't move forward until resolving this issue.

BTW, as mentioned before, everything works if the transfer Size is small (say 7 or less). However, if the transfer size is 32, I run into this issue. Also note that in my application, for that call instance, it needs to be 32 bytes as that is the number of bytes Master is sending/receiving.

smati2
Associate II

In troubleshooting my issue, right at the bottom of the loop, I added an "if" statement checking to see if for any reason initial_TxXferCount or initial_RxXferCount go below zero (though they are defined as uint16_t) OR if for any reason any of them go above "32" or EOT is reached. Sure enough it hit my breakpoint. Then, I noticed that at that point TxXferCount = 0x6 and RxXferCount =0xD. Yet, EOT was set and both TXP and RXP show they are cleared. Is that expected? BTW, at that break point, the SR flag is showing that both UDR and OVR are set! I am not sure if that's because of the break point with Transmitter hardware still continuing while on break point that caused the UDR/OVR to be set or if something actually went wrong to cause those flags to be set at EOT.

Note: Attached file (STM32H743 - Breakpoint on EOT during 32 bytes SPI transfer using HAL_SPI_TransmitReceive routine.jpg) shows my added "if" statement for the breakpoint along with the state of hspi4. Please note that the if statement check for iWriteMemory is there so that it would only do the break point if I am doing the HAL_SPI_TransmitReceive during a 32 (0x20) bytes transfer and not during shorter transfer calls.

With this, I hope someone can shed some light into the issue.

Thank you.

Pavel A.
Evangelist III

Hi,

At lines 1432 -1442 this routine checks timeout and should return with status HAL_TIMEOUT if the transfer has not ended. Since this does not happen, something else is broken (systick interrupt is blocked?)

SPI controller of STM32H7 is more complicated than in other models. Show the initialization of SPI.

smati2
Associate II

Hi @Pavel A. 

Thanks for your follow up. As for the systick, I am not intentionally blocking it and neither aware of that. The HAL_GetTick is showing that the clock is going up. So, I assume it is not blocked.

As for the initialization of SPI, I am using the .ioc and let it generate the code. Here's the SPI initialization:

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

/**

 * @brief SPI4 Initialization Function

 * @param None

 * @retval None

 */

static void MX_SPI4_Init(void)

{

 /* USER CODE BEGIN SPI4_Init 0 */

 /* USER CODE END SPI4_Init 0 */

 /* USER CODE BEGIN SPI4_Init 1 */

 /* USER CODE END SPI4_Init 1 */

 /* SPI4 parameter configuration*/

 hspi4.Instance = SPI4;

 hspi4.Init.Mode = SPI_MODE_SLAVE;

 hspi4.Init.Direction = SPI_DIRECTION_2LINES;

 hspi4.Init.DataSize = SPI_DATASIZE_8BIT;

 hspi4.Init.CLKPolarity = SPI_POLARITY_LOW;

 hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;

 hspi4.Init.NSS = SPI_NSS_HARD_INPUT;

 hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;

 hspi4.Init.TIMode = SPI_TIMODE_DISABLE;

 hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

 hspi4.Init.CRCPolynomial = 0x0;

 hspi4.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

 hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;

 hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;

 hspi4.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;

 hspi4.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;

 hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;

 hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;

 hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;

 hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;

 hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE;

 if (HAL_SPI_Init(&hspi4) != HAL_OK)

 {

  Error_Handler();

 }

 /* USER CODE BEGIN SPI4_Init 2 */

 /* USER CODE END SPI4_Init 2 */

}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

BTW, looking at other posts, I even have the MPU enabled with cache enabled and have tried it with cache disabled, but nothing really fruitful. In any case, below is the MPU init. Please note that the commented lines with "//" are how the .ioc generated, which didn't work and so my changes to that routine is right below the commented lines (below lines with "//"):

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

void MPU_Config(void)

{

 MPU_Region_InitTypeDef MPU_InitStruct = {0};

 /* Disables the MPU */

 HAL_MPU_Disable();

 /** Initializes and configures the Region and the memory to be protected

 */

 MPU_InitStruct.Enable = MPU_REGION_ENABLE;

 MPU_InitStruct.Number = MPU_REGION_NUMBER0;

 MPU_InitStruct.BaseAddress = 0x0;

 //MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;

 MPU_InitStruct.Size = MPU_REGION_SIZE_1KB;

 //MPU_InitStruct.SubRegionDisable = 0x87;

 MPU_InitStruct.SubRegionDisable = 0x0;

 MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;

 //MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;

 MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;

 //MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;

 MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;

 //MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;

 MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;

 MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;

 //MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

 MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;

 HAL_MPU_ConfigRegion(&MPU_InitStruct);

 /* Enables the MPU */

 HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

}

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

And below is from the startup of the main itself:

/**

 * @brief The application entry point.

 * @retval int

 */

int main(void)

{

 /* USER CODE BEGIN 1 */

 /* USER CODE END 1 */

 /* MPU Configuration--------------------------------------------------------*/

 MPU_Config();

 /* Enable I-Cache---------------------------------------------------------*/

 SCB_EnableICache();

 /* Enable D-Cache---------------------------------------------------------*/

 SCB_EnableDCache();

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

 HAL_Init();

 /* USER CODE BEGIN Init */

 /* USER CODE END Init */

 /* Configure the system clock */

 SystemClock_Config();

 /* USER CODE BEGIN SysInit */

 /* USER CODE END SysInit */

 /* Initialize all configured peripherals */

 MX_GPIO_Init();

 MX_SPI4_Init();

 MX_USART2_UART_Init();

 MX_TIM2_Init();

 /* USER CODE BEGIN 2 */

 //Start and enable timer 2 for 5ms slice timer

 HAL_TIM_Base_Start_IT(&htim2);

smati2
Associate II

BTW, in troubleshooting this, I changed the Master routine for longer SPI transfers of 32 consecutive bytes and split such transfer into two 16 bytes transfers. Now, the transfer completes and the received data appears to be valid. However, on these longer transfers of 16 bytes, the return code is not 0x00 (HAL_OK) but it is actually 0x80. So, I am not comfortable with the outcome of 0x80 return. Any reason why?

BTW, the process under our older flavor of STM32, namely STM32L4R5, with 32 bytes transfers work fine. For our new micro (STM32H743), at the end of the day, I want to be able to do the 32 bytes transfers as well. However, for the time being and as a work around, I am OK with splitting the 32 bytes into two 16 bytes transfers. Nevertheless, even in this case, I don't like that the return code is 0x80, even though the received data appear to be valid. Hope you can help.

Thank you.

AScha.3
Chief II

just because i have also an H743 here: i am running up to 4 SPI , one is in slave mode receiving 2KB blocks at 12 Mbit. no problems, so i think your problem is not about the spi , but something else.

first comes to my mind: problem at 32 byte -> the cache lines are 32 B.

try : switch off d-cache, makes something better ?

If you feel a post has answered your question, please click "Accept as Solution".
Pavel A.
Evangelist III

>  I don't like that the return code is 0x80

Yes, looks suspicious.