2. CMS32 CAN模块初始化:时钟使能、GPIO复用配置与波特率计算

好,咱们直接进入正题。CAN模块初始化,说白了就是让芯片知道“我要用CAN了”,然后把收发引脚接好,再告诉它用多快的速度说话。这三步缺一不可,顺序也基本固定。我刚开始接触CMS32的时候,就因为在时钟使能之前配了GPIO,结果折腾了半天——嗯,从那以后我再也没犯过这种低级错误。

2.1 时钟使能——先让CAN模块“通电”

任何外设要工作,第一步都是打开它的时钟。CMS32的CAN模块挂在APB总线上,你需要找到对应的时钟控制寄存器。

核心操作:设置时钟控制寄存器中的CANEN位,使能CAN模块时钟。

我个人习惯在系统时钟初始化之后、配置任何外设之前,先把CAN时钟打开。代码很简单,就一两行:

/* 使能CAN模块时钟 */
SYS->CLKEN |= (1 << 5);  // 假设CANEN位在bit5,具体位号请查手册

小提示:不同型号的CMS32,时钟使能位的位置可能不一样。我建议你每次换芯片型号时,都重新确认一下寄存器定义,别直接复制旧代码。

2.2 GPIO复用配置——把CAN_TX和CAN_RX“接出来”

时钟打开了,但CAN模块的TX和RX信号还憋在芯片内部呢。你得把对应的GPIO引脚配置成复用功能,让信号能走到外面去。

CMS32的CAN接口通常复用在一组固定的引脚上。比如,CAN_TX可能是PA12,CAN_RX可能是PA11。你需要做两件事:

  1. 配置引脚模式:将TX引脚设为推挽输出复用模式,RX引脚设为浮空输入或上拉输入模式。
  2. 选择复用功能:通过GPIO的复用功能选择寄存器,把引脚连接到CAN模块。
/* 配置CAN_TX (PA12) 为复用推挽输出 */
GPIOA->MODER &= ~(3 << 24);  // 清除原模式
GPIOA->MODER |= (2 << 24);   // 设为复用模式

/* 配置CAN_RX (PA11) 为复用输入 */
GPIOA->MODER &= ~(3 << 22);
GPIOA->MODER |= (2 << 22);

/* 选择复用功能为CAN */
GPIOA->AFR[1] |= (9 << 16);  // 假设AF9对应CAN,具体值看数据手册

注意:我曾经在一个项目里,因为复用功能号写错了,CAN一直收不到数据。查了两天才发现是AFR寄存器配成了别的外设。所以,配完GPIO之后,我建议你读一下寄存器确认一下。

2.3 波特率计算与设置——让CAN节点“说同一种语言”

波特率是CAN通信的基础。同一个网络上的所有节点,波特率必须完全一致,否则谁也听不懂谁。

CAN的波特率由三个参数决定:

  • BS1 (时间段1):采样点之前的时间段,通常设为4~16个时间量子。
  • BS2 (时间段2):采样点之后的时间段,通常设为2~8个时间量子。
  • SJW (同步跳转宽度):用于补偿时钟偏差,通常设为1~4个时间量子。

计算公式很简单:

波特率 = CAN时钟频率 / ( (1 + BS1 + BS2) × 分频系数 )

举个例子,假设CAN时钟是48MHz,我想要1Mbps的波特率:

  1. 先选分频系数,比如4分频,得到12MHz的CAN时钟。
  2. 再算时间量子总数:12MHz / 1Mbps = 12个时间量子。
  3. 分配BS1和BS2:比如BS1=7,BS2=4,再加上1个同步段,正好12个。
参数 推荐值 说明
BS1 4~16 采样点位置,通常设为7~9
BS2 2~8 采样点后时间,通常设为4~6
SJW 1~4 时钟偏差补偿,通常设为1
采样点位置 75%~87.5% BS1/(BS1+BS2+1) 计算

经验之谈:采样点位置建议设在75%~87.5%之间。我一般选80%左右,也就是BS1=8,BS2=2(加上同步段共11个时间量子)。这样既能容忍一定的时钟偏差,又能保证采样稳定。

/* 波特率配置示例:1Mbps,CAN时钟48MHz,4分频 */
CAN->BTR |= (3 << 16);   // 分频系数 = 4 (实际值 = 3+1)
CAN->BTR |= (7 << 20);   // BS1 = 8 (实际值 = 7+1)
CAN->BTR |= (3 << 24);   // BS2 = 4 (实际值 = 3+1)
CAN->BTR |= (0 << 28);   // SJW = 1 (实际值 = 0+1)

2.4 工作模式选择——Normal、Loopback还是Silent?

CAN模块有好几种工作模式,不同场景用不同的模式。我刚开始学的时候,经常搞混,后来总结了一张表:

模式 发送 接收 ACK应答 典型用途
Normal 正常发送 正常接收 正常应答 实际通信
Loopback 发送到内部回路 只收自己发的 自己应答自己 自测、调试
Silent 禁止发送 正常接收 不应答 监听总线
Loopback+Silent 发送到内部回路 只收自己发的 不应答 静默自测

我个人习惯,在开发阶段先用Loopback模式验证发送和接收逻辑。等确认没问题了,再切到Normal模式去接真实的总线。

避坑指南:我曾经在调试时忘了从Loopback切回Normal,结果板子连上总线后,其他节点都收不到它的数据。查了半天才发现模式没改。所以,调试完一定要记得切模式。

/* 设置工作模式:Normal */
CAN->MCR &= ~(1 << 0);   // 清除Loopback位
CAN->MCR &= ~(1 << 1);   // 清除Silent位

/* 设置工作模式:Loopback */
CAN->MCR |= (1 << 0);    // 置位Loopback位
CAN->MCR &= ~(1 << 1);   // 清除Silent位

2.5 初始化流程总览

说了这么多,咱们把整个初始化流程串起来。下面这张图,是我自己画的一个流程图,你一看就明白了:

CMS32 CAN模块初始化流程 CMS32 CAN模块初始化流程 1. 系统时钟初始化 2. CAN时钟使能 3. GPIO复用配置 4. 波特率计算与设置 5. 工作模式选择 初始化完成 提示:顺序不能乱,尤其是时钟使能必须在GPIO配置之前

你看,整个流程就是一条直线,从时钟到GPIO,再到波特率和模式。每一步都依赖上一步的结果。我个人习惯在每步之后加个寄存器回读检查,确保配置生效了再往下走。

总结一下:CAN模块初始化,说白了就是“开时钟、配引脚、设波特率、选模式”这四步。把这四步走对了,CAN就能正常工作了。剩下的就是收发数据的逻辑了,咱们后面再聊。