基于车载以太网的OTA升级
22年4月份接到一个订单:广汽乘用车M8,OTA升级的开发。OTA(Over-the-Air Technology),理解的话就是:无线传输技术。本次的开发,运用到的知识包括:TCP/IP协议,DoIP协议,UDS协议,开发语言为C语言,开发环境为Linux,gcc编译,Linux环境下调试,Make文件等等。
一. 简介
本人就职于北京的一家汽车电子科技公司,接到了广汽的订单。22年4月份接到一个订单:广汽乘用车M8,OTA升级的开发。OTA(Over-the-Air Technology),理解的话就是:无线传输技术。本次的开发,运用到的知识包括:TCP/IP协议,DoIP协议,UDS协议,开发语言为C语言,开发环境为Linux,gcc编译,Linux环境下调试,Make文件等等。
二. 协议简述
TCP/IP协议就不说了,它是互联网通信的最常用的一个通信协议,自我20年开始工作以来,首先负责的就是网络通信这一块。
DoIP是基于TCP/IP协议的一个通信协议,协议格式如下图:
这个协议栈,是公司买的AUTOSAR的软件,可以根据需求直接在软件上进行配置,然后直接生成代码。软件如下图:
UDS协议,本次OTA开发,运用了多种协议,协议之间的关系为嵌套。UDS嵌套入DoIP协议,DoIP协议嵌入TCP协议。下图简单介绍了,何为UDS协议,UDS具体划分为哪些功能。
三. 程序流程
整个升级流程分为两个阶段,预升级阶段,升级阶段。
第一阶段,为预升级阶段,验证版本号,会话模式切换,例程控制,DTC控制设置,通信控制。
第二阶段,到了实际的升级流程。其流程图如下:
会话模式切换,秘钥申请,写入ID,下载flash driver文件,例程控制
OTA的核心部分是下面三个步骤:
#34 请求下载
#36 传输数据
#37 下载结束
四. 程序简要
所有的程序都在INVO-OTA文件下面,整体的架构为,OTA功能,在整个工程中相当于进程;编译这个程序后,会生成静态库文件.so ,供其他地方调用。
工程文件如下图左侧
lib_ota.c ota初始化部分,及与上位机直接调用的接口。程序的设计思路是,写一个结构体,结构里面包含了上位机所需要调用到的函数接口。
#include "Network_DoIP_SoAd_Sys.h"
#include "Dcm/Dcm.h"
#include "Invo_Upgrade_Flag.h"
#include "invo_ota_lib.h"
static InvoOtaObj ota_obj;
static doip_config_t g_config;
static INVO_OTA_DATA_CALLBACK invo_ota_lib_get_data_callback_handler();
static INVO_OTA_UDS_MSG_CALLBACK invo_ota_lib_get_uds_msg_callback_handler();
bool invo_ota_lib_init(doip_config_t *config)
{
if (config)
{
g_config = *config;
}
else
{
(void)memset(&g_config, 0, sizeof(g_config));
}
struct bl_mesg msg;
bool ret = FALSE;
bzero( &ota_obj, sizeof( ota_obj ) );
ota_obj.dcmflag = TRUE;
ota_obj.DoIPConfigObj = 1;
if(1)
{
TcpIp_Init();
SoAd_Init();
DoIP_Init( &ota_obj.DoIPConfigObj );
Dcm_Init( NULL );
DoIP_ActivationLineSwitchActive();
ota_obj.inited = TRUE;
ret = TRUE;
PR_ERROR( "invo lib ota init successed!." );
}
else
{
ret = FALSE;
}
PR_ERROR("-----------------------------------------------");
PR_ERROR("version :%d", 1);
PR_ERROR("-----------------------------------------------");
return ret;
}
void invo_ota_lib_main()
{
if ( ota_obj.inited )
{
SoAd_MainFunction();
DoIP_MainFunction();
Dcm_MainFunction();
usleep(5000);
}
}
void invo_ota_lib_deinit()
{
if ( ota_obj.inited )
{
TcpIp_DeInit();
SoAd_DeInit();
Dcm_DeInit();
bzero( &ota_obj, sizeof( ota_obj ) );
ota_obj.dcmstart = false;
ota_obj.is_data_callback_inited = false;
ota_obj.is_msg_callback_inited = false;
ota_obj.inited = false;
}
}
/*------------------------------------------------------------------------------
* doip_get_path
-------------------------------------------------------------------------------*/
char *doip_get_path(void)
{
return g_config.download_path;
}
/*------------------------------------------------------------------------------
* doip_space_check
-------------------------------------------------------------------------------*/
int doip_space_check(uint64_t size)
{
int ret = -1;
if(1) // (g_config.do_install)
{
ret = g_config.do_space_check(size);
}
return ret;
}
/*------------------------------------------------------------------------------
* doip_do_install
-------------------------------------------------------------------------------*/
void doip_do_install(char *path)
{
if (g_config.do_install)
{
g_config.do_install(path);
}
}
/*------------------------------------------------------------------------------
* doip_write_did ***************************Start Change************************
-------------------------------------------------------------------------------*/
void doip_write_did(uint16_t did, uint8_t *data, uint32_t size)
{
if (g_config.do_write_did)
{
g_config.do_write_did(did, data, size);
}
}
/*------------------------------------------------------------------------------
* doip_read_did
-------------------------------------------------------------------------------*/
void doip_read_did(uint16_t did, uint8_t *data)
{
if (g_config.do_read_did)
{
g_config.do_read_did(did, data);
}
}
/*------------------------------------------------------------------------------
* doip_do_reboot
-------------------------------------------------------------------------------*/
void doip_do_reboot(void)
{
if (g_config.do_reboot)
{
g_config.do_reboot();
}
}
/*------------------------------------------------------------------------------
* doip_get_update_state
-------------------------------------------------------------------------------*/
doip_install_state_t doip_get_update_state(void)
{
if (g_config.get_update_state)
{
return g_config.get_update_state();
}
return INSTALL_STATE_ERR;
}
lib_ota_lib_if.h 头文件,包含了结构体类型的函数接口,宏定义数据,ENUM变量,函数引用
#ifndef _INVO_OTA_LIB_IF_H_
#define _INVO_OTA_LIB_IF_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
** ===================================================================
** Include files.
** ===================================================================
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
/*
** ===================================================================
** Macro definition.
** ===================================================================
*/
typedef enum
{
INVO_OTA_UDS_NORMAL_MSG_PROCESS = 0x1U,
INVO_OTA_UDS_ERROR_MSG_PROCESS = 0x2U,
INVO_OTA_BEING_DATA_PROCESS = 0x3U,
INVO_OTA_SUSPEND_DATA_PROCESS = 0x4U,
INVO_OTA_FINISH_DATA_PROCESS = 0x5U
} INVO_OTA_PROCESS_STATUS;
typedef enum
{
OTA_LIB_CMD_NONE = 0,
OTA_LIB_CMD_UDS = 1U,
OTA_LIB_CMD_RESET = 2U,
OTA_LIB_CMD_RESERVED
} INVO_OTA_LIB_CMD;
typedef struct
{
INVO_OTA_PROCESS_STATUS status;
INVO_OTA_LIB_CMD cmd;
int length;
unsigned char* data;
} ota_lib_message_t;
typedef enum
{
INSTALL_STATE_NOSTART,
INSTALL_STATE_RUNNING,
INSTALL_STATE_OK,
INSTALL_STATE_ERR,
} doip_install_state_t;
typedef enum
{
STATE_ACTIVATE,
STATE_NEGATIVE,
} doip_activeline_state_t;
typedef struct
{
char download_path[128];
int (*do_space_check)(uint64_t size);
int (*do_install)(char *path);
int (*do_write_did)(uint16_t did, uint8_t *data, uint32_t size);
int (*do_read_did)(uint16_t did, uint8_t *data);
void (*do_reboot)(void);
doip_install_state_t (*get_update_state)(void);
doip_activeline_state_t (*get_activeline_state)(void);
} doip_config_t;
typedef void ( *INVO_OTA_DATA_CALLBACK )( INVO_OTA_PROCESS_STATUS status,
void* data, uint32_t data_size );
typedef void ( *INVO_OTA_UDS_MSG_CALLBACK )( ota_lib_message_t msg );
/*
** ===================================================================
** func definition.
** ===================================================================
*/
extern bool invo_ota_lib_init(doip_config_t *config);
extern void invo_ota_lib_main();
extern void invo_ota_lib_deinit();
extern bool invo_ota_lib_get_init_flag();
extern bool invo_ota_lib_get_dcm_flag();
extern bool invo_ota_lib_get_sys_flag();
extern int invo_ota_lib_get_upgrade_flag();
extern void invo_ota_lib_set_uds_msg( unsigned char* usdmsg, int len );
extern void invo_ota_lib_register_data_callback( INVO_OTA_DATA_CALLBACK callback );
extern void invo_ota_lib_register_uds_msg_callback( INVO_OTA_UDS_MSG_CALLBACK callback );
extern void invo_ota_lib_SndPragramSessResp( void );
extern void invo_ota_lib_SndRespPending( void );
extern void invo_ota_lib_DoIP_SndEcuResetResp( void );
extern void invo_ota_lib_SndEcuUpgradeFailure();
#ifdef __cplusplus
}
#endif /* extern "C" */
#endif /* INVO_OTA_IFS_H */
#34 写文件请求
/************************************************************************************
* Name : DiagServ_RequestDownload
* Called by : DiagServ_Task
* Preconditions: None
* Parameters : None
* Return code : TRUE - Request has been accepted.
* FALSE - Request is not allowed.
* Description : Request to start a download progress.
*************************************************************************************/
boolean DiagServ_RequestDownload( uint32 uiMemAddr, unsigned long int uiMemSize )
{
boolean ret = FALSE;
do
{
//PR_INF("%s ucLogicType = %d", __func__, stSocUpdateInfo.ucLogicType);
if ( ( TRUE == stSocUpdateInfo.bTransDataAllowed )
|| ( LOGIC_TYPE_IDLE == stSocUpdateInfo.ucLogicType )
|| ( LOGIC_TYPE_ERASING == stSocUpdateInfo.ucLogicType ) )
{
//PR_INF("%s, break", __func__);
break;
}
stSocUpdateInfo.bTransDataAllowed = TRUE;
stSocUpdateInfo.uiMemAddr = uiMemAddr;
stSocUpdateInfo.uiMemSize = uiMemSize;
stSocUpdateInfo.uiDataIdx = 0U;
stSocUpdateInfo.uiDataLen = 0U;
stSocUpdateInfo.ucSectionIdx = 0U;
Dcm_WriteFileReq(uiMemSize);
if ( LOGIC_TYPE_SOC_DRV == stSocUpdateInfo.ucLogicType )
{
PR_INF("LOGIC_TYPE_SOC_DRV == stSocUpdateInfo.ucLogicType");
ret = TRUE;
}
else if ( LOGIC_TYPE_SOC_APP == stSocUpdateInfo.ucLogicType )
{
PR_INF("LOGIC_TYPE_SOC_APP == stSocUpdateInfo.ucLogicType");
ret = TRUE;
}
else
{
//PR_INF("ret = FALSE");
ret = FALSE;
}
}
while ( 0 );
if ( FALSE == ret )
{
_ClrSocUpdateInfo();
}
return ( ret );
}
#36 文件传输数据
/************************************************************************************
* Name : DiagServ_TransferData
* Called by : DiagServ_ProgramTask
* Preconditions: None
* Parameters : None
* Return code : None
* Description : Transfer data request, write data into RAM or FLASH.
*************************************************************************************/
boolean DiagServ_TransferData( uint8 ucSectionIdx, uint16 usDateSize, uint8* pucData )
{
boolean ret = FALSE;
//PR_INF("%s ucLogicType = %d************************", __func__, stSocUpdateInfo.ucLogicType);
do
{
if ( ( FALSE == stSocUpdateInfo.bTransDataAllowed )
|| ( usDateSize > TRANS_DATA_LEN_MAX )
|| ( ucSectionIdx != ( stSocUpdateInfo.ucSectionIdx + 1 ) )
|| ( ( stSocUpdateInfo.uiDataLen + usDateSize ) > stSocUpdateInfo.uiMemSize ) )
{
if ( stSocUpdateInfo.ucSectionIdx == 0xFF )
{
stSocUpdateInfo.ucSectionIdx = 0x01;
}
else
{
PR_DEBUG( 1U, " SOC condition falied! %d %d \n", usDateSize, stSocUpdateInfo.ucSectionIdx );
break;
}
}
stSocUpdateInfo.ucSectionIdx = ucSectionIdx;
Ethernet_OTA_MemcpytoShmBufferData( &stSocUpdateInfo.ucDataBuffer, pucData, usDateSize );
PR_DEBUG( 0U, "usDateSize:%u.\n", usDateSize );
//uiCurrMemAddr = stSocUpdateInfo.uiMemAddr + stSocUpdateInfo.uiDataIdx;
stSocUpdateInfo.uiDataIdx += usDateSize;
stSocUpdateInfo.uiDataLen += usDateSize;
stSocUpdateInfo.uiOldCrc32 = GetCrc32( stSocUpdateInfo.ucDataBuffer, usDateSize, stSocUpdateInfo.uiOldCrc32 );
switch ( stSocUpdateInfo.ucLogicType )
{
case LOGIC_TYPE_SOC_DRV:
{
ret = TRUE;
stSocUpdateInfo.count = 1;
break;
}
case LOGIC_TYPE_SOC_APP:
{
if ( stSocUpdateInfo.count == 1 )
{
stSocUpdateInfo.count = 0;
}
//Ethernet_OTA_ProcessUpgradeData( stSocUpdateInfo.ucDataBuffer, usDateSize );
Dcm_WriteDataToFile(stSocUpdateInfo.ucDataBuffer, usDateSize);
ret = TRUE;
break;
}
default:
{
PR_DEBUG( 1U, "SOC default falied! %d %d \n", usDateSize, stSocUpdateInfo.ucSectionIdx );
break;
}
}
}
while ( 0 );
if ( FALSE == ret )
{
_ClrSocUpdateInfo();
}
return ( ret );
}
#37 传输结束
/************************************************************************************
* Name : DiagServ_TransferExit
* Called by : DiagServ_ProgramTask
* Preconditions: None
* Parameters : None
* Return code : None
* Description : Transfer data end, new transfer request can be allowed.
*************************************************************************************/
boolean DiagServ_TransferExit( void )
{
PR_INF(" %s start...", __func__);
uint8 crc = 1;
boolean ret = FALSE;
do
{
PR_DEBUG( 0, "%s \n", __func__ );
PR_DEBUG( 0, "%ld %ld \n", stSocUpdateInfo.uiDataLen, stSocUpdateInfo.uiMemSize );
PR_INF("bTransDataAllowed %ld ", stSocUpdateInfo.bTransDataAllowed);
// PR_INF("stSocUpdateInfo.uiDataLen = %ld uiMemSize %ld \n", stSocUpdateInfo.uiDataLen, stSocUpdateInfo.uiMemSize);
if ( ( FALSE == stSocUpdateInfo.bTransDataAllowed )
|| ( stSocUpdateInfo.uiDataLen != stSocUpdateInfo.uiMemSize )
)
{
PR_INF(" %s, break ", __func__);
break;
}
stSocUpdateInfo.bTransDataAllowed = FALSE;
if ( LOGIC_TYPE_SOC_DRV == stSocUpdateInfo.ucLogicType )
{
PR_INF("LOGIC_TYPE_SOC_DRV == stSocUpdateInfo.ucLogicType ");
stSocUpdateInfo.ucLogicType = LOGIC_TYPE_ERASING;
}
else
{
//Ethernet_OTA_FinishUpgradeProcessing();
Dcm_WriteFileEnd(&crc);
PR_INF("%s else ",__func__);
}
ret = TRUE;
PR_INF(" %s ret = TRUE", __func__);
}
while ( 0 );
if ( FALSE == ret )
{
_ClrSocUpdateInfo();
}
PR_INF(" %s end...", __func__);
return ( ret );
}
代码部分太多了,这里就不介绍了。
五. 总结
这个项目开发了三,四个月,并且后期也一直在维护。期间还去广州广汽研究院那边出过一次差。期间遇到了很多问题,比如需要找人协调调试设备,需要跟测试部沟通,然他们编写测试程序,需要组织讨论问题的解决方案;自己出差的时候要在实车上进行调试程序,需要给甲方讲解清问题原因,问题的解决方案等等。
这是我毕业两年多来,做的最有成就感的一个项目。
更多推荐
所有评论(0)