日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Autosar Xcp移植

發(fā)布時間:2024/3/26 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Autosar Xcp移植 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言:
可能有兄弟胸中有點疑問, 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)找找, 免費的,可以滿足基本的標(biāo)定/測量需求。
本篇文章的主要目的是從配置和代碼角度學(xué)習(xí)一下XCP協(xié)議,至于能否直接用于項目請君自行斟酌。
首先說明我們配置的是Xcp on CAN, 所以必須你的MCU要能收發(fā)標(biāo)準(zhǔn)CAN消息,當(dāng)然只要你搞清楚了移植步驟,之后遷移到Xcp on CAN FD 或者 on Tcp/ip都是可以的。
Mcu 的CanDrv 至少要提供像CanTransmit(Canid, DLC, void * data)這樣的函數(shù), CAN接收到的MSG 需要傳入XcpCommand((vuint32*)&XcpRequest[0])進(jìn)行處理;所以即使你是STM32+FreeRtos這樣的組合也是可以移植Xcp協(xié)議的。

  • 環(huán)境 硬件->NXP S32K148EVB 軟件->Vector Davinci套件
  • 工具配置
    所以我們先新建立一個工程, 命名為XcpDemo, 我們把工程中BSW部分,MCAL部分都已經(jīng)配置好了,我們這里只討論如何添加Xcp這一個模塊。
    我們首先要改下DBC文件,這樣導(dǎo)入CFG中就能生成基本的通信配置
BU_: INCA MyECUBO_ 1920 XcpResponse: 8 MyECUSG_ XcpRes : 0|64@1+ (1,0) [0|1.84467440737096E+019] "" INCABO_ 1888 XcpRequest: 8 INCASG_ XcpReq : 0|64@1+ (1,0) [0|1.84467440737096E+019] "" MyECU

通過CANdb++添加如下兩個Nodes, Msgs, Sigs CANID Master -> Slave 0x760
Slave->Master 0x780 兩幀MSG各包含一個Signal SG_XcpRes和SG_XcpReq, 都占8Bytes。
打開Developer


新建如圖所示兩個SWCs 其中TestSWC是用來對標(biāo)定和測量變量做測試的,內(nèi)容如下。

#include "Dio.h"#pragma default_variable_attributes = @ ".caliConst_Ram"static uint16 cali_test1 = 3000;#pragma default_variable_attributes =#pragma default_variable_attributes = @ ".monitorRam"static uint8 moni_u8 = 3; static uint16 moni_u16 = 0; static float32 moni_float = 1.2F; static uint32 moni_u32 = 0;#pragma default_variable_attributes = FUNC(void, TestSWC_CODE) Runnable_Test(void) /* PRQA S 0850 */ /* MD_MSR_19.8 */ { /*********************************************************************************************************************** DO NOT CHANGE THIS COMMENT! << Start of runnable implementation >> DO NOT CHANGE THIS COMMENT!* Symbol: Runnable_Test*********************************************************************************************************************/if(cali_test1 > 3000){LedOn();}else{LedOff();}if(moni_u8 >= 255){moni_u8 = 0;}if(moni_u16 >= 255){moni_u16 = 0;}if(moni_u32 > 1000){moni_u32 = 0;}if(moni_float > 100.0F){moni_float = 0.0F;}moni_u8++;moni_u16++;moni_u32++;moni_float = moni_float + 1.0F; /*********************************************************************************************************************** DO NOT CHANGE THIS COMMENT! << End of runnable implementation >> DO NOT CHANGE THIS COMMENT!*********************************************************************************************************************/ }

可以看到我們定義了一個標(biāo)定變量和四個觀測變量(后面自己手動加上去的,如果用Simulink自動生成代碼的話,可以將標(biāo)定和測量變量都生成到同一個文件), 至于"#pragma default_variable_attributes = .monitorRam" 是IAR編譯器特定的宏,同時需要修改鏈接文件.icf,定義flash和ram分區(qū), 將標(biāo)定和觀測變量放到自己的分區(qū)中去,這里不展開討論。
XcpIf這個SWC是Xcp接口
在XcpIf 中定義3個Runable 它們的Trigger 分別為




由于Xcp需要使用BSW中ComM的接口, 所以需要定義一個Service Port, 并引用默認(rèn)存在的ComM接口(C/S類型)

