4. CAN扩展数据帧发送与接收配置

任务目标

在CAN驱动中配置FIFO接收缓冲区,设置缓冲区深度与接收中断,使报文到达后自动存入FIFO,并通过中断回调读取报文。本案例基于STM32 bxCAN外设,使用HAL库实现。

环境依赖

  • 硬件:STM32F103系列(或其他bxCAN内核MCU)
  • 软件:STM32CubeIDE / Keil MDK,STM32CubeF1 HAL库
  • 前置配置:CAN外设已初始化(波特率、模式等),GPIO已复用为CAN_RX/CAN_TX

关键完整代码

/* main.c 中相关全局变量与回调 */
CAN_HandleTypeDef hcan1;
CAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[8];
uint32_t RxFifoLevel = 0;

/* 配置FIFO0接收中断并使能 */
void CAN_FIFO_Init(void)
{
    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
    HAL_CAN_Start(&hcan1);
}

/* 中断回调:FIFO0有新报文时自动调用 */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    if (hcan == &hcan1)
    {
        if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK)
        {
            /* 读取FIFO当前占用级别(0~3) */
            RxFifoLevel = HAL_CAN_GetRxFifoFillLevel(hcan, CAN_RX_FIFO0);
            /* 在此处处理报文:RxHeader.StdId / ExtId / DLC,RxData[] */
        }
    }
}

/* 主函数中调用初始化 */
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_CAN1_Init();   /* CubeMX生成的CAN初始化,含FIFO分配 */

    CAN_FIFO_Init();

    while (1)
    {
        /* 主循环可轮询RxFifoLevel或处理其他任务 */
    }
}

FIFO深度配置说明:bxCAN的每个FIFO固定包含3个报文邮箱,硬件不支持软件配置深度。若需更深缓冲,可在中断回调中将报文搬移到自定义环形缓冲区(如长度32的数组)。CubeMX中通过CAN_RX_FIFO0绑定接收中断,无需额外寄存器配置。

运行与验证

  1. 使用CAN分析仪或另一块开发板向本机发送ID为0x123、数据为01 02 03 04 05 06 07 08的标准帧报文。
  2. 在HAL_CAN_RxFifo0MsgPendingCallback回调入口处设置断点,或通过串口打印RxHeader.StdId与RxData[0]。
  3. 发送3帧报文后暂停发送,观察RxFifoLevel变量:若回调连续触发3次且每次读取的FIFO填充级别依次为1→2→3,说明FIFO正确缓存了未及时读取的报文。
  4. 判定通过条件:回调中读取的StdId == 0x123,RxData[0] == 0x01,且FIFO填充级别在连续接收时能递增至3,证明FIFO缓冲与中断接收链路完整。

常见问题

  • 中断不触发:检查HAL_CAN_ActivateNotification是否在HAL_CAN_Start之前调用,且NVIC中CAN RX0中断已使能。
  • FIFO溢出:若回调处理耗时过长导致第4帧到来时FIFO已满,硬件将丢弃新报文并置位CAN_FLAG_FOV0。可在回调中仅拷贝数据,将业务处理移至主循环。
  • 填充级别读取:HAL_CAN_GetRxFifoFillLevel返回的是当前FIFO中等待读取的报文数,范围0~3,不是累计计数。