3.1 全局控制寄存器——CAN模块的“大脑中枢”
各位同学,咱们今天来聊聊MCMCAN模块里最核心的几个寄存器。说实话,我刚开始接触TC3xx的CAN模块时,面对那一大堆寄存器,头都大了。但后来我发现,只要把几个关键的寄存器吃透了,整个模块的运作逻辑就清晰了。
全局控制寄存器,英文叫Global Control Register,简称GCR。它的地址偏移量是0x0000。嗯,没错,就是第一个寄存器。它控制着整个CAN模块的开关、工作模式、时钟源选择等最基础的操作。
3.1.1 GCR的位域详解
咱们直接看表格,这样更直观:
| 位域 | 名称 | 描述 |
|---|---|---|
| [31:16] | 保留 | 读返回0,写忽略 |
| [15] | INIT | 初始化模式使能。1=进入初始化模式,0=退出初始化模式 |
| [14] | CCE | 配置更改使能。1=允许修改配置寄存器,0=禁止修改 |
| [13:12] | DAR | 自动重传禁止。00=允许自动重传,11=禁止自动重传 |
| [11:8] | TEST | 测试模式使能。一般不用,保持0 |
| [7:6] | CSS | 时钟源选择。00=系统时钟,01=外部时钟,10=备用时钟 |
| [5:0] | 保留 | 读返回0,写忽略 |
这里我要特别强调一下INIT位和CCE位。这两个位是配置CAN模块的“钥匙”。
配置流程口诀:先置INIT=1,再置CCE=1,然后才能改配置。改完后,先清CCE=0,再清INIT=0。
为什么要有这个流程?说白了就是为了防止你在CAN正在通信时,突然改了配置,导致总线崩溃。我在项目中就遇到过这种情况——同事在调试时忘了先进入初始化模式,直接写配置寄存器,结果CAN模块直接“死机”了。嗯,从那以后,我每次写配置前都会检查INIT和CCE的状态。
3.1.2 时钟源选择(CSS位)
CSS位决定了CAN模块的时钟来源。我个人习惯用系统时钟,因为这样时钟频率稳定,不容易出问题。但如果你有特殊需求,比如要用外部晶振来同步,那就可以选外部时钟。
你想想看,如果时钟源选错了,CAN的波特率就会算错,通信肯定失败。我曾经帮一个客户排查问题,他们怎么调都调不通CAN通信,最后发现是CSS位设错了,导致时钟频率翻了一倍。所以,这个位虽然小,但影响很大。
3.2 中断控制寄存器——CAN模块的“消息中心”
中断控制寄存器,英文叫Interrupt Control Register,简称ICR。它的地址偏移量是0x0004。这个寄存器负责管理CAN模块的所有中断源。
说白了,中断就是CAN模块在发生某些事件时,通知CPU的一种方式。比如,收到一帧数据、发送完成、发生错误等,都会触发中断。
3.2.1 ICR的位域详解
| 位域 | 名称 | 描述 |
|---|---|---|
| [31:16] | 保留 | 读返回0,写忽略 |
| [15] | IR | 中断请求标志。1=有中断请求,0=无中断请求 |
| [14:8] | IE | 中断使能。每一位对应一个中断源,1=使能,0=禁止 |
| [7:0] | IL | 中断线选择。每一位对应一个中断源,选择中断输出到哪条中断线 |
这里有个关键点:IR位是只读的,你不能通过写IR位来清除中断。要清除中断,必须去读对应的中断状态寄存器。嗯,这个设计我一开始也觉得有点别扭,但后来理解了——这是为了防止软件误操作导致中断丢失。
小技巧:在中断服务函数里,第一件事就是读中断状态寄存器,确认是哪个中断源触发了。然后处理完业务逻辑后,再写1到对应的状态位来清除中断。顺序千万别搞反了,否则会陷入死循环。
3.2.2 中断线选择(IL位)
IL位决定了每个中断源连接到哪条中断线上。TC3xx的CAN模块支持多条中断线,你可以把不同的中断源分配到不同的中断线上,这样CPU就可以通过不同的中断线来区分中断类型。
举个例子:你可以把接收中断分配到中断线0,把发送完成中断分配到中断线1。这样,CPU在处理中断时,就不需要去读中断状态寄存器来判断是哪个中断了,直接根据中断线就能知道。这能提高中断响应速度。
我个人习惯把紧急的中断(比如错误中断)分配到高优先级的中断线上,把普通的中断(比如接收中断)分配到低优先级的中断线上。这样,即使系统负载很高,紧急错误也能被及时处理。
3.3 时钟分频寄存器——CAN模块的“节拍器”
时钟分频寄存器,英文叫Clock Divider Register,简称CDR。它的地址偏移量是0x0008。这个寄存器用来设置CAN模块的时钟分频系数,从而得到CAN控制器的工作时钟。
你想想看,系统时钟可能是100MHz,但CAN控制器可能只需要20MHz。这时候就需要用时钟分频寄存器来分频。
3.3.1 CDR的位域详解
| 位域 | 名称 | 描述 |
|---|---|---|
| [31:16] | 保留 | 读返回0,写忽略 |
| [15:8] | DIV8 | 8位分频系数。CAN时钟 = 输入时钟 / (DIV8 + 1) |
| [7:0] | DIV | 8位分频系数。CAN时钟 = 输入时钟 / (DIV + 1) |
这里有两个分频系数:DIV8和DIV。它们有什么区别呢?
- DIV8:用于CAN FD模式,支持更高的时钟频率。
- DIV:用于传统CAN模式,时钟频率较低。
说白了,如果你用的是CAN FD,就用DIV8;如果你用的是传统CAN,就用DIV。当然,你也可以两个都用,但最终生效的是哪个,取决于你配置的CAN模式。
注意:分频系数不能为0。如果DIV=0,那么分频系数就是1,即不分频。但如果你设DIV=0,实际上时钟频率就是输入时钟,这可能会导致CAN控制器工作频率过高,超出规格。所以,我建议DIV至少设为1。
3.3.2 实际应用中的分频计算
假设系统时钟是100MHz,你想让CAN控制器工作在20MHz。那么分频系数就是100/20=5。所以DIV=5-1=4。
代码示例:
// 设置时钟分频,使CAN时钟为20MHz
// 假设系统时钟为100MHz
#define SYSTEM_CLOCK 100000000
#define CAN_CLOCK 20000000
uint32_t div_value = SYSTEM_CLOCK / CAN_CLOCK - 1;
// 写入时钟分频寄存器
MCMCAN->CDR = (div_value & 0xFF); // 使用DIV位
这里要注意,分频后的时钟频率不能超过CAN控制器的最大工作频率。TC3xx的CAN控制器最大工作频率一般是40MHz。所以,如果你系统时钟是100MHz,分频系数至少是3(100/3≈33.3MHz),不能是2(100/2=50MHz,超了)。
我曾经有个项目,为了追求更高的CAN通信速率,把分频系数设小了,结果CAN控制器工作频率超标,导致通信时好时坏。排查了好久才发现是这个问题。所以,大家一定要仔细看数据手册,确认最大工作频率。
3.4 三个寄存器的协同工作
这三个寄存器不是孤立的,它们需要协同工作。我总结了一个典型的初始化流程:
- 设置GCR的INIT=1,进入初始化模式。
- 设置GCR的CCE=1,使能配置更改。
- 设置CDR,配置时钟分频系数。
- 设置ICR,配置中断使能和中断线。
- 设置其他配置寄存器(比如位时序寄存器、过滤器寄存器等)。
- 清除GCR的CCE=0,禁止配置更改。
- 清除GCR的INIT=0,退出初始化模式,进入正常工作模式。
这个流程我建议你背下来,因为每次初始化CAN模块都要走一遍。嗯,我刚开始学的时候,也是反复写代码,才把这个流程记熟的。
核心要点:全局控制寄存器是“开关”,中断控制寄存器是“消息通道”,时钟分频寄存器是“节拍器”。三者配合,才能让CAN模块正常工作。
好了,这一章的内容就到这里。下一章我们会讲位时序寄存器和过滤器寄存器,这两个也是CAN通信的关键。到时候我会结合具体的项目案例,给大家讲讲如何配置位时序来适应不同的波特率,以及如何设置过滤器来只接收自己关心的报文。