目錄
一、診斷窗口介紹
二、診斷數據庫文件管理
三、添加基礎診斷描述文件(若沒有CDD/ODX/PDX文件)并使用對應的診斷功能進行UDS診斷
3.1、添加基礎診斷描述文件
3.2、基于基礎診斷,使用診斷控制臺進行UDS診斷
3.2.1、生成基礎診斷
3.2.2、添加診斷服務
3.2.3、發送診斷服務
3.2.4、使用基礎診斷進行27服務安全解鎖
3.2.5、添加安全解鎖dll算法文件
3.2.6、其他uds服務
四、有診斷數據庫文件(CDD、PDX、ODX)進行uds診斷
五、通過CAPL的仿真節點調用診斷庫函數進行uds診斷
5.1、CAPL中使用進行uds診斷的基本步驟
5.2、CAPL中定義診斷請求
5.3、CAPL中發送診斷請求
5.3.1、通用診斷請求
5.3.2、發送診斷數據庫中的診斷請求
5.4、CAPL中獲取診斷響應
5.4.1、診斷響應回調函數(診斷響應事件)方式獲取診斷響應
5.4.2、使用函數直接獲取診斷響應(不借助事件回調)
5.5、CAPL中借助診斷描述文件中導入的seedKey.dll文件通過安全訪問
5.6、CAPL發送其他診斷請求
CANoe的診斷功能支持解析多個總線的診斷信息,CANoe支持通過診斷控制臺或者CAPL對診斷功能進行調用,通過模擬Tester(上位機)與ECU進行診斷測試,模擬ECU和其他ECU節點進行診斷等。
一、診斷窗口介紹
CANoe的診斷功能菜單如圖所示,在這里采取從左往右的形式依次對這幾個功能進行簡要的介紹
1、Diagnostic/ISO TP:用于往CANoe工程中添加診斷描述文件,配置一些診斷參數,例如傳輸層,診斷層參數。
2、Basic Diagnostic:基礎診斷,可在1中通過添加一個基礎診斷文件后激活,可以自定義一些簡單的診斷服務
3、Diagnostic Parameters:用于配置需要讀取的診斷響應中的參數,可配置某一診斷請求為單次手動發送或者類似IG一樣的自動發送功能
4、Diagnostic Console:數據庫診斷窗口,用于發送診斷描述文件中定義的診斷服務,并在窗口內顯示對應診斷請求的響應,需要導入診斷數據庫(CDD、ODX、PDX)文件或添加基礎診斷文件后激活使用
5、Fault Memory:訪問ECU的故障碼模塊
6、Session Control:用于控制ECU的診斷會話和安全訪問
7、OBD——II:車載OBD診斷功能
8、CANdelaStudio:CANoe自帶的一個CDD查看、編輯器
9、ODXStudio:CANoe自帶的一個PDX、ODX查看、編輯器
10~12:涉及BMS J1939傳輸協議的內容,由于本人沒怎么接觸過J1939,也不怎么使用,不做介紹。
二、診斷數據庫文件管理
在CANoe中,要想能夠使用CANoe的診斷功能,必須先為該CANoe工程配置一個診斷描述文件(基礎診斷、CDD文件、ODX/PDX文件),診斷描述文件便是常說的診斷數據庫,這個文件中往往包含著ECU支持的各種診斷服務和診斷參數等信息。
添加診斷描述文件:
點擊如圖所示圖標,進入診斷描述文件配置界面。
在CAN(由于本工程是創建的CAN模板,故這里為CAN,LIN工程的話這里顯示為LIN)處右擊鼠標,選擇添加一個診斷描述文件。
如圖所示,可以選擇添加四種類型的診斷描述文件。
如果有CDD或者PDX/ODX文件,可以選擇第一個選項,添加對應的診斷數據庫。
如果沒有,可以選擇第二個選項,添加一個基礎診斷文件。(第三個和第四個我沒用過)
三、添加基礎診斷描述文件(若沒有CDD/ODX/PDX文件)并使用對應的診斷功能進行UDS診斷
3.1、添加基礎診斷描述文件
在上一步中選擇第二個選項,Add Basic Diagnostic Description(UDS),
添加完成之后如圖所示,在這里可以對傳輸層和診斷層以及附加的診斷描述文件進行配置。
如圖是傳輸層參數配置界面:
Addressing:用于配置目標ECU的物理尋址、功能尋址、診斷響應的ID
Addition ISO TP:配置傳輸層參數
STmin:流控幀參數,用于告知發送方發送連續幀時的最短間隔時間。
Block Size:用于告知發送方在接收到下一條流控幀之前,本次可以發送的連續幀的數量,為0表示沒有限制。
FC Delay:流控幀與FF CF之間的間隔時間
Max length:傳輸層支持的最大字節數,當接收到的長度大于該長度時,CANoe將會報錯并結束傳輸。
Mixing of CAN 2.0 and CANFD Frames:
若配置為ignore,則忽略與配置不符的CAN報文,如:本工程配置為CAN工程,則會忽略CANFD的診斷報文;
若配置為accept,則可以接收與本工程配置不符的CAN報文,但僅接收;
若配置為adapt,則不僅可以接收與本工程配置不符的CAN報文,且在接收到之后會使用對應的報文類型進行發送。
到此,傳輸層配置就結束了,接著是診斷層的配置
如圖是診斷層配置界面:
Tester Present Request:若勾選此選項,則CANoe會在發送完成一次診斷服務后會每隔S3 Client ms之后自動發送診斷儀在線請求(0x3E服務)
若勾選上方的From Description,則會默認發送診斷文件中的診斷儀在線命令,若勾選Manually,則可以自定義發送的診斷儀在線命令。
S3 Server Time:ECU離開非默認會話的超時時間,此時間必須大于S3 Client。
Timing:
P2 Client:CANoe發送請求后,需要在此時間內收到ECU給出的響應,否則拋出超時。
P2 Server:ECU收到診斷請求之后,需要在此時間以后給出對應的診斷響應
P2? extended client:當接收到NRC78之后,CANoe需要繼續等待響應的時間
P2 extended server:當接收到NRC78之后,需要在此時間內給出下一次響應,否則判定為超時
Response code 0x21 supported:若勾選,則CANoe會在總線上收到0x21的NRC之后在Complete
within 時間內間隔repeat request after時間重復發送一次診斷請求
Seed and key DLL:安全訪問算法動態鏈接庫文件,導入此文件,CANoe會在27服務接收到種子之后自動調用此算法計算對應的秘鑰。
Additional Descriptions:附加的診斷描述文件,這里可以添加一些當前診斷數據庫文件中未定義的附加服務(由于這里使用的是基礎診斷,就不再新增了)。
至此,基礎診斷添加完畢,其實以上所提到的參數,如無特殊要求,使用默認值即可。
3.2、基于基礎診斷,使用診斷控制臺進行UDS診斷
3.2.1、生成基礎診斷
我的診斷id配置如圖。
配置基礎診斷服務
在診斷菜單的左上角,打開Basic Diagnostic 基礎診斷,可以看到,在基礎診斷中已經有了CANoe預定的一些uds診斷服務,例如10服務? 11服務等。
為了方便起見,我們使用CANoe自帶的功能,先一鍵生成基礎的診斷服務
點擊上圖中的edit處,選擇Add All Services,添加所有診斷服務
添加完成之后,基礎診斷界面的左側就為每個診斷服務都生成了一個基本的診斷服務。
3.2.2、添加診斷服務
在對應的診斷服務處右擊鼠標,即可新增一個對應的診斷服務
最上方的Service Name可以修改此服務的名稱,紅框中的value處可以修改對應的診斷子服務id
現在,我們就有了一個1001 默認會話服務,和一個1003拓展會話的診斷服務了。
3.2.3、發送診斷服務
運行工程,我們試著發送一下這兩個診斷服務(此處的響應ECU是我使用同星智能的TsMaster軟件模擬的ECU,大家如果有真實ECU可以接入真實ECU使用,如果沒有真實的ECU,評論區我會提供一個使用使用CAPL模擬的ECU節點來進行使用)
雙擊對應的服務來發送此服務
可以看到,當我發送1001診斷之后,ECU回復了肯定響應5001,已經實現了通過基礎診斷來完成uds診斷的功能。
但是,細心的朋友應該發現診斷控制臺中拋出了紅色的錯誤信息,這是因為我們沒有給這個診斷請求設置對應的響應屬性導致的,接下來,我們一起設置一下這個診斷請求的響應
在基礎診斷配置界面,找到需要配置的服務,點擊紅框中的Response,即可配置其對應的響應,可以看到,我這個服務默認的響應是24個bit的(2個字節的50 01 加上一個字節的參數),而我的ECU實際回復了兩個字節(50 01),沒有參數,導致響應字節匹配錯誤而報出異常,這里我將響應參數刪掉,即沒有子參數。
在參數處點擊對應的參數,右鍵鼠標選擇delete,刪除掉參數即可(刪除就是0bit),此時對應的響應就是2字節了
修改完成之后,點擊最上方的commit重新生成一下對應的診斷控制臺,隨后,我們重新點擊這兩個服務進行一下診斷服務的發送
可以看到,此時右下角已經不會拋出異常錯誤了(長度已經匹配正確)
看到這里,應該就已經掌握了添加一個基礎診斷進行發送uds診斷的能力了。
3.2.4、使用基礎診斷進行27服務安全解鎖
此處我們再添加一個服務,用于對ECU進行安全訪問控制
打開基礎診斷配置界面,選擇27服務,這里將安全會話等級設置為自己需要的等級,我就設置為level1了,請求參數根據自己的需要進行設置,我這邊沒有參數,就對參數進行了刪除,然后響應參數的話,我這邊會回復4字節的種子,于是我將響應參數長度設置為4 * 8 = 32bit
然后,再添加一個27 02服務,用于發送秘鑰,并將參數長度設置為4字節(32bit),通常情況下種子與秘鑰的長度是一樣的,種子為4字節,秘鑰也就是4字節
重新commit,就可以試一下我們剛剛添加的27服務了
3.2.5、添加安全解鎖dll算法文件
由于安全訪問需要用到安全訪問算法,我們需要在診斷描述文件那里添加一個seedKey的dll算法。
(大家如果有,可以使用自己的dll算法,沒有的話,我會在評論區提供一個我自己隨便封裝的一l算法文件dll,但使用我的dll就必須使用我的仿真代碼來充當ECU了)
雙擊此處導入dll文件,導入完成后點擊ok即可
隨后來到診斷控制臺,即可雙擊2701服務進行安全解鎖了。
依次進入拓展會話(10 03),2701請求種子,2702發送秘鑰,即可對ECU實現解鎖~。
3.2.6、其他uds服務
對于其他的uds服務,添加方法和流程是一樣的,這里不再一一進行配置了,大家掌握了配置方法之后自己進行配置嘗試即可。
四、有診斷數據庫文件(CDD、PDX、ODX)進行uds診斷
這種情況就比較簡單了,當大家手里有現成的CDD文件或者pdx文件之后,直接進行導入即可使用。
同樣的,打開診斷描述文件配置界面
在對應的總線處右擊鼠標,選擇第一個選項即可
隨后在彈出的界面中選擇自己的cdd文件就可以了。
添加完成之后,只需要配置一下對應的27服務seedkey.dll文件即可使用
配置完成后,就會出現剛剛導入的cdd診斷了
其余使用項基本與基礎診斷一致,不再贅述。
五、通過CAPL的仿真節點調用診斷庫函數進行uds診斷
大家可以根據自己的需求,將CAPL代碼文件創建在對應的節點內。
在我的工程中,我就新建了一個空白節點,將代碼寫在此節點內
5.1、CAPL中使用進行uds診斷的基本步驟
在CAPL中要通過診斷庫函數進行uds診斷,需要先設置對應的診斷目標ECU(通過匹配目標ECU,CAPL才能知道我們的診斷請求應該通過哪個ID來發送,哪個ID是診斷響應)
這個過程通過函數:diagSetTarget來實現
long diagSetTarget (char ecuName[]) |
在CAPL中,診斷請求和響應也是一種變量類型,我們要發送診斷請求,可以通過定義一個診斷請求變量,然后調用CAPL的診斷發送函數來進行發送
這個過程通過關鍵字:diagRequest來定義診斷請求
通過diagSendRequest函數來發送物理尋址的診斷請求
通過diagSendFunctional函數來發送功能尋址的診斷請求
long diagSendRequest (diagRequest obj) |
long diagSendFunctional( diagRequest request) |
發送診斷請求就這么兩個步驟
5.2、CAPL中定義診斷請求
診斷請求的定義有好幾種方式
diagRequest * diagReq;//方式1,定義一個通用的診斷請求,不限制診斷服務類型,不限制ECU
diagRequest BasicDiagnosticsEcu.* diagReq1;//方式2,定義一個名為"BasicDiagnosticsEcu"的ECU的通用診斷服務,不限制診斷服務類型,限制ECU
diagRequest BasicDiagnosticsEcu.SecurityAccess390 diagReq2;//方式3,定義一個名為"BasicDiagnosticsEcu"的ECU的通用診斷服務,限制診斷服務類型,限制ECU
其中,診斷ECU名稱就是診斷描述文件的名稱,即下圖紅框處的名稱
診斷響應的定義與請求完全一致,僅關鍵字不同。
定義診斷響應使用關鍵字?diagResponse
5.3、CAPL中發送診斷請求
5.3.1、通用診斷請求
代碼
on start
{char ECU_Name[32] = "BasicDiagnosticsEcu";//定義診斷ECU名稱,此名稱需要與診斷描述文件完全相同diagSetTarget(ECU_Name);
}on key'A'
{diagRequest * diagReq;//定義一個通用的診斷對象byte diagReqData[2] = {0x10,0x02};//定義請求內容diagReq.Resize(2);//重新設定診斷請求的長度diagReq.SetPrimitiveData(diagReqData,2);//設置診斷請求的原始數據,設置為數組中的內容,并設置數據長度diagSendRequest(diagReq);//發送診斷請求write("使用CAPL發送診斷請求");
}
在這段代碼中,我使用按鍵A來發送一個1002請求,由于我定義的是一個通用的診斷請求,CAPL并不知道這個請求的內容和數據長度,所以需要人為調整這個請求的數據和長度
運行代碼,按下按鍵看看
通過運行結果可以看到,當按下A之后,對應的事件代碼被觸發,發送了我們定義的10 02請求
5.3.2、發送診斷數據庫中的診斷請求
發送診斷數據庫中的診斷請求,并通用的請求簡單一些,只需要定義完成之后調用發送函數即可,像這樣
代碼
on start
{char ECU_Name[32] = "BasicDiagnosticsEcu";//定義診斷ECU名稱,此名稱需要與診斷描述文件完全相同diagSetTarget(ECU_Name);
}on key'B'
{diagRequest BasicDiagnosticsEcu.ExtendedSession diagReq;//定義"BasicDiagnosticsEcu"內的拓展會話請求diagSendRequest(diagReq);
}
運行代碼,按下B按鍵看看
可以看到,按下按鍵B之后,對應的診斷請求:BasicDiagnosticsEcu.ExtendedSession被發送了出去,這個請求就是我們在基礎診斷數據庫中定義的.ExtendedSession 1003服務
5.4、CAPL中獲取診斷響應
CAPL提供了獲取診斷響應的兩種方式
5.4.1、診斷響應回調函數(診斷響應事件)方式獲取診斷響應
CAPL提供了類似于鍵盤事件,on start事件的診斷響應事件,當用戶設置了診斷對象的ECU之后,收到診斷響應ID的數據,會觸發這個事件
代碼
on start
{char ECU_Name[32] = "BasicDiagnosticsEcu";//定義診斷ECU名稱,此名稱需要與診斷描述文件完全相同diagSetTarget(ECU_Name);
}on key'B'
{diagRequest BasicDiagnosticsEcu.ExtendedSession diagReq;//定義"BasicDiagnosticsEcu"內的拓展會話請求diagSendRequest(diagReq);
}on diagResponse *//*號表示所有的,即所有的診斷響應都會觸發該事件
{byte diagRespData[4096];//定義存儲診斷響應的buff數組long diagRespDataLen;//診斷響應長度int i;this.GetPrimitiveData(diagRespData,4096);//將診斷響應的原始數據存儲在此數組中diagRespDataLen=this.GetPrimitiveSize(); // 獲取診斷響應的大小write("診斷響應長度為%d",diagRespDataLen);for(i=0;i<diagRespDataLen;i++){write("收到的診斷響應數據為%.2LX",diagRespData[i]);//打印診斷響應}
}
運行代碼看看
如圖所示,CAPL識別到了對應的診斷響應,并獲取到了診斷響應的值。
5.4.2、使用函數直接獲取診斷響應(不借助事件回調)
CAPL除了事件回調之外,還提供了一個函數用于直接獲取對應的診斷響應
long diagGetLastResponse (diagRequest req, diagResponse respOut); | ||||
long diagGetLastResponse (diagResponse respOut); |
在需要的時刻調用此函數即可獲取到對應的響應
代碼
on start
{char ECU_Name[32] = "BasicDiagnosticsEcu";//定義診斷ECU名稱,此名稱需要與診斷描述文件完全相同diagSetTarget(ECU_Name);
}on key'B'
{diagRequest BasicDiagnosticsEcu.ExtendedSession diagReq;//定義"BasicDiagnosticsEcu"內的拓展會話請求diagSendRequest(diagReq);
}on key'C'
{diagResponse * resp;//定義一個診斷響應byte diagRespData[4096];//定義存儲診斷響應的buff數組long diagRespDataLen;//診斷響應長度int i;diagGetLastResponse(resp);//將上一次的診斷響應賦值給剛剛定義的變量resp.GetPrimitiveData(diagRespData,4096);//將診斷響應的原始數據存儲在此數組中diagRespDataLen=resp.GetPrimitiveSize(); // 獲取診斷響應的大小write("診斷響應長度為%d",diagRespDataLen);for(i=0;i<diagRespDataLen;i++){write("收到的診斷響應數據為%.2LX",diagRespData[i]);//打印診斷響應}
}
在未按下C按鍵,即未調用獲取響應的函數之前,write窗口沒有診斷響應的信息
按下C按鍵之后,diagGetLastResponse函數被調用,成功獲取到了上一次的響應。
以上,便是CAPL中獲取診斷響應的兩種方式,大家可以根據需要自行選擇合適的方式。
5.5、CAPL中借助診斷描述文件中導入的seedKey.dll文件通過安全訪問
在上面的文章中,我們已經知道了如何使用CAPL發送一個診斷請求,也知道了如何獲取對應的診斷響應。
大家在使用CAPL進行診斷的過程中,免不了要經常進行27訪問,那么如何在CAPL中通過安全訪問呢?
這里可以直接調用診斷描述文件中導入的dll文件獲取27秘鑰
在CAPL中調用診斷描述文件中導入的dll文件獲取27秘鑰依靠函數diagGenerateKeyFromSeed
long diagGenerateKeyFromSeed ( byte seedArray[], dword seedArraySize, dword securityLevel, char variant[], char ipOption[], byte keyArray[], dword maxKeyArraySize, dword& keyActualSizeOut); // form 1 | |||
long DiagGenerateKeyFromSeed(char ecuQualifier[], byte seedArray[] , dword seedArraySize, dword securityLevel, char variant[], char option[] , byte keyArray[], dword maxKeyArraySize, dword& keyActualSizeOut); // form 2 |
通常情況下使用重載形式1即可
總共8個參數
seedArray:種子數組
seedArraySize:種子的長度
securityLevel:安全訪問等級,2701則等級為0x1,2703則等級為0x3,2709則等級為0x9...
variant:診斷描述字符串,通常寫一個空白字符串或者"Variant1"
ipOption:診斷描述字符串,通常寫一個空白字符串或者"option"
keyArray:存放秘鑰的數組
maxKeyArraySize:秘鑰數組最大可能/最大允許的長度
keyActualSizeOut:秘鑰數組中實際使用的字節/秘鑰實際的長度。此參數在c++中是一個變量的引用,在CAPL中直接傳變量即可
代碼
variables
{diagRequest * diagReqSeed;//定義一個通用的診斷請求,用于請求種子diagRequest * diagReqKey;//定義通用的診斷請求,用于發送秘鑰byte gSeedArray[4];//存儲秘鑰的數組,這里我由于我的種子長度為4,所以給4個字節dword gSeedArraySize = 4;//種子長度dword gSecurityLevel = 0x01;//安全等級char gVariant[200] = "Variant1";//診斷對象描述char gOption[200] = "option";//診斷對象描述byte gKeyArray[255];//存儲種子的數組dword gMaxKeyArraySize = 255;//種子可能的最大長度dword gActualSizeOut;//種子的實際長度
}on start
{char ECU_Name[32] = "BasicDiagnosticsEcu";//定義診斷ECU名稱,此名稱需要與診斷描述文件完全相同diagSetTarget(ECU_Name);//設置診斷對象名稱
}On key'B'
{byte reqData[2] = {0x27,0x01};//定義一個種子請求數組內容word reqDataLen;//種子請求的長度word i;reqDataLen = elcount(reqData);//計算種子請求的長度diagReqSeed.Resize(reqDataLen);//重置用于請求種子的診斷請求的長度diagReqSeed.SetPrimitiveData(reqData,reqDataLen);//將請求數組的數據設置給用于請求種子的診斷請求diagSendRequest(diagReqSeed);//發送診斷請求
}on diagResponse *
{byte respData[4096];//定義存放診斷響應的數組word respDataLen; //存放診斷響應的數組長度respDataLen = elcount(respData);//計算長度this.GetPrimitiveData(respData,respDataLen);//獲取診斷響應到響應數組中if(respData[0] == 0x67 && respData[1] == 0x01)//如果回復的前面兩個字節是67 01,即回復的是種子{byte keyReqData[6];//秘鑰請求數組word keyReqLen;//秘鑰請求數組長度keyReqLen = elcount(keyReqData);//計算長度memcpy_off(gSeedArray,0,respData,2,4);//將respdata的第2~6個字節拷貝放在gSeedArray中,即種子diagGenerateKeyFromSeed(gSeedArray,gSeedArraySize,gSecurityLevel,gVariant,gOption,gKeyArray,gMaxKeyArraySize,gActualSizeOut);//調用診斷描述文件中dll計算秘鑰的函數//此時已經獲得了秘鑰,存在在keyArray中keyReqData[0] = 0x27;keyReqData[1] = gSecurityLevel + 1;//設置對應的秘鑰請求前兩個字節的內容,第一個字節為0x27,第二個字節為種子請求+1memcpy_off(keyReqData,2,gKeyArray,0,4);//將秘鑰拷貝到秘鑰請求數組中去diagReqKey.Resize(keyReqLen);//調節診斷請求大小diagReqKey.SetPrimitiveData(keyReqData,keyReqLen);//設置發送秘鑰的診斷請求的數據diagSendRequest(diagReqKey);//發送秘鑰診斷請求}
}
運行一下,看看
由于在診斷響應處理中,我使用的是回調方式,并且在獲取完秘鑰之后就發送出去了,所以就自動的通過了安全訪問~
5.6、CAPL發送其他診斷請求
發送其他的診斷請求的方法與上述完全一致,如果是定義的通用型的,則需要調用resize函數先重新設置診斷請求的大小,再調用SetPrimitiveData函數設置診斷請求的數據,隨后調用diagSendRequest或者diagSendFunctional函數進行發送診斷請求即可。
如果是定義的數據庫中的診斷請求,就更簡單了,定義完成之后,直接調用發送函數即可。
CAPL的診斷函數異常強大,由于是入門篇,本篇就介紹到這里。