记录一次远程升级实现IAP

环境:使用华大单片机hc32l170,flash大小为128k,ram为16k。

3个程序,bootload,程序1,程序2。

在最开始的bootload中检测到并没有需要更新的程序,则直接进入程序1(APP_START_ADDRESS)工作。而在程序1中,使用NB模组从存放固件的服务器那里通过udp的方式获取到固件程序2,先存放到备份地址出(BACKUP_START_ADDRESS),并更新程序更新标志位(BOOT_PARA_ADDRESS),重启。再次回到bootload中,这次检测到需要更新,则将备份地址(BACKUP_START_ADDRESS)开始的一定范围的flash值一一复制到正式程序(APP_START_ADDRESS)的地址,最后重置更新程序标志位(BOOT_PARA_ADDRESS),再重启就会执行程序2了。

各参数地址及大小

/* Flash相关宏定义 */
#define FLASH_SECTOR_SIZE           0x200ul                           //一个sector的尺寸  0.5k
#define FLASH_BASE                  ((uint32_t)0x00000000)            //flash基地址
#define FLASH_SIZE                  (256 * FLASH_SECTOR_SIZE)        //flash尺寸  128k

/* RAM相关宏定义 */
#define SRAM_BASE                   ((uint32_t)0x20000000)            //RAM基地址
#define RAM_SIZE                    0x8000ul                          //RAM尺寸

/* BootLoader flash相关宏定义 */
#define BOOT_SIZE                   (20*FLASH_SECTOR_SIZE)             //BootLoader flash尺寸   10k
#define BOOT_PARA_ADDRESS           (FLASH_BASE + BOOT_SIZE - 0x10u) //BootLoader para存储地址,判断是否要更新程序

/* APP flash相关宏定义 */
#define APP_FLAG                    ((uint32_t)0x67890123)            //从BootLoader para区读到此值,表示APP需要更新
#define APP_START_ADDRESS            (FLASH_BASE + BOOT_SIZE)          //APP程序存储基地址
#define APP_SIZE                    (100 * FLASH_SECTOR_SIZE)        //APP程序的大小 100k
#define APP_END_ADDRESS                (APP_START_ADDRESS + APP_SIZE)        //APP程序的大小 100k
#define APP_PARA_ADDRESS            (APP_START_ADDRESS + APP_SIZE)    //APP参数存放的地址

/* 备份 APP flash相关宏定义 */
#define BACKUP_START_ADDRESS        (APP_END_ADDRESS + FLASH_SECTOR_SIZE)          //备份APP程序存储基地址
#define BACKUP_APP_SIZE                (100 * FLASH_SECTOR_SIZE)        //备份APP程序的大小 100k

/////////////////////////////////////////////////////////////
#define    FlashStart        APP_PARA_ADDRESS
#define    FlashEnd        (APP_PARA_ADDRESS + FLASH_SECTOR_SIZE)

#define    FlashSector        512

extern void MyFlash_Init(uint8_t num);
extern void FlashErase(void);
extern u8 FlashWriteNoErase(u32 Addr, u8 *buf, u16 len);
extern u8 FlashWrite(u32 Addr, u8 *buf, u16 len);
extern u8 FlashRead(u32 Addr, u8 *buf, u16 len);

#endif

 

bootload:

检测是否需要更新

uint8_t IAP_UpdateCheck(void)
{
    uint32_t u32AppFlag;
    
    u32AppFlag = *(__IO uint32_t *)BOOT_PARA_ADDRESS; //读出BootLoader para区标记值
    //printf("u32AppFlag:0x%x\r\n",u32AppFlag);
    if (APP_FLAG != u32AppFlag)                       //如果标记值不等于APP_FLAG,表示不需要升级APP程序
        return 1;
    else
        return 0;
}

bootload跳转到任意地址开始执行

/**
 *******************************************************************************
 ** \brief  IAP跳转函数
 **
 ** \param  [in] u32Addr                    APP 首地址
 **
 ** \retval Error                           APP 地址错误
 **
 ******************************************************************************/