同時定義兩個引用S/RInterface 的端口(Pport 向外發(fā)送Xcp Response, Rp 接收Tester發(fā)送的Xcp Request)

將兩個SWC拖入ECU_Compoment中, 將XcpIf 的兩個端口右鍵


代表這兩個端口不做內(nèi)部連接, 而是需要和外部其他ECU進(jìn)行Sender/Reciver交互的, Rte 會自動識別并將這個兩個Port的Read/Write操作轉(zhuǎn)化為Com_ReadSignal/Com_SendSignal, 當(dāng)然既然是Signal還有做DataMapping

這是Mapping好的Port中的Data Element 和對應(yīng)的CAN Signal

想要RTE生成正確的代碼還需要再CFG中將SWC和內(nèi)部BSW建立連接
才能生成如下代碼

# define Rte_Read_Rp_XcpRequest_De_XcpRequest Rte_Read_XcpIf_Rp_XcpRequest_De_XcpRequest # define Rte_Read_XcpIf_Rp_XcpRequest_De_XcpRequest(data) (Com_ReceiveSignal(ComConf_ComSignal_XcpReq_oXcpRequest_oCAN_42940af5_Rx, (data))) /* PRQA S 3453 */ /* MD_MSR_19.7 */ # define Rte_Call_ComM_UserRequest_RequestComMode(arg1) (ComM_RequestComMode((ComM_UserHandleType)0, arg1)) /* PRQA S 3453 */ /* MD_MSR_19.7 */

XcpDAQHandler為DAQ功能周期發(fā)送DTO,觸發(fā)周期為1ms, 但是上位機(jī)可以通過Prescaler分別控制每個DAQList的上傳周期。比如DAQList0->Prescaler = 10,那么XcpDAQHandler里會對DAQList0->Prescaler自減, 當(dāng)其為0才真正通過CAN將DTO發(fā)送出去。
XcpRxHandler 觸發(fā)為接收到ID 為0X760(Master->Slave)MSG

FUNC(void, RTE_CODE) Rte_COMCbk_XcpReq_oXcpRequest_oCAN_42940af5_Rx(void) /* PRQA S 0850 */ /* MD_MSR_19.8 */ {if (Rte_InitState == RTE_STATE_INIT){/* scheduled trigger for runnables: XcpRxHandler */(void)SetEvent(OsTask_Xcp, Rte_Ev_Run_XcpIf_XcpRxHandler); /* PRQA S 3417 */ /* MD_Rte_Os */} } /* PRQA S 6010, 6050 */ /* MD_MSR_STPTH, MD_MSR_STCAL */

可以看到這個CallBack是RTE自動生成的,會在CAN控制器接收到0x760消息的回調(diào)函數(shù)里進(jìn)行匹配和逐級調(diào)用
XcpIfInit是協(xié)議初始化

3個Runables 需要Map到Task中去才能運行起來

