Skip to main content
Graduate II
September 9, 2025
Solved

HAL_SPI_Abort_IT() does not call HAL_SPI_AbortCpltCallback() upon completion

  • September 9, 2025
  • 3 replies
  • 738 views

I have an SPI slave running on STM32L4 in DMA mode.   Here is the basic structure (simplified for this posting):

 

#define NSS (HAL_GPIO_ReadPin( SPI3_NSS_GPIO_Port, SPI3_NSS_Pin ))
#define BUFFERSIZE 32

uint8_t SpiTxBuffer[BUFFERSIZE], SpiRxBuffer[BUFFERSIZE];

typedef enum
{
 Idle,
 Wait,
 Abort,
 Done
} State_t;

State_t State = Idle;

int main(void)
{
 /*
 Usual MX boilerplate here.
 */
 while(1)
 {
 switch (State)
 {
 case Idle:
 if(!NSS)
 {
 if( HAL_SPI_TransmitReceive_DMA( &hspi, SpiTxBuffer, SpiRxBuffer,
 BUFFERSIZE ) != HAL_OK )
 {
 Error_Handler();
 }
 State = Wait;
 }
 break;

 case Wait:
 if(NSS)
 {
 if( HAL_SPI_Abort_IT(&hspi) == HAL_OK)
 {
 State = Abort;
 }
 else
 {
 Error_Handler();
 }
 }
 break;

 case Abort:
 break;

 case Done:
 DoSomethingWithTheData();
 State = Idle;
 }
 DoSomethingElse(); /*Do some task while waiting for SPI to arrive*/
 }
}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
 State = Done;
}

void HAL_SPI_AbortCpltCallback(SPI_HandleTypeDef *hspi)
{
 State = Done;
}

 In this circumstance, if the host (master) sends fewer bytes than expected and deactivates NSS, I detect this and abort the SPI exchange.  However, the AbortCallback never gets called as evidenced by the fact that State never gets set to DONE.

Any ideas?

Edit: Fixed a typo

    This topic has been closed for replies.
    Best answer by Saket_Om

    Hello @AMacd.1 

    According to your code the state will be set to Done when calling HAL_SPI_Abort_IT() inside HAL_SPI_AbortCpltCallback(). Then if the return value of this call is HAL_OK the state will be set again to Abort. In this case you will not detect that the callback is called.

    3 replies

    Saket_OmAnswer
    Technical Moderator
    September 10, 2025

    Hello @AMacd.1 

    According to your code the state will be set to Done when calling HAL_SPI_Abort_IT() inside HAL_SPI_AbortCpltCallback(). Then if the return value of this call is HAL_OK the state will be set again to Abort. In this case you will not detect that the callback is called.

    AMacd.1Author
    Graduate II
    September 10, 2025

    I see my mistake now.  My misunderstanding was based on my belief that functions that are "not blocking" return immediately and the callbacks are expected to occur at some time later (such as when an interrupt fires).  Apparently, the function HAL_SPI_Abort_IT() calls the callback function before it returns.

    Perhaps the intent of HAL_SPI_Abort_IT() is that it is to be called from an interrupt so that the main path of execution can look for the results of the callback.  Is that correct?

    So, there isn't any reason to call HAL_SPI_Abort_IT() instead of HAL_SPI_Abort() to abort an SPI DMA transfer from within the main loop is there? The documentation isn't very helpful.

     

    Technical Moderator
    September 11, 2025

    Hello @AMacd.1 

    The function HAL_SPI_Abort_IT() can be used to abort a transfer in interrupt or DMA mode. 

    Saket_Om_0-1757578425724.png

    Your code should be updated as below: 

    #define NSS (HAL_GPIO_ReadPin( SPI3_NSS_GPIO_Port, SPI3_NSS_Pin ))
    #define BUFFERSIZE 32
    
    uint8_t SpiTxBuffer[BUFFERSIZE], SpiRxBuffer[BUFFERSIZE];
    
    typedef enum
    {
     Idle,
     Wait,
     Abort,
     Done
    } State_t;
    
    State_t State = Idle;
    
    int main(void)
    {
     /*
     Usual MX boilerplate here.
     */
     while(1)
     {
     switch (State)
     {
     case Idle:
     if(!NSS)
     {
     if( HAL_SPI_TransmitReceive_DMA( &hspi, SpiTxBuffer, SpiRxBuffer,
     BUFFERSIZE ) != HAL_OK )
     {
     Error_Handler();
     }
     State = Wait;
     }
     break;
    
     case Wait:
     if(NSS)
     {
     HAL_SPI_Abort_IT(&hspi);
     while(TransferAbort != 1);
     State = Done;
     }
     break;
    
     case Abort:
     break;
    
     case Done:
     DoSomethingWithTheData();
     State = Idle;
     }
     DoSomethingElse(); /*Do some task while waiting for SPI to arrive*/
     }
    }
    
    void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
    {
     State = Done;
    }
    
    void HAL_SPI_AbortCpltCallback(SPI_HandleTypeDef *hspi)
    {
     TransferAbort = 1;
    }
    AMacd.1Author
    Graduate II
    September 10, 2025

    OK, so if I change my code so that I call HAL_SPI_Abort()  instead of HAL_SPI_Abort_IT(), and set the state to Done, the return value ends up being HAL_ERROR and I see the value 0x40 in hspi3.ErrorCode.  What's going on here?  Code Snippet:

     case Wait:
     if(NSS)
     {
     DebugVal = HAL_SPI_Abort(&hspi3);
     if( DebugVal == HAL_OK)
     {
     State = Done;
     }
     else
     {
     Error_Handler();
     }
     }
     break;

     

    Super User
    September 10, 2025

    The documentation isn't very helpful.

    This indeed is so, and users are encouraged to look in the source.

    There you can find that ErrorCode 0x40 is HAL_SPI_ERROR_ABORT.

    Function HAL_SPI_Abort sets status HAL_SPI_ERROR_ABORT in quite a few points, you can trace this function and find where it sets this status and if it can be ignored.

    Function HAL_SPI_Abort_IT can be called from background program or interrupt handler of a lower priority than the SPI or its DMA (to not block the SPI or DMA interrupts).

     

    AMacd.1Author
    Graduate II
    September 10, 2025

    @Pavel A.  Yikes!  OK, will do.  TBH, its annoying that there are errors that "can be ignored".  Rather like "the sky is falling" eh?

     

    AMacd.1Author
    Graduate II
    September 11, 2025

    @Pavel A.

    Once I get more info about the FIFO, not clearing, I'll open a new topic.  The original question has been answered to my satisfaction.