en_result_t IAP_JumpToApp(uint32_t u32Addr)
{
    uint32_t u32StackTop = *((__IO uint32_t *)u32Addr);  //读取APP程序栈顶地址

    /* 判断栈顶地址有效性 */
    if ((u32StackTop > SRAM_BASE) && (u32StackTop <= (SRAM_BASE + RAM_SIZE)))
    {
        /* 配置跳转到用户程序复位中断入口 */
        JumpAddress = *(__IO uint32_t *)(u32Addr + 4);
        JumpToApplication = (func_ptr_t)JumpAddress;
        /* 初始化用户程序的栈顶指针 */
        __set_MSP(*(__IO uint32_t *)u32Addr);
        JumpToApplication();
    }

    return Error;
}

更新程序(实际应用中,应该考虑到其中一处或多处复制错误,导致正式程序无法执行,可以实行程序版本回退的功能。我这里没有加上这些复杂的功能,就是单纯的复制)

uint8_t updata_main(void){
    uint8_t boot_para[4]={0};
    
    for(uint16_t i=0;i<(APP_SIZE/FLASH_SECTOR_SIZE);i++){
        Flash_SectorErase(APP_START_ADDRESS+FLASH_SECTOR_SIZE*i);//写falsh之前,必须擦除,擦除必须以块(512byte)擦除。(每款单片机不同,按实际的来)
        for(u16 j=0;j<FLASH_SECTOR_SIZE;j++){
            Flash_WriteByte(APP_START_ADDRESS+FLASH_SECTOR_SIZE*i+j,*((volatile uint8_t*)(BACKUP_START_ADDRESS+FLASH_SECTOR_SIZE*i+j)));//写flash,依次将备份地址的值复制到正式程序的地址
        }
        printf("%c",i%2?'*':'.');
//        for(uint16_t k=0;k<20;k++)
//            printf("::0x%x ,0x%x\r\n",*((volatile uint8_t*)(APP_START_ADDRESS+FLASH_SECTOR_SIZE*i+k)),*((volatile uint8_t*)(BACKUP_START_ADDRESS+FLASH_SECTOR_SIZE*i+k)));
    }
    FlashWriteNoErase(BOOT_PARA_ADDRESS,boot_para,4);
    return 0;
}

程序1:

u8 domain_to_ip(u8 *tempBuf) {//域名解析
    u8 start,end;
    u16 p;

    start=GetNStr(tempBuf,strlen((u8 *)tempBuf),'\"',1);
    end=GetNStr(tempBuf,strlen((u8 *)tempBuf),'\"',2);

    //for(u8 i=0;i<)
    memcpy(udpserver_ip_adrr,&tempBuf[start+1],end-start-1);

    for(u8 i=0; i<strlen((u8 *)udpserver_ip_adrr); i++) {
        if((udpserver_ip_adrr[i]<0x30 || udpserver_ip_adrr[i]>0x39) && 0x2e!=udpserver_ip_adrr[i]) {
            myprintf("域名解析错误\r\n");
            return 1;
        }
    }

    myprintf("udpserver ip addr:%s\r\n",udpserver_ip_adrr);
    return 0;
}
void ECDNSFun(void) {//域名解析 使用NB模组进行udp通信的操作步骤之一(不同厂家的模组步骤不同)
    u8 ret;
    nNBTimeOutCount ++;

    if(nNBTimeOutCount == 1)
        NBSendCmd("AT+ECDNS=\"f.laiot.net\"\r\n");
    else
    {
        ret = NBCheckRetDate("+ECDNS", 500);
        if(ret == 0)
        {
            //返回成功
            if(domain_to_ip(NBIOTBuf))
                StepJump(NBPowerRstIndex);
            else
                StepJump(CGDCONTCmdIndex);
        }
        else if(ret == 1)
        {
            //超时
            NBErrCount ++;
            if(NBErrCount > 3)
                StepJump(NBPowerRstIndex);
            else
                StepJump(ECDNSCmdIndex);
        }
    }
}
void CGDCONTFun(void) {//使用NB模组进行udp通信的操作步骤之一(不同厂家的模组步骤不同)
    u8 ret;
    nNBTimeOutCount ++;

    if(nNBTimeOutCount == 1)
        NBSendCmd("AT+CGDCONT=1,\"IP\",\"CMNET\"\r\n");
    else
    {
        ret = NBCheckRetDate("OK", 300);
        if(ret == 0)
        {
            //返回成功
            StepJump(CGACTCmdIndex);
        }
        else if(ret == 1)
        {
            //超时
            NBErrCount ++;
            if(NBErrCount > 2)
                StepJump(NBPowerRstIndex);
            else
                StepJump(CGDCONTCmdIndex);
        }
    }
}
void CGACTFun(void) {//使用NB模组进行udp通信的操作步骤之一(不同厂家的模组步骤不同)
    u8 ret;
    nNBTimeOutCount ++;

    if(nNBTimeOutCount == 1)
        NBSendCmd("AT+CGACT=1\r\n");
    else
    {
        ret = NBCheckRetDate("OK", 300);
        if(ret == 0)
        {
            //返回成功
            StepJump(SKTCREATECmdIndex);
        }
        else if(ret == 1)
        {
            //超时
            NBErrCount ++;
            if(NBErrCount > 2)
                StepJump(NBPowerRstIndex);
            else
                StepJump(CGACTCmdIndex);
        }
    }
}
void SKTCREATEFun(void) {//使用NB模组进行udp通信的操作步骤之一(不同厂家的模组步骤不同)
    u8 ret;
    nNBTimeOutCount ++;

    if(nNBTimeOutCount == 1)
        NBSendCmd("AT+SKTCREATE=1,2,17\r\n");
    else
    {
        ret = NBCheckRetDate("OK", 300);
        if(ret == 0)
        {
            //返回成功
            StepJump(SKTCONNECTCmdIndex);
        }
        else if(ret == 1)
        {
            //超时
            NBErrCount ++;
            if(NBErrCount > 2)
                StepJump(NBPowerRstIndex);
            else
                StepJump(SKTCREATECmdIndex);
        }
    }
}
void SKTCONNECTFun(void) {//使用NB模组进行udp通信的操作步骤之一(不同厂家的模组步骤不同)
    u8 ret;
    char *ATbuf = NULL;

    nNBTimeOutCount ++;

    if(nNBTimeOutCount == 1)
    {
        ATbuf = (char *)NBmalloc(120);
        if(ATbuf != NULL)
        {
            sprintf(ATbuf,"AT+SKTCONNECT=1,\"%s\",%d\r\n",udpserver_ip_adrr,udpserver_port);
            NBSendCmd(ATbuf);
            NBfree(ATbuf);
        }
        else
        {
            NBErrCount ++;
            if(NBErrCount > 3)
                StepJump(NBPowerRstIndex);
            else
                StepJump(MIPLOBSERVERSPCmdIndex);
        }
    } else {
        ret = NBCheckRetDate("OK", 300);
        if(ret == 0)
        {
            //返回成功
            StepJump(UPDATEIndex);
        }
        else if(ret == 1)
        {
            //超时
            NBErrCount ++;
            if(NBErrCount > 2)
                StepJump(NBPowerRstIndex);
            else
                StepJump(SKTCONNECTCmdIndex);
        }
    }
}
u8* udp_update_pack(u16 now_pack_num) {
    static uint8_t ppBuf[14+10];
    uint16_t crc=0;

    ppBuf[0]=0xAA;
    ppBuf[1]=udp_SN>>8;
    ppBuf[2]=udp_SN;
    ppBuf[3]=DrivceType>>8;
    ppBuf[4]=DrivceType;
    ppBuf[5]=device_info.DrivceID>>24;
    ppBuf[6]=device_info.DrivceID>>16;
    ppBuf[7]=device_info.DrivceID>>8;
    ppBuf[8]=device_info.DrivceID>>0;
    ppBuf[9]=0xE0;
    ppBuf[10]=0x0A>>8;//固定长度
    ppBuf[11]=0x0A;
    ppBuf[12]=HardwareVersion>>8;
    ppBuf[13]=HardwareVersion;
    ppBuf[14]=SoftVersion>>8;
    ppBuf[15]=SoftVersion;
    ppBuf[16]=new_firmware_ver>>8;
    ppBuf[17]=new_firmware_ver;
    ppBuf[18]=total_num_pack>>8;
    ppBuf[19]=total_num_pack;
    ppBuf[20]=now_pack_num>>8;
    ppBuf[21]=now_pack_num;
    crc=CRC16_calc(&ppBuf[1],10+14-1-2);
    ppBuf[22]=crc;
    ppBuf[23]=crc>>8;
    return ppBuf;
}
u8 udp_SocketSenddata(u8 *buf, u16 len) {//udp发送数据
    char *SendBuf = NULL;
    u16 slen1 = 0;
    u16 i;
    NBCloseCount = 0;
    SendBuf = (char *)malloc(len*2+60);

    if(SendBuf == NULL)
        return 0;
    slen1 = sprintf(SendBuf,"AT+SKTSEND=1,%d,",len);
    for(i=0; i<len; i++)
    {
        SendBuf[slen1++] = HexToChar(buf[i]>>4);
        SendBuf[slen1++] = HexToChar(buf[i]&0x0f);
    }
    SendBuf[slen1++]='\r';
    SendBuf[slen1++]='\n';
    NBSendCmd(SendBuf);
    free(SendBuf);
    return 1;
}
u8 UDPRemoteUpDate(u8* buf,u16 len) {
    en_result_t             enResult = Ok;
    static uint16_t error_pack_count=0;
    static uint32_t storage_addr_offset=0;
    uint16_t packsize=len-4;
    uint16_t packnum=(buf[2]<<8|buf[3]);//收到的升级包的序号
    myprintf("接收到的包序号:%d,包大小:%d,写入后偏移为:%d\r\n",packnum,packsize,storage_addr_offset+packsize);
    if(packnum==now_pack_num) { //比较是不是自己需要的包
        Flash_SectorErase(BACKUP_START_ADDRESS+storage_addr_offset);//注意,写入前必须要先擦除。一次擦除为一个块区的大小,512byte。正好一个通信数据包含有的固件数据也是设置为了512byte大小
        for(u16 i=0; i<512; i++)
            Flash_WriteByte(BACKUP_START_ADDRESS+storage_addr_offset+i, buf[4+i]);
        now_pack_num++;
        error_pack_count=0;
        storage_addr_offset+=packsize;//偏移量累加
        udp_SN++;
    } else {
        myprintf("固件包序号错误");
        error_pack_count++;
        if(error_pack_count>=20)
            return 2;
        return 3;
    }
    if(packnum==total_num_pack) {
        Flash_SectorErase(BOOT_PARA_ADDRESS);
        Flash_WriteWord(BOOT_PARA_ADDRESS, 0x67890123);
        NVIC_SystemReset();
        return 8;
    }
    return 1;
}