TASK(OsTask_Xcp) /* PRQA S 3408, 1503 */ /* MD_Rte_3408, MD_MSR_14.1 */ {EventMaskType ev;/* call runnable */XcpIfInit();for(;;){(void)WaitEvent(Rte_Ev_Run_XcpIf_XcpDAQHandler | Rte_Ev_Run_XcpIf_XcpRxHandler); /* PRQA S 3417 */ /* MD_Rte_Os */(void)GetEvent(OsTask_Xcp, &ev); /* PRQA S 3417 */ /* MD_Rte_Os */(void)ClearEvent(ev & (Rte_Ev_Run_XcpIf_XcpDAQHandler | Rte_Ev_Run_XcpIf_XcpRxHandler)); /* PRQA S 3417 */ /* MD_Rte_Os */if ((ev & Rte_Ev_Run_XcpIf_XcpDAQHandler) != (EventMaskType)0){/* call runnable */XcpDAQHandler();}if ((ev & Rte_Ev_Run_XcpIf_XcpRxHandler) != (EventMaskType)0){/* call runnable */XcpRxHandler();}} } /* PRQA S 6010, 60

在生成的XcpIf模板中進(jìn)行代碼編寫

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進(jìn)行代碼添加 其中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));/*如果上位機(jī)默認(rèn)按小端格式傳輸則下位機(jī)無需更改字節(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ù)不同響應(yīng)動態(tài)改變DLC的,且Com發(fā)送PDU是有Periodic 和 Trigger兩種模式,前一種模式是按照DBC中設(shè)定的周期周期發(fā)送,后一種無需計算周期直接發(fā)送,這里不如直接用CanIf層進(jìn)行發(fā)送來的方便, 但是在Com中需配置

也就是SG_XcpRes 發(fā)送成功的Notification函數(shù)Xcp_Cbk_XcpResponseTxSuccess
在里面放上XcpBasic.c中實現(xiàn)的XcpSendCallBack才正確。
配置就說這么多,這樣框架就搭好了,大家自行琢磨。

  • 代碼


我們可以看到這是Vector 提供的代碼, 其主要邏輯就是在XcpBasic.c中實現(xiàn),大家可以把它們集成到自己的工程中去,進(jìn)行編譯。

我們來理一下Xcp協(xié)議棧的運行步驟
Tester-> send msg 0x760 + 8bytes -> ECU CAN Transiver -> Ecu CAN Interrupt -> CanIf_RxIndication -> PduR_RxIndication -> Com_RxIndication -> 對Signal XcpRequest 進(jìn)行解包并拷貝至信號的緩存區(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é)議棧進(jìn)行響應(yīng)-> 如需向Tester 發(fā)出Response, 調(diào)用ApplXcpSend接口。
那么我們首先應(yīng)該分析XcpCommand這個最重要的函數(shù), 它其實就是一個狀態(tài)機(jī)

void XcpCommand( const vuint32* pCommand ) {const tXcpCto* pCmd = (const tXcpCto*) pCommand; /* PRQA S 0310 */ /* MD_Xcp_0310 */vuint8 err;if (CRO_CMD==CC_CONNECT){CRM_CMD = 0xFF; /* No Error */xcp.CrmLen = 1; /* Length = 1 */#if defined ( XCP_ENABLE_DAQ )if ( (xcp.SessionStatus & (SessionStatusType)SS_RESUME) == 0 ){XcpFreeDaq();#if defined ( XCP_ENABLE_SEND_QUEUE )xcp.SendStatus = 0; /* Clear all transmission flags */#endif} #endif /* XCP_ENABLE_DAQ */#if defined ( XCP_ENABLE_SEED_KEY )/* Lock all resources. */xcp.ProtectionStatus = (vuint8)RM_CAL_PAG|RM_DAQ|RM_PGM|RM_STIM; #endifxcp.SessionStatus = (SessionStatusType)SS_CONNECTED;xcp.CrmLen = CRM_CONNECT_LEN;/* Versions of the XCP Protocol Layer and Transport Layer Specifications. */CRM_CONNECT_TRANSPORT_VERSION = (vuint8)( (vuint16)XCP_TRANSPORT_LAYER_VERSION >> 8 );CRM_CONNECT_PROTOCOL_VERSION = (vuint8)( (vuint16)XCP_VERSION >> 8 );CRM_CONNECT_MAX_CTO_SIZE = kXcpMaxCTO;CRM_CONNECT_MAX_DTO_SIZE_WRITE(kXcpMaxDTO); /* PRQA S 3109 */ /* MD_MSR_14.3 */#if defined ( XCP_ENABLE_CALIBRATION_PAGE )CRM_CONNECT_RESOURCE = RM_CAL_PAG; /* Calibration */ #elseCRM_CONNECT_RESOURCE = 0x00; /* Reset resource mask */ #endif #if defined ( XCP_ENABLE_DAQ )CRM_CONNECT_RESOURCE |= (vuint8)RM_DAQ; /* Data Acquisition */ #endifCRM_CONNECT_COMM_BASIC = 0; #if defined ( XCP_ENABLE_COMM_MODE_INFO )CRM_CONNECT_COMM_BASIC |= (vuint8)CMB_OPTIONAL; #endif #if defined ( XCP_CPUTYPE_BIGENDIAN )CRM_CONNECT_COMM_BASIC |= (vuint8)PI_MOTOROLA; #endifgoto positive_response;}else{if ( (xcp.SessionStatus & (SessionStatusType)SS_CONNECTED) != 0 ){#if defined ( XCP_ENABLE_SEND_QUEUE )if ( (xcp.SendStatus & (vuint8)(XCP_CRM_PENDING|XCP_CRM_REQUEST)) != 0 ){xcp.SessionStatus |= (SessionStatusType)SS_ERROR; END_PROFILE(1); /* Timingtest *//* No response */return;} #endif#if defined ( XCP_ENABLE_GET_SESSION_STATUS_API )xcp.SessionStatus |= (SessionStatusType)SS_POLLING; #endifCRM_CMD = 0xFF; /* No Error */xcp.CrmLen = 1; /* Length = 1 */switch (CRO_CMD){case CC_SYNC:xcp.CrmLen = CRM_SYNCH_LEN;CRM_CMD = PID_ERR;CRM_ERR = CRC_CMD_SYNCH;break;#if defined ( XCP_ENABLE_COMM_MODE_INFO )case CC_GET_COMM_MODE_INFO:{xcp.CrmLen = CRM_GET_COMM_MODE_INFO_LEN;CRM_GET_COMM_MODE_INFO_DRIVER_VERSION = (vuint8)( ((CP_XCP_VERSION & 0x0F00) >> 4) |(CP_XCP_VERSION & 0x000F) );CRM_GET_COMM_MODE_INFO_COMM_OPTIONAL = 0;CRM_GET_COMM_MODE_INFO_QUEUE_SIZE = 0;CRM_GET_COMM_MODE_INFO_MAX_BS = 0;CRM_GET_COMM_MODE_INFO_MIN_ST = 0; /*查UDS相關(guān)概念*/}break; #endif case CC_DISCONNECT:xcp.CrmLen = CRM_DISCONNECT_LEN;XcpDisconnect();break;...代碼太多就不拷貝了大家可以仔細(xì)閱讀 }

這里總結(jié)下Xcp 標(biāo)定/測量的流程
首先肯定要建立連接
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 標(biāo)定權(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進(jìn)行解鎖
S-M FF 解鎖成功,可以標(biāo)定
M->S SET_MTA 傳入需要標(biāo)定變量的地址(Master根據(jù)A2L文件)
M->S Download Size+Data 這樣存在RAM中的標(biāo)定變量就可以被改寫了

測量的話分為Polling 和 DAQ, 其中Polling較為簡單,但效率較低,不適合速度快一點的采集,DAQ模式較為復(fù)雜,這里就不具體分析代碼了,只有弄明白什么是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 設(shè)置Prescaler, 即采樣周期)
相當(dāng)于先在ECU中建立好一張采樣表,并設(shè)定周期開始采樣。

其他需要注意的是一定要打開XCP_ENABLE_SEND_QUEUE這個宏,否則DAQ會丟幀,另外根據(jù)工具(上位機(jī))的不同需要注意一下上位機(jī)傳數(shù)據(jù)的大小段格式。
附上Xcp_Cfg.h

#if !defined(__XCP_CFG_H__) #define __XCP_CFG_H__/* define here configuration parameters for customizing XcpBasic driver */ #define V_MEMROM0 #define kXcpMaxCTO 8 #define kXcpMaxDTO 8 #define V_MEMROM0 #define XCP_DISABLE_COMM_MODE_INFO #define XCP_DISABLE_SERV_TEXT #define XCP_DISABLE_SERV_TEXT_PUTCHAR #define XCP_DISABLE_SERV_TEXT_PRINT #define XCP_DISABLE_CALIBRATION_PAGE #define XCP_TRANSPORT_LAYER_VERSION 0x100 #define XCP_DISABLE_UNALIGNED_MEM_ACCESS #define XCP_ENABLE_USE_BYTE_ACCESS #define XCP_CPUTYPE_LITTLEENDIAN #define XCP_ENABLE_SEND_EVENT /*send event支持*/ #define XCP_ENABLE_CALIBRATION #define XCP_ENABLE_PARAMETER_CHECK #define XCP_ENABLE_SEED_KEY #define XCP_ENABLE_DAQ #define XCP_DISABLE_DAQ_HDR_ODT_DAQ #define XCP_ENABLE_DAQ_PROCESSOR_INFO #define XCP_ENABLE_DAQ_RESOLUTION_INFO #define XCP_ENABLE_DAQ_PRESCALER #define XCP_ENABLE_DAQ_OVERRUN_INDICATION #define XCP_ENABLE_SEND_QUEUE #define kXcpDaqMemSize (1024*2)#endif /* __XCP_CFG_H__ */

總結(jié)

以上是生活随笔為你收集整理的Autosar Xcp移植的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。