第四章:CAN 波特率计算、过滤器与掩码原理
导读:很多初学者直接复制网上 CAN 初始化代码,只修改波特率参数,遇到通讯不上、只能发不能收、部分 ID 收不到报文等问题完全无从下手。本章讲解 CAN 位时序、波特率计算,过滤器掩码底层逻辑,提供完整可直接工程使用的初始化代码,同时拆解每一个配置参数的实际含义。
4.1 CAN 波特率与时序基础
CAN 总线通讯的首要前提:总线上所有节点波特率必须完全一致,只要任意一个节点波特率不匹配,整个网络就会出现乱码、丢包、BusOff。
常见工程波特率:
| 波特率 | 典型应用场景 | 最大总线长度 |
|---|---|---|
| 1000Kbps | 短距离,板间、机器人底盘 | 40m |
| 500Kbps | 车载设备、工业模块 | 100m |
| 250Kbps | 中等距离工控组网 | 250m |
| 125Kbps | 远距离分布式设备 | 500m |
CAN 的一个 bit 位,由多个时间量子tq组成。
tq:时间量子,CAN 控制器的最小时间单位,由 APB 时钟 + 预分频器计算得来。- 一个完整 bit = SYNC_SEG + BS1 + BS2
位段组成
- SYNC_SEG(同步段):固定 1 个
tq。用于总线节点同步跳变沿,总线上检测到电平跳变就在此段完成同步。 - BS1(位段 1):可以设置 1‑16 个
tq,包含 PROP_SEG + PHASE_SEG1,用于补偿网络物理延迟。 - BS2(位段 2):可以设置 1‑8 个
tq,PHASE_SEG2,补偿相位误差。
公式:一个 bit 总时间量子数 = 1 (SYNC_SEG) + BS1 + BS2 波特率 = 外设时钟 / (预分频器 * (1 + BS1 + BS2))
工程惯例:推荐 1+BS1+BS2 = 16 tq,这是行业最常用配置,兼容性最好。 例:APB1 时钟 36M(STM32F103),目标 500K 波特率。 目标:每 bit 16 tq。 每 bit 时间 = 1 / 500000 = 2us 每个 tq = 2us /16 = 125ns 预分频 = 36M * 125ns = 4.5,取整数预分频 4。 此时:tq = 4 / 36M = 111.11ns;16tq =1.7778us,波特率≈562.5K,不是标准 500K。 所以调整总 tq=18:1+BS1+BS2 =18;BS1=13,BS2=4,预分频 4。 波特率 = 36M/(418)=500K,得到标准 500K。
STM32F103 经典配置表(APB1=36MHz)
| 目标波特率 | Prescaler 预分频 | BS1 | BS2 | 总 tq |
|---|---|---|---|---|
| 1000K | 4 | 6 | 3 | 1+6+3=10 |
| 500K | 4 | 13 | 4 | 1+13+4=18 |
| 250K | 8 | 13 | 4 | 18 |
| 125K | 16 | 13 | 4 | 18 |
注意:STM32 不同系列 APB1 时钟不一样,F4 APB1 最大 42M,F1 是 36M,不能直接复制别人的 BS1 BS2 和预分频,要按照自己芯片时钟计算。
4.2 过滤器与掩码深度原理
CAN 控制器硬件过滤器,作用:硬件层面过滤不需要的报文,减少 CPU 中断压力。如果不配置过滤器,收到总线上全部报文;配置过滤器之后,只有 ID 匹配的报文才存入 FIFO,触发接收中断。
很多人记不住掩码公式,这里讲透匹配逻辑:
(接收报文ID & 掩码Mask) == (过滤ID & 掩码Mask)
- Mask 掩码位 = 1:对应 ID 位必须严格相等,才匹配通过
- Mask 掩码位 = 0:对应 ID 位不管是 0 还是 1,都忽略,直接放行
举工程实例(标准帧 11bit ID): 实例 1:只想接收 ID=0x200 的报文。 ID Filter=0x200,Mask=0x7FF(二进制 111 1111 1111)。掩码全部置 1,每一位 ID 必须完全等于 0x200,其他 ID 全部过滤丢弃。
实例 2:接收 0x100 ~ 0x10F 这一组 ID,只看高 7bit,低 4bit 忽略。 ID Filter=0x100;Mask=0x7F0。 Mask 低 4bit=0,代表低 4bit 不做校验;只要 ID 高 7 位等于 0x100,无论低 4 位是什么全部接收。 报文 ID=0x101、0x105、0x10F 都会匹配进入接收 FIFO。
掩码通俗记忆:掩码为 1 代表管,掩码为 0 代表不管。
过滤器两种工作模式:
- 列表模式:每个过滤器存完整 ID,只接收这几个 ID;适合少量固定 ID。
- 掩码模式(屏蔽模式):Filter+Mask 组合匹配一组 ID;适合接收连续 ID 区间。
STM32CAN 拥有 14 个过滤器(Filter0~Filter13),可以分配给 CAN1、CAN2;支持 32 位模式、16 位模式,支持标准帧和扩展帧。
常见踩坑
- 过滤器配置错误:想要接收 0x100,但是 Mask 写 0,总线上所有报文全部进来,CPU 中断爆满。
- 过滤器模式选错:列表模式却传入 Mask,匹配逻辑错乱。
- 过滤器序号冲突:双 CAN 芯片,过滤器归属分配错误,收不到报文。
- 过滤器开启之后,不符合条件报文硬件直接丢弃,软件收不到。调试阶段,建议先配置 Mask=0x0,接收全部报文,确认通讯正常后再收紧过滤条件。
4.3 STM32 HAL 库 CAN 完整初始化代码
硬件前提:hcan 实例,PA11 CAN_RX PA12 CAN_TX,APB1 时钟 36M,波特率 500K。 代码包含:CAN 初始化、过滤器配置、开启接收中断。
#include "can.h"
/**
* @brief CAN初始化,500K,标准帧,FIFO0接收中断开启
*/
uint8_t CAN_Init_500K(void)
{
hcan.Instance = CAN1;
hcan.Init.Prescaler = 4; //预分频
hcan.Init.Mode = CAN_MODE_NORMAL; //正常工作模式;回环测试模式CAN_MODE_LOOPBACK
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.TimeSeg1 = CAN_BS1_13TQ;
hcan.Init.TimeSeg2 = CAN_BS2_4TQ;
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = ENABLE; //自动恢复BusOff,工程建议开启
hcan.Init.AutoWakeUp = DISABLE;
hcan.Init.AutoRetransmission = ENABLE; //传输出错自动重发
hcan.Init.ReceiveFifoLocked = DISABLE;
hcan.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
return 1;
}
//配置过滤器,过滤器0,掩码模式,接收ID=0x100以及0x100~0x10F一组报文示例
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterBank = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //掩码模式
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x100 << 5; //标准ID左移5位,STM32寄存器要求
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x7F0 <<5; //掩码
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.SlaveStartFilterBank = 14;
if(HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
return 2;
}
//开启FIFO0消息挂起中断
if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING)!=HAL_OK)
{
return 3;
}
//启动CAN外设
if(HAL_CAN_Start(&hcan)!=HAL_OK)
{
return 4;
}
return 0;
}
重要知识点:STM32 标准 ID 写入 FilterIdHigh 的时候需要左移 5 位。 寄存器格式:11 位标准 ID 放在 bit22~bit12,所以赋值
ID << 5。很多教程没有讲这个点,复制代码过滤器完全失效。
回环自测模式(开发无硬件时软件自测 CAN 代码)
//把初始化Mode改为CAN_MODE_LOOPBACK
hcan.Init.Mode = CAN_MODE_LOOPBACK;
回环模式下,MCU 内部自发自收,不需要外接 TJA1050 收发器,用来验证初始化、发送接收回调、过滤器逻辑是否正确,调试软件逻辑非常好用。
注意:回环模式只是内部仿真,无法和外部真实 CAN 设备通讯。调试完务必切回 CAN_MODE_NORMAL。
4.4 常用参数配置说明
AutoBusOff = ENABLE:当进入 Bus‑Off,硬件自动尝试恢复总线;如果关闭,BusOff 之后必须软件手动调用 HAL_CAN_Init 重新初始化。工业项目建议开启。AutoRetransmission = ENABLE:传输出错硬件自动重发;关闭则出错放弃报文。绝大多数场景开启。- FilterBank 过滤器编号,F1 系列一共 0‑13 共 14 组过滤器。
4.5 完整收发函数整合(承接第二章)
把第二章的安全发送函数放进来,组成完整业务调用接口。
extern CAN_HandleTypeDef hcan;
/**
* @brief CAN安全发送标准数据帧
* @param std_id 标准ID
* @param data 数据缓冲区
* @param len 0~8
* @retval 0成功,1参数错误,2总线故障,3发送失败
*/
uint8_t CAN_Send_StdData(uint16_t std_id,uint8_t *data,uint8_t len)
{
if(len>8 || data == NULL)
{
return 1;
}
if(hcan.State != HAL_CAN_STATE_READY)
{
return 2;
}
CAN_TxHeaderTypeDef tx_header={0};
uint32_t tx_mailbox;
tx_header.StdId = std_id;
tx_header.IDE = CAN_ID_STD;
tx_header.RTR = CAN_RTR_DATA;
tx_header.DLC = len;
tx_header.TransmitGlobalTime = DISABLE;
if(HAL_CAN_AddTxMessage(&hcan,&tx_header,data,&tx_mailbox) == HAL_OK)
{
return 0;
}
return 3;
}
调用示例,主循环发送 ID=0x200,4 字节数据:
uint8_t send_buf[4]={0x11,0x22,0x33,0x44};
CAN_Send_StdData(0x200,send_buf,4);
4.6 本章调试排错清单
- 发送成功,但是收不到外部设备报文:
- 检查波特率 BS1 BS2 预分频,确认芯片 APB 时钟;
- 检查过滤器 ID、掩码,标准 ID 是否左移 5 位;
- 确认中断是否开启,HAL_CAN_ActivateNotification 是否调用;
- 能收到报文,但是只有部分 ID 进来:过滤器掩码条件过严,调大掩码(掩码值减小);
- 回环模式收发正常,接上外部硬件完全不通:检查 Mode 是不是忘记切回 NORMAL;硬件收发器、120Ω 电阻。
- 频繁 BusOff:优先排查硬件接线,不要单纯调软件参数。
第四章到此结束。
更多推荐


所有评论(0)