戰(zhàn)UDS診斷:從會(huì)話控制到固件刷寫(xiě)全流程)
簡(jiǎn)介PCAN-UDS診斷協(xié)議實(shí)現(xiàn)包面向汽車(chē)電子、嵌入式診斷開(kāi)發(fā)人員提供基于UDS統(tǒng)一診斷服務(wù)標(biāo)準(zhǔn)的8項(xiàng)基本功能覆蓋分配、配置、地址映射配置、信息與通訊等模塊。資源共31個(gè)文件以C頭文件h、靜態(tài)庫(kù)lib和動(dòng)態(tài)鏈接庫(kù)dll為核心配合cpp示例源碼、工程文件vcproj/sln及PDF英文用戶手冊(cè)便于開(kāi)發(fā)者快速集成到PCAN硬件環(huán)境中。壓縮包約2MB結(jié)構(gòu)清晰包含C#、Pascal、VB等多語(yǔ)言接口文件。目前已有2073人學(xué)習(xí)下載。借助該資源開(kāi)發(fā)人員可直接調(diào)用標(biāo)準(zhǔn)接口實(shí)現(xiàn)診斷會(huì)話控制、故障碼讀取等常用UDS服務(wù)并通過(guò)附帶示例理解地址映射與通訊配置過(guò)程縮短基于PCAN工具鏈的ECU診斷功能開(kāi)發(fā)周期。包內(nèi)同時(shí)提供32位與64位庫(kù)文件可適配不同開(kāi)發(fā)環(huán)境實(shí)用性較強(qiáng)。 手里有PCAN的人很多但大多數(shù)人把它當(dāng)成總線監(jiān)控器用抓個(gè)包、發(fā)幾幀裸CAN報(bào)文然后就放一邊了。實(shí)際上PCAN配合Python生態(tài)完全能完成傳統(tǒng)OEM診斷儀90%以上的日常工作——UDS診斷協(xié)議ISO 14229里的會(huì)話控制、DTC讀取、安全訪問(wèn)、固件刷寫(xiě)都可以在PCAN上跑通。這篇文章我從工具選型講起一路聊到34/36/37刷寫(xiě)流程和NRC報(bào)錯(cuò)排查適合手里有PCAN、想自己寫(xiě)診斷工具的人也適合剛接觸車(chē)載測(cè)試的工程師做參考。放心這個(gè)方案不燒錢(qián)也不需要買(mǎi)幾萬(wàn)塊的CANoe。1. 為什么用PCAN做UDS診斷工具選型與前期準(zhǔn)備1.1 從“總線監(jiān)控器”到“診斷平臺(tái)”的轉(zhuǎn)變PCAN在行業(yè)里有個(gè)很樸素的評(píng)價(jià)窮人的CANalyzer。CANoe和CANalyzer功能確實(shí)強(qiáng)但授權(quán)費(fèi)用對(duì)個(gè)人開(kāi)發(fā)者、學(xué)生車(chē)隊(duì)和小團(tuán)隊(duì)來(lái)說(shuō)不現(xiàn)實(shí)而且大部分功能在日常診斷開(kāi)發(fā)里其實(shí)用不上。PCAN這類USB-CAN適配器走的是另一條路硬件只負(fù)責(zé)收發(fā)CAN報(bào)文協(xié)議解釋、流程控制全交給軟件想怎么折騰都行。這個(gè)思路非常適合UDS診斷。UDS說(shuō)到底就是一套規(guī)定好的CAN報(bào)文交互規(guī)則底層CAN收發(fā)硬件只要能可靠收發(fā)、帶時(shí)間戳往上堆協(xié)議層完全是軟件的事。PCAN的驅(qū)動(dòng)和API非常穩(wěn)定被各種開(kāi)源工具鏈支持得也很好這就讓我可以在Python環(huán)境里快速實(shí)現(xiàn)診斷會(huì)話、讀故障碼、安全訪問(wèn)甚至整包刷寫(xiě)。1.2 具體型號(hào)建議與驅(qū)動(dòng)安裝細(xì)節(jié)PCAN家族型號(hào)不少做UDS診斷我建議這樣選型號(hào)特點(diǎn)適用場(chǎng)景PCAN-USB經(jīng)典單通道只支持標(biāo)準(zhǔn)CAN入門(mén)學(xué)習(xí)、簡(jiǎn)單診斷PCAN-USB FD單通道支持CAN FD當(dāng)前性價(jià)比最高的選擇PCAN-USB Pro雙通道可切換CAN/CAN FD需要同時(shí)監(jiān)聽(tīng)和仿真的場(chǎng)景PCAN-USB X6/X12多通道多ECU總線仿真?zhèn)€人開(kāi)發(fā)診斷工具首選PCAN-USB FD。不要一上來(lái)就買(mǎi)Pro雙通道功能在初期基本用不上等真的需要“一個(gè)通道仿真、一個(gè)通道抓車(chē)上的總線”時(shí)再升級(jí)不遲。注意部分ECU仍然是標(biāo)準(zhǔn)CANFD設(shè)備完全向下兼容所以選FD不會(huì)吃虧。驅(qū)動(dòng)這塊有個(gè)容易被忽略的點(diǎn)PCAN設(shè)備插上Windows后免驅(qū)就能識(shí)別但做開(kāi)發(fā)必須裝完整的Driver Package里面包含PCANBasic.dll和固件升級(jí)工具。很多人在網(wǎng)上搜“pcan驅(qū)動(dòng)官網(wǎng)下載”時(shí)被第三方站點(diǎn)繞暈直接去PCAN官網(wǎng)的Downloads頁(yè)面找對(duì)應(yīng)型號(hào)驅(qū)動(dòng)包就行。裝完在設(shè)備管理器里能看到PCAN-USB FD Channel 1之類的設(shè)備就說(shuō)明驅(qū)動(dòng)正常了。固件方面早期某些批次的PCAN-USB FD在CAN FD模式下偶發(fā)不穩(wěn)定建議把固件刷到官方最新版本刷固件工具在驅(qū)動(dòng)包里自帶。1.3 python-can環(huán)境快速初始化Python做UDS診斷基本依賴python-can庫(kù)它把PCAN的API封裝成了統(tǒng)一接口。安裝很簡(jiǎn)單pip install python-can連接PCAN通道的代碼也很直接import can bus can.Bus(interfacepcan, channelPCAN_USBBUS1, bitrate500000)這里有個(gè)坑channel名必須寫(xiě)“PCAN_USBBUS1”這種格式這是PCAN驅(qū)動(dòng)里通道的固定命名。如果設(shè)備是第二通道就寫(xiě)PCAN_USBBUS2。bitrate按ECU實(shí)際波特率設(shè)置常見(jiàn)的是500k也有250k的配錯(cuò)波特率的表現(xiàn)是能發(fā)出去但收不到任何響應(yīng)我第一次調(diào)試時(shí)就因?yàn)檫@個(gè)浪費(fèi)了半個(gè)小時(shí)。還有一個(gè)硬件層面的細(xì)節(jié)PCAN適配器D-Sub頭附近有一個(gè)跳線或開(kāi)關(guān)控制120歐終端電阻單點(diǎn)接ECU時(shí)如果總線上沒(méi)有其他終端就把它打開(kāi)如果掛在已經(jīng)有終端電阻的網(wǎng)絡(luò)上記得關(guān)掉否則會(huì)引入信號(hào)反射偶發(fā)丟幀。2. 尋址、會(huì)話控制與第一條UDS報(bào)文2.1 物理尋址與功能尋址先分清0x7E0和0x7DFUDS診斷的第一步不是發(fā)服務(wù)指令而是搞清楚該往哪個(gè)CAN ID上發(fā)。診斷請(qǐng)求的CAN ID分兩種物理尋址和功能尋址。物理尋址是診斷儀和單個(gè)ECU點(diǎn)對(duì)點(diǎn)通信請(qǐng)求ID和響應(yīng)ID一一對(duì)應(yīng)。最常見(jiàn)的配置是請(qǐng)求0x7E0響應(yīng)0x7E8也就是請(qǐng)求ID加8。但這不是絕對(duì)的不同整車(chē)廠、不同網(wǎng)段完全可能用其他ID拿到一臺(tái)從沒(méi)做過(guò)的ECU先查診斷調(diào)查表或網(wǎng)絡(luò)拓?fù)湮臋n別憑經(jīng)驗(yàn)硬往上套。功能尋址則是對(duì)一條總線上所有ECU廣播最典型的是0x7DF。向0x7DF發(fā)一條會(huì)話控制指令總線上所有支持UDS的ECU都會(huì)收到并嘗試響應(yīng)。這個(gè)特性在整車(chē)廠刷寫(xiě)多個(gè)ECU時(shí)很好用但開(kāi)發(fā)調(diào)試階段慎用——之前我在一輛整車(chē)上用功能尋址發(fā)了個(gè)10 03結(jié)果七個(gè)ECU同時(shí)回響應(yīng)日志瞬間被刷屏而且有些服務(wù)在功能尋址下按規(guī)定是不允許響應(yīng)的容易引發(fā)誤判。2.2 10服務(wù)三種會(huì)話切換UDS里10服務(wù)DiagnosticSessionControl負(fù)責(zé)切換診斷會(huì)話。最常見(jiàn)的三種會(huì)話子功能會(huì)話名稱用途01默認(rèn)會(huì)話上電初始狀態(tài)只能做基礎(chǔ)讀取02編程會(huì)話刷寫(xiě)專用支持34/36/37等服務(wù)03擴(kuò)展會(huì)話診斷開(kāi)發(fā)最常用支持寫(xiě)入、安全訪問(wèn)為什么診斷開(kāi)發(fā)中一上來(lái)就切03擴(kuò)展會(huì)話因?yàn)楹芏喾?wù)受限27安全訪問(wèn)、2E寫(xiě)入DID、某些OEM私有服務(wù)都要求在擴(kuò)展會(huì)話或編程會(huì)話下才被允許。默認(rèn)會(huì)話下請(qǐng)求這些服務(wù)輕則收到NRC 0x7F服務(wù)在當(dāng)前會(huì)話不支持重則ECU直接無(wú)視請(qǐng)求。2.3 第一條UDS請(qǐng)求與ISO-TP分幀問(wèn)題用PCAN發(fā)一條進(jìn)入擴(kuò)展會(huì)話的請(qǐng)求代碼很短import can import time bus can.Bus(interfacepcan, channelPCAN_USBBUS1, bitrate500000) # 進(jìn)入擴(kuò)展會(huì)話10 03 req can.Message( arbitration_id0x7E0, data[0x02, 0x10, 0x03], is_extended_idFalse ) bus.send(req) resp bus.recv(timeout1) if resp.arbitration_id 0x7E8 and resp.data[0] 0x50: print(進(jìn)入擴(kuò)展會(huì)話成功ECU響應(yīng), resp.data.hex()) else: print(響應(yīng)異常, resp.arbitration_id, resp.data.hex())這里解釋一下數(shù)據(jù)域格式第一條0x02表示后面有效數(shù)據(jù)是2個(gè)字節(jié)0x10是服務(wù)ID0x03是子功能。正響應(yīng)時(shí)ECU會(huì)返回0x50也就是請(qǐng)求服務(wù)ID加0x40。這條請(qǐng)求只有3字節(jié)有效數(shù)據(jù)小于7字節(jié)一個(gè)CAN幀就裝下了叫單幀。但如果請(qǐng)求或響應(yīng)數(shù)據(jù)超過(guò)7字節(jié)就必須走ISO-TP多幀協(xié)議——首幀、連續(xù)幀、流控幀那一套。python-can庫(kù)本身不處理ISO-TP分幀需要自己實(shí)現(xiàn)或者用isotp這個(gè)Python庫(kù)pip install isotp刷寫(xiě)時(shí)傳輸數(shù)據(jù)動(dòng)輒幾百KB不理解ISO-TP根本沒(méi)法繼續(xù)。在標(biāo)準(zhǔn)CAN上一幀最多8字節(jié)數(shù)據(jù)域其中第一字節(jié)是PCI字節(jié)所以有效數(shù)據(jù)上限是7字節(jié)。超過(guò)7字節(jié)的處理邏輯發(fā)送端先發(fā)一個(gè)首幀告知總長(zhǎng)度接收端回一個(gè)流控幀告訴“可以發(fā)按照什么節(jié)奏發(fā)”然后發(fā)送端連續(xù)發(fā)連續(xù)幀直到發(fā)完。這套機(jī)制在刷寫(xiě)流程里是絕對(duì)繞不開(kāi)的。3. 19服務(wù)讀DTC狀態(tài)掩碼位逐位拆解與響應(yīng)解析3.1 19服務(wù)常見(jiàn)子功能19服務(wù)ReadDTCInformation是讀取診斷故障碼的核心入口子功能非常多。日常診斷用得最多的是這幾個(gè)19 01按狀態(tài)掩碼統(tǒng)計(jì)DTC數(shù)量19 02按狀態(tài)掩碼讀取DTC列表19 04讀取指定DTC的快照記錄19 06讀取指定DTC的擴(kuò)展數(shù)據(jù)注意19 02請(qǐng)求后面跟的一個(gè)字節(jié)是DTC狀態(tài)掩碼例如19 02 08表示“讀所有confirmed狀態(tài)的DTC”19 02 FF表示“讀所有狀態(tài)的DTC”。掩碼選錯(cuò)了讀出來(lái)的列表就不完整這是排查偶發(fā)故障時(shí)最容易被忽視的細(xì)節(jié)。3.2 DTC狀態(tài)掩碼位的含義DTC狀態(tài)掩碼一共8個(gè)bit每個(gè)bit代表一個(gè)狀態(tài)維度這是ISO 14229里最值得死記硬背的表格之一Bit含義bit0testFailed 測(cè)試失敗bit1testFailedThisOperationCycle 本次操作循環(huán)測(cè)試失敗bit2pendingDTC 待確認(rèn)故障bit3confirmedDTC 已確認(rèn)故障bit4testNotCompletedSinceLastClear 上次清除后未完成測(cè)試bit5testFailedSinceLastClear 上次清除后測(cè)試失敗bit6testNotCompletedThisOperationCycle 本次操作循環(huán)未完成測(cè)試bit7warningIndicatorRequested 請(qǐng)求點(diǎn)亮故障燈實(shí)際排查故障時(shí)讀confirmed用掩碼0x08讀當(dāng)前正在報(bào)的故障用0x01讀歷史和當(dāng)前全部故障用0x0Cbit2bit3即0x040x08。這個(gè)0x0C組合在GVDP、GAP等整車(chē)開(kāi)發(fā)階段的實(shí)車(chē)排查中非常常用一臺(tái)車(chē)同時(shí)報(bào)了好幾個(gè)故障用0x0C能一次性把待確認(rèn)和已確認(rèn)的DTC都帶出來(lái)。3.3 一次完整的19 02請(qǐng)求-響應(yīng)解析比如現(xiàn)在要讀這臺(tái)ECU所有confirmed狀態(tài)的DTC請(qǐng)求req can.Message(arbitration_id0x7E0, data[0x02, 0x19, 0x02, 0x08], is_extended_idFalse) bus.send(req) resp bus.recv(timeout1)假設(shè)ECU返回的CAN數(shù)據(jù)是0x10 0x14 0x59 0x02 0x08 0x01 0xE0第一字節(jié)0x10是首幀表明后面總共有0x14即20字節(jié)數(shù)據(jù)一個(gè)CAN幀裝不下后續(xù)還有連續(xù)幀。數(shù)據(jù)段里0x59是正響應(yīng)SID0x02是子功能回顯0x08是DTC狀態(tài)可用性掩碼0x01 0xE0是DTC數(shù)量?jī)蓚€(gè)字節(jié)之后每4字節(jié)一組前3字節(jié)是DTC編號(hào)最后1字節(jié)是實(shí)時(shí)狀態(tài)。DTC編號(hào)本身是3字節(jié)壓縮BCD碼例如C00101通常對(duì)應(yīng)P0123之類的擴(kuò)展故障碼具體映射關(guān)系看OEM的DTC定義表。這三個(gè)字節(jié)不是簡(jiǎn)單的十六進(jìn)制數(shù)而是壓縮BCD格式很多人在解析時(shí)直接把0xC00101當(dāng)成整數(shù)讀結(jié)果對(duì)不上OEM文檔里的P碼列表就是因?yàn)檫@個(gè)格式?jīng)]搞清楚。順便提一句14服務(wù)ClearDiagnosticInformation清除DTC的命令是14 FF FF FF正響應(yīng)返回54三個(gè)FF表示清除所有DTC。實(shí)車(chē)排查完故障后清零DTC再跑一遍測(cè)試是驗(yàn)證修復(fù)是否生效的基本姿勢(shì)。4. 27服務(wù)安全訪問(wèn)Seed/Key握手的完整鏈路4.1 為什么要做安全訪問(wèn)UDS設(shè)計(jì)里有一層訪問(wèn)控制就是27服務(wù)SecurityAccess。目的是防止非授權(quán)操作——比如不是每天都要做的刷寫(xiě)、寫(xiě)VIN碼、標(biāo)定寫(xiě)入都需要先通過(guò)安全訪問(wèn)校驗(yàn)。ECU不會(huì)平白無(wú)故讓總線上的任意節(jié)點(diǎn)改寫(xiě)它的Flash哪怕你已經(jīng)進(jìn)入了編程會(huì)話。安全訪問(wèn)的原理是挑戰(zhàn)-響應(yīng)機(jī)制診斷儀向ECU請(qǐng)求一個(gè)隨機(jī)數(shù)SeedECU發(fā)回來(lái)診斷儀根據(jù)Seed和一套特定算法算出Key再回傳給ECUECU內(nèi)部用同樣的算法算一遍一致就放行不一致就拒絕。4.2 Seed/Key握手時(shí)序完整的安全訪問(wèn)握手長(zhǎng)這樣進(jìn)入擴(kuò)展會(huì)話10 03請(qǐng)求Seed27 01ECU返回Seed67 01 Seed數(shù)據(jù)通常4字節(jié)診斷儀計(jì)算Key發(fā)送Key27 02 Key數(shù)據(jù)ECU校驗(yàn)正響應(yīng)67 02或返回NRC代碼上請(qǐng)求Seed很簡(jiǎn)單bus.send(can.Message(arbitration_id0x7E0, data[0x02, 0x27, 0x01], is_extended_idFalse)) resp bus.recv(timeout1) # 正響應(yīng)里從data[3]開(kāi)始是Seed具體長(zhǎng)度看data[2] seed int.from_bytes(resp.data[3:], big) print(fSeed: 0x{seed:08X})拿到Seed后要算Key。每家ECU廠商的Seed/Key算法都是保密的屬于核心知識(shí)產(chǎn)權(quán)不可能公開(kāi)發(fā)表。這里說(shuō)的是量產(chǎn)ECU的實(shí)際情況。為了演示流程我舉個(gè)簡(jiǎn)單例子說(shuō)明什么叫“算法”def calc_key(seed): # 僅演示量產(chǎn)ECU算法不是這樣的 key ((seed ^ 0xA5A5A5A5) 0x12345678) 0xFFFFFFFF return key實(shí)際項(xiàng)目里算法通常以DLL動(dòng)態(tài)庫(kù)的形式由供應(yīng)商提供給診斷工具開(kāi)發(fā)方或者寫(xiě)在診斷調(diào)查表里。沒(méi)有算法授權(quán)只能對(duì)著文檔逆向那又是另一個(gè)話題了。4.3 安全訪問(wèn)相關(guān)的NRC與易踩的坑安全訪問(wèn)相關(guān)的NRC有幾個(gè)非常典型0x33安全訪問(wèn)被拒絕最常見(jiàn)的是Key算錯(cuò)0x36嘗試次數(shù)超過(guò)限制ECU已經(jīng)鎖死需要斷電重新上電解除0x37時(shí)間延遲未到說(shuō)明還在鎖定冷卻期安全訪問(wèn)失敗這塊有一個(gè)隱藏比較深的坑是會(huì)話重置很多ECU在切換會(huì)話時(shí)會(huì)重置安全狀態(tài)也就是說(shuō)先做了安全訪問(wèn)再切會(huì)話安全狀態(tài)就丟了。正確順序一定是先切到目標(biāo)會(huì)話再請(qǐng)求安全訪問(wèn)。另外有些ECU對(duì)安全訪問(wèn)加了時(shí)間約束——從拿到Seed到回傳Key必須在規(guī)定時(shí)間內(nèi)完成超時(shí)就被拒絕。Python腳本本身很快但如果中間插了日志寫(xiě)入甚至print到控制臺(tái)拖慢了速度幾毫秒的延遲在某些嚴(yán)格實(shí)現(xiàn)下也會(huì)導(dǎo)致失敗真的遇到過(guò)。還有一個(gè)容易被忽略的在默認(rèn)會(huì)話下請(qǐng)求27服務(wù)很多ECU直接回NRC 0x7F新手一看“服務(wù)不支持”其實(shí)是沒(méi)切會(huì)話。排查安全訪問(wèn)問(wèn)題第一步永遠(yuǎn)先確認(rèn)當(dāng)前會(huì)話狀態(tài)第二步確認(rèn)ECU此時(shí)是否處于解鎖狀態(tài)第三步才懷疑Seed/Key算法。5. 34/36/37服務(wù)刷寫(xiě)從檢查點(diǎn)到完整時(shí)序5.1 刷寫(xiě)前的檢查點(diǎn)流程刷寫(xiě)B(tài)ootloader或應(yīng)用程序是UDS里最重的一次操作也是出問(wèn)題最多的地方。OEM對(duì)刷寫(xiě)有一套嚴(yán)格的前置檢查點(diǎn)流程刷寫(xiě)工具必須先滿足這些條件才能動(dòng)手確認(rèn)電源電壓在ECU允許的范圍內(nèi)通常通過(guò)22服務(wù)讀DID實(shí)現(xiàn)確認(rèn)點(diǎn)火開(kāi)關(guān)狀態(tài)、車(chē)速信號(hào)等條件符合要求進(jìn)入編程會(huì)話10 02通過(guò)安全訪問(wèn)校驗(yàn)27服務(wù)禁止DTC存儲(chǔ)和故障燈點(diǎn)亮85 02必要時(shí)關(guān)閉網(wǎng)絡(luò)管理報(bào)文防止ECU因?yàn)椤笆?lián)”觸發(fā)看門(mén)狗復(fù)位這個(gè)“檢查點(diǎn)”流程每個(gè)OEM定義的不完全一樣但基本思路一致刷寫(xiě)是一個(gè)不可逆的冒險(xiǎn)操作必須保證供電穩(wěn)定、環(huán)境安全、權(quán)限合法。跳過(guò)檢查點(diǎn)直接刷ECU半路斷電或者電壓跌落輕則刷寫(xiě)失敗重則變磚。變磚可不是開(kāi)玩笑的某些情況下要返廠用專用工具救磚。5.2 34服務(wù)請(qǐng)求下載參數(shù)怎么算34服務(wù)RequestDownload用于告訴ECU“我要往哪個(gè)地址寫(xiě)多少數(shù)據(jù)”。請(qǐng)求格式包含數(shù)據(jù)格式標(biāo)識(shí)、地址長(zhǎng)度格式、起始地址和內(nèi)存大小。addressAndLengthFormat這個(gè)字節(jié)是關(guān)鍵高4位是地址長(zhǎng)度字節(jié)數(shù)低4位是內(nèi)存大小長(zhǎng)度字節(jié)數(shù)。0x44表示4字節(jié)地址4字節(jié)內(nèi)存大小0x24表示2字節(jié)地址4字節(jié)內(nèi)存大小0x22表示2字節(jié)地址2字節(jié)內(nèi)存大小。很多NRC 0x31就是從這里來(lái)的——長(zhǎng)度格式和ECU實(shí)際定義不匹配。舉例想把一段數(shù)據(jù)寫(xiě)到起始地址0x00040000長(zhǎng)度0x10000字節(jié)使用4字節(jié)地址和4字節(jié)內(nèi)存大小那么請(qǐng)求就是34 00 44 00 04 00 00 00 01 00 00解析一下34是服務(wù)ID00是dataFormatIdentifier表示無(wú)壓縮、無(wú)加密44是地址長(zhǎng)度格式00 04 00 00是起始地址00 01 00 00是內(nèi)存大小。ECU正響應(yīng)會(huì)返回74后面帶最大允許的單塊長(zhǎng)度等信息。5.3 36/37服務(wù)與刷寫(xiě)完整時(shí)序36服務(wù)TransferData用于真正傳數(shù)據(jù)請(qǐng)求格式是36加塊序列計(jì)數(shù)器加數(shù)據(jù)。塊序列計(jì)數(shù)器從1開(kāi)始每成功發(fā)一塊ECU回一次76計(jì)數(shù)器加1。37服務(wù)RequestTransferExit表示結(jié)束傳輸。一個(gè)典型的UDS刷寫(xiě)時(shí)序如下步驟請(qǐng)求正響應(yīng)說(shuō)明110 0250 02進(jìn)入編程會(huì)話227 01 / 27 0267 02安全訪問(wèn)385 02C5 02禁止DTC記錄434 00 44 ...74 ...請(qǐng)求下載協(xié)商地址和長(zhǎng)度536 01 數(shù)據(jù)最多7字節(jié)76 01傳輸?shù)谝粔K636 02 數(shù)據(jù)76 02傳輸?shù)诙K依次類推737 0077 00請(qǐng)求退出傳輸811 0151 01硬件復(fù)位讓ECU跳到新程序每幀36只能帶7字節(jié)數(shù)據(jù)一個(gè)幾百KB的bin要發(fā)幾萬(wàn)次必須寫(xiě)循環(huán)自動(dòng)拆包發(fā)送。每次發(fā)送后要等ECU的76響應(yīng)再發(fā)下一塊不能無(wú)腦連發(fā)ECU內(nèi)置Flash寫(xiě)入需要時(shí)間發(fā)太快會(huì)把ECU的接收緩沖撐爆觸發(fā)流控錯(cuò)誤。PCAN本身對(duì)總線上的收到時(shí)間戳記錄得很精確通過(guò)相鄰兩幀響應(yīng)的間隔可以反推ECU實(shí)際寫(xiě)Flash的耗時(shí)這個(gè)數(shù)據(jù)對(duì)后續(xù)調(diào)優(yōu)刷寫(xiě)速度很有價(jià)值。還有一個(gè)細(xì)節(jié)有些ECU的34響應(yīng)里會(huì)指定blockLength意思是每次36允許傳輸?shù)淖畲笞止?jié)數(shù)。如果10服務(wù)里申請(qǐng)的內(nèi)存大小比blockLength大就要分多次34/36/37循環(huán)每輪傳輸完一個(gè)block再發(fā)下一次34繼續(xù)。這種分段刷寫(xiě)邏輯在UDS標(biāo)準(zhǔn)里叫“分段傳輸”O(jiān)EM的診斷調(diào)查表里一般會(huì)寫(xiě)清楚。6. 實(shí)戰(zhàn)踩坑NRC 0x31這樣級(jí)別的報(bào)錯(cuò)怎么排查6.1 一次34服務(wù)NRC 0x31的完整定位過(guò)程N(yùn)RC全稱Negative Response Code是ECU在服務(wù)執(zhí)行失敗時(shí)返回的錯(cuò)誤碼格式是0x7F加服務(wù)ID加NRC。舉個(gè)例子34服務(wù)失敗時(shí)ECU返回的完整數(shù)據(jù)可能是7F 34 31。我印象很深的一次排查一臺(tái)新項(xiàng)目ECU刷寫(xiě)流程走到34服務(wù)ECU穩(wěn)定返回NRC 0x31也就是請(qǐng)求超出范圍。當(dāng)時(shí)第一反應(yīng)是地址不對(duì)但反復(fù)核對(duì)代碼里的起始地址和內(nèi)存大小感覺(jué)沒(méi)問(wèn)題。后來(lái)把PCAN的Trace功能打開(kāi)把整段刷寫(xiě)日志導(dǎo)出來(lái)逐幀看才發(fā)現(xiàn)問(wèn)題請(qǐng)求數(shù)據(jù)是34 00 44 00 04 00 00 00 01 00 00請(qǐng)求地址0x00040000長(zhǎng)度0x00010000看起來(lái)沒(méi)什么問(wèn)題。但翻到ECU的內(nèi)存映射文檔發(fā)現(xiàn)0x00040000這個(gè)地址屬于Bootloader保護(hù)區(qū)Application有效起始地址應(yīng)該是0x00020000。也就是說(shuō)在ECU看來(lái)這個(gè)請(qǐng)求的地址參數(shù)就是超出范圍的跟服務(wù)實(shí)現(xiàn)無(wú)關(guān)。這類問(wèn)題的經(jīng)驗(yàn)是NRC 0x31出現(xiàn)后先把請(qǐng)求里所有參數(shù)逐字節(jié)和診斷調(diào)查表核對(duì)一遍——地址范圍、內(nèi)存大小、長(zhǎng)度格式任何一個(gè)不對(duì)都會(huì)咬死這個(gè)錯(cuò)誤碼。別上來(lái)就懷疑ECU壞了ECU在絕大多數(shù)時(shí)候是對(duì)的。6.2 NRC常見(jiàn)值速查與排查方法論日常診斷遇到的NRC常用的就那幾個(gè)NRC含義典型觸發(fā)原因0x10一般拒絕服務(wù)在當(dāng)前狀態(tài)不被接受0x11服務(wù)不支持該ECU沒(méi)有實(shí)現(xiàn)這個(gè)服務(wù)0x12子功能不支持服務(wù)有但子功能沒(méi)實(shí)現(xiàn)0x13消息長(zhǎng)度錯(cuò)誤請(qǐng)求數(shù)據(jù)長(zhǎng)度和標(biāo)準(zhǔn)不符0x14請(qǐng)求超出范圍參數(shù)值超出允許范圍0x22條件不滿足前置條件沒(méi)達(dá)到比如沒(méi)進(jìn)對(duì)應(yīng)會(huì)話0x24請(qǐng)求序列錯(cuò)誤服務(wù)調(diào)用順序不對(duì)比如跳過(guò)34直接360x31請(qǐng)求超出范圍參數(shù)格式或取值范圍錯(cuò)誤0x33安全訪問(wèn)被拒絕Key錯(cuò)誤或未解鎖0x36嘗試次數(shù)超限安全訪問(wèn)連續(xù)失敗次數(shù)過(guò)多0x37時(shí)間延遲未到鎖定冷卻期還沒(méi)結(jié)束0x78響應(yīng)掛起ECU正在處理稍后會(huì)給最終響應(yīng)排查NRC的通用思路我總結(jié)成四步第一步定位失敗的服務(wù)ID和NRC明確是哪個(gè)環(huán)節(jié)出了問(wèn)題第二步檢查當(dāng)前會(huì)話狀態(tài)和安全訪問(wèn)狀態(tài)這兩個(gè)是前置條件第三步把請(qǐng)求報(bào)文逐字節(jié)拆開(kāi)和ISO 14229以及OEM的診斷調(diào)查表對(duì)照看長(zhǎng)度、子功能、參數(shù)范圍是否有出入第四步打開(kāi)PCAN日志對(duì)整個(gè)會(huì)話做時(shí)序回放看是否存在服務(wù)調(diào)用順序錯(cuò)誤或超時(shí)。6.3 時(shí)序與尋址類疑難雜癥的排查思路NRC 0x31這種是“參數(shù)型報(bào)錯(cuò)”還有一類是“時(shí)序型報(bào)錯(cuò)”表現(xiàn)更隱蔽ECU不是直接回NRC而是完全沒(méi)響應(yīng)或者回一個(gè)0x78之后長(zhǎng)時(shí)間沒(méi)有最終結(jié)果。一次典型的Pending超時(shí)請(qǐng)求一個(gè)耗時(shí)操作ECU回0x78表示“我還在處理”正常應(yīng)該在P2*擴(kuò)展響應(yīng)時(shí)間通常5000ms內(nèi)給出最終響應(yīng)。如果超過(guò)這個(gè)時(shí)間還沒(méi)響應(yīng)那就是ECU內(nèi)部卡住了或者請(qǐng)求的數(shù)據(jù)量超過(guò)它單次處理能力。處理辦法是把請(qǐng)求拆小降低單次數(shù)據(jù)量或者延長(zhǎng)等待時(shí)間重試。尋址類問(wèn)題也很常見(jiàn)尤其是PCAN抓整車(chē)總線時(shí)請(qǐng)求0x7E0響應(yīng)不是0x7E8而是0x7E9之類的鄰居節(jié)點(diǎn)ID。這說(shuō)明請(qǐng)求ID配錯(cuò)了ECU節(jié)點(diǎn)地址表里這個(gè)地址對(duì)應(yīng)當(dāng)前響應(yīng)ID并不是請(qǐng)求ID加8。另一個(gè)經(jīng)典問(wèn)題是功能尋址發(fā)廣播會(huì)話控制總線上多個(gè)ECU同時(shí)回復(fù)日志里響應(yīng)幀ID五花八門(mén)第一次遇到會(huì)嚇一跳其實(shí)那不是故障是設(shè)計(jì)如此——功能尋址本來(lái)就該是多ECU響應(yīng)。這類問(wèn)題的排查高度依賴時(shí)間戳。PCAN的收發(fā)接口在硬件上打時(shí)間戳python-can里每條Message也帶著timestamp字段。排查時(shí)序問(wèn)題時(shí)把請(qǐng)求、響應(yīng)、NRC的時(shí)間戳對(duì)齊打印往往一眼就能看出是誰(shuí)慢了、誰(shuí)丟了。我習(xí)慣把所有交互記錄寫(xiě)成CSV帶幀ID、方向、數(shù)據(jù)、時(shí)間戳四列排查問(wèn)題的時(shí)候用Excel篩選或者Python腳本分析比在終端里翻日志高效得多。最后再分享一個(gè)我自己的小習(xí)慣所有診斷腳本都會(huì)在收發(fā)函數(shù)里統(tǒng)一加一層日志封裝無(wú)論成功失敗都把請(qǐng)求幀和響應(yīng)幀原樣落盤(pán)。這套日志體系在平時(shí)看著冗余真到現(xiàn)場(chǎng)排查問(wèn)題、和ECU供應(yīng)商對(duì)齊現(xiàn)象時(shí)一份完整干凈的報(bào)文日志比什么都值錢(qián)。PCAN做UDS診斷硬件從來(lái)不是瓶頸真正拉開(kāi)差距的是對(duì)協(xié)議細(xì)節(jié)的耐心和對(duì)日志的整理習(xí)慣。本文還有配套的精品資源點(diǎn)擊獲取