环境:使用华大单片机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); } } }
标签:u8,APP,ret,远程,升级,ADDRESS,ppBuf,else,IAP From: https://www.cnblogs.com/KingZhan/p/16865091.html