> 技术文档 > 【Bootloader刷写方案设计】基于UDS协议通过CAN网络对单片机进行bootloader刷写_提取单片机程序 uds

【Bootloader刷写方案设计】基于UDS协议通过CAN网络对单片机进行bootloader刷写_提取单片机程序 uds


前言:bootloader是一段存储于单片机内的引导程序,它的存在使得工程师在更新程序时无需使用烧录器和拆开外部封装,仅仅通过在线或者离线的方式在外部进行更新。本篇将基于UDS协议,利用NXP的S32K144单片机及其开发平台阐述bootloader的设计方案。

目录

一、Bootloader刷写框架

二、Boot底层配置

时钟配置

CAN配置

boot工程配置 

 三、Boot底层设计

1、CAN任务分配

2、调度任务分配 

3、UDS服务分配  

4、编写跳转逻辑 

上位

总结


一、Bootloader刷写框架

         本篇文章阐述的刷写框架包括UDS-ISO14299协议、UDS-ISO15765-2协议、CAN网络协议、S32K144单片机底层开发、刷写上位机开发。

         UDS-ISO14299协议:该协议定义了各项通用功能服务,在设计时可根据需求选用所使用的服务。本篇中以10服务、27服务、34服务、36服务、37服务为例介绍boot的设计方案。

         UDS-ISO15765-2协议:该协议定义了UDS服务中的TP层协议,是为了解决传输过程中CAN网络与14299协议定义的长度不一致问题。该层包含传输过程中每一帧数据发送的规则、每一帧报文的响应时间与发送时间等、但是这一部分不做为详情来设计,也就是说暂时不考虑TP层的时间响应规范,仅按照各帧格式来传输数据。

        CAN网络协议:一种多主控的总线系统。它采取广播式的方法进行消息的传输,是国际上应用最广泛的现场总线之一。本篇以标准CAN协议+拓展帧的形式描述boot的设计方案。

        S32K144单片机底层开发:NXP的车规级芯片,内存上,主要由512k的Flash+64K的FlexNVM+4K的FlexRAM构成。目前开发所用到的是其FTM定时器模块、CAN模块和采用外部flashdriver的形式对内存进行擦写。

        刷写上位机开发:使用同星科技的TSMaster上位机配置UDS流程进行刷写、使用QT进行开发刷写、使用LabVIEW进行开发刷写。

二、Boot底层配置

软件实现架构为在1ms周期性任务中依次调用CAN数据接收-UDS服务-CAN数据发送任务函数

时钟配置

S3K144的时钟有很多,此处的boot利用频率为48MHZ的内部快速RC振荡时钟配合FTM模块产生1ms的时钟周期进行任务调度。T=Count/Clock Source=625/625000=1ms ;

通过官方提供的SDK包调用相关函数来启用该定时器,步骤如下:初始化FTM模块-注册任务中断-使能中断-初始化定时器计数-启用定时器计数。参考代码如下:

FTM_DRV_Init(INST_FLEXTIMER_MC1, &flexTimer_mc1_InitConfig,&ftmStateStruct );INT_SYS_InstallHandler(FTM3_Ovf_Reload_IRQn, &sysTick1ms_ISR, (isr_t*) 0U);INT_SYS_EnableIRQ(FTM3_Ovf_Reload_IRQn);FTM_DRV_InitCounter(INST_FLEXTIMER_MC1, &flexTimer_mc1_TimerConfig);FTM_DRV_CounterStart(INST_FLEXTIMER_MC1);

CAN配置

时钟:选用系统时钟

模块:选用标准CAN协议、波特率采用500kbit/s、选用内部时钟源

调用:初始化模块-配置接收邮箱-配置发送邮箱-开始接收数据-配置CAN事件回调 

boot工程配置 

