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 三个寄存器的协同工作

这三个寄存器不是孤立的,它们需要协同工作。我总结了一个典型的初始化流程:

  1. 设置GCR的INIT=1,进入初始化模式。
  2. 设置GCR的CCE=1,使能配置更改。
  3. 设置CDR,配置时钟分频系数。
  4. 设置ICR,配置中断使能和中断线。
  5. 设置其他配置寄存器(比如位时序寄存器、过滤器寄存器等)。
  6. 清除GCR的CCE=0,禁止配置更改。
  7. 清除GCR的INIT=0,退出初始化模式,进入正常工作模式。

这个流程我建议你背下来,因为每次初始化CAN模块都要走一遍。嗯,我刚开始学的时候,也是反复写代码,才把这个流程记熟的。

核心要点:全局控制寄存器是“开关”,中断控制寄存器是“消息通道”,时钟分频寄存器是“节拍器”。三者配合,才能让CAN模块正常工作。

好了,这一章的内容就到这里。下一章我们会讲位时序寄存器和过滤器寄存器,这两个也是CAN通信的关键。到时候我会结合具体的项目案例,给大家讲讲如何配置位时序来适应不同的波特率,以及如何设置过滤器来只接收自己关心的报文。