ST FDCAN Introduction

Preface

FDCAN (Flexible Data-Rate CAN) is an upgraded version of CAN. Its features include:

  • The maximum length of the data segment per frame has increased from 8 bytes to 64 bytes.
  • The speed has increased from 1 Mbps to 5 Mbps, and even higher speeds are possible. In a data frame, the arbitration segment (ID and ACK) maintains the same maximum speed as CAN, which is 1 Mbps. This ensures robustness and reliability of the bus. However, the data segment can operate at 5 Mbps or even higher. Different baud rates can be used within a single data frame, hence the name “Flexible Data-Rate” (FD).
  • It is backward compatible with CAN.

FDCAN Format

  • a first arbitration phase
  • a data phase
  • a second arbitration phase

Frame architecture comparison between CAN-FD and CAN 2.0

After identifier, CAN 2.0 and CAN-FD have a different action:

  • CAN 2.0 sends an RTR bit to precise the type of frame: data frame (RTR is dominant) or remote frame(RTR is recessive).
  • CAN-FD sends always a dominant RRS (reserved) as it only supports data frames.
Main differences between CAN-FD and CAN 2.0

STM32CubeMX Setting

You can refer to the H7 configuration options available online. When using G4 or G0, there will be a lack of configuration options for Message RAM allocation. The rest of the settings are similar.

FDCAN sample code

you could refer as below for transmit data

#include <string.h>

FDCAN_TxHeaderTypeDef TxHeader1;

uint8_t TxData1[64];

typedef struct {
  uint8_t flag;
  uint8_t index;
  FDCAN_TxHeaderTypeDef TxHeader[16];
  uint8_t TxData[64*16];
} FDCAN_SendFailTypeDef;

FDCAN_SendFailTypeDef fdcan1_send_fail = {0};

void fdcan1_transmit(uint32_t can_id, uint32_t DataLength, uint8_t tx_data[])
{
  TxHeader1.Identifier = can_id;
  TxHeader1.IdType = FDCAN_EXTENDED_ID;
  if(can_id < 0x800) {  //exactly not right
    TxHeader1.IdType = FDCAN_STANDARD_ID;
  }
  TxHeader1.TxFrameType = FDCAN_DATA_FRAME;
  TxHeader1.DataLength = DataLength;
  TxHeader1.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  TxHeader1.BitRateSwitch = FDCAN_BRS_ON;
  TxHeader1.FDFormat = FDCAN_FD_CAN;
  TxHeader1.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
  TxHeader1.MessageMarker = 0;  //Tx Event FIFO Use
  if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader1, tx_data) != HAL_OK) {
    memcpy(&fdcan1_send_fail.TxHeader[fdcan1_send_fail.index], &TxHeader1, sizeof(FDCAN_TxHeaderTypeDef));
    memcpy(&fdcan1_send_fail.TxData[64*fdcan1_send_fail.index], tx_data, can_dlc2len(DataLength));
    fdcan1_send_fail.index = (fdcan1_send_fail.index + 1) & 0x0F;  //0~15
    fdcan1_send_fail.flag = 1;
  } 
}

int main(void)
{
  ...
  
  /* USER CODE BEGIN 2 */
  fdcan1_config();
  fdcan2_config();
  
  for(uint8_t i = 0; i < 64; i++) {
    TxData1[i] = i;
    TxData2[i] = i;
  }

  HAL_TIM_Base_Start_IT(&htim6);

  uint32_t count = 0;
  uint32_t cnt_100us = 0;
  uint32_t cnt_500us = 0;
  uint32_t i = 0;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    if(tim6_flag && count < 1000) {
      tim6_flag = 0;
      TxData1[0] = count >> 8 & 0xFF;
      TxData1[1] = count & 0xFF;

      ++cnt_100us;
      cnt_500us = cnt_100us / 5;
      if(cnt_500us && (cnt_100us%5==0) ) {
        switch(cnt_500us) {
          case 1: fdcan1_transmit(0x108, FDCAN_DLC_BYTES_64, TxData1); 
                   fdcan1_transmit(0x101, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x102, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x103, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x104, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x105, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x106, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x107, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345678, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345671, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345672, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345673, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345674, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345675, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345676, FDCAN_DLC_BYTES_64, TxData1);
                   fdcan1_transmit(0x12345677, FDCAN_DLC_BYTES_64, TxData1); 
                   break;
          case 17: /* next send */ break;
          case 18: break;
          case 19: break;
          case 20: ++count; cnt_100us = 0; break; //10ms
        }
      } else {  //fail retransmission once

        if(fdcan1_send_fail.flag && cnt_500us > 16) {  //can't conflict with normal send
          for(i = 0; i < fdcan1_send_fail.index; i++) {
            fdcan1_transmit(fdcan1_send_fail.TxHeader[i].Identifier, 
                            fdcan1_send_fail.TxHeader[i].DataLength,
                            &fdcan1_send_fail.TxData[64*i]);
          }
          fdcan1_send_fail.index = 0; 
          fdcan1_send_fail.flag = 0;  //maybe send 4 times or more
        }

        if(fdcan2_send_fail.flag && cnt_500us > 16) {
          for(i = 0; i < fdcan2_send_fail.index; i++) {
            fdcan2_transmit(fdcan2_send_fail.TxHeader[i].Identifier, 
                            fdcan2_send_fail.TxHeader[i].DataLength,
                            &fdcan2_send_fail.TxData[64*i]);
          }
          fdcan2_send_fail.index = 0;
          fdcan2_send_fail.flag = 0;
        }

      }
    }

  }
  /* USER CODE END 3 */
}

receive

FDCAN_RxHeaderTypeDef RxHeader1;

uint8_t RxData1[64];
uint8_t RxData2[64];


uint8_t dlc2len[]={0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64};
uint8_t can_dlc2len(uint32_t RxHeader_DataLength)
{
  return dlc2len[RxHeader_DataLength>>16];
}

uint8_t cnt = 0;
uint8_t brs[] = {'-', 'B'};
uint8_t esi[] = {'-', 'E'};
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
  if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != 0) {

    //memset(&RxHeader1, 0, sizeof(FDCAN_RxHeaderTypeDef));	//if use, lose frame
        
    HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader1, RxData1);
    if (hfdcan->Instance == FDCAN1) {
      printf("fdcan1, ");
    } else if (hfdcan->Instance == FDCAN2) { 
      printf("fdcan2, ");
    } else {
    }
    printf("0x%8X, %02d, %c, %c:",RxHeader1.Identifier, 
                                  can_dlc2len(RxHeader1.DataLength), 
                                  brs[RxHeader1.BitRateSwitch>>20 & 0x1],
                                  esi[RxHeader1.ErrorStateIndicator>>31 & 0x1]);
    for(cnt = 0; cnt < can_dlc2len(RxHeader1.DataLength); cnt++) {
      printf(" %02X", RxData1[cnt]);
    }
    printf("\n\r");
  }
}

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Cart