六轴关节驱动器加一个网关挂在同一根 CAN 总线上,报文能通不代表 CPU 不被无关帧淹没。SJA1000 用 4 个验收码寄存器 ACR 加 4 个验收屏蔽寄存器 AMR,在硬件层决定收不收,单滤波/双滤波乘标准帧/扩展帧共四种模式。本文讲清屏蔽规则、位映射、ID 规划与初始化代码,附六坑对照表。
机器人多轴关节驱动器、伺服网关、物联网 RTU 常常挂在同一根 CAN 总线上,波特率 500kbps 时总线每秒可跑几千帧,其中绝大多数与某个节点无关。能通不等于能用:如果所有帧都进 CPU 中断再由软件判断 ID,主循环会被无关报文切得七零八落,实时任务抖动、控制周期被拖长。SJA1000 的 验收滤波器把"收不收"这件事搬到硬件层——它用 4 个验收码寄存器(ACR0~ACR3)和 4 个验收屏蔽寄存器(AMR0~AMR3)组成滤波器,只有通过验收的帧才写入接收 FIFO 并置位接收标志。三条可落地的结论:第一,AMR 的某一位为 0 表示该位必须与 ACR 相同才接收,为 1 表示该位被屏蔽、与验收结果无关;第二,单滤波与双滤波、标准帧与扩展帧组合出四种滤波方式,双滤波可以拿一组专门做广播或组播;第三,滤波器必须在复位状态下写入,改完再退出复位。下面用四张表把它讲透。
CAN 是广播式总线,每个节点都能在物理层收到所有帧,是否"收下"由控制器的验收滤波器决定。软件过滤与硬件验收滤波的差别,在多轴高帧率场景下会被放大。
| 对比维度 | 软件过滤(全部进中断再判 ID) | 硬件验收滤波(ACR/AMR) |
|---|---|---|
| 过滤位置 | CPU 中断服务程序内 | CAN 控制器内部,进 FIFO 之前 |
| 中断次数 | 总线每一帧都触发一次 | 只有验收通过的帧触发 |
| CPU 占用 | 随总线负载线性上升 | 与无关帧数量无关 |
| 实时性影响 | 无关帧挤占控制周期,抖动大 | 无关帧不产生中断,周期稳定 |
| 接收缓冲压力 | 无关帧可能挤占接收缓冲区造成超载 | 无关帧不进入 FIFO |
| 适用场景 | 总线负载极低、帧种单一的调试阶段 | 多轴关节网络、网关、高帧率工业现场 |
关键判断是中断的"性价比"。以 6 轴关节网络为例,每轴每 1ms 上报一次位置、速度、电流,再加上网关轮询,总线负载很容易到 40%~60%。若所有帧都进中断,CPU 每秒要处理数千次与己无关的中断,而真正有用的可能只有几百次——硬件滤波就是把这几千次砍到几百次的那把刀。
验收滤波的规则只有一句话:所有 AMR 为 0 的位,ACR 与 CAN 帧的对应位必须相同才算验收通过;所有 AMR 为 1 的位,其验收功能被屏蔽,帧的该位与验收结果无关。ACR 与 AMR 永远成对工作,一个给"期望值",一个给"关心哪些位"。
一句话记住规则:AMR=0 的位是"必须匹配的位",AMR=1 的位是"随便的位"。把 AMR 全写 0,就是要求 ACR 与帧逐位相等;把 AMR 全写 1,就是无条件接收所有帧。
因此,AMR 决定了滤波器的"粗细":只想按高几位区分节点,就把低位对应的 AMR 置 1;要精确到某个 ID 的某一帧,就把对应位 AMR 置 0。这也是 CAN 网络上"分组地址"和"广播地址"能共存的底层机制。
SJA1000 兼容 CAN2.0B,既有标准帧(11 位标识符)又有扩展帧(29 位标识符),每种帧格式都有单滤波和双滤波,因此实际共有四种滤波方式。模式由方式寄存器 MOD 的验收滤波方式位 AFM 选择:AFM=1 为单滤波,AFM=0 为双滤波;帧格式则由发送信息帧的第 1 个字节最高位 FF 决定(FF=1 扩展帧,FF=0 标准帧)。
| 滤波方式 | 帧格式 | 参与滤波的位 | 寄存器映射 |
|---|---|---|---|
| 单滤波(AFM=1) | 标准帧 | 11 位标识符 + RTR + 数据场前 2 字节 | ID 与 RTR → ACR0/AMR0、ACR1/AMR1(低 4 位未用);Data1 → ACR2/AMR2;Data2 → ACR3/AMR3 |
| 扩展帧 | 29 位标识符 + RTR | 依次对应 ACR0~ACR3、AMR0~AMR3(ACR3/AMR3 低 2 位未用) | |
| 双滤波(AFM=0) | 标准帧 | 滤波器1:11 位 ID + RTR + 数据场第 1 字节(共 20 位);滤波器2:11 位 ID + RTR | 滤波器1 = ACR0/1 + AMR0/1 及 ACR3/AMR3 低 4 位;滤波器2 = ACR2/AMR2 及 ACR3/AMR3 高 4 位 |
| 扩展帧 | 滤波器1 与滤波器2 均只匹配 29 位标识符的高 16 位 | 滤波器1 = ACR0/1 + AMR0/1;滤波器2 = ACR2/3 + AMR2/3 |
这里有两个容易踩的坑。第一,标准帧单滤波时数据场的前两个字节参与了滤波,它们不能再当普通用户数据用;如果接收的标准帧数据场长度不足 2 字节,缺少的部分可以不参与滤波。第二,扩展帧双滤波只匹配 29 位标识符的高 16 位,低 13 位是"随便的位",这意味着用双滤波做扩展帧分组时,组内 ID 的低 13 位可以自由分配给不同节点。
关节网络的典型需求是:节点既要收发给自己的单播帧,又要收全网广播帧。双滤波正好对应两个地址窗口。下面以一个 6 轴关节网络为例做 ID 规划,假设采用扩展帧、标识符高 16 位用于分组。
| 报文用途 | 标识符高 16 位 | 目标节点 | 滤波器归属 | AMR 设置 |
|---|---|---|---|---|
| 本节点运动指令(单播) | 0x1122 | 关节 1 驱动器 | 滤波器 1(ACR0/ACR1) | AMR0/AMR1 = 0x00,逐位精确匹配 |
| 全网广播(同步/急停) | 0x3344 | 所有节点 | 滤波器 2(ACR2/ACR3) | AMR2/AMR3 = 0x00,逐位精确匹配 |
| 关节组组播(可选) | 0x33xx | 同一组关节 | 并入滤波器 2 | AMR3 = 0x00FF,屏蔽低 8 位做组播 |
| 本节点状态上报(发送) | 0x2211 | 网关 | 不参与接收滤波 | 由发送缓冲区第 2~4 字节写入 |
这样配置后,节点只会在"自己的单播帧"和"广播帧"到来时产生接收中断,其余关节的帧一律被硬件丢弃。若总线节点与消息种类不多,应尽量使用标准帧,每帧字节少、通讯速率更快;若要支持广播或组播,则宜采用双滤波,拿出一个滤波器专门做广播/组播地址,灵活性和抗干扰性都更好。
ACR 与 AMR 只能在复位状态下设置,所以初始化顺序必须是"进复位 → 写 ACR/AMR → 选模式 → 退出复位"。下面这段 C 代码对应上表的双滤波配置,可直接套到 8051/STM32 外扩 SJA1000 的驱动里。
/* SJA1000 验收滤波器初始化(PeliCAN 模式 + 双滤波)
* 对应规划:滤波器1 = 0x1122(单播),滤波器2 = 0x3344(广播)
* 注意:ACR/AMR 只能在复位状态下写入
*/
#define SJA_BASE 0x8000 /* 片选基址,按实际总线地址修改 */
#define REG(off) (*(volatile unsigned char *)(SJA_BASE + (off)))
#define MOD 0x00 /* 方式寄存器 */
#define CMR 0x01 /* 命令寄存器 */
#define SR 0x02 /* 状态寄存器 */
#define CDR 0x1F /* 时钟分频寄存器 */
#define ACR0 0x10 /* 验收码 0 */
#define AMR0 0x14 /* 验收屏蔽 0 */
void sja_filter_init(void)
{
REG(MOD) = 0x01; /* MOD.0=1:进入复位状态(必须) */
REG(CDR) = 0x80; /* CDR.7=1:选择 PeliCAN 模式 */
/* 滤波器1:高16位 = 0x1122,AMR=0 表示逐位精确匹配 */
REG(ACR0) = 0x11;
REG(ACR0 + 1) = 0x22;
/* 滤波器2:高16位 = 0x3344 */
REG(ACR0 + 2) = 0x33;
REG(ACR0 + 3) = 0x44;
/* 屏蔽寄存器全 0:所有位都参与匹配(要组播可把对应位置 1) */
REG(AMR0 + 0) = 0x00;
REG(AMR0 + 1) = 0x00;
REG(AMR0 + 2) = 0x00;
REG(AMR0 + 3) = 0x00;
REG(MOD) = 0x00; /* AFM=0 选双滤波,同时退出复位进入工作态 */
}
/* 发送一帧扩展帧(8 字节数据),标识符高16位 = 0x2211 */
void sja_send_frame(const unsigned char *data)
{
while (!(REG(SR) & 0x04)) { } /* SR.2=1 表示发送缓冲区已释放 */
REG(16) = 0x88; /* 第1字节:FF=1 扩展帧 + DLC=8 */
REG(17) = 0x22; /* 标识符高16位 */
REG(18) = 0x11;
REG(19) = 0x00; /* 标识符低13位(可任意) */
REG(20) = 0x00;
for (int i = 0; i < 8; i++) {
REG(21 + i) = data[i]; /* CAN 地址 21~28 为数据场 */
}
REG(CMR) = 0x05; /* 请求发送 + 释放接收缓冲区 */
while (!(REG(SR) & 0x08)) { } /* SR.3=1 表示发送完成 */
}
两个细节:发送缓冲区与接收缓冲区共用 CAN 地址 16~28 这 13 个字节,发送时写入、接收时读出;地址 32~95 是接收 FIFO RAM,96~109 是发送信息备份,驱动里不要越界访问。接收侧通过访问接收信息计数器 RMC 可得知当前 FIFO 里已收到几帧。
如果换成带 bxCAN 的 STM32,思路完全一样,只是寄存器换成了"滤波器组"。理解 ACR/AMR 之后,迁移到 bxCAN 只是查表换算。
| 概念 | SJA1000 | STM32 bxCAN |
|---|---|---|
| 匹配值寄存器 | ACR0~ACR3(4 个) | FilterIdHigh / FilterIdLow |
| 屏蔽寄存器 | AMR0~AMR3(4 个) | FilterMaskIdHigh / FilterMaskIdLow |
| 滤波模式 | 单滤波 / 双滤波(AFM 位) | 标识符掩码模式 / 标识符列表模式 |
| 资源数量 | 4 组 ACR/AMR | STM32F1 为 14 个滤波器组,F4 双 CAN 可达 28 个 |
| 分配方式 | 固定映射,双滤波拆成两组 | 可绑定到 FIFO0/FIFO1,灵活分配 |
| 写入时机 | 必须在复位状态下写入 | 需先置 FINIT 位进入初始化模式 |
| 现象 | 根因 | 处理 |
|---|---|---|
| 滤波器改了不生效 | ACR/AMR 在非复位状态下写入被忽略 | 先置 MOD.0=1 进复位,写完再退出 |
| 收不到本该收到的帧 | AMR 该位误置 1,把需要匹配的位屏蔽了 | 核对 AMR=0 的位是否与 ACR 一致 |
| 收到大量无关帧 | AMR 全 1 或屏蔽位过多,等于放行所有帧 | 收紧 AMR,只屏蔽确实不关心的位 |
| 标准帧数据前 2 字节异常 | 单滤波下 Data1/Data2 被占用参与滤波 | 把需要过滤的数据放在第 3 字节之后 |
| 扩展帧双滤波分组失效 | 只匹配高 16 位,低 13 位被当成自由位 | 组播分组依据放在高 16 位,低 13 位规划给节点 |
| 总线重载时丢帧 | 接收 FIFO 溢出,超载能力不足 | 提高中断优先级及时读走,或提高波特率/降负载 |
对多轴关节驱动器和工业物联网网关,验收滤波器的价值在于把"收不收"从 CPU 搬到硬件:4 个 ACR 加 4 个 AMR 组成滤波器,AMR=0 的位必须与 ACR 逐位相同,AMR=1 的位被屏蔽;单滤波/双滤波与标准帧/扩展帧组合出四种滤波方式,双滤波可拿一组专门做广播或组播,是"单播 + 广播"共存的标准做法。
落地记住三点:ACR/AMR 必须在复位状态下写入、AMR 决定滤波的粗细、标准帧单滤波会占用数据场前两个字节。把 ID 规划先做好再写滤波器,节点的中断次数和实时抖动都会明显下降。
本文基于《CAN 控制器 SJA1000 验收滤波器原理与应用》等技术资料整理优化,并结合机器人关节多轴 CAN 网络与工业物联网网关的工程实践进行了系统化梳理。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应