Linux驱动-SPI-MCP2515外设模块写CNF1-CNF2-CNF3-RXB0CTRL寄存器值
提示:Linux驱动-SPI-MCP2515外设模块写CNF1-CNF2-CNF3-RXB0CTRL寄存器值
一、参考资料
RK3568驱动指南|第十六篇 SPI-第190章 配置模式下寄存器的配置
一、为什么要了解这个知识点
看数据手册: 配置模式下可以修改CNF1、CNF2、CNF3,其实就是这三个参数决定了 CAN的波特率。 所以在配置后 第一步就直接配置波特率,MCP2515 外设CAN相关的波特率不是直接在设备树中直接配置的,设备树中那个配置是配置SPI 的波特率,而对于CAN的模块IC的波特率需要在代码中进行配置,只有配置了才能通过SPI API 来控制CAN,才能有数据收发。


二、基本概念-知识点必备
以下知识点 基础中的基础,务必掌握,不然无法理解看懂数据手册,更无法理解一些算法计算原理。
1、波特率-比特率
- 波特率(Baud Rate):符号速率。指单位时间内传输的符号(Symbol)个数,单位是 Baud(波特)。
- 比特率(Bit Rate):数据传输速率。指单位时间内传输的比特(Bit)个数,单位是 bps(比特每秒)
最关键的关系式:比特率 = 波特率 × 每个符号携带的比特数
- 如果采用二进制(0和1)调制,一个符号就是1个bit,此时 波特率 = 比特率(日常大家默认的就是这个)。
- 如果采用复杂调制(如CAN FD、以太网PAM5),一个符号可能代表2个bit(QPSK)或更多,此时波特率就小于比特率。
2、晶振-时钟
定义
- 晶振本质:晶振利用石英晶体的压电效应(给电压会变形,变形会产生电压),当外界电信号频率与晶体的固有机械振动频率一致时,它会剧烈谐振,输出一个频率极其纯净的正弦波。
- 时钟本质:晶振产生物理振荡,经过芯片内部电路整形后,输出给外设(如CAN控制器、串口)的稳定方波信号,才叫“时钟”(Clock)。
晶振-时钟区别
从信号形态来看:
| 对比项 | 晶振(Crystal) | 时钟(Clock Signal) |
|---|---|---|
| 物理实体 | 一块石英晶体片(无源晶振)或一个带电路的模块(有源晶振) | 芯片引脚(如 OSC_IN)或特定GPIO(如 CLKOUT)上的电压波形 |
| 输出信号形状 | 正弦波(平滑的波浪形) | 方波(陡峭的0/1跳变沿) |
| 能否直接给数字电路用 | 不能。数字电路(MCU、CAN控制器)只认0和1,不认正弦波。 | 能。方波的上升沿触发计数器开始工作。 |
| 角色比喻 | 心脏的起搏点(产生原始搏动) | 脉搏(传递到全身各器官的实际跳动节拍) |
晶振: 硬件元器件,通电之后产生固定频率的机械振动,转化为电信号。
时钟:是规则的电脉冲信号,芯片内部所有逻辑(CPU、外设、CAN、SPI)靠这个脉冲做工作节拍。
晶振-时钟联系
首先基于晶振和时钟的区别,然后再看联系:
- 晶振是时钟的信号源;时钟是晶振经过芯片内部电路整形处理之后得到的节拍。
- 晶振(物理振动) → 芯片内部振荡电路 → 基准时钟(Fosc)
拿到基准时钟后,再做分频 / 倍频,生成各路外设子时钟
分频:把时钟速度降低(MCP2515 就是干这个)
MCP2515:基准时钟Fosc=8MHz,经过 BRP 预分频,产生 CAN 最小时间单位 TQ。
(TQ=\frac{2\times(BRP+1)}{F_{osc}})
倍频:把时钟速度升高(RK3568 这类主控 PLL 锁相环)
外部 24M 晶振,经过 PLL 倍频,生成 1.5GHz CPU 时钟。
✔ 晶振是源头;软件只能对源头时钟做分频 / 倍频,软件修改不了晶振本身物理频率。
拿到基准时钟后,再做分频 / 倍频,生成各路外设子时钟
所以,我们看到各个外设基于晶振参数进行时钟换算,不同协议下的外设时钟算法又不一样。
- 晶振:老式钟表里面的摆锤,固定来回摆动(物理实物)
- 基准时钟:摆锤带动齿轮,输出规整 “滴答、滴答” 脉冲
- 分频:齿轮组,把快滴答转成慢滴答,供给不同功能使用
3、Can 位时间
定义: 位时间(Bit Time) 是指一个二进制位在总线上传输所需的时间, 它又由 4 段组成, 每段
由称为 Time Quantum(Tq, 时间单位) 的最小时间单位构成。
结合波特率-比特率定义:
- 波特率(Baud Rate):符号速率。指单位时间内传输的符号(Symbol)个数,单位是 Baud(波特)。
- 比特率(Bit Rate):数据传输速率。指单位时间内传输的比特(Bit)个数,单位是 bps(比特每秒)
所以,位时间就是比特率、波特率的倒数吧。
这里涉及到一个概念TQ,位时间分为4段,每段的单位时间是TQ。
4、Can中的TQ
Time Quantum(Tq, 时间单位) 的最小时间单位构成