1、链接文件描述(.ld)

       链接文件包含地址、数据存储区域、向量表等定义,可以通过修改文件中的地址分配来将app与boot分开。在此版boot开发过程中,只关心MEMORY的分布情况,如下所示,在MEMORY中,将内存划分成3部分。一部分是存储flash数据的段,地址从0x00000000-0x00080000,其中包含了中断向量表、flash的配置信息、存储代码的区域;一部分是m_data(SRAM_L),地址从0x1FFF8000-0x20000000,存储初始化过的全局变量,拷贝中断向量列表和RAM_CODE.一部分是m_data2(SRAM_U),地址从0x20000000-0x20007000,存储.bss段,一般来说为没有初始化的全局变量、堆、栈。

2、链接文件修改 

此次开发的boot不涉及其它操作,因此只需要在ld文件中定义boot的代码即划分多少空间给boot工程使用即可。如下所示,可以看出,只修改了text段的分配。此处工程中,定义了boot代码段起始地址为0x00000410,长度为0x12DF0即完整的flash段大小为76.5K。注意S32K144芯片的flash的地址是从0x0000000-0x00080000即512K,因此boot与app此处的flash大小定义之和不能超出512K。即boot空间越大,app可用空间越少,反之亦然。

 三、Boot底层设计

1、CAN任务分配

        片段1-编写中断任务

片段2-编写接收任务

       基于UDS的帧,可以根据其每条报文的第一个字节来划分为单帧或者是连续帧。

处理单帧:

报文第一个字节的高四位为0判定该报文为单帧。将接收的数据转存至定义的接收buf中,为UDS功能服务提供有效数据。 

处理其它帧:

条件1:报文第一个字节的高四位不为0判定该报文为其它帧。

条件2:在以上条件下如果为第一帧,判定为首帧,反馈流控帧。

条件3:在以上条件下如果满足连续帧第一包格式,则正常接收连续帧。

反馈流控帧-计算数据流大小-计算连续帧的包数。首帧处理如下:

memset(&CanTp_RxBuf, 0xff,sizeof(CanTp_RxBuf));TP_FSResponseSend();stRxMessageMode.u8MessgeSta = TP_Message_CFMode;for (uint8_t i=0;i<7;i ++){CanTp_RxBuf[i] = CanTp_RxData[i+1];}CanTp_TotalRxBufSize = ((CanTp_RxData[0]&0x0f)<<8)+CanTp_RxData[1];u8ReceiveCFTotleNum = (CanTp_TotalRxBufSize-6)/7;if (0 != ((CanTp_TotalRxBufSize-6)%7)){u8ReceiveCFTotleNum = ((CanTp_TotalRxBufSize-6)/7)+1;}u8ReceiveCFTotleNumOverCnt = u8ReceiveCFTotleNum;

连续帧数据处理如下:

 if (TP_Message_CFMode == stRxMessageMode.u8MessgeSta) { if ((CanTp_RxData[0] == u8ReceiveCFCount)) { if (TP_Message_NormalRX == u8CFCountNext) { for (uint8_t i=7*(0+(u8ReceiveCFCount - (CF_START_CNT - 1)));i CanTp_TotalRxBufSize) { break; } CanTp_RxBuf[i] = CanTp_RxData[i-7*(0+(u8ReceiveCFCount - (CF_START_CNT - 1)))+1]; } } else { for (uint16_t i=7*(15+(u8ReceiveCFCount - 0x1F));i CanTp_TotalRxBufSize) { break; } CanTp_RxBuf[i+112*(u8ReceiveCFTWOLevelCnt-1)] = CanTp_RxData[i-7*(15+(u8ReceiveCFCount - 0x1F))+1]; } } u8ReceiveCFCount ++;if ((u8ReceiveCFTotleNum > (CF_MAX_CNT - CF_START_CNT + 1) )||(TP_Message_LargeRX == u8CFCountNext)){if (u8ReceiveCFCount > CF_MAX_CNT){u8ReceiveCFTWOLevelCnt ++;u8CFCountNext = TP_Message_LargeRX;u8ReceiveCFCount = CF_START_CNT - 1;}}else {u8CFCountNext = TP_Message_NormalRX;}u8ReceiveCFTotleNumOverCnt --;if (0 == u8ReceiveCFTotleNumOverCnt) //this pack receive over{u8ReceiveCFTWOLevelCnt = 0;u8ReceiveCFCount = CF_START_CNT;u8CFCountNext = TP_Message_NormalRX;stRxMessageMode.u8MessgeSta = TP_Message_NormalMode;} } else { // u8SendFCWaitCFFg = 2; //res 3e rec } } else //avoid err {u8ReceiveCFTWOLevelCnt = 0;u8ReceiveCFCount = CF_START_CNT;u8CFCountNext = TP_Message_NormalRX; }

 片段3-编写发送任务

