cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 CAN Bus skips some messages

RockCoder
Visitor

Hi, I am using three STM32 devices for CAN communication:

Hardware

  • Device A: STM32G431 (FDCAN)

  • Device B: STM32F303 (Classic CAN)

  • Device C: STM32H7A3 (FDCAN)

Setup

  • A sends a continuous sequence of commands to B.

  • B processes each command and replies to A over the same CAN bus.

  • One specific command requires B to perform a synchronization sequence with C (also over CAN).

  • While B is synchronizing with C, A continues to send commands to B.

Issue

  • During the B↔C synchronization, B intermittently misses some frames from A (i.e., A transmits, but B does not process/see a corresponding message).

  • The failure is not immediate: A↔B + B↔C run together fine for a while (sometimes short, sometimes long), then a frame from A is skipped.

  • When the synchronization with C is disabled, A↔B runs reliably with no missed frames.


CAN ID from A to B: 0x12C
CAN ID from B to A: 0x12D
CAN IDs between B and C (sync purposes): 0x2BC, 0x2BD and 0x2BE

As you can see from the inserted can config the sampling point for 3 devices is at 87.5%. Bitrate is 500000 bps for three of them. 

The CAN configurations for device A:

static void MX_FDCAN1_Init(void)
{

  /* USER CODE BEGIN FDCAN1_Init 0 */

  /* USER CODE END FDCAN1_Init 0 */

  /* USER CODE BEGIN FDCAN1_Init 1 */

  /* USER CODE END FDCAN1_Init 1 */
  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_NO_BRS;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = ENABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = DISABLE;
  hfdcan1.Init.NominalPrescaler = 40;
  hfdcan1.Init.NominalSyncJumpWidth = 1;
  hfdcan1.Init.NominalTimeSeg1 = 6;
  hfdcan1.Init.NominalTimeSeg2 = 1;
  hfdcan1.Init.DataPrescaler = 25;
  hfdcan1.Init.DataSyncJumpWidth = 1;
  hfdcan1.Init.DataTimeSeg1 = 6;
  hfdcan1.Init.DataTimeSeg2 = 1;
  hfdcan1.Init.StdFiltersNbr = 8;
  hfdcan1.Init.ExtFiltersNbr = 0;
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN FDCAN1_Init 2 */
  FDCAN_FilterTypeDef sFilterConfig;

  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 0;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x12C; 
  sFilterConfig.FilterID2 = 0x18F;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 1;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_REJECT;
  sFilterConfig.FilterID1 = 0x00; 
  sFilterConfig.FilterID2 = 0x12B;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 2;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_REJECT;
  sFilterConfig.FilterID1 = 0x190; 
  sFilterConfig.FilterID2 = 0x31F;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  /* USER CODE END FDCAN1_Init 2 */

}

 Device B:

static void MX_CAN_Init(void)
{

  /* USER CODE BEGIN CAN_Init 0 */

  /* USER CODE END CAN_Init 0 */

  /* USER CODE BEGIN CAN_Init 1 */

  /* USER CODE END CAN_Init 1 */
  hcan.Instance = CAN;
  hcan.Init.Prescaler = 9;
  hcan.Init.Mode = CAN_MODE_NORMAL;
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan.Init.TimeSeg1 = CAN_BS1_6TQ;
  hcan.Init.TimeSeg2 = CAN_BS2_1TQ;
  hcan.Init.TimeTriggeredMode = DISABLE;
  hcan.Init.AutoBusOff = DISABLE;
  hcan.Init.AutoWakeUp = DISABLE;
  hcan.Init.AutoRetransmission = ENABLE;
  hcan.Init.ReceiveFifoLocked = DISABLE;
  hcan.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CAN_Init 2 */
  CAN_FilterTypeDef canfilterconfig;

  canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
  canfilterconfig.FilterBank = 0;  // which filter bank to use from the assigned ones
  canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
  canfilterconfig.FilterIdHigh = 0x12C<<5;
  canfilterconfig.FilterIdLow = 0;
  canfilterconfig.FilterMaskIdHigh = 0x7FF<<5;
  canfilterconfig.FilterMaskIdLow = 0x0000;
  canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
  canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;

  HAL_CAN_ConfigFilter(&hcan, &canfilterconfig);
  
  canfilterconfig.FilterActivation = CAN_FILTER_ENABLE;
  canfilterconfig.FilterBank = 1;  // which filter bank to use from the assigned ones
  canfilterconfig.FilterFIFOAssignment = CAN_FILTER_FIFO1;
  canfilterconfig.FilterIdHigh = 0x2BD<<5;
  canfilterconfig.FilterIdLow = 0;
  canfilterconfig.FilterMaskIdHigh = 0x7FF<<5;
  canfilterconfig.FilterMaskIdLow = 0x0000;
  canfilterconfig.FilterMode = CAN_FILTERMODE_IDMASK;
  canfilterconfig.FilterScale = CAN_FILTERSCALE_32BIT;

  HAL_CAN_ConfigFilter(&hcan, &canfilterconfig);
  
  /* USER CODE END CAN_Init 2 */

}

