2025-09-25 4:32 AM - last edited on 2025-09-25 4:43 AM by Andrew Neil
I’m currently developing a custom board based on an STM32U585.
On the board, I have three SPI devices connected to the MCU, each managed by its own chip-select line:
* MAX9939 → requires LSB first and can be controlled in transmit-only SPI mode.
* LSM6DSL (IMU) → requires MSB first and full-duplex mode.
* CAT25256 (EEPROM) → also requires MSB first and full-duplex mode.
To handle these differences, I switch the SPI configuration dynamically at runtime. The function I use for reconfiguration is:
int SPI_SetConfig(SPI_HandleTypeDef *hspi, struct _myspi_config_t *config)
{
printf(">>>>>>>>>>>>QUI\r\n");
// Skip re-initialization if configuration already matches
if (hspi->Init.CLKPolarity == config->CPOL &&
hspi->Init.CLKPhase == config->CPHA &&
hspi->Init.FirstBit == config->order &&
hspi->Init.Direction == config->direction &&
hspi->Init.DataSize == config->datasize &&
hspi->Init.BaudRatePrescaler == config->prescaler) {
return 0;
}
// Abort any ongoing transfer
if (HAL_SPI_GetState(hspi) != HAL_SPI_STATE_READY) {
HAL_SPI_Abort(hspi);
}
__HAL_SPI_DISABLE(hspi);
hspi->Init.CLKPolarity = config->CPOL;
hspi->Init.CLKPhase = config->CPHA;
hspi->Init.FirstBit = config->order;
hspi->Init.DataSize = config->datasize;
hspi->Init.Direction = config->direction;
hspi->Init.BaudRatePrescaler = config->prescaler;
if (HAL_SPI_Init(hspi) != HAL_OK) {
printf("SPI_SetConfig()::error while changing polarity\r\n");
return -1;
}
__HAL_SPI_ENABLE(hspi);
return 0;
}
## The issue
When I switch configurations repeatedly between devices, I encounter the following problem:
Calling HAL_SPI_Transmit_DMA() starts normally (no error returned).
However, HAL_SPI_TxCpltCallback() is never called.
If I check the state with HAL_SPI_GetState(), the SPI remains stuck in HAL_SPI_STATE_BUSY_TX.
If I wait in a loop for transmission completion, the loop never exits.
I checked both the SPI and DMA registers, and they appear to be fine. Despite that, the DMA transfer never completes.
If I skip one particular call to SPI_SetConfig(), the issue does not occur. But I don’t understand why this happens, and I’m not confident it won’t show up again under other conditions.
## Question
What could cause HAL_SPI_Transmit_DMA() to get stuck in BUSY_TX after reconfiguring the SPI peripheral multiple times?
How can I ensure safe switching between different SPI configurations without breaking DMA transfers?
Thanks in advance!
Edited to apply source code formatting - please see How to insert source code for future reference.
2025-09-25 4:45 AM
Welcome to the forum.
For best results, please see: How to write your question to maximize your chances to find a solution
Before adding the complications of DMA, have you got this working without it?
2025-09-25 5:06 AM
I changed to polling mode:
int status = 0;
if ((status=HAL_SPI_Transmit((SPI_HandleTypeDef*)handle, data, length, SPI_WAIT)) != HAL_OK) {
printf("eeprom_write: HAL_SPI_Transmit error %d\r\n", status);
return MEMORY_STATUS_NOK;
}
and I get the following message:
eeprom_write: HAL_SPI_Transmit error 1
1 is HAL_ERROR.
It is unable to complete the transfer.
SPI_WAIT is set to 500
2025-09-25 5:10 AM
I read also the SPI ErrorCode. The Error code is 32.
#define HAL_SPI_ERROR_FLAG (0x00000020UL) /*!< Error on RXP/TXP/DXP/FTLVL/FRLVL Flag */
2025-09-25 5:59 AM - last edited on 2025-09-25 6:18 AM by Andrew Neil
I finally found the solution. I think the problem is related to an HW bug, because the SPI device seems unable to transmit both in DMA or Polling mode.
I solved the issue by using this source code to switch among SPI configurations:
int SPI_SetConfig(SPI_HandleTypeDef *hspi, struct _myspi_config_t *config)
{
if (hspi->Init.CLKPolarity == config->CPOL &&
hspi->Init.CLKPhase == config->CPHA &&
hspi->Init.FirstBit == config->order &&
hspi->Init.Direction == config->direction &&
hspi->Init.DataSize == config->datasize &&
hspi->Init.BaudRatePrescaler == config->prescaler) {
return 0; // No change needed
}
if (HAL_SPI_GetState(hspi) != HAL_SPI_STATE_READY) {
HAL_SPI_Abort(hspi);
}
__HAL_SPI_DISABLE(hspi);
if (hspi->Instance == SPI1) {
__HAL_RCC_SPI1_FORCE_RESET();
__HAL_RCC_SPI1_RELEASE_RESET();
} else if (hspi->Instance == SPI2) {
__HAL_RCC_SPI2_FORCE_RESET();
__HAL_RCC_SPI2_RELEASE_RESET();
} else if (hspi->Instance == SPI3) {
__HAL_RCC_SPI3_FORCE_RESET();
__HAL_RCC_SPI3_RELEASE_RESET();
}
hspi->Init.CLKPolarity = config->CPOL;
hspi->Init.CLKPhase = config->CPHA;
hspi->Init.FirstBit = config->order;
hspi->Init.DataSize = config->datasize;
hspi->Init.Direction = config->direction;
hspi->Init.BaudRatePrescaler = config->prescaler;
if (HAL_SPI_Init(hspi) != HAL_OK)
{
printf("SPI_SetConfig()::error while changing polarity\r\n");
return -1;
}
__HAL_SPI_ENABLE(hspi);
return 0;
}
I found that __HAL_RCC_SPIx_FORCE_RESET() / __HAL_RCC_SPIx_RELEASE_RESET() macros reset the SPIx device by re-initializing all registers, FIFO and so on.
I haven't found any equivalent HAL function to get the same result. Now I am able to switch, to transmit/receive in both DMA and polling mode. For these reasons I think this issue is related to an internal HW bug, caused by an internal inconsistent status.
Edited to apply proper source code formatting - please see How to insert source code for future reference.
You need to use the </> button rather than "preformatted"