记录一次远程升级实现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);
}
}
}