Device C:

void MX_FDCAN1_Init(void)
{

  /* USER CODE BEGIN FDCAN1_Init 0 */

  /* USER CODE END FDCAN1_Init 0 */

  /* USER CODE BEGIN FDCAN1_Init 1 */

  /* USER CODE END FDCAN1_Init 1 */
  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_FD_NO_BRS;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = ENABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = DISABLE;
  hfdcan1.Init.NominalPrescaler = 12;
  hfdcan1.Init.NominalSyncJumpWidth = 1;
  hfdcan1.Init.NominalTimeSeg1 = 6;
  hfdcan1.Init.NominalTimeSeg2 = 1;
  hfdcan1.Init.DataPrescaler = 12;
  hfdcan1.Init.DataSyncJumpWidth = 1;
  hfdcan1.Init.DataTimeSeg1 = 6;
  hfdcan1.Init.DataTimeSeg2 = 1;
  hfdcan1.Init.MessageRAMOffset = 0;
  hfdcan1.Init.StdFiltersNbr = 8;
  hfdcan1.Init.ExtFiltersNbr = 0;
  hfdcan1.Init.RxFifo0ElmtsNbr = 4;
  hfdcan1.Init.RxFifo0ElmtSize = FDCAN_DATA_BYTES_64;
  hfdcan1.Init.RxFifo1ElmtsNbr = 0;
  hfdcan1.Init.RxFifo1ElmtSize = FDCAN_DATA_BYTES_8;
  hfdcan1.Init.RxBuffersNbr = 0;
  hfdcan1.Init.RxBufferSize = FDCAN_DATA_BYTES_8;
  hfdcan1.Init.TxEventsNbr = 0;
  hfdcan1.Init.TxBuffersNbr = 0;
  hfdcan1.Init.TxFifoQueueElmtsNbr = 1;
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
  hfdcan1.Init.TxElmtSize = FDCAN_DATA_BYTES_64;
  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN FDCAN1_Init 2 */
  FDCAN_FilterTypeDef sFilterConfig;

  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 0;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x01; 
  sFilterConfig.FilterID2 = 0x12B;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 1;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_REJECT;
  sFilterConfig.FilterID1 = 0x12C; 
  sFilterConfig.FilterID2 = 0x18F;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 2;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x190; 
  sFilterConfig.FilterID2 = 0x1F3;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 3;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_REJECT;
  sFilterConfig.FilterID1 = 0x1F4; 
  sFilterConfig.FilterID2 = 0x257;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 4;
  sFilterConfig.FilterType = FDCAN_FILTER_RANGE;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x258; 
  sFilterConfig.FilterID2 = 0x351;
  if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig) != HAL_OK)
    Error_Handler();
  /* USER CODE END FDCAN1_Init 2 */

}

For sending and receiving CAN messages, the functions are as follows:
Device A:

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    CanFrameBuffer *frame = &canRxBuffer[canRxWriteIdx];
    memset(frame, 0, sizeof(CanFrameBuffer));

    if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, frame->data) != HAL_OK)
    {
        Error_Handler();
    }

    canRxWriteIdx++;
    if (canRxWriteIdx >= CAN_RX_BUF_SIZE)
        canRxWriteIdx = 0;
}

