Fixing STM32 CAN reception

Users of STM32 boards having Controller Area Network (CAN) bus reception capability may have faced an issue in receiving CAN frames when CAN FIFO overrun state is not handled properly. Let's see how we can fix it.

STM32 board [Image courtesy : DigiKey and ST Microelectronics]

This post assumes that the CAN bit-timing and baud-rate are configured properly as described in this post. Also an assumption has been made that the initial set of source files for STM32 board's firmware has been created by STM32CubeMX utility.

In this example, we're going to use STM32 F7xxx board. Kindly refer to appropriate headers and source files related to the CAN peripheral for your board. Under the directory Drivers/STM32F7xx_HAL_Driver/Src/, one should be able to find stm32f7xx_hal_can.c source file, where the majority of logic for CAN frames transmission and reception happens. Consider the following scenarios which happen in this file:

  1. HAL_CAN_IRQHandler() function : This routine is called when a CAN frame Rx/Tx related interrupt is set. In this function, we see that the CAN FIFO overrun error flag is set (FOV) which happens when the number of CAN frames received exceeds the FIFO capacity (defaults to three frames). When such errors happen we need to enable CAN receive (Rx) interrupt flag, otherwise the reception stops once the error-flag is set. Best way would be to make use of the HAL_CAN_ErrorCallback() weak-handler function where we can clear the error-flag and enable the CAN Rx interrupt.

    // Statement where the error-flag is appended in CAN IRQHandler.
    errorcode |= HAL_CAN_ERROR_FOV0;
    
    /**
     * Callback function for CAN errors.
     * @param canHandle  Pointer to a CAN_HandleTypeDef structure that contains
     *        the configuration information for a given CAN peripheral.
     */
    void HAL_CAN_ErrorCallback(CAN_HandleTypeDef* canHandle)
    {
      // For CAN Rx frames received in FIFO number 0.
      __HAL_CAN_CLEAR_FLAG(canHandle, CAN_FLAG_FOV0);
      __HAL_CAN_Receive_IT(canHandle, CAN_FIFO0);
    
      // For CAN Rx frames received in FIFO number 1.
      __HAL_CAN_CLEAR_FLAG(canHandle, CAN_FLAG_FOV1);
      __HAL_CAN_Receive_IT(canHandle, CAN_FIFO1);
    }
    
  2. HAL_CAN_Receive_IT() function : This routine is called when CAN Rx interrupt flags have been set and when it's good to receive those frames. One important thing to note here is that the CAN peripheral on which the frames are received is locked but not unlocked before the routine exits. This has been fixed in the recent HAL release by ST Microelectronics. We need to add an unlock-call, a macro definition in disguise, just before the function returns HAL_OK.

    __HAL_UNLOCK(hcan);
    

Everything is good now for CAN reception. Good luck!