过滤器理论与实验
190.1.13 过滤器理论与实验
/* canif.c:本课 16 位列表模式实验时的过滤器配置。 */
void Can_Filter_Init(void) /* 配置过滤器组 0,并在配置完成后启动 CAN。 */
{
CAN_FilterTypeDef Can_FilterConfig; /* 定义 HAL 过滤器配置结构体。 */
Can_FilterConfig.FilterBank = 0; /* 选择过滤器组 0。 */
Can_FilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; /* 实验改为标识符列表模式。 */
Can_FilterConfig.FilterScale = CAN_FILTERSCALE_16BIT; /* 把两个 32 位寄存器划分为四个 16 位列表槽。 */
Can_FilterConfig.FilterIdHigh = 0x12 << 5; /* 把标准 ID 0x12 放到该 16 位槽的 bit15:5。 */
Can_FilterConfig.FilterIdLow = 0x0; /* 第二个列表槽保留为 0;这也会额外匹配相应的 ID 0 帧。 */
Can_FilterConfig.FilterMaskIdHigh = 0x0; /* 列表模式下该字段不再是掩码,而是第三/第四列表槽之一。 */
Can_FilterConfig.FilterMaskIdLow = 0x0; /* 列表模式下该字段同样作为另一个 16 位 ID 槽。 */
Can_FilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; /* 把匹配报文送入 FIFO0。 */
Can_FilterConfig.FilterActivation = CAN_FILTER_ENABLE; /* 启用过滤器组 0。 */
Can_FilterConfig.SlaveStartFilterBank = 14; /* 双 CAN 器件用于划分 CAN1/CAN2;本单 CAN F103 中没有实际作用。 */
if (HAL_CAN_ConfigFilter(&hcan, &Can_FilterConfig) != HAL_OK) /* 把配置写入 bxCAN 过滤寄存器。 */
{
Error_Handler(); /* 配置失败时进入工程统一错误处理。 */
}
HAL_CAN_Start(&hcan); /* 配置完成后启动 CAN。 */
}
/* 课堂重新讲解的初始 16 位掩码全通配置;以下四行与上面的列表实验互为替代方案。 */
Can_FilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; /* 选择标识符/掩码模式。 */
Can_FilterConfig.FilterIdHigh = 0x0; /* 第一组过滤值为 0。 */
Can_FilterConfig.FilterIdLow = 0x0; /* 第二组过滤值为 0。 */
Can_FilterConfig.FilterMaskIdHigh = 0x0; /* 对应掩码全 0,所有位都不参与比较。 */
Can_FilterConfig.FilterMaskIdLow = 0x0; /* 另一组掩码也全 0,因此形成全通过配置。 */
源码范围说明:代码块记录了视频中真正测试的
IDLIST + 16BIT + (0x12 << 5)变体,以及随后用于解释的初始全 0 掩码配置。两组赋值是互相替换的实验状态,不应同时放在同一函数中。归档的06Can_DHT11_OLED_Filter最终已回到IDMASK + 全 0,所以不能只看归档末态误以为课堂没有做列表实验。
一、过滤器理论
bxCAN 接收过滤器位于“总线接收逻辑”和“FIFO0/FIFO1”之间。报文到达控制器后,硬件把它的标识符及帧类型位与启用的过滤器比较:
CAN 总线上的报文
↓
硬件过滤器组 0…N
├─ 匹配 → 写入该过滤器指定的 FIFO0 或 FIFO1
└─ 全部不匹配 → 不进入接收 FIFO,软件无需处理
过滤器不会阻止报文在物理总线上传播,也不会修改发送节点;它只决定本节点是否把该帧存入接收 FIFO。硬件过滤能减少 CPU 唤醒、FIFO 占用和软件 ID 判断量。
1.1 过滤器的数量
“过滤器数量”更准确地叫 filter bank(过滤器组)数量:
- STM32F103 这类单 CAN1 器件有 14 个专用过滤器组,编号 0~13;
- STM32F105/107 互联型器件带 CAN1/CAN2,共享 28 个过滤器组;
- STM32F407 等带双 bxCAN 的器件也通常由 CAN1/CAN2 共享 28 个组,具体分界由
SlaveStartFilterBank配置。
不能简单记成“所有 F1 都 14、所有 F4 都 28”;应查看具体型号参考手册和 HAL 条件宏。项目所用 STM32F103ZET6 只有 CAN1,因此 HAL 源码走“14 个专用组”的分支,SlaveStartFilterBank=14 不参与双 CAN 划分。
1.2 过滤器的作用
同一 CAN 总线上的所有节点都能观察到物理报文,但一个 ECU 往往只关心其中少数 ID。如果所有帧都进入 FIFO,再由 switch(Recv_ID) 丢弃无关帧,会增加中断和软件负担;过滤器可以在硬件层先完成第一道筛选。
过滤规则比较的不只是“十六进制 ID 文本”。过滤器槽中还按规定位置包含 IDE、RTR,以及在某些格式下的扩展 ID 位。因此,配置时必须按参考手册位布局把标准 ID 左移,不能直接写 0x12。
过滤器的另一个职责是 FIFO 分配。不同过滤器组可以把不同类型报文送到 FIFO0 或 FIFO1,从而让软件用不同中断、优先级或处理路径分流数据。
1.3 过滤器的结构
每个过滤器组有两个 32 位寄存器 CAN_FxR1 和 CAN_FxR2,总计 64 个配置位。FilterScale 和 FilterMode 共同决定如何解释这 64 位:
| 尺度 | 模式 | 一个过滤器组表达的规则 |
|---|---|---|
| 32 位 | 掩码 | 1 个 32 位过滤值 + 1 个 32 位掩码 |
| 32 位 | 列表 | 2 个精确的 32 位标识符槽 |
| 16 位 | 掩码 | 2 组“16 位过滤值 + 16 位掩码” |
| 16 位 | 列表 | 4 个精确的 16 位标识符槽 |
所以原笔记“过滤器可保存 2 或 4 个 16 位数据、1 或 2 个 32 位数据”缺少模式条件。不是每个数都同时是独立 ID:在掩码模式中,其中一半是掩码。
项目 HAL 在 16 位尺度下的字段装载顺序是:
FR1[15:0] ← FilterIdLow
FR1[31:16] ← FilterMaskIdLow
FR2[15:0] ← FilterIdHigh
FR2[31:16] ← FilterMaskIdHigh
列表模式下四个字段都被解释成 ID 槽;掩码模式下则组成 IdLow/MaskIdLow 与 IdHigh/MaskIdHigh 两对。字段名称主要兼顾 32 位模式,不能仅凭 High/Low/Mask 字面猜测 16 位列表模式的槽位意义。
1.3.1 例题:列表模式应用
列表模式做精确相等匹配。对于 16 位尺度的标准帧槽,位布局为:
bit15 bit5 bit4 bit3 bit2 bit0
┌────────────────────────────────────┬────┬────┬─────────────┐
│ Standard ID[10:0] │ RTR│ IDE│ ExtID[17:15]│
└────────────────────────────────────┴────┴────┴─────────────┘
本实验是标准数据帧:ID=0x12、RTR=0、IDE=0,所以配置值为:
Filter16 = (0x12 << 5) | (0 << 4) | (0 << 3) = 0x0240
这就是课程写 0x12 << 5 的原因。标准 ID 占 11 位,要从 16 位槽的 bit15 开始排列,低 5 位留给 RTR、IDE 和扩展 ID 的一部分。
实验步骤与现象:
- 把模式改为
CAN_FILTERMODE_IDLIST,尺度保持CAN_FILTERSCALE_16BIT; - 将一个列表槽写为
0x12 << 5,编译并烧入接收端,ID0x12报文可以进入 FIFO0; - 把同一槽改为
0x13 << 5,重新编译烧写,发送端仍发0x12时不再匹配; - 改回
0x12 << 5后恢复接收。
本实验还有一个常被忽略的副作用:16 位列表模式的四个槽都会参与匹配。课堂只给一个槽写 0x12 << 5,其余三个保持 0,因此除了 ID 0x12 的标准数据帧,还可能接受与零槽编码相等的帧,例如标准数据帧 ID 0。若只想接收一个 ID,可把四个槽都填成同一个目标值,或选择更合适的掩码/尺度,不能把 0 自动理解为“槽已禁用”。
列表模式匹配的是打包后的 ID/RTR/IDE 位。0x12 << 5 同时限定为标准数据帧;若希望接收相同 ID 的远程帧,RTR 位必须按规则配置另一个列表槽。
1.3.2 例题:掩码模式应用
掩码模式的准确比较公式是:
(接收值 & 掩码) == (过滤值 & 掩码)
逐位理解:
- 掩码位为 1:接收帧该位必须与过滤值对应位相同;过滤值可以是 0,也可以是 1;
- 掩码位为 0:该位不比较,接收帧取 0 或 1 都可以。
因此原笔记“掩码为 1 的位要求接收 ID 也为 1”不完整。掩码只决定“比不比较”,真正要求相等的目标值来自过滤 ID。
几个 11 位标准 ID 的例子:
| 目标 | 过滤 ID | 11 位 ID 掩码 | 结果 |
|---|---|---|---|
| 全部通过 | 任意 | 0x000 | ID 位全部不比较 |
只匹配 ID 0x12 | 0x012 | 0x7FF | 11 个 ID 位全部精确相等 |
匹配 0x120~0x12F | 0x120 | 0x7F0 | 比较高 7 位,低 4 位任意 |
| 匹配偶数 ID | 0x000 | 0x001 | 只比较最低位,要求它等于过滤值的 0 |
| 匹配奇数 ID | 0x001 | 0x001 | 只比较最低位,要求它等于过滤值的 1 |
写进 16 位过滤槽时,ID 和 11 位掩码都要左移 5 位。例如只比较 11 位 ID 0x12:
Can_FilterConfig.FilterIdHigh = 0x12 << 5; /* 目标标准 ID。 */
Can_FilterConfig.FilterMaskIdHigh = 0x7FF << 5; /* 仅比较全部 11 个 ID 位。 */
上述掩码没有比较低 5 位,所以重点是 ID;若还要严格限定标准数据帧,可让掩码同时比较 RTR 和 IDE,过滤值中对应位保持 0。16 位槽中可使用:
Mask16 = (0x7FF << 5) | (1 << 4) | (1 << 3) = 0xFFF8
最初工程四个字段全写 0,掩码也全 0,所以比较式两边恒为 0,表现为“全部通过”。左移 0 仍是 0,因此早期代码即使没有显式写 <<5,全通结果也不受影响;一旦配置非零 ID 或掩码,就必须按位布局计算。
32 位尺度下,标准 ID 位于 32 位过滤值的 bit31:21,通常要按 32 位格式左移 21 位并正确放置 IDE/RTR。它不是把 16 位写法中的 5 随便换成另一个数字,而是要按参考手册完整布局。扩展 ID 需要 29 位,更适合使用 32 位尺度。
过滤器实验应同时观察三件事:发送端实际 ID、接收端是否有帧、过滤器最终配置。轮询工程在无匹配帧时打印 recv eorro,只代表 FIFO0 当前为空,不是过滤器硬件“报错”。
本节位布局、四种组态和过滤组数量均依据项目所用 HAL 装载逻辑与 ST 官方 RM0008 bxCAN 过滤器章节 核对。