4、CAN数据采集:CAN收发器(TJA1050)、CAN控制器(MCP2515)、SPI接口通信、数据采样率设置
好,咱们进入第四章。这一章是硬骨头,也是实战中最容易出问题的地方。
很多朋友觉得CAN通信嘛,不就是两根线一接,数据就来了?
其实没那么简单。你想想看,MCU的IO口电平是3.3V或者5V,而CAN总线上的差分信号是2.5V共模电压加上±1V的摆幅。这中间怎么搭桥?
答案就是:CAN收发器 + CAN控制器。
我个人习惯把这对组合叫做「CAN通信的左右护法」。收发器负责物理层,控制器负责数据链路层。而它们之间的通信桥梁,就是SPI。
4.1 CAN收发器 TJA1050:物理层的「翻译官」
TJA1050,恩智浦的经典片子。我最早接触它是在2015年做的一个商用车黑匣子项目上,当时选型就因为它皮实、抗干扰。
它的核心作用就两个:
- 发送方向: 把控制器来的TX数字信号,转成CAN_H和CAN_L的差分信号。
- 接收方向: 把总线上的差分信号,转成RX数字信号送回控制器。
说白了,它就是个电平转换+驱动增强器。
关键引脚速记:
- TXD: 发送数据输入(低电平有效,注意!)
- RXD: 接收数据输出
- CANH / CANL: 总线差分对
- Vref: 参考电压输出(2.5V,我一般悬空不用)
- RS: 斜率控制/待机模式
我曾经踩过的坑:
有一次调试,总线一直报错。查了半天发现TXD引脚默认是高电平,而我初始化时忘了拉低。TJA1050的TXD是低电平使能发送,高电平是进入接收模式。这个逻辑跟很多人的直觉相反,一定要注意。
另外,RS引脚接一个10kΩ电阻到地,可以控制上升沿斜率,减少EMI。如果对电磁兼容要求高,这个电阻别省。
4.2 CAN控制器 MCP2515:数据链路层的「管家」
MCP2515是Microchip的独立CAN控制器。为什么不用MCU自带的CAN外设?
原因很简单:很多低成本MCU不带CAN控制器。而且MCP2515内置了3个发送缓冲器和2个接收缓冲器,还有6个29位ID的滤波器。这个配置对于黑匣子这种需要过滤大量报文的应用来说,非常实用。
MCP2515的主要职责:
- 按照CAN协议(2.0A/B)对数据进行组帧和解帧
- 处理仲裁、位填充、CRC校验
- 管理错误状态和错误计数
- 通过SPI接口与MCU交换数据
嗯,这里要注意:MCP2515内部有多个寄存器,操作起来有点像操作一个外置的「CAN协处理器」。你通过SPI写它的控制寄存器,它帮你完成底层的CAN通信。
我的个人习惯:
初始化MCP2515时,我一般先复位,然后配置位时序(后面会讲),再配置验收滤波器和屏蔽寄存器。最后把CANCTRL寄存器里的REQOP位设为0x00,进入正常模式。顺序错了,芯片可能不工作。
4.3 SPI接口通信:MCU与MCP2515的「高速公路」
SPI,全称Serial Peripheral Interface。MCP2515支持SPI模式0,0(CPOL=0, CPHA=0),时钟频率最高10MHz。
我建议你实际项目中跑5MHz左右,留点余量。
SPI通信的基本流程:
- MCU拉低CS片选引脚
- 发送指令字节(读、写、位修改、复位等)
- 发送地址字节(寄存器地址)
- 发送或接收数据字节
- 拉高CS片选引脚
下面是一个典型的写寄存器代码示例:
// SPI写MCP2515寄存器
void MCP2515_WriteReg(uint8_t addr, uint8_t data) {
CS_LOW(); // 拉低片选
SPI_TransferByte(0x02); // 写指令
SPI_TransferByte(addr); // 寄存器地址
SPI_TransferByte(data); // 写入数据
CS_HIGH(); // 拉高片选
}
读操作类似,只是指令变成0x03。还有一个很实用的指令是「位修改」指令0x05,可以只修改某个寄存器的特定位,不用先读后写。这个我在调试滤波器配置时经常用。
我曾经遇到的坑:
SPI通信时,CS片选必须在整个传输过程中保持低电平。有一次我代码里在发送指令和地址之间不小心拉高了CS,结果MCP2515直接丢弃了后续数据。查了两天才发现这个时序问题。
4.4 数据采样率设置:位时序的「黄金分割」
采样率,其实就是CAN总线的波特率。对于黑匣子来说,最常见的设置是500kbps和250kbps。
但设置波特率不是简单写个数字。MCP2515内部有一个位时序逻辑,你需要配置三个参数:
- BRP(波特率预分频器): 决定一个位时间的基本时间单元TQ
- TSEG1(时间段1): 包含传播段和相位缓冲段1
- TSEG2(时间段2): 相位缓冲段2
- SJW(同步跳转宽度): 用于补偿时钟偏差
一个CAN位时间 = 1个同步段 + TSEG1 + TSEG2。
采样点位置 = (1 + TSEG1) / (1 + TSEG1 + TSEG2)。
我个人习惯把采样点设置在87.5%左右。为什么?因为总线信号在传输过程中会有延迟,采样点靠后一些,能采到更稳定的电平。
下面是一个500kbps的配置示例(假设MCU时钟16MHz):
// 500kbps配置,采样点87.5%
// 16MHz / (2 * (1+8+3)) = 500kbps
MCP2515_WriteReg(CNF1, 0x01); // BRP=1, SJW=1
MCP2515_WriteReg(CNF2, 0x90); // TSEG1=8, 采样点1次
MCP2515_WriteReg(CNF3, 0x02); // TSEG2=3
采样率设置速查表(16MHz晶振):
| 波特率 | BRP | TSEG1 | TSEG2 | SJW | 采样点 |
|---|---|---|---|---|---|
| 1Mbps | 0 | 8 | 7 | 1 | 56.25% |
| 500kbps | 1 | 8 | 3 | 1 | 87.5% |
| 250kbps | 3 | 8 | 3 | 1 | 87.5% |
| 125kbps | 7 | 8 | 3 | 1 | 87.5% |
避坑指南:
我曾经在一个项目中,两端的CAN节点用了不同的晶振(一个16MHz,一个8MHz)。结果同样的配置参数,两边采样点不一样,导致通信时好时坏。后来我统一了晶振频率,问题解决。所以,同一条总线上的节点,晶振最好一致。
4.5 知识体系总览
下面这张图,是我自己画的一个数据流框架图。你可以把它当作本章的「地图」:
4.6 实战小结
这一章的内容,说白了就是三件事:
- TJA1050负责物理层——把数字信号变成差分信号,或者反过来。
- MCP2515负责数据链路层——通过SPI接收MCU的指令,完成CAN协议的处理。
- 采样率设置——通过配置位时序寄存器,让所有节点在同一个节奏上工作。
我在实际项目中,最常遇到的问题就是采样点不匹配。两个节点波特率一样,但采样点不同,就会出现偶发性的丢帧。所以,我建议你在调试阶段,先用示波器抓一下CAN_H和CAN_L的波形,看看位时间是否准确。
嗯,这一章就到这里。内容不少,但都是干货。你把这些吃透了,CAN数据采集这块基本就稳了。
公众号:蓝海资料掘金营,微信deep3321