做外包项目这些年,我最大的体会是:真正拉开差距的不是代码写得有多花哨,而是把通信方案从需求阶段到现场联调完整走通的能力。上个月刚交付完一个 ESP32 + TWAI (CAN) 通信的硬件与软件设计项目,客户最初的需求只有一句话:"把几个传感器数据通过 CAN 总线发到 PLC,再接收 PLC 的控制命令。"
一句话的需求背后是一整套链路问题:选什么收发器、终端电阻怎么处理、ESP32 内部的 TWAI 控制器怎么配置位时序、驱动 API 怎么初始化、现场联调时波形不对怎么定位。这篇文章就把这个项目的完整过程拆开讲,从硬件电路设计到 ESP-IDF 驱动实现,再到现场调试踩坑,适合正在做 ESP32 CAN 通信、或者接了类似外包项目不知道从哪里下手的嵌入式工程师参考。
1. 外包项目从需求沟通到通信方案选型:为什么是 TWAI 而不是 RS485
1.1 客户现场的真实约束
项目背景是一家自动化设备厂商需要给产线的几个检测工位加装数据采集节点。每个工位有一个 ESP32 为核心的控制器,需要把温湿度、振动、开关量这些数据汇总到主 PLC,同时接收 PLC 下发的启停命令。客户明确要求走 CAN 总线,因为 PLC 侧已经有成熟的 CAN 通信模块,产线上也挂了其他品牌的仪表节点。
接外包项目第一件事绝对不是画板子,而是把需求边界问清楚。我列了一份清单和客户逐项确认:
- 总线速率:客户 PLC 侧配置是 500 kbps,所有节点必须统一。
- 帧格式:现场其他设备用的是扩展帧(29 位 ID),不是标准帧。
- 节点数量:当前 6 个,后续会扩到 12 个左右。
- 总线长度:预估在 20 米以内,线束走线槽,会经过变频器区域。
- 供电方式:每个节点独立 24V 供电,板载 DC-DC 转 3.3V。
这些看似基础的信息,直接决定了后面所有的硬件选型和软件设计。比如"扩展帧"这一点如果不提前问清楚,等代码写了一半才发现 ID 解析对不上,返工成本就很高。
1.2 CAN 方案对比 RS485 的实际优势
客户提出用 CAN 的时候,我也评估过 RS485 方案。RS485 在半双工主从轮询模式下,实现起来确实简单,但对于"多个节点主动上报 + PLC 随时下发命令"这种场景,CAN 的载波监听多路访问和逐位仲裁机制有天然优势。
具体到应用层感受就是:CAN 节点不需要被主机点名才能发言。传感器工位检测到异常可以立刻往总线上发消息,ID 小的帧会优先赢得仲裁,这个优先级是由硬件仲裁决定的,不需要软件调度。对于有实时性要求的工业设备,这个特性非常关键。
RS485 当然也能做多主机通信,但要么用轮询,要么做令牌机制,总线上充满了无效的查询帧,有效带宽利用率低。CAN 在 500 kbps 下虽然是半双工,但最小的数据帧传输时间也就几十微秒,对于传感器周期上报和命令下发完全够用。
1.3 ESP32 在项目中的定位
项目选型阶段还对比过 STM32 和 ESP32。最终选择 ESP32 不是因为 CAN 能力比 STM32 强多少,而是因为这个项目除了 CAN 通信,还需要 WiFi 配置参数、蓝牙调试接口和本地 LCD 显示。ESP32 一颗芯片全搞定,不需要额外挂 WiFi 模组。
关于 TWAI 这个名称,很多新手会陌生。TWAI 就是 Two-Wire Automotive Interface,是乐鑫对 CAN 控制器的内部叫法,协议层面兼容 ISO 11898-1 标准。在 ESP32 数据手册里你找不到"CAN"这个表述,都叫 TWAI。ESP32 芯片内部已经集成了 TWAI 控制器,但控制器输出的是 TX/RX 逻辑电平,不是差分信号,所以外部必须再接一颗 CAN 收发器芯片。
2. 硬件电路:收发器芯片、终端电阻、保护电路那些容易做错的决定
2.1 收发器选型:3.3V 逻辑芯片是首选
ESP32 是 3.3V IO,CAN 收发器的选择直接影响电路复杂度。市面上常见的收发器芯片主要分两类,我整理了一个对比表:
| 芯片型号 | 供电电压 | 逻辑电平 | ESP32 直连兼容性 | 备注 |
|---|---|---|---|---|
| TJA1050 | 5V | RXD 输出接近 5V | 不兼容,需电平转换或分压 | 经典型号但已显得过时 |
| MCP2551 | 5V | RXD 输出接近 5V | 不兼容,需处理电平 | 量大但 ESP32 不建议直接接 |
| SN65HVD230 | 3.3V | 3.3V | 兼容 | 老牌 3.3V 收发器,好用 |
| TJA1051T/3 | 3.3V | 3.3V 逻辑 | 兼容 | 电平设计更省心 |
| ISO1042 | 5V/3.3V 可选 | 隔离输出 | 兼容,需配隔离电源 | 成本高但抗干扰最好 |
这个项目我选了 SN65HVD230。理由很简单:3.3V 供电、3.3V 逻辑电平,和 ESP32 的 GPIO 直接对接,不需要在中间加电平转换电路。虽然这芯片上市有年头了,但稳定性和供货都成熟,外包项目最怕的就是原理图没问题结果芯片交期三个月。
ROS 边还有一颗内置收发器且支持 CAN?的模组不是此项目的用途,这里不展开。
2.2 终端电阻:不是每个节点各放一个
CAN 总线终端电阻是外包项目里最容易出问题的地方。按 ISO 11898 规范,120Ω 终端电阻应该放在总线物理距离的两端,而不是每个节点都放一个。如果每个节点板上都贴了 120Ω 电阻,节点一多并联电阻值就会远低于 60Ω,总线信号幅度会被吃掉,波形畸变,通信距离和稳定性直线下降。
我在这块板子上的处理方式是:每个节点预留两组 1206 封装电阻位,一个 0Ω 跳线电阻位。默认生产时不贴终端电阻,只有确认该节点处于总线物理末端时,产线工人才会焊上 120Ω 电阻。这样既保证了灵活性,又避免非末端节点因为误贴电阻影响整条总线。
如果要求更严格的抗干扰性能,我会用分体式终端:用两个 60Ω 电阻串联,中点通过 4.7nF 电容接地。两个 60Ω 串联对差分信号来说等效阻抗还是 120Ω,但中点电容给共模噪声提供了一条低阻泄放路径,能明显降低总线辐射和共模干扰。这个项目因为客户预算和交期原因没有用分体式方案,但后续类似项目我会优先推荐。
2.3 保护电路:TVS 和共模电感不能省
工业现场不是实验室,CANH 和 CANL 两条线要沿着线槽和变频器动力线一起走几十米,感应出来的浪涌电压轻轻松松超过收发器极限。SN65HVD230 这类收发器虽然内部有基本的 ESD 保护,但针对 IEC 61000-4-2 级别的静电放电和感应浪涌还是不够。
我在这块板子上加了三级防护,从连接器往里依次是:
- PESD1CAN,一颗专门为 CAN 总线设计的双向 TVS 二极管,放在 CANH/CANL 之间,钳位差模过压。
- 共模电感,具体型号是 ACT45B-510-2P,两个绕组分别串在 CANH 和 CANL 上。共模电感对差分信号几乎无影响,但对共模噪声呈现出高阻抗,能显著抑制变频器产生的共模干扰。
- 收发器芯片的 Rs 引脚,SN65HVD230 的斜率控制引脚。这个引脚悬空或接高电平是待机模式,接低电平是高速模式。我们直接用 10kΩ 电阻到地,让它工作在高速模式。
2.4 PCB 布局上的两个细节
PCB 布局对 CAN 通信的影响经常被低估。这块板子第一版打样回来后,CAN 通信在桌面测试正常,但装进设备外壳后就出现偶发错误帧。后来定位到一个重要原因:收发器和排针连接器之间的走线太长,且没有做阻抗控制。
修改后的布局约束有两点:一是 CAN 收发器尽量靠近连接器放置,收发器到连接器的走线控制在 10mm 以内;二是 CANH/CANL 两条走线要等长成对走,线宽不低于 10mil,间距尽量拉开,避免两条线之间形成不必要的耦合电容。电源去耦电容 100nF 紧贴收发器电源引脚放置,这点很多参考设计都会画,但真正摆件时会因为空间紧张把电容放远了,效果差很多。
3. TWAI 总线机制与位时序:不理解透这些,代码写了也会翻车
3.1 TWAI 控制器与 CAN 协议的对应关系
ESP32 内部的 TWAI 控制器实现的是 CAN 协议的数据链路层和物理层中的编码部分,它负责帧的封装、CRC 校验、位填充、仲裁和错误处理。所以我们写代码的时候不需要自己算 CRC、不需要手动做位填充,驱动层封装好了这些底层细节。
但理解帧结构仍然必要,因为 debug 的时候需要读波形、看 CAN 分析仪上的报文解析,最终都要回到帧结构上。一个标准数据帧从左到右依次是:SOF 显性起始位、11 位 ID、RTR 位、IDE 位、DLC 数据长度码、最多 8 字节数据、15 位 CRC、CRC 定界符、ACK 槽、ACK 定界符、EOF 帧结束和 IFS 帧间隔。
这里尤其要说 ACK 槽。发送节点在 ACK 槽阶段输出的是隐性电平,总线上任意一个节点只要正确收到了这个帧,就会在这个位置拉一个显性电平来应答。如果总线上没有其他节点,或者接收节点校验失败,发送节点看到 ACK 槽还是隐性电平,就会报 ACK 错误。这个特性在调试时非常有用。
3.2 位时序的组成和采样点
CAN 总线上每个 bit 的时长在 500 kbps 下就是固定 2μs,但一个 bit 内部被分成了若干时间量子 TQ。这些 TQ 分成四段:同步段、传播段、相位缓冲段 1、相位缓冲段 2。同步段固定 1 个 TQ,用来让总线上的节点对齐边沿。
采样点位于相位缓冲段 1 和相位缓冲段 2 的交界处。理论上位时间长度的 50% 到 90% 之间都可以采样,但工程上推荐把采样点放在 75% 到 85% 附近。原因很简单:采样点太靠前,总线传播延迟和信号上升下降时间带来的边沿偏移会让采样误判;采样点太靠后,留给相位缓冲段 2 的余量就不够,时钟偏差稍大就会采到下一个 bit 里。
3.3 500 kbps 位时序的推导过程
这块板子的 ESP32 APB 时钟是 80 MHz。500 kbps 意味着每个 bit 所占用的周期数是 80 MHz / 500 kHz = 160 个时钟周期。TWAI 控制器通过预分频器 BRP 把 APB 时钟再分频得到 TQ 时钟,分频后一个 TQ 对应若干个 APB 周期。
我实际配置采用了 brp = 8,这样 TQ 频率就是 10 MHz,即每个 TQ 100ns。每个 bit 需要 2μs / 100ns = 20 个 TQ。合理的分配是:同步段 1 TQ,传播段和相位缓冲段 1 合计 14 TQ,相位缓冲段 2 是 5 TQ。采样点位置就是 (1 + 14) / 20 = 75%,正好落在推荐区间。
对应到 ESP-IDF 驱动代码里,timing_config 结构体的目标是做同样的参数设置。不过实际操作时我在代码注释里保留了推导过程,方便后续维护的人理解这些数字怎么来的,而不是面对一个 magic number 不敢动。
3.4 仲裁机制理解:ID 越小为什么优先级越高
CAN 总线仲裁机制是另一个容易踩坑的知识点。多个节点同时发送时,总线上的电平是线与关系:显性电平覆盖隐性电平。每个节点在发送 ID 的每一位时都会回读总线状态,如果自己发的是隐性位但检测到总线上是显性电平,就立即停止发送,转为接收状态。
这意味着 ID 数值小的帧在最高位第一次出现差异时就会赢得仲裁。高优先级消息永远不需要等待总线空闲才能抢到发送权,这也是 CAN 被评为"确定性强"的原因。实际项目中应该把急停、报警这类需要低延迟的消息分配较小的 ID,把周期上报的采集数据分配较大的 ID。这个原则写在交付文档里,客户后续自己加节点时也会遵循。
4. ESP-IDF 驱动层实现:从初始化到收发一体的完整代码路径
4.1 引入 TWAI 驱动头文件与引脚规划
这个项目基于 ESP-IDF v5.x 开发。虽然 Arduino 框架也有 TWAI 库,但我最终选择 ESP-IDF,原因有三个:驱动 API 更贴近 TWAI 控制器本身、错误状态反馈更细、长期运行的稳定性更好。外包项目交付之后客户要持续维护,IDF 工程对后续裁剪也更友好。
引脚规划上,ESP32 的 TWAI 控制器允许把 TX 和 RX 映射到任意 GPIO。很多参考设计默认用 GPIO 21 做 TX、GPIO 22 做 RX,但这个项目里这两个引脚被 LVGL 屏幕占用了,所以我改成了 GPIO 4 和 GPIO 5。这里的一个注意事项是:RX 输入引脚不要和板上其他高速翻转信号离得太近,避免串扰导致误触发。
4.2 驱动初始化的完整流程
初始化的流程分三步:先配置驱动三个结构体,然后调用 twai_driver_install 安装驱动,最后 twai_start 启动控制器。
#include "driver/twai.h" #define TWAI_TX_PIN GPIO_NUM_4 #define TWAI_RX_PIN GPIO_NUM_5 void twai_init(void) { twai_general_config_t g_config = { .mode = TWAI_MODE_NORMAL, .tx_io = TWAI_TX_PIN, .rx_io = TWAI_RX_PIN, .clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, .tx_queue_len = 10, .rx_queue_len = 20, .alerts_enabled = TWAI_ALERT_ERR_PASS | TWAI_ALERT_BUS_OFF | TWAI_ALERT_RX_DATA | TWAI_ALERT_ERR_ACT | TWAI_ALERT_ERR_PASS, .intr_flags = ESP_INTR_FLAG_LEVEL1, }; twai_timing_config_t t_config = { .brp = 8, .tseg_1 = 14, .tseg_2 = 5, .sjw = 1, .triple_sampling = false, }; twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); twai_driver_install(&g_config, &t_config, &f_config); twai_start(); }需要说明的是,上面 t_config 的 brp、tseg_1、tseg_2 是直接结构体赋值的方式。如果不想手动算,ESP-IDF 也提供了现成的宏,比如 TWAI_TIMING_CONFIG_500KBITS()。但手动算一次能帮助理解位时序,而且某些定制场景下宏不一定匹配实际晶振,手动配置更可把控。
4.3 发送函数:从业务数据到 CAN 报文
发送的核心是把业务数据装进 twai_message_t 结构体。这个结构体里的 identifier 字段是 ID,data_length_code 是数据长度,data 是数据载荷。因为客户现场用的是扩展帧,必须显式设置 TWAI_MSG_FLAG_EXTD 标志位。
typedef struct { uint16_t temperature_x10; uint8_t vibration_level; uint8_t switch_state; uint8_t reserved[4]; } sensor_payload_t; void send_sensor_report(const sensor_payload_t *payload) { twai_message_t msg = {0}; msg.identifier = 0x18FF50E5; msg.flags = TWAI_MSG_FLAG_EXTD; msg.data_length_code = 8; memcpy(msg.data, payload, sizeof(sensor_payload_t)); if (twai_transmit(&msg, pdMS_TO_TICKS(50)) != ESP_OK) { ESP_LOGE("TWAI", "transmit failed, err %d", twai_get_last_error()); } }发送时有一个特别容易被忽视的地方:twai_transmit 的阻塞时间。如果总线上有持续的错误状态,发送队列满了之后 transmit 会一直阻塞直到超时。我把超时时间设置成略大于一个最坏情况下的发送周期,而不是填 portMAX_DELAY。这样即使总线异常,业务任务也不会被彻底卡死,还能通过日志把失败信息上报。
4.4 接收任务与过滤规则
接收端我单独建了一个 FreeRTOS 任务,阻塞在 twai_receive 上。这里比较关键的一点是消息过滤配置。客户 PLC 下发命令的 ID 只有两个:0x18FF2233 是启停控制,0x18FF2244 是参数设置。如果我使用 ACCEPT_ALL 会收到总线上所有节点的帧,对软件来说是浪费资源,也增加了解析负担。
正确的做法是配置验收滤波器,让控制器只把关心的帧放进 RX 队列:
twai_filter_config_t f_config = { .acceptance_code = (0x18FF2000 << 3), // 只校验高16位,匹配 0x18FF2xxx 范围 .acceptance_mask = 0x0007FFFF, .single_filter = true, };关于过滤器的位运算,我强烈建议拿到 ESP32 技术参考手册对照着算一遍。不同 IDF 版本对 code 和 mask 的移位处理可能不同,网上很多帖子给出的公式已经过时。最可靠的验证方式是配置完之后用上位机发几种不同 ID 的帧,观察中断触发情况,以实际测试结果为准。
void twai_rx_task(void *arg) { twai_message_t msg; while (1) { if (twai_receive(&msg, portMAX_DELAY) == ESP_OK) { if (msg.identifier == 0x18FF2233) { handle_start_stop_cmd(msg.data[0]); } else if (msg.identifier == 0x18FF2244) { handle_parameter_cmd(msg.data); } } } }4.5 错误状态监控与总线恢复
CAN 控制器内部有两个错误计数器,发送错误计数器 TEC 和接收错误计数器 REC。错误计数超过一定阈值,节点会进入错误被动状态,再严重会进入 Bus-Off。这个状态不能等到通信完全断了才被业务代码感知,我注册了 alerts 来监控。
void twai_alert_task(void *arg) { uint32_t alerts; while (1) { if (twai_read_alerts(&alerts, pdMS_TO_TICKS(1000)) == ESP_OK) { if (alerts & TWAI_ALERT_ERR_ACT) { ESP_LOGW("TWAI", "back to active"); } if (alerts & TWAI_ALERT_ERR_PASS) { ESP_LOGW("TWAI", "enter error passive"); } if (alerts & TWAI_ALERT_BUS_OFF) { ESP_LOGE("TWAI", "bus off detected, recovering"); twai_initiate_recovery(); } } } }Bus-Off 恢复这个点,现场调试阶段大概率会用上。总线短路、强干扰或者波特率配置错误都可能导致节点进入 Bus-Off,此时节点不会自动恢复发送,必须调用 twai_initiate_recovery 或者重新 stop/start。最好的做法是像上面这样用一个独立任务监控 alert,自动完成恢复流程,同时把状态推到日志里方便远程排查。
5. 联调排错:示波器波形、ACK 错误和总线关断的真实排查过程
5.1 第一版测试板通电:一连串 ACK 错误
硬件和驱动都写完,最期待的联调环节来了。我拿来两块 ESP32 开发板,各插一个 SN65HVD230 收发器模块,用两根杜邦线把 CANH 和 CANL 对接,以为马上就能看到数据互传,结果控制台刷出来一屏 ACK 错误。
当时第一反应是收发器模块坏了,或者杜邦线接触不良。用万用表量了 CANH 和 CANL 之间的电阻,发现是无穷大。问题出在我图省事,直接把两块开发板的收发器模块对接,但两个模块之间没有接终端电阻。CAN 总线上的 ACK 机制依赖收发器的差分输出驱动能力,没有终端电阻时,显性电平的差分幅度可能达不到接收节点的判定阈值,于是接收节点没有正确应答,发送节点就报 ACK 错误。
解决办法是在收发器模块两端分别并联了 120Ω 电阻。这里并联上电阻后 CANH/CANL 之间静态阻值变成了 60Ω,说明终端配置已正确。这个经历也验证了前面硬件设计里在 PCB 上做可选电阻的决策是必要的。
5.2 用示波器看 CAN 波形:三件必查的事
接入终端电阻后,两板通信正常了,但后续第三块板加入总线后又开始出问题。这次我直接用示波器挂在 CANH 和 CANL 之间看差分波形,排查分三步。
第一步看幅值。正常显性位的差分电压应该在 1.5V 到 3V 之间,如果看到的幅值偏低,大概率是终端电阻并联过多或者总线负载过重。第二步看位时间。500 kbps 下每个 bit 时长 2μs,示波器调到 1μs/格就能清晰看到帧起始 SOF 之后一长串方波,如果每个 bit 宽度不对,那就是位时序配置和实际晶振频率不匹配。第三步看边沿。上升沿和下降沿如果有明显振铃,说明终端电阻匹配不好或者线缆过长。
排查时发现第三块板导致的异常是典型的边沿振铃。原因是第三块板子没有终端电阻,但连接线又比较长,形成了反射。把板子上的可选终端电阻焊上后,振铃明显消失。
5.3 "我能发不能收"的过滤器坑
联调中还遇到一个看似诡异的问题:A 板发消息,B 板收不到,但 A 板自己的 CAN 分析仪能收到。一开始怀疑是 B 板硬件问题,换一块板还是不行。后来检查 B 板的过滤配置,发现验收掩码算错了,把关心的 ID 也给过滤掉了。
这个排查过程走了不少弯路,但教训很深刻:当软件里没有手动配置过滤时,一定要用 ACCEPT_ALL 先验证物理链路,确认链路通了再逐步收缩过滤范围。物理层通不通和过滤配得对不对,是两个层面的问题,混在一起排查会浪费大量时间。我当时先把 B 板过滤临时改成 ACCEPT_ALL,马上就能收到 A 板消息,证明链路没问题,再回头对着手册重新算过滤掩码。
5.4 工业现场偶发错误帧:共地问题浮出水面
实验室测试一切正常,到了客户现场装上去,问题开始变得"玄学":通信几分钟正常,然后突然出现一批错误帧,过一会儿又自己恢复。这种偶发性问题最难查,幸好我在代码里接了报警日志,从时间戳和错误计数变化找到了规律:错误帧总是出现在现场某台大功率电机启动的瞬间。
示波器测量 CANH 和 CANL 对地的共模电压,发现电机启动瞬间共模电压尖峰接近 30V。虽然差分信号看起来正常,但过高的共模电压超过了收发器输入共模范围,导致接收端误判。根本原因不是 CAN 本身抗干扰差,而是分站设备和 PLC 之间没有可靠共地,地电位差在电机启动瞬间被拉大。
解决这个问题的长期方案是用隔离收发器,类似 ISO1042,配合板载隔离电源,让 CAN 总线侧与 ESP32 的电源地彻底分开。短期措施是检查现场接线,把各节点的 24V 电源负极可靠接在一起,减小地电位差。最终客户选择了暂不改硬件,由现场施工统一整改地线,同时我也在交付文档里写明了后续升级隔离方案的注意事项。
5.5 周立功 USBCAN 在 Windows 11 下的驱动兼容问题
程序调试阶段客户现场主要用了周立功 USBCAN 分析仪抓总线报文。对方工程师反馈在 Windows 11 笔记本上,分析仪软件识别不到设备,反复提示驱动异常。这个问题和 ESP32 本身无关,但非常消耗联调时间。
周立功的早期驱动版本确实存在 Win11 兼容性不佳的情况,我的建议是去官网下载最新版本的驱动和上位机软件,如果装了旧版本驱动,需要先彻底卸载再装新版,否则设备管理器里可能会残留一个带感叹号的设备。如果换了新版驱动仍然不行,试试在设备管理器里手动更新驱动、指向安装包里的驱动目录,通常能解决。
6. 复盘:如果再做一次,我会改掉的三个决定
6.1 从第一天就加入自环回自测代码
这个项目一开始没有做自测代码,硬件回来后第一件事就是接外部设备联调,结果链路不通时很难判断是 ESP32 这边初始化失败还是外部设备的问题。如果一开始就在固件里留一个 TWAI 自环回模式,也就是驱动配置里把 mode 设成 TWAI_MODE_SELF_TEST,收发器外部无需接任何设备就能验证控制器本身工作是否正常。
后续我会在所有交付项目里默认加这个自测固件。出厂前烧录自测 firmware,硬件测试通过后再烧正式 firmware。这个习惯能省下大量现场排障时间。
6.2 预留 CAN FD 的扩展空间
项目开始时客户明确只要经典 CAN 2.0 帧,所以收发器选型没有考虑 CAN FD。复盘时候我觉得,在这种面向未来有升级可能性的项目里,物理层芯片可以直接选支持 CAN FD 的型号,比如 MCP2562FD 或者 TJA1044。经典 CAN 和 CAN FD 的物理层是兼容的,只是波特率可能不同。
选支持 FD 的收发器成本增加很少,但后续客户想把总线速率提上去、传输更大的诊断数据时,就不需要改硬件重新过认证了。当然,要真正支持 CAN FD 帧,ESP32 芯片本身也得支持,所以在和客户谈需求时还是要讲清楚这一点,避免对方以为换颗收发器就能升级。
6.3 需求阶段就把 DBC 文件或者至少 ID 清单落到纸面
项目中期出现过一次 ID 分配冲突:客户后来新增了一个第三方节点,占用了我们已经在用的 ID,导致两套数据在总线上错乱。原因是需求阶段客户只口头说"ID 你们自己规划",没有形成统一的 ID 分配表。
后来的解决方式是我起草了一份 ID 分配表,规定了每个功能域的 ID 范围、帧周期、DLC 长度、字节序,让客户确认后作为项目交付物之一。这看起来是文档工作,但对嵌入式通信项目来说,它就是协议本身。没有这份东西,每个人按自己的习惯发数据,联调就是互相猜谜。
这个项目交付后客户又问过我可不可以把两个节点通信距离拉长到 100 米,我说 500 kbps 下这个距离比较勉强,建议要么降速到 125 kbps,要么加中继。最后客户选择了降速,改动成本很小,位时序配置改一下就行。这也是当初把位时序推导过程写清楚的好处,改起来心理有底。