【Bootloader刷写方案设计】基于UDS协议通过CAN网络对单片机进行bootloader刷写
前言: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<7*(0+(u8ReceiveCFCount - 0x1F));i ++)
{
if (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<7*(15+(u8ReceiveCFCount - 0x1E));i ++)
{
if (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 < SupSerItem) && (NULL != pstUDSService))
{
if(UDSSerNum == pstUDSService[UDSSerIndex].u8UDS_ServiceID)
{
pstUDSService[UDSSerIndex].pfSerNameFun(&stUdsAppMsg);
break;
}
UDSSerIndex++;
}
if(stUdsAppMsg.u8TxDataLen > 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_t u8UDS_ServiceID;//0x10,0x27
uint8_t u8UDS_SessionMode;//default session,program session,extend session
uint8_t u8UDS_ServiceType;//FUN,PHY
uint8_t u8UDS_ReqLevel;//request level.Lock/unlock
void (*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 < m_SecurityItem)
{
if(RequestSubfunction == stUdsServiceSecurity[SecuritySerIndex].u8RequestID)
{
isFindSecurityService = true;
stUdsServiceSecurity[SecuritySerIndex].u8SequenceLock = ON;
if (ON == stUdsServiceSecurity[SecuritySerIndex].u8SecurityUnlock)
{
(void) memset((void *)&Encrypt_Seed[0], 0x00,4u);
}
else
{
/*get random */
DISABLE_INTERRUPTS();
seed = RandomGenerator(u32ECUSysTick);
ENABLE_INTERRUPTS();
Encrypt_Seed[0] = (uint8_t)((seed >> 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 < stUdsServiceSecurity[SecuritySerIndex].u8AES_SEED_LEN;i ++)
{
m_pstPDUMsg->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] << 24) | ((uint32_t)m_pstPDUMsg->u8RxDataBuf[5] << 16) | ((uint32_t)m_pstPDUMsg->u8RxDataBuf[6] << 8) | ((uint32_t)m_pstPDUMsg->u8RxDataBuf[7]);
GetTp_FlashInf()->tp_total_len = ((uint32_t)m_pstPDUMsg->u8RxDataBuf[8] << 24) | ((uint32_t)m_pstPDUMsg->u8RxDataBuf[9] << 16) | ((uint32_t)m_pstPDUMsg->u8RxDataBuf[10] << 8) | ((uint32_t)m_pstPDUMsg->u8RxDataBuf[11]);
}
if (GetTp_FlashInf()->tp_total_len <= 2048)
{
FlsDownloadStateType.u8FlashResourcePart = RP_FLS_DRIVER;
memset(&bin_data[0], 0xFF, sizeof(bin_data));
}
else
{
FlsDownloadStateType.u8FlashResourcePart = RP_FLS_APP;
if(APP_ERAESE_SUCCESS != FlsDownloadStateType.u8APPState)
{
UDS_NegativeResponse(RSE,m_pstPDUMsg);
return;
}
}
GetTp_FlashInf()->tp_address_acc = start_address;//APP Start Address
GetTp_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) <= 5)
{
CanTp_TotalRxBufSize = 8;
}
else
{
CanTp_TotalRxBufSize = CanTp_TotalRxBufSize - 2;
CanTp_TotalRxBufSize = CheckBYTEALIGN(CanTp_TotalRxBufSize,8);
}
Flsdriver_ProgramData(GetTp_FlashInf()->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_buf
GetTp_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
" : : "r" (stack_pointer) : "sp");
__asm volatile ("MSR psp, %0
" : : "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服务设计方案而非全部描述的原因是服务存在通用性,同时代码也只是一种思维的体现,列举出来只是用于学习与交流,不一定是最优解法。如同文末提供的三种上位机工具,说明解决问题的思路不仅仅只有一种,善于思考与学习才是我们成长的重要因素。
本文地址:https://www.vps345.com/14491.html