void ProcessUSBData(uint8_t *Buf, uint16_t Len)
{
  uint16_t bytesSent = 0;

    while (bytesSent < Len)
    {
        uint8_t chunk[CAN_PAYLOAD_SIZE] = {0};
        uint8_t chunkSize = (Len - bytesSent) > CAN_PAYLOAD_SIZE ?
                             CAN_PAYLOAD_SIZE : (Len - bytesSent);
                             
        TxHeader.DataLength = dlc_from_len(chunkSize);

        while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) == 0);
        if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, &Buf[bytesSent]) != HAL_OK)
            Error_Handler();
        bytesSent += chunkSize;
    }

    // Always send an explicit 0x00 terminator frame
    uint8_t terminator = 0x00;
    TxHeader.DataLength = FDCAN_DLC_BYTES_1;
    while (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) == 0);
    if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, &terminator) != HAL_OK)
        Error_Handler();
}


Device B. I use two Fifos:
Fifo0: messages from A
Fifo1: messages from C

void HAL_CAN_RxFifo0FullCallback(CAN_HandleTypeDef *hcan)
{
   while (HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO0) > 0) {
      CAN_RxHeaderTypeDef hdr;     
      uint8_t d[8];
      
      if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &hdr, d) != HAL_OK)
      {
         Error_Handler(); // Handle receive error
      }
        
      if (hdr.StdId == Sync_Res) {
            syncAckReceived = 1;
            rx1_sync_isr_count++;
            continue;
       }     
      
      uint8_t next = (uint8_t)((RxWrite + 1u) % RXBUFSIZE);
      if (next == RxRead) {
         // overflow: drop oldest (or set a flag/counter)
         g_swDrops++;
         continue;  
      }
      
      // Use real DLC (0..8) and clear tail
      uint8_t dlc = (uint8_t)(hdr.DLC & 0x0Fu);
      if (dlc > 8) dlc = 8;
      
      RxBuf[RxWrite].id  = hdr.StdId;   // you’re using standard IDs
      RxBuf[RxWrite].dlc = dlc;
      memcpy(RxBuf[RxWrite].data, d, dlc);
      if (dlc < 8) {
         memset(RxBuf[RxWrite].data + dlc, 0, 8 - dlc);
      }
      
      RxWrite = next;
   }
}

For sending messages:
while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) == 0);
CAN_SendCommand(cmdID, chunk, idx);

bool CAN_SendCommand(PIPMOD_COMMANDS cmdID, uint8_t *data, uint8_t idx)
{
   TxHeader.DLC = 8;  // data length
   TxHeader.IDE = CAN_ID_STD;
   TxHeader.RTR = CAN_RTR_DATA;
   TxHeader.StdId = cmdID;  
   
   if (data == NULL)
   {
      TxHeader.DLC = 0;
   }
   
   if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, (uint8_t *)data, &TxMailbox) != HAL_OK)
   {
      sprintf(responseBuffer, "CAN Transmission failed: %s", GetCommandName(cmdID));
      EnqueueLCErrorResponse(&arCommUnits[idx], UTF8create(responseBuffer, 4), ERROR_INTERFACE_PORTAL);
      return false;
   } 
   
   while (HAL_CAN_IsTxMessagePending(&hcan, TxMailbox));
   return true;
}
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    while (HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO1) > 0) {
        CAN_RxHeaderTypeDef hdr;     
        uint8_t data[8];             

        if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO1, &hdr, data) != HAL_OK) {
            Error_Handler();
        }
        rx1_isr_count++;
        if (hdr.StdId == Sync_Res) {
            syncAckReceived = 1;
            rx1_sync_isr_count++;
        }
    }
}

Device C:

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
    {
        Error_Handler();
    }
 
    if (RxHeader.Identifier == SYNC_START)                              IsSyncStart             = 1;
    if (RxHeader.Identifier == SYNC_END)                                IsSyncRes               = 1;  
}