此处的boot发送仅处理简单的刷写回复流程即从uds服务中复制有效信息,然后调用CAN SDK的发送函数进行回复。

if (TP_Message_NormalMode == stTxMessageMode.u8MessgeSta){ (void) memcpy((void *)&CanMsgTx.data[0], &stTxMessageMode.CanTp_DataBuf[0],8);if (CanIf_TxConfirmation(&CanMsgTx) == true) {stTxMessageMode.u8MessgeSta = TP_Message_WaitMode;}}

2、调度任务分配 

片段1-编写调度任务 读取UDS服务列表-遍历服务-处理任务并准备回复内容-复制数据准备发送

 /*get UDS service Information*/ pstUDSService = GetUDSServiceInfo(&SupSerItem);/*get UDS service ID*/UDSSerNum = stUdsAppMsg.u8RxDataBuf[1u];while((UDSSerIndex  0){(void) memset(&GetTPMessageMode(TP_Message_TXMode)->CanTp_DataBuf[0], FillInCode,sizeof(GetTPMessageMode(TP_Message_TXMode)->CanTp_DataBuf));(void) memcpy((void *)&GetTPMessageMode(TP_Message_TXMode)->CanTp_DataBuf[0],&stUdsAppMsg.u8TxDataBuf[0],stUdsAppMsg.u8TxDataLen + 1);GetTPMessageMode(TP_Message_TXMode)->u8MessgeSta = TP_Message_NormalMode;}else{GetTPMessageMode(TP_Message_TXMode)->u8MessgeSta = TP_Message_WaitMode;}

3、UDS服务分配  

根据处理UDS服务的方式,定义各项信息,采取回调函数的方案编写服务列表

/*support function/physical ID request*/#define ERRO_REQUEST_ID (0u) /*received ID failled*/#define SUPPORT_PHYSICAL_ADDR (1u << 0u) /*support physical ID request */#define SUPPORT_FUNCTION_ADDR (1u << 1u) /*support function ID request*//*define session mode*/#define UDS_ST_Default_SESSION (1u << 0u) /*default session*/#define UDS_ST_Programming_SESSION (1u << 1u) /*program session*/#define UDS_ST_Externed_SESSION (1u << 2u) /*extend session*//*security request*/#define NONE_SECURITY (1u << 0u) /*none security can request*/#define SECURITY_LEVEL_1 (1u << 1u) /*security level 1 0102request*/#define SECURITY_LEVEL_2 (1u << 2u) /*security level 2 0304request*/#define SECURITY_LEVEL_3 (1u << 3u) /*security level 3 0708request*/typedef struct UDSServiceInfo{uint8_tu8UDS_ServiceID;//0x10,0x27uint8_t u8UDS_SessionMode;//default session,program session,extend sessionuint8_t u8UDS_ServiceType;//FUN,PHYuint8_t u8UDS_ReqLevel;//request level.Lock/unlockvoid (*pfSerNameFun)(ST_TP_UdsAppMsgInfo_T*);} ST_UDS_SERVICEMESSAGE_T;/*dig serverice config table*/static const ST_UDS_SERVICEMESSAGE_T stUDSServiceMessage[] ={ /*diagnose mode control*/ { 0x10u,UDS_ST_Default_SESSION | UDS_ST_Programming_SESSION | UDS_ST_Externed_SESSION, SUPPORT_PHYSICAL_ADDR | SUPPORT_FUNCTION_ADDR,SECURITY_LEVEL_3 | SECURITY_LEVEL_2 | SECURITY_LEVEL_1 | NONE_SECURITY, DigSession }, /*reset ECU*/ { 0x11u,UDS_ST_Programming_SESSION,SUPPORT_PHYSICAL_ADDR | SUPPORT_FUNCTION_ADDR,SECURITY_LEVEL_3 | SECURITY_LEVEL_1, ResetECU }, /*security access*/ { 0x27u,UDS_ST_Programming_SESSION, SUPPORT_PHYSICAL_ADDR,SECURITY_LEVEL_3 | SECURITY_LEVEL_2 | SECURITY_LEVEL_1 | NONE_SECURITY, SecurityAccess }, /*routine control*/ { 0x31u,UDS_ST_Programming_SESSION, SUPPORT_PHYSICAL_ADDR, SECURITY_LEVEL_3, RoutineControl }, /*request download data */ { 0x34u,UDS_ST_Programming_SESSION, SUPPORT_PHYSICAL_ADDR, SECURITY_LEVEL_3, RequestDownload }, /*transter data*/ { 0x36u,UDS_ST_Programming_SESSION, SUPPORT_PHYSICAL_ADDR, SECURITY_LEVEL_3, TransferData }, /*request exit transfer data*/ { 0x37u,UDS_ST_Programming_SESSION, SUPPORT_PHYSICAL_ADDR, SECURITY_LEVEL_3, RequestTransferExit },};

 编写默认诊断会话服务---不同的功能ID进入不同的会话

10服务解读:此服务为诊断会话控制服务,它控制boot进入不同的会话,在不同的会话下,根据设计规范会执行不同的服务。一般来说,分别是默认会话、编程会话、拓展会话。

 实例解析及参考代码如下:

/*dig session*/static void DigSession(ST_TP_UdsAppMsgInfo_T *m_pstPDUMsg){ ASSERT(NULL == m_pstPDUMsg); uint8_t u8RequestSubfunction = m_pstPDUMsg->u8RxDataBuf[2u]; /*set send postive message*/ m_pstPDUMsg->u8TxDataLen = 6u; m_pstPDUMsg->u8TxDataBuf[0u] = m_pstPDUMsg->u8TxDataLen; m_pstPDUMsg->u8TxDataBuf[1u] = m_pstPDUMsg->u8RxDataBuf[1u] + 0x40u; m_pstPDUMsg->u8TxDataBuf[2u] = u8RequestSubfunction; /*sub function*/ switch(u8RequestSubfunction) { case 0x01u : /*default mode*/ SetCurrentSession(UDS_ST_Default_SESSION); break; case 0x02u : /*program mode*/ if (UDS_ST_Default_SESSION != GetUDS_SESSIONINFO()->u8CurSessionMode) { SetCurrentSession(UDS_ST_Programming_SESSION); } else {// UDS_NegativeResponse(RSE,m_pstPDUMsg); } break; case 0x03u : /*extend mode*/ SetCurrentSession(UDS_ST_Externed_SESSION); break; default :// UDS_NegativeResponse(SFNS,m_pstPDUMsg); break; }}

编写安全访问会话服务---先申请种子,然后校验密钥,校验通过则解锁对应等级,否则锁定

服务解读及参考代码如下:

//利用结构体定义各个等级的安全信息typedef struct{uint8_t u8RequestID;uint8_t u8UnlockID;uint8_t u8AES_SEED_LEN;uint8_t u8SetSecurityLevel;uint8_t u8SecurityUnlock;uint8_t u8SequenceLock;}ST_UDSSERVICE_SECURITY_T;ST_UDSSERVICE_SECURITY_T stUdsServiceSecurity[] ={{0x01,0x02,2u,SECURITY_LEVEL_1,0u,0u},{0x03,0x04,4u,SECURITY_LEVEL_2,0u,0u},{0x05,0x06,4u,NONE_SECURITY,0u,0u},{0x07,0x08,4u,SECURITY_LEVEL_3,0u,0u},{0x09,0x0A,4u,SECURITY_LEVEL_3,0u,0u},};/*security access*/static void SecurityAccess(ST_TP_UdsAppMsgInfo_T *m_pstPDUMsg){ ASSERT(NULL == m_pstPDUMsg); uint8_t isFindSecurityService = false; uint8_t SecuritySerIndex = 0;uint8_t m_SecurityItem = sizeof(stUdsServiceSecurity) / sizeof(stUdsServiceSecurity[0u]); uint8_t RequestSubfunction = m_pstPDUMsg->u8RxDataBuf[2u]; uint32_t seed = 0x00000000;//遍历安全信息表,找到对应的子功能后开始准备种子和校验密钥while(SecuritySerIndex > 24) & 0xFF); Encrypt_Seed[1] = (uint8_t)((seed >> 16) & 0xFF); Encrypt_Seed[2] = (uint8_t)((seed >> 8) & 0xFF); Encrypt_Seed[3] = (uint8_t)((seed ) & 0xFF); }Encrypt_SeedAndKey_LvlFBL();m_pstPDUMsg->u8TxDataLen = 2u + stUdsServiceSecurity[SecuritySerIndex].u8AES_SEED_LEN;for (uint8_t i = 0;i u8TxDataBuf[i+3] = Encrypt_Seed[4-stUdsServiceSecurity[SecuritySerIndex].u8AES_SEED_LEN+i];}break;}else if(RequestSubfunction == stUdsServiceSecurity[SecuritySerIndex].u8UnlockID){isFindSecurityService = true;m_pstPDUMsg->u8TxDataLen = 2u;if(stUdsServiceSecurity[SecuritySerIndex].u8SecurityUnlock){SetSecurityLevel(stUdsServiceSecurity[SecuritySerIndex].u8SetSecurityLevel);}else{ if (true == IsReceivedKeyRight(m_pstPDUMsg->u8RxDataBuf,Encrypt_Key2, stUdsServiceSecurity[SecuritySerIndex].u8AES_SEED_LEN)) { SetSecurityLevel(stUdsServiceSecurity[SecuritySerIndex].u8SetSecurityLevel); stUdsServiceSecurity[SecuritySerIndex].u8SecurityUnlock = ON; GetUDS_SESSIONINFO()->u8SecurityReqLock = OFF; } else { GetUDS_SESSIONINFO()->u8SecurityReqLock = ON; }}break;}else {}SecuritySerIndex ++;}if(0 == m_pstPDUMsg->u8TxDataLen){return;}if(true != isFindSecurityService){UDS_NegativeResponse(SFNS,m_pstPDUMsg);return;}//答复服务 if (OFF == GetUDS_SESSIONINFO()->u8SecurityReqLock) { m_pstPDUMsg->u8TxDataBuf[0u] = m_pstPDUMsg->u8TxDataLen; m_pstPDUMsg->u8TxDataBuf[1u] = m_pstPDUMsg->u8RxDataBuf[1u] + 0x40u; m_pstPDUMsg->u8TxDataBuf[2u] = RequestSubfunction; } else { UDS_NegativeResponse(IK,m_pstPDUMsg); }}

编写数据传输会话服务

首先34服务获取APP的地址、长度等信息

如图所示,Tx的00 01 42 00为APP的起始地址,00 00 EC 74为APP的长度,这两条报文都是由上位机发送。ECU回复的30 00 01表示允许继续发送连续帧,04 74 20 04 20表示该项服务正确接收。

/*request download*/static void RequestDownload(ST_TP_UdsAppMsgInfo_T *m_pstPDUMsg){uint32_t start_address = 0x00000000; crc = 0xFFFFFFFF;if(FL_REQUEST_STEP != FlsDownloadStateType.eDownloadStep){if (FL_TRANSFER_STEP == FlsDownloadStateType.eDownloadStep){API_EraseMemory(m_pstPDUMsg);}Flash_InitDowloadInfo();Flash_SetNextDownloadStep(RP_FLS_APP,FL_REQUEST_STEP);}if (0x44 == m_pstPDUMsg->u8RxDataBuf[3]){start_address = ((uint32_t)m_pstPDUMsg->u8RxDataBuf[4] <u8RxDataBuf[5] <u8RxDataBuf[6] <u8RxDataBuf[7]);GetTp_FlashInf()->tp_total_len = ((uint32_t)m_pstPDUMsg->u8RxDataBuf[8] <u8RxDataBuf[9] <u8RxDataBuf[10] <u8RxDataBuf[11]);}if (GetTp_FlashInf()->tp_total_len tp_address_acc = start_address;//APP Start AddressGetTp_FlashInf()->tp_address_cur = GetTp_FlashInf()->tp_address_acc;GetTp_FlashInf()->tp_Flash_StartAdd_Cur = start_address;m_pstPDUMsg->u8TxDataLen = 4u;m_pstPDUMsg->u8TxDataBuf[0u] = m_pstPDUMsg->u8TxDataLen;m_pstPDUMsg->u8TxDataBuf[1u] = m_pstPDUMsg->u8RxDataBuf[1u] + 0x40u;m_pstPDUMsg->u8TxDataBuf[2u] = 0x20;m_pstPDUMsg->u8TxDataBuf[3u] = 0x04;m_pstPDUMsg->u8TxDataBuf[4u] = 0x02;}

 然后36服务以连续帧的形式接收所有打包的数据

/*transfer data*/static void TransferData(ST_TP_UdsAppMsgInfo_T *m_pstPDUMsg){static uint32_t sector_num_cur = 0xFFFFFFFF;static uint16_t u16FldrvBufOst = 0;uint16_t CanTp_TotalRxBufSize = m_pstPDUMsg->u16RxDataTotalLen;uint16_t CanTp_ReadBufSize = CanTp_TotalRxBufSize - 2;uint8_t crc_buf[1024];if(FL_TRANSFER_STEP != FlsDownloadStateType.eDownloadStep){if(FL_REQUEST_STEP == FlsDownloadStateType.eDownloadStep){Flash_SetNextDownloadStep(RP_FLS_APP,FL_TRANSFER_STEP);}else{UDS_NegativeResponse(RSE,m_pstPDUMsg);return;}}if (RP_FLS_APP == FlsDownloadStateType.u8FlashResourcePart){DISABLE_INTERRUPTS();if((GetTp_FlashInf()->tp_address_acc - GetTp_FlashInf()->tp_Flash_StartAdd_Cur)/MEMORY_SECTOR_SIZE != sector_num_cur){sector_num_cur = (GetTp_FlashInf()->tp_address_acc - GetTp_FlashInf()->tp_Flash_StartAdd_Cur)/MEMORY_SECTOR_SIZE;}if ((CanTp_TotalRxBufSize-2) tp_address_cur, &m_pstPDUMsg->u8RxDataBuf[3], (CanTp_TotalRxBufSize));(void) memset(&crc_buf[0], 0xFF, sizeof(crc_buf));Flsdriver_Read(GetTp_FlashInf()->tp_address_cur,CanTp_ReadBufSize,&crc_buf[0]);GetTp_FlashInf()->tp_address_cur = GetTp_FlashInf()->tp_address_acc + (CanTp_TotalRxBufSize);GetTp_FlashInf()->tp_address_acc = GetTp_FlashInf()->tp_address_cur;crc = crc32(crc_buf,CanTp_ReadBufSize);// crc_bufGetTp_FlashInf()->tp_Flash_Length_Cur += CanTp_TotalRxBufSize;ENABLE_INTERRUPTS();}else if (RP_FLS_DRIVER == FlsDownloadStateType.u8FlashResourcePart){memcpy(&bin_data[0+u16FldrvBufOst], &m_pstPDUMsg->u8RxDataBuf[3], CanTp_ReadBufSize);u16FldrvBufOst = CanTp_ReadBufSize+u16FldrvBufOst;if (u16FldrvBufOst == GetTp_FlashInf()->tp_total_len){u16FldrvBufOst = 0;memcpy(&crc_buf[0], &bin_data[0], GetTp_FlashInf()->tp_total_len);crc = crc32(crc_buf,GetTp_FlashInf()->tp_total_len);}}m_pstPDUMsg->u8TxDataLen = 2u;m_pstPDUMsg->u8TxDataBuf[0u] = m_pstPDUMsg->u8TxDataLen;m_pstPDUMsg->u8TxDataBuf[1u] = m_pstPDUMsg->u8RxDataBuf[1u] + 0x40u;m_pstPDUMsg->u8TxDataBuf[2u] = m_pstPDUMsg->u8RxDataBuf[2];}

最后37服务直接退出即可。

/*request transfer exit*/static void RequestTransferExit(ST_TP_UdsAppMsgInfo_T *m_pstPDUMsg){ ASSERT(NULL == m_pstPDUMsg);if(FL_EXIT_TRANSFER_STEP != FlsDownloadStateType.eDownloadStep){if ((FL_TRANSFER_STEP == FlsDownloadStateType.eDownloadStep)){Flash_SetNextDownloadStep(RP_FLS_APP,FL_EXIT_TRANSFER_STEP);}else{UDS_NegativeResponse(RSE,m_pstPDUMsg);Flash_InitDowloadInfo();return;}}/*tranmitted postive message.*/m_pstPDUMsg->u8TxDataLen = 1u;m_pstPDUMsg->u8TxDataBuf[0u] = m_pstPDUMsg->u8TxDataLen;m_pstPDUMsg->u8TxDataBuf[1u] = m_pstPDUMsg->u8RxDataBuf[1u] + 0x40u;}

4、编写跳转逻辑 

通过重新映射向量表地址来实现跳转。关闭中断-重新定义栈地址-重新定义程序开始地址。

volatile uint32_t appEntry, appStack;void shutdown_drivers(void){FTM_DRV_Deinit(3);FLEXCAN_DRV_Deinit(0);DISABLE_INTERRUPTS();}void System_Reset(void){SystemSoftwareReset();}void bootup_application(uint32_t appEntry, uint32_t appStack){static void (*jump_to_application)(void);static uint32_t stack_pointer; jump_to_application = (void (*)(void))appEntry;stack_pointer = appStack;S32_SCB->VTOR = APP_IMAGE_START;__asm volatile (\"MSR msp, %0\\n\" : : \"r\" (stack_pointer) : \"sp\");__asm volatile (\"MSR psp, %0\\n\" : : \"r\" (stack_pointer) : \"sp\");jump_to_application();} void JumpTo_Application(void) {shutdown_drivers(); appStack = *(volatile uint32_t*) APP_IMAGE_START; /* setup app jump */appEntry = *(volatile uint32_t*)(APP_IMAGE_START + 4);bootup_application(appEntry,appStack);while(1){};}

 最后定时器超时即无UDS服务,则执行app跳转函数

四 上位机

方案1 采用同星科技TSMaster的UDS诊断工具编写诊断流程进行刷写。

此方案是借助内置的诊断工具,配置如图所示对应的流程后即可开始刷写。

方案2 采用LabVIEW编写诊断流程进行刷写。

此方案是借助Labview开发平台,通过调用第三方库,自行编写完成UDS的一系列诊断。

方案3 采用QT编写诊断流程进行刷写。 与方案二类似,也是通过调用第三方库,自行编写完成UDS的一系列诊断。此上位机可以识别PEAKCAN、ZLGCAN、CANalyst等多个CAN设备并进行刷写。

详细的刷写讲解视频可以从以下链接观看。 

 QT制作的一个基于CAN总线bootloader上位机_哔哩哔哩_bilibili


总结

       以上便是文章全部内容啦!本篇文章简单的列举了几个常用的UDS服务设计方案而非全部描述的原因是服务存在通用性,同时代码也只是一种思维的体现,列举出来只是用于学习与交流,不一定是最优解法。如同文末提供的三种上位机工具,说明解决问题的思路不仅仅只有一种,善于思考与学习才是我们成长的重要因素。