ST FDCAN介紹

前言

FDCAN(Flexible Data-Rate CAN)是CAN的升級版。特點包括

  • 每幀數據段最大長度由8字節上升到64字節。
  • 速度由1Mbps上升到5Mbps,甚至還可以更高。在一個數據幀中仲裁段(ID和ACK)的速率和CAN一樣最高1Mbps,這樣可以保證總線的健壯可靠,但是數據段可以5Mbps甚至更高,一個數據幀中使用不同的波特率,這就是FD(Flexible Data-Rate)的由來。
  • 向下兼容CAN

FDCAN格式

  1. 第一仲裁階段
  2. 數據階段
  3. 第二仲裁階段

CAN-FD 與 CAN 2.0 的幀架構比較

在標識符之後,CAN 2.0 和 CAN-FD 有一個不同的操作:

  • CAN 2.0 發送 RTR 位明確幀類型:數據幀(RTR 為顯性)或遠程幀(RTR 為隱性)。
  • CAN-FD 只支持數據幀,因此總是發送顯性 RRS(預留)。

CAN-FD 與 CAN 2.0 之間的主要差異

STM32CubeMX Setting

這邊可以參考網路上H7的設定選項,使用G4或G0會缺少Messge Ram分配這區塊設定,其他部分雷同

FDCAN傳輸參考程式

這邊可以參考輸出如下

#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 */
}

讀取如下

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