位时间的 4 个段:
(1) 同步段(Synchronization Segment, Sync_Seg)
- 用于同步接收器的时钟到传输器的时钟。
- 持续 1 个 Tq。
(2) 传播时间段(Propagation Time Segment, Prop_Seg) - 用于补偿信号在总线上传输的时间延迟。
- 可配置为 1 到 8 个 Tq。
(3) 相位缓冲段 1(Phase Buffer Segment 1, Phase_Seg1) - 用于补偿相位错误和支持重同步跳转。
- 可配置为 1 到 8 个 Tq。
(4) 相位缓冲段 2(Phase Buffer Segment 2, Phase_Seg2) - 用于补偿相位错误和支持重同步跳转。
- 可配置为 1 到 8 个 Tq。
5、Can中的BRP
我们在如上分析 晶振-时钟联系 的时候, 拿到了晶振参数后根据不同协议算不同协议下 外设的时钟计量方案。 这个BRP 就是CAN 外设中一个跟位时间计算相关的一个概念参数。
这里简单理解:BRP 为 cnf1寄存器设置的预分频系数, 可以任意设置, 确保最后的 TQ 为整数即可, 这里设置为 0x01。
协助理解:如上不是分析了晶振-时钟联系吗,晶振是震荡器 物理正玄波,通过电路转换才会转化为数字电路可用的时钟,时钟和波特率之间是有计算公式的,总体来说 波特率 = 时钟频率 / (分频系数A × 分频系数B) , 这个BRP 就是分配系数。
6、计算TQ
TQ 如上已经分析了概念,就是单位时间,最小单位构成。 它直接就是波特率的倒数。 所有拿到了TQ,可以得到波特率的。反向一样有了波特率 可以得到TQ的,这里面跟4段组成也有关联关系的。
看数据手册:

这里又有一个概念FOSC ,它就是晶振频率呀,如8Hz、10Hz 、20Hz 等。
假使BRP=1 即ox01;外设晶振 8MHz ,那么TQ 怎么算。
这样:TQ=2*(1+1)/8*1000000 所以1TQ 值为500ns,可以确定最小单位 TQ 的值为 500ns

7、通过TQ和要想设置的波特率 反算 CAN 位时间-进而得到总共多少个TQ
也就是说 假若 CAN的波特率是125Kbps,那么通过晶振算出来的TQ,进而得到 时间位下有多少个TQ, 16 哥TQ。

8、相位时间段-计算每段组成-每段多少个TQ
看数据手册:


最终确定各个段的值位: 同步段 1 个 TQ, 传播段 2 个 TQ, 相位缓冲段 PS1 7 个 TQ, 相位缓冲段 PS2 6 个 TQ 但其实有几种不同的组合如下:

二、分析寄存器CNF1、CNF2、CNF3 的值
我们为什么要知道CNF1、CNF2、CNF3 寄存器的值,那是因为 最终这三个参数是来影响CAN的波特率。
CNF1 寄存器值 分析
计算CNF1 寄存器值

