提示:Linux驱动-SPI-MCP2515外设模块写CNF1-CNF2-CNF3-RXB0CTRL寄存器值


一、参考资料

RK3568驱动指南|第十六篇 SPI-第190章 配置模式下寄存器的配置

Linux驱动-SPI-读寄存器函数

一、为什么要了解这个知识点

看数据手册: 配置模式下可以修改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 0000010x01

如果修改 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
原因:

  1. 满足常规场景晶振误差;
  2. 兼容性最好,和市面上绝大多数 CAN 节点匹配;
  3. 避免边界时序异常。

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);

总结

  • 根据需求:设置配置模式后,设置波特率。设置波特率就是设置对应的寄存器值。
  • 搞定需求同时,最重要的是补充基本知识点。如果基础知识点都不会、专业属于没搞懂,很难搞得定需求的,要彻底理解。
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