1. 引言

在现代汽车电子、工业控制和嵌入式系统中,控制器局域网(Controller Area Network, CAN)总线是应用最广泛的通信协议之一。随着技术发展,对通信带宽、可靠性和功能安全的要求不断提高,传统的CAN协议在某些高性能场景下逐渐显现出局限性。为此,博世公司(Bosch)推出了CAN FD(CAN with Flexible Data-Rate)协议,而基于此协议的控制器则被称为FDCAN(Flexible Data-Rate CAN Controller)。

经典CAN,通常指遵循ISO 11898-1标准的CAN 2.0协议。它定义了两种帧格式:

  • 标准帧:11位标识符,数据场最大8字节。
  • 扩展帧:29位标识符,数据场最大8字节。
    其通信速率在仲裁阶段和数据阶段是固定的,最高可达1 Mbps(实际应用中常见125 kbps, 250 kbps, 500 kbps, 1 Mbps)。

FDCAN是支持CAN FD协议的控制器。CAN FD协议在兼容经典CAN帧格式的基础上,引入了“灵活数据速率”模式,主要特点包括:

  • 可变数据速率:仲裁阶段使用标准速率(与经典CAN相同),数据阶段可使用更高的速率(最高可达5 Mbps或更高,取决于物理层)。
  • 更大的数据场:数据场长度从经典的8字节扩展到最多64字节。
  • 增强的CRC校验:为更长的数据场提供了更强大的错误检测能力。
    在这里插入图片描述

FDCAN与经典CAN的主要区别:

特性维度 经典CAN (CAN 2.0) FDCAN (CAN FD)
协议标准 ISO 11898-1 (CAN 2.0 A/B) ISO 11898-1:2015 (包含CAN FD)
最大数据长度 8 字节 64 字节
通信速率 固定速率(最高1 Mbps) 双速率:仲裁阶段标准速率,数据阶段可提速(理论最高8-12 Mbps,常用5 Mbps)
帧格式 标准帧(11位ID)、扩展帧(29位ID) 兼容经典帧,新增FD帧(带FDF位标识)
CRC校验 15位CRC(标准/扩展帧) 17位或21位CRC(根据数据长度),错误检测能力更强
带宽效率 较低(数据场小,固定速率) 显著提高(数据场更大,数据阶段速率更高)
硬件要求 传统CAN控制器/收发器 需支持CAN FD的控制器(FDCAN)和收发器
向后兼容性 - 可配置为经典CAN模式,与现有CAN网络通信
典型应用 车身控制、舒适系统、中低速网络 自动驾驶域控制器、车载诊断(OBD)、高带宽ECU通信、新能源车电池管理

1.1 帧结构差异

经典CAN帧的数据场长度由DLC(数据长度码)的0-8表示。而CAN FD帧的DLC编码更为复杂,可以表示0-8、12、16、20、24、32、48、64字节的数据长度。帧中新增的FDF (FD Frame) 位、BRS (Bit Rate Switch) 位和ESI (Error State Indicator) 位是识别FD帧的关键。

1.2 位时序与采样点

由于CAN FD在数据阶段速率更高,对物理层(电缆、连接器)和收发器的要求更严格。更高的速率意味着更短的位时间,需要更精确的位时序配置和更稳定的采样点,以应对信号反射和振铃的影响。

1.3 错误处理与状态

FDCAN控制器通常集成了更强大的错误管理和日志功能,例如提供更详细的错误代码和状态寄存器,便于系统诊断和功能安全(如ISO 26262)相关的需求。

2 CubeMX STM32H7 FDCAN的使用

2.1 CubeMX配置FDCAN

2.1.1 基础参数设置

在这里插入图片描述
数据帧的采样率计算如下 :采样率计算公式
传输速率的计算公式如下:
CANFD时钟频率/[(Prescaler+1)*(Time_Seg1+Time_Seg2+3)]
仲裁帧的参数设置如下:
在这里插入图片描述
使用中断接收数据配置如下:
在这里插入图片描述

2.1.2 代码编写

初始化部分

发送代码:

  //FDCAN的延迟时间校准
HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1,hfdcan1.Init.DataPrescaler*hfdcan1.Init.DataTimeSeg1,0);
  HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1);
  if (HAL_FDCAN_Start(&hfdcan1) == HAL_OK)
  {
    printf("FDCAN Start OK\n\r");
  }
  else {
    printf("FDCAN Start ERROR\n\r");
  }
  //准备仲裁信息
  FDCAN_TxHeaderTypeDef Txheader;
  Txheader.IdType = FDCAN_STANDARD_ID;
  Txheader.Identifier = 0x123;
  Txheader.BitRateSwitch = FDCAN_BRS_ON;
  Txheader.DataLength = FDCAN_DLC_BYTES_8;
  Txheader.ErrorStateIndicator = FDCAN_ESI_PASSIVE;
  Txheader.FDFormat = FDCAN_FD_CAN;
  Txheader.TxEventFifoControl = FDCAN_NO_TX_EVENTS;
  Txheader.MessageMarker = 0x00;
  Txheader.TxFrameType = FDCAN_DATA_FRAME;
  //准备发送数据
  uint8_t buf[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
  //发送数据
  if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1,&Txheader,buf)!=HAL_OK) {
      printf("FDCAN Send Error!\n");

    }else {
      printf("FDCAN Send OK!\n\r");
    }

使用中断接收代码

  //延迟时间校准
  HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan2,hfdcan2.Init.DataPrescaler*hfdcan2.Init.DataTimeSeg1,0);
  HAL_FDCAN_EnableTxDelayCompensation(&hfdcan2);
  //设置ID过滤器
  FDCAN_FilterTypeDef sFilterConfig;
  sFilterConfig.IdType = FDCAN_STANDARD_ID;
  sFilterConfig.FilterIndex = 0;
  sFilterConfig.FilterType = FDCAN_FILTER_MASK;
  sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
  sFilterConfig.FilterID1 = 0x0000;
  sFilterConfig.FilterID2 = 0x0000; 
  // 掩码全为0,表示不检查任何ID位
  if (HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig)!=HAL_OK) {
    printf("FDCAN Config Error!\n");
  };
  HAL_FDCAN_ConfigGlobalFilter(&hfdcan2, FDCAN_REJECT, FDCAN_REJECT, DISABLE, DISABLE);
  //开启FDCAN 使能中断
  HAL_FDCAN_Start(&hfdcan2);
  if (HAL_FDCAN_ActivateNotification(&hfdcan2,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0)!=HAL_OK) {
    printf("FDCAN ActivateNotify ERROR!\n\r");
  };

//在中断服务函数中接收数据
FDCAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8]; // FDCAN最大支持64字节数据
uint8_t fdcan_flag = 0;
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
  if (hfdcan->Instance == FDCAN1) {
    // 处理 CAN1 接收数据
  } else if (hfdcan->Instance == FDCAN2) {
    // 处理 CAN2 接收数据
    if ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET) {

      if (HAL_FDCAN_GetRxFifoFillLevel(hfdcan, FDCAN_RX_FIFO0)) {
        if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK)
        {
          fdcan_flag=1;
        }else {
          HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
        }
      }
    }
  }
}

2.1.3 注意事项

  1. 传输速度配置总线上的设备全都一致
  2. 发送端使用FIFO发送时,FIFO的大小不能为0
  3. FIFO1用来发送FIFO2用来接收
  4. 不同的FDCAN的RAM偏移地址不能相同
  5. 在中断处理函数中不能使用printf,会卡死了
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