CubeMX STM32H7 FDCAN
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 注意事项
- 传输速度配置总线上的设备全都一致
- 发送端使用FIFO发送时,FIFO的大小不能为0
- FIFO1用来发送FIFO2用来接收
- 不同的FDCAN的RAM偏移地址不能相同
- 在中断处理函数中不能使用printf,会卡死了
更多推荐

所有评论(0)