尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Android车机CAN开发全链路:SocketCAN、DBC、ISO-TP与UDS实战

Android车机CAN开发全链路:SocketCAN、DBC、ISO-TP与UDS实战 在Android车机上做CAN开发和我以前在MCU上写驱动完全是两个物种。MCU时代你把报文往寄存器里一丢中断来了读一下就行到了Android这一层内核已经帮你把CAN总线抽象成了SocketCAN网络接口can0、vcan0都是活生生的网卡收发得用socket解析得靠DBC远程诊断得搬出ISO-TP和UDS。这篇文章从实际项目角度把这条链路从头到尾捋一遍覆盖CAN、SocketCAN、CAN FD、DBC、ISO-TP与UDS这几块内容Android车载应用工程师和系统工程师都能在里面找到能直接拿去用的东西。1. 从can0到诊断仪Android车载CAN这条链路到底串了什么1.1 一条报文从ECU出来到App界面上中间隔了哪些层很多刚接触车载的Android工程师最容易懵的一点是不知道一条CAN报文究竟经过了什么路径才变成界面上的车速、转速。我的建议是先把这条链路拆成四层后面所有问题都能往这几层里归类。第一层是物理层。ECU里的CAN收发器把逻辑0和1转换为CAN_H与CAN_L两根线上的差分电平显性位0对应CAN_H约3.5V、CAN_L约1.5V隐性位1对应两根线都约2.5V。车厂线束里CAN_H和CAN_L一定是双绞线目的就是抗共模干扰。第二层是内核层。CAN控制器收到完整报文后通过驱动把数据交给Linux内核的CAN子系统统一挂载成一个网络接口也就是你看到的can0、can1。这一步是整个SocketCAN模型的核心CAN在Linux里不是字符设备而是网络设备。第三层是传输通道。应用层想要收发报文不再操作串口或者寄存器而是创建socket(AF_CAN, SOCK_RAW, CAN_RAW)然后bind到can0上通过read/write或者send/recv收发。可以把它理解成用UDP socket收发网络包只不过这个网络是CAN总线。第四层是协议与业务解析。普通信号报文直接拿DBC文件解码如果是诊断相关的数据报文还要经过一层ISO-TP协议做拆包组包再到UDS层解析服务ID、子功能、NRC错误码。这也是为什么很多人拿到诊断仪抓包数据一头雾水——同一串十六进制数据不同层看意义完全不同。1.2 应用工程师与系统工程师的分工不太一样我见过不少团队在这一块是脱节的。应用工程师拿不到can0的读写权限只能靠系统服务转发系统工程师写完SocketCAN收发的底层服务又不太清楚上层DBC该怎么设计。从Android项目的实际分工看系统工程师关心的是内核有没有开CONFIG_CAN、设备树里CAN控制器有没有配好、can0能不能正常up、App进程有没有CAP_NET_RAW权限、需不需要用BCM模块做周期发送。应用工程师关心的则是拿到一个CAN ID之后怎么解析出信号、诊断服务的请求响应怎么拼、0x19读出来的DTC怎么翻译成故障文字。但这套技术栈有个特点边界没有想象中那么清晰。调试一个UDS刷写问题应用工程师必须懂ISO-TP流控系统工程师也得知道DBC里的字节序坑。所以我建议双方都通读一遍下面这几个核心知识点至少在看日志、抓包分析时能对上话。1.3 学习顺序的建议如果是从零开始我的学习顺序是先CAN协议基础再SocketCAN实操然后是DBC解析最后才是CAN FD和ISO-TP/UDS。不要一上来就看UDS刷写流程底层帧都不认识上层协议就是空中楼阁。下面几章就按这个顺序展开。2. CAN底层协议ID、仲裁和位时序排障时全都要用2.1 报文格式和ID的误区ID不是地址CAN 2.0A标准帧的组成是SOF1位、ID11位、RTR、IDE、r0、DLC4位、数据场0到8字节、CRC15位、ACK、EOF、IFS。CAN 2.0B扩展帧的区别是ID扩展到29位用IDE位来区分标准帧和扩展帧。在SocketCAN里cansend发扩展帧时ID会带EFF标记比如cansend can0 18FF50E5#11223344这个18开头的5字节ID就是扩展帧。经常有人问CAN报文中ID号代表什么。这里必须先纠正一个误区CAN的ID不是节点的地址。它有两层作用第一是标识报文内容比如0x123可能是车速0x456可能是转速第二是决定总线仲裁优先级。一个ECU可以发多个ID多个ECU也可以发同一个ID比如故障码广播这和地址-设备的对应关系完全是两码事。2.2 仲裁、位填充和位时序排障的底层逻辑仲裁是CAN总线最核心的机制。总线空闲时多个节点同时发送从SOF之后开始逐位比较显性位是0隐性位是1显性位会覆盖隐性位。所以发送节点如果发了一位显性却在总线上读到隐性说明有别人在发更优先的报文自己立刻退出。结论就是ID数值越小优先级越高。这也是为什么动力相关的报文通常分配小ID舒适娱乐类分配大ID。位填充是很多人忽略的机制。CAN协议规定连续发送5个相同电平之后必须插入一个反相位的位目的是保证总线上有足够多的跳变沿让各节点的PLL能持续同步时钟。所以实际传输的位长并不是理论帧长度平均会有一定膨胀。做总线负载估算时不能简单按ID DLC 数据的位数算要把填充位算进去。位时序是配置控制器时最烦的部分。每一个位时间由同步段SS、传播段PTS、相位缓冲段PSB1和PSB2组成采样点就在PSB1和PSB2交界处。假设位时间为16个TqSS1、PTS3、PSB18、PSB24采样点就是(138)/1675%。经典CAN的采样点一般建议落在75%到87.5%之间我常用80%左右CAN FD的数据段采样点另说后面单独讲。2.3 波特率与总线负载的经验配比车载常见波特率大概是这么分布的125kbps用于低速车身控制250kbps用于舒适系统500kbps用于动力系统1Mbps在某些网关和诊断链路上也有。诊断走的大多是250k或500k。用SocketCAN起一个500k的接口就是一条命令ip link set can0 up type can bitrate 500000这里有个容易踩的坑如果总线上已经跑着其他节点你的can0波特率配错了不会立刻报错但总线上会出现大量错误帧candump里看到一堆(error frame)。排障时先确认波特率再查其他问题。总线负载率我一般控制在线缆设计建议的范围内经典CAN不超过50%CAN FD可以稍微高一些。负载率的估算公式可以简化为总线负载率 ≈ (每秒报文数 × 平均帧位长) / 波特率比如500kbps总线上每秒有2000帧标准帧平均帧长按130位算负载率就是(2000×130)/50000052%。负载过高时高优先级小ID报文不一定受影响但低优先级大ID报文延迟会非常明显UDS诊断经常超时。3. SocketCAN在Android上的落地与调试工具链3.1 内核侧怎么确认CAN接口真的出来了Android车机上跑SocketCAN前提是内核把CAN子系统编进去。排查第一步是看内核配置很多板子支持读取zcat /proc/config.gz | grep CAN正确输出里应该有CONFIG_CANy、CONFIG_CAN_RAWy如果需要BCM周期发送还得有CONFIG_CAN_BCMy。如果板子内核没开只能重编内核没有捷径。CAN控制器有两种接入方式。一种是SoC内部自带的CAN控制器设备树里配好引脚、时钟、中断就行上电后出现can0另一种是外接芯片比如通过SPI接MCP2515这类控制器设备树里要多配一个SPI设备节点驱动加载成功后同样注册成can0。遇到明明连了CAN设备但ifconfig看不到can0的情况先看内核日志有没有报驱动probe失败再翻设备树配置。3.2 Android上缺can-utils怎么办三板斧与替代方案在PC Linux上调试CAN很舒服can-utils工具集齐全。到Android上会发现系统精简严重candump、cansend都不存在。我的做法是交叉编译一份静态链接的can-utilspush到/data/local/tmp下直接跑。注意必须静态编译否则Android和PC的linker不兼容跑起来会报找不到共享库。常用三板斧# 起接口500k ip link set can0 up type can bitrate 500000 # 抓包只显示不退出 candump can0 # 发一帧标准帧ID0x123数据长度4字节 cansend can0 123#DEADBEEFcandump输出里每一行是timestamp can0 123#DEADBEEF这种格式timestamp是秒微秒。调试时我会加-n 100限帧数避免刷屏停不下来只关心某个ID就用-i 123或者用-m做掩码过滤。这些工具虽然小但实际排查问题比什么分析软件都直接。如果板子上连ip link都不支持完整参数Android的toybox对网络接口支持有限可以尝试用ifconfig can0 up配合busybox里的工具但最终建议还是把完整的iproute2静态编译进去。3.3 App层收发raw socket和CAN_RAW_FILTER真正进入应用开发核心是创建一个PF_CAN的raw socket。伪代码逻辑大概是int s socket(PF_CAN, SOCK_RAW, CAN_RAW); struct sockaddr_can addr; addr.can_family AF_CAN; addr.can_ifindex if_nametoindex(can0); bind(s, (struct sockaddr *)addr, sizeof(addr)); // 只接收ID0x123和0x456的帧 struct can_filter rfilter[2] { { .can_id 0x123, .can_mask CAN_EFF_MASK }, { .can_id 0x456, .can_mask CAN_EFF_MASK }, }; setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, rfilter, sizeof(rfilter)); // 收发 struct can_frame frame; read(s, frame, sizeof(frame));这里最烦的是权限。raw socket要求进程有CAP_NET_RAW能力普通第三方App默认没有直接打开会报Operation not permitted。车机项目里通常的解法是把CAN收发做成一个系统服务App通过AIDL/Binder调用或者把App签名成系统应用在privapp-permissions里授予权限。另一个坑是CAN_RAW_FILTER的mask语义和很多人直觉相反。mask位为0表示这一位必须精确匹配为1表示这一位不关心。如果你写can_id 0x100, can_mask 0x7FF结果是什么都过滤不掉。想要只匹配0x100mask应该取CAN_EFF_MASK0x1FFFFFFF。3.4 vcan虚拟接口不上车也能做的本地仿真开发阶段不可能总在实车上调试vcan是救命的。Linux和很多Android板子内核都支持vcan模块ip link add dev vcan0 type vcan ip link set up vcan0vcan不需要真实CAN硬件所有发进去的报文都会回环。配合can-utils可以在车机上模拟一个ECU节点vcan0上跑一个脚本周期性发车速报文App这边收数据、解析DBC、刷新UI这一整套流程在办公室就能联调。我习惯在vcan环境里先把协议栈跑稳定再拿到实车上验证能节省大量台架时间。4. CAN FD带来的不只是带宽还有一批新坑4.1 FD帧格式的变化BRS位和64字节数据场CAN FD全称CAN with Flexible Data-rate和经典CAN最大的区别有四个数据场最多64字节、支持仲裁段和数据段双速率、CRC增强、新增FDF和BRS位。经典CAN一帧最多8字节数据传一个192字节的UDS响应要拆成24帧以上CAN FD一帧就能装64字节同样数据3帧搞定效率和实时性完全不在一个量级。在SocketCAN里CAN FD帧用struct canfd_frame表示和can_frame最大的差异是data数组长度是64。如果还是用旧的struct can_frame去读收到的FD帧会被截断这个坑特别隐蔽App层一定要在代码里区分CAN_MTU和CANFD_MTU。4.2 DLC到字节数的映射这个表必须背下来DLC字段只有4位能表示0到15但数据字节数不止0到8这9种。CAN FD引入了一张跳跃映射表DLC数据字节数经典CAN数据字节数CAN FD0-80-80-89不支持1210不支持1611不支持2012不支持2413不支持3214不支持4815不支持64很多解析工具在显示FD报文时DLC是9到15实际数据长度必须查表直接拿DLC当字节数就错了。比如DLC13CAN FD真实长度是32字节不是13字节。4.3 双速率和采样点为什么在台架上好好的换台车就误码CAN FD允许仲裁段用低速比如500kbps数据段用高速比如2Mbps。启动接口时是这样的ip link set can0 up type can bitrate 500000 dbitrate 2000000 fd on数据段速率提上去之后对物理层的要求也变了。最典型的问题就是采样点配置不当导致误码。经典CAN采样点放在80%通常没问题CAN FD的数据段是高速段位时间短线束上的反射和振铃对采样点更敏感我实际项目中踩过同一套代码台架上稳定跑换到另一台车上数据段开始报CRC错误和位错误。排查链路是有顺序的先量CAN_H和CAN_L波形看差分信号摆幅够不够再检查终端电阻确定是60欧左右最后才是调采样点。CAN FD数据段采样点我会先给70%到80%根据误码率再微调。采样点在位时序里偏前或偏后对付不同的线束阻抗和延时效果差别很大不能一套配置走天下。4.4 经典CAN和CAN FD混合部署的兼容策略实际车上不是所有节点都支持CAN FD经常出现一个网段里既有经典CAN节点又有FD节点的情况。需要注意经典CAN控制器收到FD帧会因为识别不了FDF位而报格式错误然后发错误帧干扰总线。CAN FD控制器可以收发经典帧但反过来不行。解决方案一般是三种第一整个网段统一换FD控制器第二网段内只发经典帧把FD能力留给诊断刷写通道第三如果硬件支持FD tolerant模式可以让经典节点忽略FD帧而不是报错。我见过不少量产项目采用第二种正常运行全走经典帧刷写或大块数据传输时切到FD速率提升非常明显。5. DBC文件怎么把总线bit流翻译成业务信号5.1 读DBC文件先看BU_、BO_、SG_这三样DBC文件是CAN总线信号的字典网上流传的和厂里给的格式大体一致。一段典型的报文定义长这样VERSION BS_: BU_: ECU1 ECU2 BO_ 123 VehicleInfo: 8 ECU1 SG_ VehicleSpeed : 8|161 (0.1,0) [0|250] km/h ECU2 SG_ Gear : 0|41 (1,0) [0|7] ECU2 VAL_ 123 Gear 0 P 1 R 2 N 3 D 4 S ;BU_是网络节点列表相当于报文从哪个ECU发出BO_定义一条报文格式是BO_ 报文ID 报文名: 报文长度(字节) 发送节点。这里报文ID是十进制数0x123会写成291。SG_定义报文里的信号这是最核心的部分。SG_ VehicleSpeed : 8|161 (0.1,0) [0|250] km/h ECU2这行拆开看8是起始位16是信号长度1是字节序和符号标记1表示小端Intel0表示大端Motorola表示无符号-表示有符号(0.1,0)是因子和偏移实际物理值原始值×因子偏移[0|250]是物理值范围km/h是单位最后的ECU2是接收节点。5.2 大端小端和符号位这是解析出错最多的两个地方热搜词里can 大端小端出现频率一直很高因为这里确实反直觉。以8位信号为例小端Intel格式起始位是该信号最低位LSB所在的bit序号大端Motorola格式起始位是最高位MSB所在的bit序号。同一个信号放在同一字节里小端起始位是8假设从byte1开始大端起始位是15。以16位信号横跨byte1和byte2为例格式起始位低字节位置高字节位置Intel小端8byte1低8位byte2高8位Motorola大端15byte2低8位byte1高8位如果你拿大端DBC用 Intel 方式解析数值往往是乱的或者出现强烈跳变。我就见过车速信号在真值附近来回抖查了半天发现是把Motorola当Intel解了。符号位也很关键。SG_行里1表示无符号字段原始值就是非负整数1-表示有符号需要按二进制补码解析。很多温度、角度信号是负的比如SG_ Temp : 0|81- (0.5,-40) [-40|87.5] deg ECU2如果忽略符号标记低温数据全解析成255附近的大数。5.3 工具链怎么选从CANdb到cantools再到程序内解析编辑DBC我用Vector CANdb比较多但它在Linux和Mac上跑不方便所以更多时候用Python的cantools库。解析一行代码import cantools db cantools.database.load_file(vehicle.dbc) msg db.get_message_by_name(VehicleInfo) data {VehicleSpeed: 123.4, Gear: 3} encoded msg.encode(data) decoded msg.decode(bytes.fromhex(D2 04 03 00 00 00 00 00))cantools同时支持从总线log批量解析、生成C源码很实用。Android应用里要解析DBC我通常不引入Python解释器而是把需要的报文和信号信息抽出来在Kotlin/Java里写一个轻量解析器。DBC语法本身不复杂核心就是起始位、长度、字节序、符号、因子、偏移几百行代码足够覆盖90%的场景。关键是要把DBC当配置数据管理版本入库别散落在各个App里hardcode。5.4 没有DBC怎么反推信号先统计周期再找连续区间实车项目往往会遇到拿不到完整DBC的情况只能用总线log反推。我的方法分三步第一步按CAN ID聚合统计每个ID的发送周期周期固定的通常是周期报文周期变化很大的是事件报文第二步找随工况变化的字节或位段比如车速从0到80变化时哪个byte区间在同步变化锁定候选区间第三步根据变化的离散程度推因子和偏移最后用小范围实车数据验证。这个过程很像DEBUG数据逆向初期比较花时间但一旦把常用信号反推出来自己搭一套临时DBC后续联调效率会高很多。6. ISO-TP与UDS远程诊断和刷写到底怎么打通6.1 ISO-TP的四种帧SF、FF、CF、FCISO-TPISO 15765-2是跑在CAN上层的传输协议解决的是一条CAN帧最多8字节但UDS诊断动辄几十上百字节的问题。它有四种帧类型。单帧SF首字节高4位是0低4位是数据长度适用于不超过7字节的请求。比如02 10 03就是单帧长度2字节内容是10 03服务是诊断会话控制。首帧FF首字节高4位是1后12位是总长度适用于大于7字节的响应。经典CAN下FF最多带6字节数据。连续帧CF首字节高4位是2低4位是顺序号。从1开始计数每帧带7字节数据。流控帧FC首字节高4位是3低4位是流控状态0表示Clear To Send继续发1表示Wait等待2表示Overflow溢出。FC里还包含BS块大小和STmin最小发送间隔两个参数。举一个实际例子ECU响应一个UDS服务返回数据总长28字节。28大于7所以发FF先带6字节剩下22字节分4个CF各带7字节、最后不对要算仔细28-62222/73余1所以是FF 4个CF最后一个CF带1字节。发送方发出FF后必须等ECU回FCFC里说CTS才继续发CF。这中间的时序是ISO-TP最容易出问题的地方。6.2 Android上实现ISO-TP的两种路径实现ISO-TP有两条路一是基于SocketCAN内核的raw socket在应用层自己写状态机二是用外接USB-CAN盒子盒子上往往自带协议栈或者有透传模式。自研方案适合系统源码可控制的车机。内核层面can-utils里带了isotpsend和isotprecv可以在命令行先验证ECU通路命令大概是isotpsend -s 7E0 -d 7E8 can0但真正要把它做成一个稳定的系统服务需要自己实现状态机。简化思路是发送侧把上层数据拆包按SF/FF/CF/FC的规则去发接收侧维护一个组装缓冲区收到FF后等CF顺序号错乱或超时要能报错并复位。外接USB-CAN盒子方案更适合后装和调试阶段。盒子上位机软件通常直接支持ISO-TP和UDS开发时先用盒子确认ECU逻辑再回头优化车机端代码能省很多排查时间。也有很多团队用python-can加isotp库在PC上做原型验证先跑通再移植到Android。6.3 UDS常用服务与NRC排查思路UDSISO 14229是应用层诊断协议。请求方通常叫测试仪服务端是ECU。物理寻址时测试仪发到0x7E0ECU从0x7E8回功能寻址发到0x7DF总线上多个ECU都会响应。正向响应报文的服务ID是请求SID加0x40比如请求0x22正常响应是0x62。常用服务一张表过一下SID服务名典型用途0x10诊断会话控制切到扩展会话、编程会话0x11ECU复位刷写完成后复位ECU0x14清除诊断信息清DTC0x19读取DTC信息读故障码、快照、扩展数据0x22按ID读数据读VIN、版本、标定值0x27安全访问种子/密钥解锁0x28通信控制关非诊断报文刷写时用0x2E按ID写数据写指纹、配置0x2FIO控制控制执行器动作0x31例程控制擦除Flash、校验编程依赖0x34/0x36/0x37请求下载/传输数据/退出传输刷写数据通道NRC是负响应码。排查UDS问题时NRC比看波形还好用。最常见的几个0x11 Service Not Supported请求的SID不支持先确认ECU版本。0x12 Sub Function Not Supported子功能号不对比如0x10服务只支持01、02、03你发了04。0x13 Incorrect Message Length: 报文长度不对多发或少发字节都算。0x22 Conditions Not Correct条件不满足比如没满足前置条件就想写数据。注意0x22本身也是一个SID读数据同一个值在不同位置含义完全不同。0x24 Request Sequence Error请求顺序错典型是没做安全访问就直接请求下载。0x31 Request Out Of Range参数范围超限比如擦除的地址不对。0x33 Security Access Denied安全访问被拒通常是没有先请求种子。0x35 Invalid Key密钥算错了。0x36 Exceed Number Of Attempts尝试次数超限连续输错钥匙会被锁一段时间。0x37 Required Time Delay Not Expired延时未到刚解锁完不能立刻再解锁。排障时先看是不是0x24顺序问题再看0x31范围问题最后才怀疑物理层。很多刷不进去的问题最终都挂在顺序和NRC上。6.4 一次UDS刷写流程的时间线把刷写整个流程串起来能帮你理解这些服务是怎么配合的。量产项目里流程可能很长但骨架大致是阶段典型服务目的预编程10 03、85 02、28 03 03进入扩展会话关DTC停非诊断报文进入编程会话10 02让ECU进入可刷写状态安全解锁27 05 / 27 06交换种子和密钥写入身份信息2E F1 xx写入指纹、VIN、软件号等预编程例程31 01 FF 00擦除应用区或执行前置依赖下载数据34 / 36 / 37请求下载分块传数据退出传输校验与复位31 01 / 11校验编程依赖复位ECU启用新程序刷写里有一个非常关键的时间参数叫P2与P2*。默认P2是50msP2*是5000ms。测试仪发出请求后在P2时间内没收到响应要再等P2*如果P2*超时还没响应才算超时。很多自制刷写工具容易把超时设得太短导致明明ECU在擦Flash耗时几秒就判定失败。另外进入非默认会话后测试仪要周期性发0x3E测试仪在线报文防止会话超时回退。S3Server超时常见是5秒我会按2秒周期发0x3E留足余量。0x19服务偶尔也要发用来确认DTC有没有被清掉、刷写前和刷写后的DTC状态是否正常。7. 实车联调中真正值得记录的细节与坑7.1 物理层问题从电压到终端电阻别总怀疑协议栈联调时遇到命令发了没响应90%不是UDS协议栈的问题而是物理层就没通。我带队排障时第一件事是拿示波器或万用表量CAN_H和CAN_L间的电压。正常静止状态下两线电压都应在2.5V附近差分接近0V。发送显性位时CAN_H抬到3.5VCAN_L拉到1.5V差分为2V。如果量到一直是一条线到0V很大概率是共地没接好板子和ECU参考地不一致如果电压没到标准幅值检查收发器供电和总线负载。终端电阻也经常出问题。标准CAN总线两端各一个120欧从中间量进去应该是约60欧。很多开发板为了方便已经板载120欧电阻你再外接一个120欧的调试工具总阻值会变成40欧左右信号反射和幅值都会变差。上车前先确认板子是不是自带终端避免重复接。线束方面CAN_H和CAN_L必须保持双绞不能为了省事单独飞两根长线。CAN FD数据段速率高对线束质量更敏感我见过用两根普通杜邦线跑2Mbps数据段误码率直接高到没法用。7.2 几个值得复盘的坑第一个坑是DBC大端解析错误。现象是速度信号看上去在变但数值乱跳查了很久发现DBC里是Motorola代码里按Intel解。这种问题在应用层很难通过日志发现因为CRC、DLC都是对的只有数值不合理。最终解法是抓一段固定状态下比如P挡怠速的总线log用工具手工解一条已知信号验证坐标别拿动态数据去猜。第二个坑是CAN_RAW_FILTER的mask写反了。我想只收0x123这一个ID结果把mask设成0x7FF最后收到了总线上所有帧。位掩码的语义是1表示忽略、0表示必须匹配和子网掩码的习惯恰好相反。代码里如果有这行建议加注释CAN_RAW_FILTER的mask 0表示匹配该位。第三个坑是安全访问的种子和密钥算法。ISO 14229只定义了交互流程没定义算法密钥计算完全由每家ECU自己定。换一个ECU型号就换一套算法这是正常的别想着用一个通用公式覆盖所有车型。联调时先确认厂家seed的长度是多少有的ECU返回4字节有的返回8字节拼报文时长度错了会直接回NRC 0x13。第四个坑是ISO-TP流控等待。ECU擦Flash期间会回一个FC Wait来拖住你。很多自制协议栈把FC Wait当异常处理直接抛超时导致刷写失败。正确做法是收到Wait就继续等同时打印日志观察状态机状态不要随便把超时时间写死成几十毫秒。第五个坑是刷写完成后忘了验DTC。刷写过程本身成功了但ECU报了新的DTC比如因为通信中断产生的电压故障码不清掉会影响后续生产检测。刷完复位后我习惯读一遍0x19确认DTC状态符合预期再结束流程。7.3 接手新平台或新项目时建议先做这三件事第一件事确认can0是否存在并且能up。内核没开CAN子系统、设备树没配对、权限不够所有上层工作都白搭。先跑通candump can0能看到总线上已有的周期报文证明通路OK。第二件事用vcan搭一套本地仿真环境把DBC解析、UDS会话保持、刷写流程的核心逻辑先在办公室里跑通。实车台架时间宝贵能仿真解决的都不要上车。第三件事把DBC、UDS服务定义、密钥算法、超时参数全部数据化做成配置文件入库管理。这看起来是工程管理问题但实际调试时如果每个人手上DBC版本都不一样排障会变成一场灾难。我现在的习惯是每次联调前先从库里拉最新的DBC跑完后把log、问题、解决方案统一归档。这套流程跑顺之后项目进度会稳很多。
返回列表