3. CAN初始化实战:模式选择、位时序配置与滤波器设置

好,咱们直接进入正题。CAN模块的初始化,说白了就是给硬件“定规矩”——告诉它用什么模式工作、跑多快、收哪些报文。这一步要是没弄对,后面通信全是乱码。我见过不少工程师,上来就抄例程,结果环回模式调通了,一上总线就翻车。嗯,咱们今天就把这三个核心配置彻底讲透。

3.1 模式选择:正常、环回与静默

CMS32的CAN控制器支持三种工作模式。你想想看,这就像开车——正常模式是上路跑,环回模式是在驾校练车,静默模式是只监听不说话。

模式 发送 接收 ACK应答 典型用途
正常模式 有效 有效 发送 实际总线通信
环回模式 内部回环 接收自己发送 不发送 自测、单板调试
静默模式 禁止 有效 不发送 总线监听、故障分析
我的习惯:调试阶段先用环回模式验证硬件通路,再用静默模式监听总线看有没有其他节点在发数据,最后才切正常模式。三步走,稳得很。

模式配置在CMS32的CAN控制寄存器里,一般是通过CAN_MCR(模式控制寄存器)的对应位来切换。代码实现很简单:

// CMS32 CAN模式配置示例
void CAN_SetMode(uint8_t mode) {
    switch(mode) {
        case CAN_MODE_NORMAL:
            CAN->MCR &= ~(CAN_MCR_TEST_MASK);  // 清除测试模式
            CAN->MCR &= ~(CAN_MCR_SLEEP_MASK); // 退出睡眠
            break;
        case CAN_MODE_LOOPBACK:
            CAN->MCR |= CAN_MCR_TEST_MASK;      // 进入测试模式
            CAN->MCR |= CAN_MCR_LBACK_MASK;     // 使能环回
            break;
        case CAN_MODE_SILENT:
            CAN->MCR |= CAN_MCR_TEST_MASK;
            CAN->MCR |= CAN_MCR_SILENT_MASK;    // 使能静默
            break;
    }
}
注意:模式切换必须在CAN模块处于初始化状态(CAN_MCR的INIT位为1)时进行。我曾经在调试时忘了这茬,模式切来切去都没反应,查了半天才发现是INIT位没置位。

3.2 位时序配置:TSEG1、TSEG2与SJW

位时序是CAN通信的“心跳”。配置不对,两个节点就算波特率一样也聊不到一块去。CMS32的CAN控制器把1个位时间分成3段:同步段(SS)、时间段1(TSEG1)、时间段2(TSEG2)。

为什么这么分?因为CAN总线是异步的,没有单独的时钟线。每个节点靠采样点来“猜”对方发的是0还是1。采样点位置就由TSEG1和TSEG2的比例决定。

核心公式:
位时间 = (1 + TSEG1 + TSEG2) × 时间量子
采样点位置 = (1 + TSEG1) / (1 + TSEG1 + TSEG2) × 100%

我个人习惯把采样点设在87.5%左右。为什么是这个值?因为CAN总线信号在传输过程中会有延迟,采样点靠后一些,能容忍更大的线缆长度和节点数。当然,如果总线很短(比如同一块板子上),75%也行。

参数 范围 说明
TSEG1 1~16 包含传播段和相位缓冲段1,决定采样点位置
TSEG2 1~8 相位缓冲段2,决定采样点后的时间
SJW 1~4 同步跳转宽度,用于补偿时钟偏差

SJW这个参数容易被忽略。它决定了CAN控制器能容忍多大的时钟偏差。举个例子,如果两个节点的晶振精度分别是±0.1%和±0.2%,SJW设得太小,跑久了就会同步失败。我建议SJW至少设为2,保守一点设成3也行。

