STM32 CAN/CAN-FD 初始化检查清单

这是一份快速参考,概括了初始化STM32 CAN-FD设备的关键步骤:

  1. 模式选择:CAN、CAN-FD without BitRate SwitchingCAN-FD with BitRate Switching 三种模式中做出选择。
  2. CubeMX配置: 计算并配置标称段数据段(如果启用速率切换)的波特率参数。开启NVIC: FDCAN interrupt0。
  3. 过滤器配置: 在CAN-FD初始化之后启动硬件之前,配置至少一个过滤器以允许接收报文。
  4. 使能接收: 配置过滤器后调用HAL_FDCAN_ConfigFilter(),应用到硬件并开启HAL_FDCAN_ActivateNotification()接收中断。
  5. 启动设备: 调用HAL_FDCAN_Start()函数启动CAN-FD外设。
  6. 编写中断函数: 实现接收回调函数HAL_FDCAN_RxFifo0Callback()来处理接收到的报文数据。

STM32 CAN/CAN-FD 通信配置详细指南

第一步:选择通信模式

在STM32CubeMX中,根据您的应用需求选择合适的CAN-FD模式。

  • CAN: 传统CAN协议,最高1 Mbps波特率,数据帧最大8字节。
  • CAN-FD without BitRate Switching: CAN-FD协议,数据帧最大64字节,但整个报文都使用标称波特率。在这种模式下,数据段的位定时参数(Data Time Seg)会被忽略,但为了避免混淆,强烈建议将其配置为与标称段相同
  • CAN-FD with BitRate Switching: CAN-FD协议,数据帧最大64字节,并在数据段切换到更高的波特率,以实现高速传输。这是利用CAN-FD全部性能的模式。

第二步:位定时和波特率计算

位定时参数决定了通信的波特率和可靠性。一个位的总时间量子数(Time Quanta, tq)由以下公式决定:

总时间量子数=Prescaler×(1+Time Seg1+Time Seg2)

其中,1 代表固定的同步段(SYNC_SEG),它是确保所有节点同步的关键部分。

  • 时间量子(tq)的经验公式: 为了保证硬件逻辑正常工作,时间量子(tq)不应过短。经验上,tq 的长度应在 100 ns 到 125 ns 之间,对应的时钟频率为8到10MHz。
  • 采样点位置: 采样点通常位于 Time Seg1 结束之后,最佳位置建议在总位时间的 75%-85% 之间。

采样点百分比=1+Time Seg1+Time Seg2(1+Time Seg1)×100%

第三步:过滤器和中断配置

这一步是确保能够成功接收报文的关键

即使不配置任何过滤器,CAN控制器也能接收所有报文,但许多现代CAN-FD外设(如STM32的FDCAN)在默认情况下会丢弃所有报文。因此,必须至少配置一个过滤器来告诉硬件如何处理接收到的报文。

将您的过滤器和中断激活代码放在CAN-FD初始化之后、主循环(while(1))之前的初始化区域:

// FLAG: 配置CANFD过滤器

// 注意,在调用MX_FDCANx_Init()之后
FDCAN_FilterTypeDef sFilterConfig;

/* 配置标准ID过滤器:接收所有标准报文到FIFO0 */
sFilterConfig.IdType = FDCAN_STANDARD_ID;
sFilterConfig.FilterIndex = 0;
sFilterConfig.FilterType = FDCAN_FILTER_MASK;
sFilterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;
sFilterConfig.FilterID1 = 0x000;
sFilterConfig.FilterID2 = 0x000;

if (HAL_FDCAN_ConfigFilter(&hfdcanX, &sFilterConfig) != HAL_OK)
{
    Error_Handler();
}

// 在这里激活接收FIFO0的新报文中断通知
if (HAL_FDCAN_ActivateNotification(&hfdcanX, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK)
{
    Error_Handler();
}

// 注意,后面可以开启CANFD

第四步:启动设备并编写中断函数

在完成所有配置后,调用HAL_FDCAN_Start()来启动CAN-FD外设。然后,编写中断服务程序(ISR)来处理接收到的报文。

void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
    // 检查中断是否来自 FDCAN1 模块
    if (hfdcan->Instance == FDCAN1)
    {
        /* 检查是否为新报文到达中断 */
        if ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET)
        {
            /* 从FIFO0中获取报文数据 */
            if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &HO10025_state_yaw.RxHeader, HO10025_state_yaw.rx_buffer) == HAL_OK)
            {
                /* 报文读取成功,在这里处理您的数据 */

                // 您也可以判断报文ID,并根据ID执行不同的操作
                // if (RxHeader.Identifier == 0x123) {
                //   // 执行特定ID的报文处理
                // }
            }
        }
    }

    /* 其他 FDCAN 模块的中断处理可以放在这里 */
}