Autosar Xcp移植
前言:
可能有兄弟胸中有點疑問, Vector ETAS等軟件包都有XCP,你移植個吊,但是我的MICORSAR BSW中沒發(fā)現(xiàn)XCP靜態(tài)代碼,倒是在CFG中發(fā)現(xiàn)了XCP配置選項,難道這部分代碼全是動態(tài)生成?
好吧,不論如何現(xiàn)在也不知道怎么在工具里配置XCP,先移植Vector Xcp Basic, 順便讀下代碼, 加深一下理解。這個軟件包大家可以在網(wǎng)上找找,或者Vector官網(wǎng)找找, 免費的,可以滿足基本的標定/測量需求。
本篇文章的主要目的是從配置和代碼角度學習一下XCP協(xié)議,至于能否直接用于項目請君自行斟酌。
首先說明我們配置的是Xcp on CAN, 所以必須你的MCU要能收發(fā)標準CAN消息,當然只要你搞清楚了移植步驟,之后遷移到Xcp on CAN FD 或者 on Tcp/ip都是可以的。
Mcu 的CanDrv 至少要提供像CanTransmit(Canid, DLC, void * data)這樣的函數(shù), CAN接收到的MSG 需要傳入XcpCommand((vuint32*)&XcpRequest[0])進行處理;所以即使你是STM32+FreeRtos這樣的組合也是可以移植Xcp協(xié)議的。
- 環(huán)境 硬件->NXP S32K148EVB 軟件->Vector Davinci套件
- 工具配置
所以我們先新建立一個工程, 命名為XcpDemo, 我們把工程中BSW部分,MCAL部分都已經(jīng)配置好了,我們這里只討論如何添加Xcp這一個模塊。
我們首先要改下DBC文件,這樣導入CFG中就能生成基本的通信配置
通過CANdb++添加如下兩個Nodes, Msgs, Sigs CANID Master -> Slave 0x760
Slave->Master 0x780 兩幀MSG各包含一個Signal SG_XcpRes和SG_XcpReq, 都占8Bytes。
打開Developer
新建如圖所示兩個SWCs 其中TestSWC是用來對標定和測量變量做測試的,內(nèi)容如下。
可以看到我們定義了一個標定變量和四個觀測變量(后面自己手動加上去的,如果用Simulink自動生成代碼的話,可以將標定和測量變量都生成到同一個文件), 至于"#pragma default_variable_attributes = .monitorRam" 是IAR編譯器特定的宏,同時需要修改鏈接文件.icf,定義flash和ram分區(qū), 將標定和觀測變量放到自己的分區(qū)中去,這里不展開討論。
XcpIf這個SWC是Xcp接口
在XcpIf 中定義3個Runable 它們的Trigger 分別為
由于Xcp需要使用BSW中ComM的接口, 所以需要定義一個Service Port, 并引用默認存在的ComM接口(C/S類型)
同時定義兩個引用S/RInterface 的端口(Pport 向外發(fā)送Xcp Response, Rp 接收Tester發(fā)送的Xcp Request)
將兩個SWC拖入ECU_Compoment中, 將XcpIf 的兩個端口右鍵
代表這兩個端口不做內(nèi)部連接, 而是需要和外部其他ECU進行Sender/Reciver交互的, Rte 會自動識別并將這個兩個Port的Read/Write操作轉(zhuǎn)化為Com_ReadSignal/Com_SendSignal, 當然既然是Signal還有做DataMapping
這是Mapping好的Port中的Data Element 和對應的CAN Signal
想要RTE生成正確的代碼還需要再CFG中將SWC和內(nèi)部BSW建立連接
才能生成如下代碼
XcpDAQHandler為DAQ功能周期發(fā)送DTO,觸發(fā)周期為1ms, 但是上位機可以通過Prescaler分別控制每個DAQList的上傳周期。比如DAQList0->Prescaler = 10,那么XcpDAQHandler里會對DAQList0->Prescaler自減, 當其為0才真正通過CAN將DTO發(fā)送出去。
XcpRxHandler 觸發(fā)為接收到ID 為0X760(Master->Slave)MSG
可以看到這個CallBack是RTE自動生成的,會在CAN控制器接收到0x760消息的回調(diào)函數(shù)里進行匹配和逐級調(diào)用
XcpIfInit是協(xié)議初始化
3個Runables 需要Map到Task中去才能運行起來
在生成的XcpIf模板中進行代碼編寫
FUNC(void, XcpIf_CODE) XcpDAQHandler(void) /* PRQA S 0850 */ /* MD_MSR_19.8 */ {uint8 i = 0;for (i = 0; i < xcp.Daq.DaqCount; i++){vuint8 event = xcp.Daq.u.DaqList[i].eventChannel;XcpEvent(event);} } FUNC(void, XcpIf_CODE) XcpIfInit(void) /* PRQA S 0850 */ /* MD_MSR_19.8 */ {Rte_Call_ComM_UserRequest_RequestComMode(COMM_FULL_COMMUNICATION);XcpInit(); }FUNC(void, XcpIf_CODE) XcpRxHandler(void) /* PRQA S 0850 */ /* MD_MSR_19.8 */ {uint8 XcpRequest[8] = {0};Rte_Read_Rp_XcpRequest_De_XcpRequest(XcpRequest); /*將CMD讀到XcpRequest中*/XcpCommand((vuint32*)&XcpRequest[0]); } 對三個Runable進行代碼添加 其中XcpCommand XcpInit xcp.Daq.u.DaqList 等都是在Vector 提供的XcpBaic 代碼包中定義和實現(xiàn)的,這里只需正確使用。 #include "Appl_Cbk.h" #include "CanIf.h" #include "XcpBasic.h" #include "Os.h"這是XcpIf.c 自己添加的頭文件
#define XCP_RESPONSE_CANIF_PDUID 0 static vuint8 xcpResourceMask = 0;void Xcp_Cbk_XcpResponseTxSuccess(void) {XcpSendCallBack(); }void ApplXcpSend( vuint8 len, BYTEPTR msg ) {PduInfoType PduInfoPtr;if(len > 8){len = 8;}PduInfoPtr.SduLength = len;PduInfoPtr.SduDataPtr = msg;CanIf_Transmit(XCP_RESPONSE_CANIF_PDUID, &PduInfoPtr); }void ApplXcpSendFlush(void) {}void ApplXcpInit() {}vuint8 ApplXcpGetSeed( vuint8 resourceMask, vuint8 *seed ) {/*RM_CAL_PAG|RM_DAQ|RM_PGM|RM_STIM 可分別返回不同SEED*/xcpResourceMask = resourceMask;seed[0] = 0;seed[1] = 1;seed[2] = 2;seed[3] = 3;seed[4] = 4;seed[5] = 5;return 6; }vuint8 ApplXcpUnlock( const vuint8 * key, vuint8 length ) {/* Ckeck the key */if((length == 2) && (key[0] == 0xBE) && (key[1] == 0xEF)){xcp.ProtectionStatus &= ~xcpResourceMask; /* Reset the appropriate resource protection mask bit */return 1;}else{return 0; /*Key not correct*/} }MTABYTEPTR ApplXcpGetPointer( vuint8 addr_ext, vuint32 addr ) {addr_ext = addr_ext;// /*S32K為小端格式*/// vuint32 new_addr = ( ((addr & 0XFF000000) >> 24) | ((addr & 0X00FF0000) >> 8) |// ((addr & 0X0000FF00) << 8) | ((addr & 0X000000FF) << 24));/*如果上位機默認按小端格式傳輸則下位機無需更改字節(jié)序*/return (MTABYTEPTR)addr; }void ApplXcpInterruptDisable() {SuspendAllInterrupts(); }void ApplXcpInterruptEnable( void ) {ResumeAllInterrupts(); }ApplXcpSend 這一類函數(shù)都是在XcpBasic.h中申明,但是沒有具體實現(xiàn),需要用戶自己實現(xiàn)的函數(shù),它們只在Xcp協(xié)議內(nèi)部使用,大家可以參考代碼包中的例程。
這里說下為啥ApplXcpSend 調(diào)用CanIf_Transmit 而不是之前生成的
Rte_Write_XcpIf_Pp_XcpResponse_De_XcpResponse(uint64 data);
因為這個函數(shù)實際會調(diào)用Com_SendSignal , 如果發(fā)送Response走Com->PduR->CanIf->CanDrv
的話Com中的Signal DLC是事先定好的,比如8Bytes那么每次都會發(fā)8Bytes,而Xcp Response 是需要根據(jù)不同響應動態(tài)改變DLC的,且Com發(fā)送PDU是有Periodic 和 Trigger兩種模式,前一種模式是按照DBC中設定的周期周期發(fā)送,后一種無需計算周期直接發(fā)送,這里不如直接用CanIf層進行發(fā)送來的方便, 但是在Com中需配置
也就是SG_XcpRes 發(fā)送成功的Notification函數(shù)Xcp_Cbk_XcpResponseTxSuccess
在里面放上XcpBasic.c中實現(xiàn)的XcpSendCallBack才正確。
配置就說這么多,這樣框架就搭好了,大家自行琢磨。
- 代碼
我們可以看到這是Vector 提供的代碼, 其主要邏輯就是在XcpBasic.c中實現(xiàn),大家可以把它們集成到自己的工程中去,進行編譯。
我們來理一下Xcp協(xié)議棧的運行步驟
Tester-> send msg 0x760 + 8bytes -> ECU CAN Transiver -> Ecu CAN Interrupt -> CanIf_RxIndication -> PduR_RxIndication -> Com_RxIndication -> 對Signal XcpRequest 進行解包并拷貝至信號的緩存區(qū)-> 調(diào)用Rte_COMCbk_XcpReq_oXcpRequest_oCAN_42940af5_Rx callback激活Task(Xcp_Task) -> 調(diào)用Runable XcpRxHandler() -> Rte_Read_Rp_XcpRequest_De_XcpRequest 將緩存區(qū)的Signal 數(shù)據(jù)讀出并傳入XcpCommand -> Xcp 協(xié)議棧進行響應-> 如需向Tester 發(fā)出Response, 調(diào)用ApplXcpSend接口。
那么我們首先應該分析XcpCommand這個最重要的函數(shù), 它其實就是一個狀態(tài)機
這里總結(jié)下Xcp 標定/測量的流程
首先肯定要建立連接
M->S FF 00 00 00 00 00 00 00 請求建立連接
S->M FF 連接成功
M->S F8 00 01 00 00 00 00 00 解鎖RM_CAL_PAG 標定權(quán)限
S->M FF 06 01 02 03 04 05 06 Slave發(fā)回6Bytes 種子
M->S F7 02 BE EF 00 00 00 00 發(fā)回KEY進行解鎖
S-M FF 解鎖成功,可以標定
M->S SET_MTA 傳入需要標定變量的地址(Master根據(jù)A2L文件)
M->S Download Size+Data 這樣存在RAM中的標定變量就可以被改寫了
測量的話分為Polling 和 DAQ, 其中Polling較為簡單,但效率較低,不適合速度快一點的采集,DAQ模式較為復雜,這里就不具體分析代碼了,只有弄明白什么是DAQList,Odt ,OdtEntryAddress, OdtEntrySize以及它們在這套代碼中是如何分配內(nèi)存的就可以了
需注意由于這里采用CAN通信,一個DTO最多發(fā)送8個字節(jié), 其中第一個字節(jié)是Odt編號,也就說真正用于發(fā)送Data的只有7個字節(jié),而Xcp不像UDS或Tcp那樣可以發(fā)連續(xù)幀,有FlowCtrl
一個Dto發(fā)送一個Odt包含這個Odt中所有EntryAddres中存放的EntrtySize大小的數(shù)據(jù)
所以一個Odt中最多可包含MaxDto-1個Entry 并且要Size 都為1Bytes
也就說我有4個 uint32 moni_vars 那么一個Odt肯定放不下, 需要四個Odt
DAQ 簡要的流程是 FreeDAQ -> Allocate DAQList -> Allocate Odt -> Allocate Entry ->
WriteDAQ (為每個Entry 寫上變量的具體地址和大小,比如 uint32 moni_var1 adress:0x1FFF8000 size:4bytes)-> Start DAQ(這里可以分別為每個DAQList 設置Prescaler, 即采樣周期)
相當于先在ECU中建立好一張采樣表,并設定周期開始采樣。
其他需要注意的是一定要打開XCP_ENABLE_SEND_QUEUE這個宏,否則DAQ會丟幀,另外根據(jù)工具(上位機)的不同需要注意一下上位機傳數(shù)據(jù)的大小段格式。
附上Xcp_Cfg.h
總結(jié)
以上是生活随笔為你收集整理的Autosar Xcp移植的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XCP BASIC安装
- 下一篇: 有什么适合打游戏的TWS降噪耳机?游戏降