:ISO15765多幀傳輸原理與VIN讀取報文解析)
干過幾年車載總線測試的人都知道只要跟UDS診斷打交道CANoe基本是繞不開的家伙。ISO15765這幾個字聽起來挺唬人但它本質(zhì)上解決一個問題CAN總線一幀只能塞8個字節(jié)診斷數(shù)據(jù)動不動就是幾十個字節(jié)怎么傳ISO15765規(guī)定了一套“拆包-打包”的規(guī)則業(yè)內(nèi)常叫它ISO-TP。很多人第一次在Trace里看到一堆以 10、21、30 開頭的幀直接懵掉——明明我發(fā)的是個讀VIN的請求怎么總線上回了一大串這篇文章就專門把這層窗戶紙捅破。我會用CANoe作為分析工具從ISO15765多幀傳輸?shù)膸Y(jié)構(gòu)講起再到如何配置CANoe的傳輸層最后用一次讀取VIN17字節(jié)數(shù)據(jù)的真實報文逐字節(jié)拆給你看。無論你是剛接觸ECU測試的應屆生還是被診斷報文折磨過的老同志照著走一遍下次再看到帶多幀傳輸?shù)腡race心里會踏實很多。1. 多幀傳輸?shù)降自诮鉀Q什么問題1.1 為什么會有ISO15765CAN總線最早是給控制信號設計的像轉(zhuǎn)速、油門、剎車這類量一幀8字節(jié)綽綽有余。但后來車廠開始做診斷和刷寫要把“讀取故障碼”“寫入配置”“更新固件”這種動輒幾十上百字節(jié)的數(shù)據(jù)塞進CAN8字節(jié)顯然不夠用。總不能為了一次診斷就前前后后發(fā)十幾幀原始數(shù)據(jù)吧接收方怎么知道哪些幀屬于同一個消息哪一幀在前、哪一幀在后中間斷了怎么辦于是ISO 15765-2成了事實標準它定義了一種傳輸協(xié)議也就是我們常說的ISO-TP。它把上層的數(shù)據(jù)拆成多幀加上標識信息按順序發(fā)出去接收方再把這些幀拼成完整數(shù)據(jù)。CANoe里很多診斷報文顯示成一條“Diagnostic”消息底層其實是ISO-TP在背后自動拆裝。1.2 四種幀類型的“角色分工”ISO-TP用四種幀類型完成整個多幀流程單幀SF數(shù)據(jù)長度不超過7字節(jié)經(jīng)典CAN直接用一幀傳完。首幀F(xiàn)F數(shù)據(jù)長度超過7字節(jié)時第一幀先發(fā)出去里面帶著總長度和首段數(shù)據(jù)。流控幀F(xiàn)C接收方收到首幀后返回一個“允許發(fā)送”的指令里面攜帶BS、STmin等參數(shù)。連續(xù)幀CF發(fā)送方按流控幀的指示一幀一幀把剩余數(shù)據(jù)發(fā)完。這四種幀靠第一個字節(jié)的PCI協(xié)議控制信息區(qū)分。PCI高四位是幀類型標識例如0x0代表單幀0x1代表首幀0x2代表連續(xù)幀0x3代表流控幀。在CANoe的Trace窗口里你往往會看到“SF/FF/FC/CF”的縮寫要能一眼認出來。1.3 物理尋址與功能尋址ISO15765的尋址通常分兩種物理尋址點對點比如測試儀發(fā)給某個ECU請求ID一般是0x7E0響應ID是0x7E8。這種最常用。功能尋址廣播一個請求發(fā)給多個ECU常用ID是0x7DF。功能尋址一般只用于請求不用于響應。CANoe里配置傳輸層的時候要分清這兩種尋址對應的CAN ID。很多人配置錯誤導致收不到響應多半是物理尋址ID沒配對。2. 實戰(zhàn)前必須搞定的CANoe環(huán)境配置2.1 我推薦的CANoe配置方式先說一下環(huán)境。我用的是CANoe 16/17系列接口有VN1640和虛擬CAN通道。如果你只是想學多幀報文分析不一定非要真實硬件CANoe自帶的虛擬CAN總線也能跑通整個流程。安裝的時候務必確認裝了“Diagnostics”相關(guān)組件部分功能需要授權(quán)沒授權(quán)的話診斷窗口會用不了。新建工程時選擇CAN總線配置兩條虛擬通道Channel1和Channel2。用CANoe的“Simulation Setup”把通道連接起來再加上一個DBC文件定義好節(jié)點和報文。沒有DBC也可以先裸發(fā)CAN幀但解析不出ISO-TP消息所以建議老老實實建一個基礎(chǔ)DBC。DBC我用CANdbVector自帶的數(shù)據(jù)庫編輯器創(chuàng)建。典型配置是定義兩個節(jié)點Tester測試儀和ECU然后定義物理請求報文ID0x7E0物理響應報文ID0x7E8。ISO-TP本身不強制這些ID具體是什么值但UDS診斷領(lǐng)域約定俗成大部分OEM都沿用這套邏輯。2.2 配置傳輸層的關(guān)鍵步驟DBC建好后在CANoe的“Diagnostics/ISO TP”區(qū)域右鍵添加一個ISO TP傳輸對象。這里要設置發(fā)送ID和接收ID請求ID 0x7E0響應ID 0x7E8。尋址類型物理尋址。協(xié)議類型ISO 15765-2CAN。處理模式可以把“對完整幀進行重組”打開這樣在Diagnostic窗口里看到的是一條完整診斷響應而不是一堆拆分后的裸CAN幀。這一步最容易踩的坑是“診斷描述文件CDD”。CANoe診斷控制臺加載CDD后可以直接用服務名發(fā)診斷請求非常方便。但如果手頭沒有CDD也可以手動發(fā)送CAN幀靠CANoe的TP層去拆包。兩種方式我都會在實戰(zhàn)里覆蓋到。2.3 Trace窗口和過濾技巧ISO-TP報文在Trace里非常刷屏尤其是發(fā)送幾十個字節(jié)的多幀響應一秒鐘可能幾十條CF幀。我的習慣是先把Trace窗口的默認列配置好查看CAN ID、數(shù)據(jù)場、幀類型、絕對時間、相對時間這幾列再打開設置里的“右鍵過濾”功能。點擊Trace窗口工具欄上的“Display Filter”圖標可以只顯示請求ID和響應ID或者只顯示ISO-TP相關(guān)報文。不要在一堆其他網(wǎng)絡管理、應用報文里找診斷幀效率太低。還可以在“Write Window”里用CAPL腳本打印關(guān)鍵信息比如打印收到的完整多幀數(shù)據(jù)分析速度更快。3. 手把手實戰(zhàn)用VIN讀取把多幀報文拆明白3.1 一個真實的讀取VIN全過程我以一個ECU返回VIN碼的場景為例。UDS服務是22讀數(shù)據(jù)DID是F190VIN。請求數(shù)據(jù)是22 F1 90只有3個字節(jié)CAN單幀完全能裝下所以請求幀只發(fā)一個單幀方向幀類型CAN IDData請求SF0x7E002 22 F1 9002是單幀PCI代表單幀且數(shù)據(jù)長度為2字節(jié)后邊跟22 F1 90。這個請求沒有任何懸念一幀就完事了。重點在響應。假設ECU返回的VIN是ABC123XYZ45678901ASCII碼一共17個字節(jié)。UDS響應數(shù)據(jù)為62 F1 902字節(jié)DID 1字節(jié)服務響應再加上17字節(jié)VIN總共20字節(jié)。20字節(jié)超過了7字節(jié)所以必須走ISO-TP多幀。3.2 首幀F(xiàn)F怎么解析ECU發(fā)送的第一幀如下方向幀類型CAN IDData響應FF0x7E810 14 62 F1 90 41 42 43首幀PCI的格式是第一個字節(jié)高四位為1表示首幀低四位是完整數(shù)據(jù)長度的bit11~bit8第二個字節(jié)是完整數(shù)據(jù)長度的bit7~bit0。所以10 14拼接得到長度0x014也就是20字節(jié)正好是將來重組后的完整響應長度。首幀數(shù)據(jù)區(qū)只有6字節(jié)放的是完整響應最前面的6個字節(jié)62響應服務ID與請求的22服務對應。F1 90DID字段。41、42、43就是VIN里最前面的三個字符A B C。所以這條首幀的完整含義就是我要傳20字節(jié)數(shù)據(jù)前6字節(jié)我已經(jīng)發(fā)給你了下面是后面14字節(jié)等我收到流控幀再安排節(jié)奏。3.3 流控幀F(xiàn)C怎么解析ECU發(fā)完首幀后正常不會立刻繼續(xù)發(fā)連續(xù)幀。它必須先等測試儀Tester回一個流控幀告訴它“你按這個節(jié)奏來發(fā)”。測試儀收到首幀后回一個流控幀方向幀類型CAN IDData請求FC0x7E030 00 00流控幀PCI的第一個字節(jié)高四位是3代表流控幀。低四位叫FSFlow Status0Continue繼續(xù)發(fā)。1Wait先等著。2Overflow接收緩沖區(qū)溢出丟棄本次傳輸。示例里的FS0表示可以繼續(xù)。第二個字節(jié)是BSBlock Size表示允許發(fā)送方連續(xù)發(fā)送的連續(xù)幀數(shù)量。BS0是個特殊值表示“不限幀數(shù)一口氣發(fā)完”。第三個字節(jié)是STminSeparation Time min表示兩個連續(xù)幀之間的最小時間間隔。STmin0x00代表不強制額外延時但很多工具還是會留一點間隔避免CAN發(fā)送緩沖溢出。3.4 連續(xù)幀CF的序號和數(shù)據(jù)拼接收到FC后ECU開始連發(fā)剩余的連續(xù)幀。完整響應的20字節(jié)中首幀已經(jīng)帶了6個還剩14個字節(jié)剛好分成兩個連續(xù)幀每個7字節(jié)第一幀CF方向幀類型CAN IDData響應CF0x7E821 31 32 33 58 59 5A 34PCI第一個字節(jié)高四位是2表示連續(xù)幀低四位是序列號SN。第一個連續(xù)幀的SN通常是1部分協(xié)議棧也有從0開始的后續(xù)會聊到。數(shù)據(jù)區(qū)放的是后續(xù)7個字節(jié)31 32 33 58 59 5A 34也就是ASCII字符1 2 3 X Y Z 4。第二幀CF方向幀類型CAN IDData響應CF0x7E822 35 36 37 38 39 30 31SN2數(shù)據(jù)區(qū)是剩余7個字節(jié)35 36 37 38 39 30 31也就是5 6 7 8 9 0 1。接收方拿到FFCF1CF2后按順序把數(shù)據(jù)區(qū)拼接起來FF數(shù)據(jù)6字節(jié)62 F1 90 41 42 43CF1數(shù)據(jù)7字節(jié)31 32 33 58 59 5A 34CF2數(shù)據(jù)7字節(jié)35 36 37 38 39 30 31拼起來就是62 F1 90 41 42 43 31 32 33 58 59 5A 34 35 36 37 38 39 30 31轉(zhuǎn)成ASCII就是ABC123XYZ45678901。到這里一次完整的多幀響應就解析完了。3.5 傳輸參數(shù)的計算邏輯有人會問BS和STmin怎么選簡單說BS決定“發(fā)幾幀停一下”。BSN表示發(fā)送方每發(fā)N個連續(xù)幀后必須等接收方重新發(fā)一個FC才繼續(xù)。STmin決定“幀與幀之間的最小時間”。如果ECU接收處理速度慢STmin就要給大一點如果太快又會造成總線負載升高。STmin的具體含義還要注意單位。0x00到0x7F表示0到127ms0x80到0xF0時單位變成100us比如0xFA其實不是標準值。常見的10是1ms32是5ms64是10ms。實際項目中STmin會根據(jù)ECU底層驅(qū)動能力來確定。CANoe的診斷傳輸層配置里可以直接選配置完它會自動生成對應的FC參數(shù)。4. 多幀傳輸常見的坑現(xiàn)象與排查做過幾輪診斷測試后你會發(fā)現(xiàn)多幀傳輸?shù)膯栴}翻來覆去就那么幾個。我整理了一份在CANoe環(huán)境下的避坑經(jīng)驗按出現(xiàn)頻率排序。4.1 看不見首幀或連續(xù)幀Trace里全是裸CAN幀很多時候明明發(fā)了20字節(jié)的響應Trace窗口里卻看不到帶有10、21、30的報文反而看到一串完整的原始數(shù)據(jù)被拆成8字節(jié)一條的CAN幀。這是CANoe把ISO-TP層“幫助”了它默認重組了完整消息并且在Diagnostics窗口和Trace里只展示重組后的結(jié)果。解決方法是到Trace窗口的“Display”里打開ISO-TP的詳細模式或者直接在報文列表里看“CAN TP”列。如果你希望看到傳輸層的裸幀可以右鍵“ISO TP”選項選擇“show transport protocol frames”。不要誤以為ECU沒發(fā)多幀其實數(shù)據(jù)早就到了。4.2 發(fā)完FF后一直沒有FC響應典型的故障是ECU發(fā)完首幀后測試儀不回復流控幀。排查順序如下檢查故障注入是不是CAPL腳本或者DBC里把流控幀屏蔽了。檢查地址ID確認請求ID和響應ID配對是否正確。檢查診斷協(xié)議配置CANoe的ISO TP傳輸層如果發(fā)送ID和接收ID設置反了收到的FF會被當成無效幀丟棄。檢查超時時間CANoe默認的N_As/N_Bs超時是1秒如果測試儀沒來得及解析也會出現(xiàn)超時。這個坑最隱蔽的地方在于很多新手用CDD和診斷窗口發(fā)送請求時CANoe會自動幫你回FC但如果你直接用“CAN IG”面板手動發(fā)送幀CANoe不會自動回流控幀此時ECU發(fā)完FF就一直等最終超時報N_Bs超時。所以我建議純學習階段就用診斷窗口或CAPL來走完整流程別用手動CAN發(fā)送去模擬測試儀。4.3 CF序號為什么不是從0開始我在拿到首幀之后見過很多人在Trace里盯著第一個連續(xù)幀的SN看。有人收到21開頭的幀就會問為什么不是20ISO15765標準里SN是一個4位循環(huán)計數(shù)器從0到15循環(huán)。但在標準實現(xiàn)中首幀之后第一個連續(xù)幀的SN通常從1開始后續(xù)依次遞增。部分工具或協(xié)議棧會從0開始這個在判斷時會有歧義。我的經(jīng)驗是不要糾結(jié)起始值重點檢查連續(xù)幀的SN是否連續(xù)。如果中間出現(xiàn)跳號比如前面是SN3后面直接SN5那基本可以判定丟幀需要做重傳處理。CANoe在重組時如果發(fā)現(xiàn)SN不連續(xù)會在Trace里顯示錯誤或者直接超時。你可以在診斷窗口的“Trace Filter”里勾選“Protocol errors”快速定位這類問題。4.4 BlockSize與STmin搭配出問題有時候首幀發(fā)完流控幀也回得很快但連續(xù)幀發(fā)了幾幀就停了。這時候排查BS和STmin的取值。BS3代表每發(fā)3個CF就要等接收方再發(fā)一次FC。如果接收方遲遲不發(fā)第二個FC傳輸就卡住。BS0雖然不限次數(shù)但工程上不建議在復雜的CAN網(wǎng)絡中無腦設0因為一旦總線擁堵要么出錯誤幀要么接收方緩沖區(qū)溢出。STmin設置太小時比如0x00連續(xù)幀之間幾乎沒有間隔。如果ECU底層寫Flash或者做校驗可能來不及處理導致后續(xù)CF被丟棄。一般項目里STmin至少設到0x0A10ms。在CANoe里可以通過“CAN Statistics”窗口看到總線負載和錯誤幀情況如果錯誤幀多先懷疑STmin。4.5 常見問題速查表現(xiàn)象可能原因解決方向Trace只顯示重組后的診斷消息CANoe默認合并TP層開啟“show transport protocol frames”發(fā)完FF后無FC手動發(fā)送幀導致無流控響應改用診斷窗口或CAPL報文接收超時N_BsFC未返回或超時參數(shù)過短檢查ID、協(xié)議配置和超時時間連續(xù)幀SN跳變丟幀或總線錯誤檢查STmin和BS查看錯誤幀響應長度與實際不符FF中的長度計數(shù)錯對照FF的12位長度字段重新計算功能尋址收不到響應功能尋址不能用于響應請求用0x7DF響應必須物理尋址0x7E85. 進階玩法與個人心得5.1 用CAPL腳本自動發(fā)送并校驗多幀手動發(fā)一次UDS請求沒有問題但做壓力測試和自動化回歸時就要用CAPL來跑。我常用的套路是用diagSetP2Parameter設置超時。用diagSendRequest發(fā)送CDD里定義好的診斷請求。通過on diagResponse回調(diào)接收完整響應。在回調(diào)里用diagGetParameter解析響應中的VIN字節(jié)并比對預期值。用CAPL的好處是你不需要關(guān)心底層拆包拼包邏輯CANoe的協(xié)議棧已經(jīng)把FF/CF/FC全部處理好了。它會給你一個完整響應的字節(jié)數(shù)組直接用MemCmp做結(jié)果校驗。批量刷寫、反復讀寫DID的自動化腳本基本都是這個思路。5.2 與Python聯(lián)合測試的擴展有些團隊習慣用Python控制CANoe做集成測試。可以用CANoe的COM接口啟動工程、發(fā)送診斷請求、讀取響應。比如調(diào)用CANoe.Application對象再通過Diagnostic對象觸發(fā)請求這樣就能在Python測試框架里跑診斷自動化。不過這種方案對CANoe版本依賴較強COM接口偶爾會因為版本不匹配出幺蛾子。如果是學習階段先用CAPL把邏輯調(diào)通再考慮Python封裝。5.3 一個調(diào)試小技巧最后分享一個我壓箱底的小技巧。排查多幀傳輸問題時我習慣在Trace窗口添加兩列Ack和Error。當某個CF幀出現(xiàn)CRC或ACK錯誤時這兩列會標紅立刻能定位到物理層干擾。很多時候你以為ISO-TP配置不對其實是總線上丟幀。另外CANoe的“Graphics Window”配合ISO-TP層可以把BS和STmin的時序可視化。調(diào)了幾次參數(shù)后你會對“STmin1ms和5ms的實際總線效果”有直觀感覺。這些參數(shù)的變化用肉眼很難看出來但圖形曲線騙不了人。根據(jù)我個人經(jīng)驗ISO15765多幀傳輸真正難的不是協(xié)議本身而是你第一次面對一堆看似雜亂幀時的心態(tài)。記住每類幀的角色FF是報幕員FC是交通指揮CF是跑腿的SF是一句話能說完的事。用CANoe多看幾次真實報文多拆幾輪字節(jié)這個坎很快就能邁過去。