2024-05-16 6:32 AM
I use a STM32L496 and I'm implementing the SPI1 port for the communication with an external device.
I have used CubeMX to set things up with SPI_NSS_HARD_OUTPUT and an external pull-up on the SPI1_NSS pin. I have connected SPI1_MOSI to SPI1_MISO to read the transmitted data back.
When using HAL_SPI_TransmitReceive_DMA() or HAL_SPI_TransmitReceive_IT() the data is all send and received. The 32 bits are transferred within about 16us as SPI1_SCK is at 2MHz. SPI1_NSS goes low when __HAL_SPI_ENABLE() is used by HAL_SPI_TransmitReceive_DMA() or HAL_SPI_TransmitReceive_IT(). But after this it takes about 200us before SPI1_NSS goes high when __HAL_SPI_DISABLE() is used from within HAL_SPI_RxCpltCallback().
Why this long delay?
There is no difference between HAL_SPI_TransmitReceive_DMA() and HAL_SPI_TransmitReceive_IT().
When using SPI_NSS_SOFT instead of SPI_NSS_HARD_OUTPUT and make SPI1_NSS low before the use of HAL_SPI_TransmitReceive_DMA() or HAL_SPI_TransmitReceive_IT(), and high when HAL_SPI_GetState() doesn't show SPI1 is still busy, it also shows a delay of about 200us. Why?
Regards.
2024-05-21 3:06 AM
This delay of about 200us is caused by the handling of the interrupts as implemented within HAL.
It partly consists of the useless handling of the Half Complete interrupt (DMA_IT_HT), which can't be disabled, as it is set by HAL_DMA_Start_IT() because HAL_SPI_TransmitReceive_DMA() makes hdma->XferHalfCpltCallback not equal to NULL.
The largest part is simply the handling of the DMA_IT_TC interrupt within HAL.
My own code (as part of the interrupt handling) took less than 4us.
2025-11-25 6:13 AM
Hello man!
I have a similar problem with stm32h755iit6 microcontroller, my microcontroller got stuck in hal_spi_transmit_dma function for too long. Did you solve your problem?
2025-11-25 6:36 AM
Hello Djordje,
I'm not completely sure how I fixed it, or at least reduced the delay to a reasonable length.
Within stm32l4xx_it.c I found the following edits:
/**
* @brief This function handles DMA2 channel3 global interrupt.
*/
void DMA2_Channel3_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Channel3_IRQn 0 */
if(LL_DMA_IsActiveFlag_TC3(DMA2)) {
LL_DMA_ClearFlag_GI3(DMA2);
/* Call function Reception complete Callback */
ReceiveCompleteCallback();
}
else if (LL_DMA_IsActiveFlag_TE3(DMA2)) {
/* Call Error function */
ReceiveErrorCallback();
}
if (0) {
/* USER CODE END DMA2_Channel3_IRQn 0 */
/* USER CODE BEGIN DMA2_Channel3_IRQn 1 */
}
/* USER CODE END DMA2_Channel3_IRQn 1 */
}
/**
* @brief This function handles DMA2 channel4 global interrupt.
*/
void DMA2_Channel4_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Channel4_IRQn 0 */
if(LL_DMA_IsActiveFlag_TC4(DMA2)) {
LL_DMA_ClearFlag_GI4(DMA2);
/* Call function Transmission complete Callback */
TransmitCompleteCallback();
}
else if(LL_DMA_IsActiveFlag_TE4(DMA2)) {
/* Call Error function */
TransmitErrorCallback();
}
if (0) {
/* USER CODE END DMA2_Channel4_IRQn 0 */
/* USER CODE BEGIN DMA2_Channel4_IRQn 1 */
}
/* USER CODE END DMA2_Channel4_IRQn 1 */
}The callback functions also contain some LL functions to disable stuff:
void ReceiveCompleteCallback(void)
{
// DMA Rx transfer completed
DmaIrq |= CBUS_DMA_RX_COMPLETE;
}
void TransmitCompleteCallback(void)
{
// DMA Tx transfer completed
DmaIrq |= CBUS_DMA_TX_COMPLETE;
}
void ReceiveErrorCallback(void)
{
// Disable DMA Rx Channel
LL_DMA_DisableChannel(SPI_DMA, SPI_DMA_RX_CH);
// Disable DMA Tx Channel
LL_DMA_DisableChannel(SPI_DMA, SPI_DMA_TX_CH);
DmaIrq |= CBUS_DMA_RX_ERROR;
}
void TransmitErrorCallback(void)
{
// Disable DMA Rx Channel
LL_DMA_DisableChannel(SPI_DMA, SPI_DMA_RX_CH);
// Disable DMA Tx Channel
LL_DMA_DisableChannel(SPI_DMA, SPI_DMA_TX_CH);
DmaIrq |= CBUS_DMA_TX_ERROR;
}So this might be something you can start with.