void UpdateFun(void) {
    u8 ret=0,DH,DL;
    u16 start,end;
    nNBTimeOutCount ++;
    u8 *tempnbuf = NULL;
    if(nNBTimeOutCount == 1)
        udp_SocketSenddata(udp_update_pack(now_pack_num),14+10);
    else {
        ret = NBCheckRetDate("+SKTRECV:", 3000);

        if(ret == 0)
        {
            start=GetNStr(NBIOTBuf,strlen(NBIOTBuf),'\"',1);
            end=GetNStr(NBIOTBuf,strlen(NBIOTBuf),'\"',2);
            myprintf("::%d,%c,%c,%d\r\n",end-start+1,NBIOTBuf[start+1],NBIOTBuf[end-1],UART1_COUNT);
            tempnbuf=mymalloc((end-start+1-2)/2);
            for(u16 i=0; i<(end-start+1-2); i+=2) { //注意i的溢出问题
                DH = CharToHex(NBIOTBuf[start+1+i]);//  30
                DL = CharToHex(NBIOTBuf[start+1+i+1]);
                tempnbuf[i/2] = ((DH<<4) | DL) ;
            }
            ret=udp_data_pro(tempnbuf,(end-start+1-2)/2);
            if(8==ret) {
                StepJump(IdleIndex);
            } else {
                StepJump(UPDATEIndex);
            }
            myfree(tempnbuf);
        }
        else if(ret == 1)
        {
            StepJump(IdleIndex);
        }
    }
}

 

posted @ 2022-11-07 10:12  kingzhan  阅读(383)  评论(0编辑  收藏  举报