第四节 CAN节点配置:节点使能与关闭、位时序配置、采样点设置
好,咱们今天聊点硬核的。CAN节点配置这块,说白了就是让芯片上的CAN模块“活过来”,并且按照你想要的速度和精度去工作。我刚开始接触TC3xx的时候,觉得这部分挺简单的,不就是使能一下、配几个寄存器嘛。结果呢?第一次调板子,通信就是不通,折腾了两天才发现是采样点没算对。嗯,从那以后,我再也不敢小看这一步了。
4.1 节点使能与关闭
先说说最基础的操作——让CAN节点工作起来。在TC3xx里,每个CAN节点都有一个独立的控制寄存器。我个人习惯,在系统初始化阶段就把所有用不到的节点先关掉,省得它们乱发消息干扰总线。
核心寄存器:CAN_NCR(Node Control Register)
使能节点的步骤其实就三步:
- 先把节点置为初始化状态(Init位写1)
- 配置好你想要的参数(位时序、过滤器这些)
- 最后把Init位清0,节点就开始正常工作了
我遇到过一种情况:有学员在调试时,发现节点怎么都发不出数据。查了半天,原来是之前某个异常中断把节点锁死了。这时候怎么办?强制复位!把NCR里的CCE位和INIT位配合操作,就能让节点“重启”。
注意:关闭节点时,一定要确保总线上没有正在传输的报文。否则强制关闭,可能会导致总线错误。我曾经在一次现场测试中吃过这个亏,整个网络都乱了。
/* 使能CAN节点0的示例代码 */
IfxCan_Node_Config nodeConfig;
IfxCan_Node_initModuleConfig(&nodeConfig, &g_mcan.canModule);
/* 配置节点ID为0 */
nodeConfig.nodeId = IfxCan_NodeId_0;
/* 使能节点 */
IfxCan_Node_initModule(&g_mcan.canNode[0], &nodeConfig);
/* 如果需要关闭节点 */
IfxCan_Node_deinitModule(&g_mcan.canNode[0]);
4.2 位时序配置(TSEG1/TSEG2/SJW)
这部分是CAN通信的“心脏”。你想想看,总线上那么多节点,为什么能同步?靠的就是位时序。说白了,就是把一个bit的时间切成几段,每段干不同的事。
TC3xx的位时序由三个关键参数组成:
| 参数 | 全称 | 作用 |
|---|---|---|
| TSEG1 | Time Segment 1 | 包含传播段和相位缓冲段1,用于补偿信号延迟 |
| TSEG2 | Time Segment 2 | 相位缓冲段2,用于调整采样点位置 |
| SJW | Synchronization Jump Width | 同步跳转宽度,决定节点能容忍多大的时钟偏差 |
我个人的配置经验是这样的:
- TSEG1:通常设成TSEG2的2倍左右。为什么?因为传播段要覆盖总线上的往返延迟,你想想看,总线越长,这个值就得越大。
- TSEG2:别设太小,至少留3~4个时间量子(tq),不然采样点太靠前,容易采到信号边沿。
- SJW:一般设成1~2个tq就够了。设太大反而容易引入噪声。我记得有一次为了兼容一个老设备,把SJW设成了4,结果误码率反而高了。
小技巧:如果你不确定怎么配,可以用英飞凌的MCAL配置工具,它会自动帮你算。但我建议你还是手动算一遍,这样才能真正理解背后的原理。
/* 位时序配置示例:500kbps,40MHz时钟 */
#define CAN_CLOCK_FREQ 40000000 /* 40MHz */
#define CAN_BAUDRATE 500000 /* 500kbps */
/* 计算时间量子数 */
/* 假设我们想要16个tq */
#define TQ_NUM 16
/* 计算预分频值 */
uint32 prescaler = CAN_CLOCK_FREQ / (CAN_BAUDRATE * TQ_NUM);
/* 结果:40M / (500k * 16) = 5 */
/* 分配TSEG1和TSEG2 */
/* 通常TSEG1 = 10, TSEG2 = 5, SJW = 1 */
IfxCan_Node_setBitTiming(&g_mcan.canNode[0],
prescaler, /* 预分频值 */
10, /* TSEG1 */
5, /* TSEG2 */
1); /* SJW */
4.3 采样点设置
采样点,这是CAN通信里最容易踩坑的地方。说白了,就是节点在bit的什么位置去读取总线电平。我见过太多工程师,配完位时序就不管采样点了,结果通信时好时坏。
采样点的计算公式很简单:
采样点位置 = (1 + TSEG1) / (1 + TSEG1 + TSEG2) × 100%
举个例子,刚才我们配的TSEG1=10,TSEG2=5:
采样点 = (1 + 10) / (1 + 10 + 5) = 11 / 16 = 68.75%
这个值怎么样?说实话,有点偏低了。我个人习惯把采样点设在80%~87.5%之间。为什么?因为CAN总线上的信号有上升时间和下降时间,采样点太靠前,可能采到不稳定的电平;太靠后,又可能来不及处理下一个bit。
推荐采样点范围:
- 低速CAN(125kbps以下):80%~85%
- 中速CAN(250k~500kbps):82%~87%
- 高速CAN(1Mbps以上):85%~87.5%
我曾经在一个项目里,把采样点设成了75%,结果在高温环境下(85°C),通信就开始丢帧。后来用示波器一看,发现信号边沿变缓了,采样点正好落在上升沿上。调整到85%后,问题就解决了。
避坑指南:如果你用的是CAN FD,采样点设置更要小心。CAN FD的数据段速率更高,对采样点的敏感度也更高。我建议在CAN FD模式下,采样点统一设在85%左右,别低于80%。
/* 调整采样点到85% */
/* 假设总tq数还是16 */
/* 85%采样点 => 采样点位置 = 0.85 * 16 ≈ 13.6,取整为14 */
/* 所以 TSEG1 = 13, TSEG2 = 2 */
/* 验证:采样点 = (1+13)/(1+13+2) = 14/16 = 87.5% */
IfxCan_Node_setBitTiming(&g_mcan.canNode[0],
5, /* 预分频值 */
13, /* TSEG1 */
2, /* TSEG2 */
1); /* SJW */
嗯,这里要注意一点:TSEG2不能小于SJW。因为SJW是在TSEG2里“借”时间的,如果TSEG2比SJW还小,那同步就没法做了。我刚开始学的时候,就犯过这个低级错误。
最后总结一下我的配置流程:
- 先确定目标波特率和时钟频率
- 计算预分频值,让总tq数在8~25之间(推荐16~20)
- 根据采样点目标,分配TSEG1和TSEG2
- 设置SJW为1~2个tq
- 用示波器实测,微调参数
记住,理论计算只是第一步。实际调试时,一定要用示波器看波形,确认采样点位置对不对。我每次调完CAN,都会在总线上发一个固定的报文,然后抓波形,数一下采样点落在哪里。这个习惯,帮我避免了好几次线上事故。