bool CAN_SendCommand(PORTAL_COMMANDS cmdID, uint8_t *data, uint8_t idx)
{
   if (cmdID == SYNC_RES) { // Device B uses normal CAN (only 8 bytes)
      TxHeader.FDFormat = FDCAN_CLASSIC_CAN;
      TxHeader.DataLength = FDCAN_DLC_BYTES_8;
   }
   else {
      TxHeader.FDFormat = FDCAN_FD_CAN;
      TxHeader.DataLength = FDCAN_DLC_BYTES_64;
   }
   
   if (data == NULL)
   {
      TxHeader.DataLength = FDCAN_DLC_BYTES_0;           // no payload
   }
   
   TxHeader.Identifier          = cmdID;
   TxHeader.IdType              = FDCAN_STANDARD_ID;
   TxHeader.TxFrameType         = FDCAN_DATA_FRAME;
   TxHeader.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
   TxHeader.BitRateSwitch       = FDCAN_BRS_OFF;
   TxHeader.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
   TxHeader.MessageMarker       = 0;
   
   if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, (uint8_t *)data) != HAL_OK)
   {
      sprintf(responseBuffer, "CAN Transmission failed: %s", GetCommandName(cmdID));
      EnqueueLCErrorResponse(&arCommUnits[idx], UTF8create(responseBuffer, 4), ERROR_INTERFACE_PORTAL);
      return false;
   }
   
   return true;
}

The function for the sync between B and C:
Device B:

bool DoHwSync()
{
   bool finishCmd = false;
   PIPMOD_COMMANDS cmdID1, cmdID2;
   cmdID1 = Sync_Start; //0x2BC
   cmdID2 = Sync_End;   //0x2BD
   
   if(IS_FLAG_SET(pipettorState, STATE_WAIT_FOR_SYNC)) 
   {
      
      if(!IsSyncResSent) {
         while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) == 0);
         if(!CAN_SendCommand(cmdID1, NULL, NULL)) return finishCmd;

      }
      
      IsSyncResSent = 1;
      // Wait for sync res from C
      if(!syncAckReceived) return finishCmd;
      syncAckReceived = 0;

      while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) == 0);
      if(!CAN_SendCommand(cmdID2, NULL, NULL)) return finishCmd;

      IsSyncResSent = 0;
      finishCmd = true;
      return finishCmd;
   }
   return finishCmd;
}

Device C:

bool HardSync(uint8_t asyncCmdIdx)
{
   bool finishCmd = false;
   uint32_t startTime = 0;
   PORTAL_COMMANDS cmdID;
   cmdID = SYNC_RES; //0x2BD
   int32_t _timeout = 0;
   
   if(IS_FLAG_SET(portalState, STATE_HARD_SYNC_PENDING) && IS_FLAG_SET(portalState, STATE_HARD_SYNC_WAITING))
   {
      if(!IsSyncStart) return finishCmd;

      IsSyncStart = 0;
      
      if(!CAN_SendCommand(cmdID, NULL, asyncCmdIdx)) 
         return finishCmd;

      uint32_t startTime = HAL_GetTick();
      while (!IsSyncRes)
      {
         if (HAL_GetTick() - startTime > 5000) // 5 seconds
         {
            IsSyncRes = 0;
            return ErrorHandlerSynchronize(asyncCmdIdx, 2);
         }
      }
      
      IsSyncRes = 0;
      
      RESET_FLAG(portalState, STATE_HARD_SYNC_PENDING);
      RESET_FLAG(portalState, STATE_HARD_SYNC_WAITING); 
      
      finishCmd = true;
      return finishCmd;
   }
   return finishCmd;
}

As I said earlier, device B skipped one or two messages from A when sync action takes place between B and C, but it does not happen immediately (for example after 5 successful sync actions then it fails). 

Another thing is take the CAN topology in the line (there are not only 3 CAN devices) is kind of tree/star, which is mistake made from the beginning. However, CAN transmission still works as expected until we have this sync action. The whole CAN line has only 2 terminator resistors (60 ohm is measured). I also tried to set bitrate to 250000 and 125000 bps but it still did not work.

I apologise for the long messages but I hope someone could help me with this.

Thank you in advance.

1 REPLY 1
mƎALLEm
ST Employee

Hello @RockCoder and welcome to the community,

More likely it's an implementation issue also depends on the load of your CAN bus.

My comments:

Setting BS2 to 1 is not a good idea. Try to increase BS1 and BS2 to increase them as much as possible and decreasing CAN prescaler. 

I suggest to use CLASSIC frame format from FDCAN sides instead of  FDCAN_FRAME_FD_NO_BRS.

Try to handle the received frames in a ring buffer filled in the Rx call backs and handled in main.

Play with the Rx interrupt priorities.. Increase it.

Check also if a FIFO overrun has occurred: FOVR bit is set in the CAN_RFR register:

The behavior depends on if the FIFO Lock is enabled or disabled:

mALLEm_0-1761309908924.png

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.