两个比较核心的点:
- bit5-0 取决于BRP, BRP 我们选择的多少:1 , 就是ox01
- bit7-6 SJW ,有四种选择,我们选择 SJW=1TQ 模式吧。
综合 来说,最终的二进制位bit7-0 : 00000001 就是 ox01 ,所以寄存器值 是ox01
回顾 CNF1 结构
bit7 bit6:SJW1、SJW0(同步跳转宽度)
bit5~bit0:BRP 预分频
现在配置:
SJW = 1TQ → bit7bit6 = 00
BRP = 1 → bit5~bit0 = 000001
二进制:00 000001 → 0x01
如果修改 SJW,只要 BRP 不变,只有高 2 位变化,低 6 位不变。
举例:
- SJW=2TQ → bit7bit6 =
01→ 二进制01000001→ CNF1 = 0x41 - SJW=3TQ → bit7bit6 =
10→ 二进制10000001→ CNF1 = 0x81 - SJW=4TQ → bit7bit6 =
11→ 二进制11000001→ CNF1 = 0xC1
工程实操建议(MCP2515 通用经验)
虽然 SJW=2/3/4 在数学上合法,但绝大多数开源例程、教程统一使用 SJW=1TQ
原因:
- 满足常规场景晶振误差;
- 兼容性最好,和市面上绝大多数 CAN 节点匹配;
- 避免边界时序异常。
CNF2 寄存器值分析
CNF2 寄存器计算

- bit7 为相位缓冲 PS2 段的时间长度位, 这里设置为 1, 由 CNF3 寄存器决定。
- bit6 设置为 0 采样点配置位, 这里设置为 0, 表示进行一次采样。
- Bit5-bit3 表示相位缓冲段 PS1 的长度, 相位缓冲段 PS1 为 7 个 TQ, 所以 Bit5-bit3 应为 6,换算为二进制为 110,
- Bit2-bit0 表示传播段长度, 传播段长度位 2 个 TQ, 所以 Bit5-bit3 应为 1, 换算为二进制就为 001。
综上 CNF2 的值为 10110001, 换算成 16 进制就为 0xB1。
MCP2515 大坑-寄存器里面写的数字,不是直接等于 TQ!要 + 1 才是真实 TQ 时长
- 想要实际 7TQ → 寄存器写
6 - 想要实际 2TQ → 寄存器写
1
为什么?
- PRSEG、PHSEG1、PHSEG2、BRP、SJW,全部都是:硬件计数 = 寄存器写入值 + 1。
- 硬件计数器底层逻辑(最根本原因)
芯片内部是硬件计数器:从 0 开始计数。 计数器 = 0 → 跑 1 个 TQ; 计数器 = 1 → 跑 2 个 TQ; 计数器 = N → 跑 N+1 个 TQ
CNF3 寄存器值分析
寄存器值计算

- bit2-0 表示相位缓冲段 PS2 长度, 相位缓冲段为 6 个 TQ,所以 bit2-0 值应为 5, 转换为二进制为 101。 而其他位设置为 0 即可, 综上 CNF3 要设置的值为 0x05
三、写寄存器值
查找CNF1-CNF2-CNF3 寄存器地址
还是回到最开始需求 , 给寄存器写值。 在第二部中我们已经计算出了寄存器的值,分别为: 0x01 0xb1 0x05,所以拿到寄存器地址然后写入即可:
继续看 数据手册,如下:

根据寄存器地址和寄存器值-写方法 写进去
1、定义声明寄存器值
#define CNF1 0x2a // 寄存器定义
#define CNF2 0x29
#define CNF3 0x28
#define RXB0CTRL 0x60
#define CANINTE 0x2b
#define CANCTRL 0xf // CAN控制寄存器
2、spi 写函数
这个方法在上一篇RK3568驱动指南|第十六篇 SPI-第190章 配置模式下寄存器的配置学习过,直接借鉴用。
// MCP2515写寄存器函数
void mcp2515_write_reg(char reg, char value) {
int ret;
char write_buf[] = {0x02, reg, value}; // SPI写缓冲区,用于发送写寄存器命令
ret = spi_write(spi_dev, write_buf, sizeof(write_buf)); // 发送SPI写命令
if (ret < 0) {
printk("mcp2515_write_reg error\n");
}
}
3、设置到配置模式后-设置寄存器的值
mcp2515_reset(); // 复位MCP2515设备
value = mcp2515_read_reg(0x0e); // 读取寄存器值
printk("value is %x\n", value); // 打印读取的值
mcp2515_write_reg(CNF1, 0x01); // 写入寄存器配置值
mcp2515_write_reg(CNF2, 0xb1);
mcp2515_write_reg(CNF3, 0x05);
总结
- 根据需求:设置配置模式后,设置波特率。设置波特率就是设置对应的寄存器值。
- 搞定需求同时,最重要的是补充基本知识点。如果基础知识点都不会、专业属于没搞懂,很难搞得定需求的,要彻底理解。
更多推荐

所有评论(0)