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绑定接收中断,无需额外寄存器配置。
运行与验证
- 使用CAN分析仪或另一块开发板向本机发送ID为
0x123、数据为01 02 03 04 05 06 07 08的标准帧报文。 - 在
HAL_CAN_RxFifo0MsgPendingCallback回调入口处设置断点,或通过串口打印RxHeader.StdId与RxData[0]。 - 发送3帧报文后暂停发送,观察
RxFifoLevel变量:若回调连续触发3次且每次读取的FIFO填充级别依次为1→2→3,说明FIFO正确缓存了未及时读取的报文。 - 判定通过条件:回调中读取的
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,不是累计计数。