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