一. 简介

本人就职于北京的一家汽车电子科技公司,接到了广汽的订单。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 );
}

代码部分太多了,这里就不介绍了。

五.  总结

这个项目开发了三,四个月,并且后期也一直在维护。期间还去广州广汽研究院那边出过一次差。期间遇到了很多问题,比如需要找人协调调试设备,需要跟测试部沟通,然他们编写测试程序,需要组织讨论问题的解决方案;自己出差的时候要在实车上进行调试程序,需要给甲方讲解清问题原因,问题的解决方案等等。

这是我毕业两年多来,做的最有成就感的一个项目。

Logo

更多推荐