前言: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\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服务设计方案而非全部描述的原因是服务存在通用性,同时代码也只是一种思维的体现,列举出来只是用于学习与交流,不一定是最优解法。如同文末提供的三种上位机工具,说明解决问题的思路不仅仅只有一种,善于思考与学习才是我们成长的重要因素。

Logo

纵情码海钱塘涌,杭州开发者创新动! 属于杭州的开发者社区!致力于为杭州地区的开发者提供学习、合作和成长的机会;同时也为企业交流招聘提供舞台!

更多推荐