2. CMS32芯片CAN模块概览:芯片手册解读、CAN控制器寄存器映射、时钟配置与波特率计算
做CAN通信,第一步不是写代码,而是读手册。很多人上来就翻例程,结果波特率配错了,查半天找不到原因。我个人的习惯是:先把芯片手册里CAN相关章节通读一遍,把寄存器映射和时钟树理清楚,再动手写代码。这样后面调试时心里才有底。
CMS32系列芯片的CAN模块,说白了就是一个符合CAN 2.0B标准的控制器。它负责协议层的处理——帧封装、仲裁、错误检测这些脏活累活,全交给硬件。我们软件要做的,就是配置好寄存器,然后把数据往发送缓冲区一丢就行。
2.1 芯片手册解读:找到CAN模块的关键信息
拿到CMS32的手册,别从头读到尾。我建议你直接翻到CAN控制器章节,重点看这几块内容:
- 模块特性:支持CAN 2.0B、标准帧/扩展帧、数据帧/远程帧、FIFO模式、自动重发等。确认你的应用场景是否满足。
- 引脚描述:CAN_TX和CAN_RX分别映射到哪个GPIO?有没有复用功能选择?我遇到过一块板子,CAN_TX和UART_TX共用了同一个引脚,结果两个功能打架,折腾了一下午。
- 时钟源:CAN模块的时钟来自哪里?是APB总线时钟,还是独立的CAN时钟?这个直接决定波特率怎么算。
- 中断与DMA:接收完成、发送完成、错误警告、总线关闭等中断向量号是多少?DMA请求通道是哪个?
2.2 CAN控制器寄存器映射:核心寄存器一览
CMS32的CAN控制器寄存器,按功能可以分成几组。我整理了一个表格,方便你对照手册看:
| 寄存器组 | 寄存器名称 | 偏移地址 | 功能说明 |
|---|---|---|---|
| 控制与状态 | CAN_CTRL | 0x00 | 模块使能、工作模式选择、初始化请求 |
| CAN_STAT | 0x04 | 总线状态、错误状态、接收/发送状态 | |
| 位时序 | CAN_BTR | 0x08 | 波特率预分频、同步跳转宽度、时间段1/2 |
| 中断 | CAN_IER | 0x0C | 中断使能:接收、发送、错误、唤醒等 |
| CAN_IFR | 0x10 | 中断标志位,写1清除 | |
| 报文缓冲 | CAN_TXBUF[0:2] | 0x20~0x3C | 3个发送缓冲区,每个13字节 |
| CAN_RXBUF[0:2] | 0x40~0x5C | 3个接收缓冲区,每个13字节 | |
| 验收滤波 | CAN_ACR[0:3] | 0x60~0x6C | 验收代码寄存器,4组 |
| CAN_AMR[0:3] | 0x70~0x7C | 验收屏蔽寄存器,4组 |
这里有个坑,我提醒一下:CAN_CTRL寄存器的bit0是初始化请求位。你要修改任何配置(比如波特率、验收滤波),必须先把这个位置1,让模块进入初始化模式。改完后再清0,模块才会回到正常工作模式。这个顺序搞反了,配置写不进去,芯片也不会报错——你查半天都查不出来。
2.3 时钟配置:CAN模块的“心跳”从哪来?
CAN模块的时钟,决定了它能跑多快。CMS32的CAN时钟通常来自APB总线时钟,但也有独立时钟源的情况。具体看芯片手册的时钟树图。
我以CMS32L051为例(你们用的型号可能不同,但思路一样):
- 系统主时钟HCLK = 48MHz(来自内部PLL)
- APB总线时钟PCLK = HCLK / 2 = 24MHz
- CAN模块时钟CAN_CLK = PCLK(默认)
也就是说,CAN模块的输入时钟是24MHz。这个值就是后面算波特率的基准。
2.4 波特率计算:把公式变成代码
CAN的波特率计算公式,其实就一个:
波特率 = CAN_CLK / (BRP + 1) / (TSEG1 + TSEG2 + 1 + SJW)
其中:
- BRP:波特率预分频值(0~255)
- TSEG1:时间段1(采样点之前的部分,通常包含传播段和相位段1)
- TSEG2:时间段2(采样点之后的部分,即相位段2)
- SJW:同步跳转宽度(通常取1~4)
举个例子,我要配一个500kbps的波特率,CAN_CLK=24MHz:
- 先选BRP=2,那么CAN时钟被分成 24MHz / (2+1) = 8MHz,即每个时间量子(TQ)是125ns。
- 一个位需要 1 / 500kbps = 2000ns,也就是 2000 / 125 = 16个TQ。
- 分配这16个TQ:TSEG1=10,TSEG2=5,SJW=1。加起来 10+5+1+1=17?不对,公式里SJW只算一次。实际是 TSEG1 + TSEG2 + 1 = 10+5+1=16,正好。
- 采样点位置 = (TSEG1 + 1) / (TSEG1 + TSEG2 + 1) = 11/16 = 68.75%。这个值在60%~80%之间,可以接受。
写成代码就是:
// 配置CAN波特率为500kbps,CAN_CLK=24MHz
CAN_BTR = (2 << 0) // BRP = 2
| (1 << 8) // SJW = 1
| (10 << 16) // TSEG1 = 10
| (5 << 20); // TSEG2 = 5
经验之谈:我建议你把常用的波特率(125k、250k、500k、1M)对应的BRP、TSEG1、TSEG2值算好,做成一个表格注释在代码里。下次换项目直接查表,不用重新算。我曾经在一个项目里,因为波特率算错了一位,导致两辆车上的ECU互相收不到数据,排查了整整两天——最后发现是TSEG1少算了1个TQ。
2.5 知识结构图:CAN模块配置流程
下面这张图,把CAN模块从时钟到波特率的配置流程串起来了。你可以把它当作一个检查清单:
2.6 避坑指南:我踩过的几个坑
最后,分享几个我在CMS32 CAN配置上踩过的坑,希望能帮你省点时间:
- 坑一:初始化模式没退出。配置完CAN_BTR后,忘了清掉CAN_CTRL的初始化位。结果模块一直卡在初始化状态,发不出也收不到任何报文。我当时用示波器量CAN_TX引脚,发现一直是隐性电平,才意识到问题。
- 坑二:BRP算错导致波特率偏差。有一次我算500kbps,BRP设成了3,结果实际波特率是24M / (3+1) / 16 = 375kbps。两边的节点波特率不匹配,总线一直报错。后来用CAN分析仪抓波形,量位时间才发现不对。
- 坑三:验收滤波配置太严。默认验收滤波是全通的,但如果你改了ACR和AMR,记得确认滤波规则。我曾经把AMR设成全0(精确匹配),结果只收一个ID的报文,其他全丢了。排查了半天,还以为是硬件坏了。