// CMS32 CAN位时序配置示例(500kbps,8MHz时钟)
void CAN_BitTimingConfig(void) {
    // 假设CAN时钟为8MHz,目标波特率500kbps
    // 位时间 = 8MHz / 500kbps = 16个时间量子
    // 配置:SS=1, TSEG1=12, TSEG2=3, SJW=2
    // 采样点 = (1+12)/(1+12+3) = 13/16 ≈ 81.25%
    
    CAN->BTR |= (2 << CAN_BTR_SJW_POS);    // SJW = 2
    CAN->BTR |= (12 << CAN_BTR_TSEG1_POS); // TSEG1 = 12
    CAN->BTR |= (3 << CAN_BTR_TSEG2_POS);  // TSEG2 = 3
}
避坑指南:我曾经在一个项目里,两个板子波特率都配成了500kbps,但就是通信失败。用示波器一抓,发现一个采样点在80%,另一个在70%。虽然波特率一样,但采样点偏差太大,导致位同步失败。所以,同一总线上的所有节点,采样点必须一致。

3.3 滤波器设置:只收你想收的

CAN总线上的报文是广播式的——所有节点都能收到。但大多数节点只关心特定ID的报文。滤波器就是干这个的:把不想要的报文挡在门外,减轻CPU负担。

CMS32的CAN控制器支持两种滤波器模式:

  • 标识符列表模式:只接收与预设ID完全匹配的报文。适合节点只关心1~2个ID的场景。
  • 掩码模式:通过掩码指定哪些位必须匹配,哪些位可以忽略。适合需要接收一组ID的场景。

举个例子,假设你只想接收ID为0x123和0x124的报文:

// CMS32 CAN滤波器配置示例(掩码模式)
void CAN_FilterConfig(void) {
    // 配置滤波器组0为掩码模式
    CAN->FCTRL |= (1 << CAN_FCTRL_FEN_POS);  // 使能滤波器
    
    // 设置ID掩码:只匹配高11位
    CAN->FMASK[0] = 0x7FF;  // 掩码全1,表示所有位都必须匹配
    
    // 设置期望ID
    CAN->FID[0] = 0x123;    // 只接收ID=0x123的报文
    
    // 如果需要接收0x123和0x124,可以用掩码模式:
    // CAN->FMASK[0] = 0x7FE;  // 最低位忽略
    // CAN->FID[0] = 0x122;    // 匹配0x122和0x123
}
注意:滤波器配置必须在CAN模块退出初始化状态之前完成。我见过有人把滤波器配在CAN启动之后,结果滤波器根本没生效,所有报文都涌进来了,CPU直接跑飞。

3.4 知识体系总览

下面这张图把CAN初始化的三个核心模块串起来了。你可以把它当作调试时的“地图”——哪个环节出问题,就回来看对应的配置。

CAN初始化知识体系 CAN初始化核心配置总览 模式选择 正常 / 环回 / 静默 位时序配置 TSEG1 / TSEG2 / SJW 滤波器设置 列表模式 / 掩码模式 环回自测 验证发送接收通路 静默监听 分析总线流量 采样点计算 TSEG1/(1+TSEG1+TSEG2) SJW容差 补偿时钟偏差 ID精确匹配 列表模式 ID范围匹配 掩码模式 调试建议 1. 先配模式:环回验证硬件 → 静默监听总线 → 正常通信 2. 再配时序:根据时钟和波特率计算TSEG1/TSEG2,采样点建议80%~87.5% 3. 最后配滤波器:先配掩码全开确认能收到报文,再逐步收紧 4. 所有配置必须在CAN退出初始化状态前完成

3.5 初始化完整流程

最后,我把CAN初始化的完整步骤整理成一个清单。你每次写初始化代码时,按这个顺序来,基本不会漏东西。

  1. 使能CAN时钟——别笑,我真见过有人忘了开时钟,调了半天以为芯片坏了。
  2. 配置CAN引脚——TX/RX要配成复用功能,注意上下拉。
  3. 进入初始化模式——置位CAN_MCR的INIT位。
  4. 配置模式——正常/环回/静默,按需选择。
  5. 配置位时序——TSEG1、TSEG2、SJW,算好采样点。
  6. 配置滤波器——设置ID和掩码。
  7. 退出初始化模式——清除INIT位,CAN开始工作。
  8. 验证状态——读状态寄存器,确认CAN已进入运行态。
我的经验:第8步很多人会跳过。但我觉得这一步特别重要——读一下状态寄存器,确认CAN确实进入了运行态,而不是卡在某个中间状态。我曾经在一个项目里,初始化代码写对了,但硬件复位时序有问题,导致CAN一直卡在初始化模式。要是没读状态寄存器,这问题能查一整天。