这个话题,相信很多朋友都有讨论和使用。今天我就说说我自已成功使用的方法。首先单片机需要有这个IAP功能。STC单片机在选型时要注意,不是哪一款都可以,要有IAP功能才行。单片机通过串口或其它通讯方式,接收升级BIN文件,写入到内部FLASH中,校验通过,执行自动升级过程。我的升级思想及过程如下。
1:出厂程序:(含有IAP功能)程序起址地址为单片机内存的后半部份,例如0X8000,KEIL中需要设置一下
2:为什么要设置成后半部份,这是因为我把前半部份的空间留给后续需要升级时的程序。
3:因为中断向量部份在最前面的内存空间,无法改变,因此我的升级方法比较简单暴力。当接收完校验通过后,直接改变中断向量部份空间。
4:贴出代码:
- int main()
- {
- LEDs_up_ten_flag=0; //流水灯开始
- GPIOInit(); //GPIO+时钟初始化
- Iap_Isp(); //升级包检测
- Uart1_Init(); //115200bps@24MHz
- Uart2_Init(); //9600bps@24MHz
- Timer0Init(); //1毫秒@24.000MHz
- printf("\r\n青橙新能源4G控制充电板V00启动成功!\r\n");
- LETCAT1_Init(); //模组初始化及登陆服务器
- LB0910_Init(); //0910初始化
- BUZZFlag=1;
- WDT_CONTR = 0x27;//使能看门狗,溢出时间约为5s
- BeatTimescnt=BeatTimesSet-10; //快速上线心跳
- // Read_PortTimes(); //读出端口时间数据 云端已实现
- RestTimes=0;
- LEDs_up_ten_flag=1;//流水灯禁止
- while(1)
- {
- LED_show();
- Uart1_drive(); //串口功能函数,主循环中调用
- Uart2_drive(); //串口功能函数,主循环中调用 HeartbeatServer();
- WDT_CONTR |= 0x10; //清看门狗,否则系统复位
- if(RestTimes>=120)
- {
- RestTimes=0;
- IAP_CONTR |= 0x60;
- }
- }
- }
- #include "EEPROM.H"
- u16 code IAP_Code1[60]={
- 0x0200,0x0400,0x0600,0x0800,0x0A00,0x0C00,0x0E00, //升级版地址30K
- 0x1000,0x1200,0x1400,0x1600,0x1800,0x1A00,0x1C00,0x1E00,
- 0x2000,0x2200,0x2400,0x2600,0x2800,0x2A00,0x2C00,0x2E00,
- 0x3000,0x3200,0x3400,0x3600,0x3800,0x3A00,0x3C00,0x3E00,
- 0x4000,0x4200,0x4400,0x4600,0x4800,0x4A00,0x4C00,0x4E00,
- 0x5000,0x5200,0x5400,0x5600,0x5800,0x5A00,0x5C00,0x5E00,
- 0x6000,0x6200,0x6400,0x6600,0x6800,0x6A00,0x6C00,0x6E00,
- 0x7000,0x7200,0x7400,0x7600,0x7800
- };
-
- /*u16 code IAP_Code2[50]={ 0x8000,0x8200,0x8400,0x8600,0x8800,0x8A00,0x8C00,0x8E00,
- 0x9000,0x9200,0x9400,0x9600,0x9800,0x9A00,0x9C00,0x9E00, //基础版软件放置地址 25K
- 0xA000,0xA200,0xA400,0xA600,0xA800,0xAA00,0xAC00,0xAE00,
- 0xB000,0xB200,0xB400,0xB600,0xB800,0xBA00,0xBC00,0xBE00,
- 0xC000,0xC200,0xC400,0xC600,0xC800,0xCA00,0xCC00,0xCE00,
- 0xD000,0xD200,0xD400,0xD600,0xD800,0xDA00,0xDC00,0xDE00,
- 0xE000,0xE200,
- };
- */
- void IapIdle() //关闭 IAP功能 并保护数据
- {
- IAP_CONTR = 0; // 关闭 IAP 功能
- IAP_CMD = 0; // 清除命令寄存器
- IAP_TRIG = 0; // 清除触发寄存器
- IAP_ADDRH = 0x80; // 将地址设置到非 IAP 区域
- IAP_ADDRL = 0;
- }
- u8 IapRead(u16 addr) // 读数据的操作
- {
- char dat;
- IAP_CONTR = WT_24M; // 使能 IAP
- IAP_CMD = 1; // 设置 IAP 读命令
- IAP_ADDRL = addr; // 设置 IAP 低地址
- IAP_ADDRH = addr >> 8; // 设置 IAP 高地址
- IAP_TRIG = 0x5a; // 写触发命令 (0x5a)
- IAP_TRIG = 0xa5; // 写触发命令 (0xa5)
- _nop_();
- dat = IAP_DATA; // 读 IAP 数据
- IapIdle(); // 关闭 IAP 功能
- return dat;
- }
- void IapProgram(u16 addr, u8 dat) //写数据操作。
- {
- IAP_CONTR = WT_24M; // 使能 IAP
- IAP_CMD = 2; // 设置 IAP 写命令
- IAP_ADDRL = addr; // 设置 IAP 低地址
- IAP_ADDRH = addr >> 8; // 设置 IAP 高地址
- IAP_DATA = dat; // 写 IAP 数据
- IAP_TRIG = 0x5a; // 写触发命令 (0x5a)
- IAP_TRIG = 0xa5; // 写触发命令 (0xa5)
- _nop_();
- IapIdle(); // 关闭 IAP 功能
- }
- void IapErase(u16 addr)
- {
- IAP_CONTR =WT_24M; // 使能 IAP
- IAP_CMD = 3; // 设置 IAP 擦除命令
- IAP_ADDRL = addr; // 设置 IAP 低地址
- IAP_ADDRH = addr >> 8; // 设置 IAP 高地址
- IAP_TRIG = 0x5a; // 写触发命令 (0x5a)
- IAP_TRIG = 0xa5; // 写触发命令 (0xa5)
- _nop_(); //
- IapIdle(); // 关闭 IAP 功能
- }
- void Iap_Isp()
- {
- ///系统切换,当读到IAPADD地址存有IAP程序中断向量文件时,写到0X0000地址中,并重启到新的
- u8 IapBootCode=0;
- u16 i;
- for(i=0;i<0x0200;i++)
- {
- IapBootCode=IapRead[i]; //读出ISP中断向量文件块
- IapProgram(ISPadd+i, IapBootCode); //保存在专用区,IAP返回ISP时使用。备用 ISPadd=0xF400
- }
- IapBootCode=IapRead(IAPadd); //读出升级文件IAP保存区起始跳转数据,如果有,表示有升级程序,需要写入起始区,0x0000;写入后就可以使用升级程序了
- if(IapBootCode==0x02)
- {
- IapErase(0x0000); //擦除中断向量文件块,准备写入新的IAP中断向量
- for(i=0;i<0x0200;i++)
- {
- IapBootCode=IapRead(IAPadd+i); //读出IAP中断向量文件块,
- IapProgram(i, IapBootCode); //将IAP的中断向量区写入起始位置
- }
- IapErase(IAPadd); //擦除IAP中断向量文件块,防止死循环。
- IAP_CONTR=0x60; //重启,将进入IAP程序
- }
-
- }
- if(CmpMemory(pbuf,&cmd7[0], sizeof(cmd7)-1)) //升级程序包接收转存功能程序
- {
- u16 m=0,n=0,temchar=0;
- static xdata Buftem[0x0200]={0}; //IAP临时保存
- static u8 Hchar=0; static u8 Lchar=0; //临时字高位 //临时字低位
- static bit Saveflag=0; // 存储标志
- ET0=0;
- BeatTimescnt=0; //收到指令,将心跳时间复位
- RestTimes=0;
- StorageArray(&IAP_num[0],&Uart1_Rxd_buff[0],15); //转化提取升级包序号
- IAPnum=(u8)atoi(IAP_num);
- pbuf=&Uart1_Rxd_buff[0]; //接收指令转赋值
-
- for(n=0;n<Uart1_bufcnt;n++)
- {
- temchar=*(pbuf+n);
- if(temchar=='\"') //判断“号的数量,
- m++;
- if(m==19&&temchar!='\"') //提取数据包数据,存入EEPARM中
- {
- if(Saveflag==0)
- {
- if(temchar>='0'&&temchar<='9') //如果接收到的是数字, 转化16进制高位
- { Hchar=temchar-0x30;}
- else
- { Hchar=temchar-87;} //如果接收到的是字母, 转化16进制高位
- Saveflag=!Saveflag; //数据保存标志取反
- }
- else
- {
- if(temchar>='0'&&temchar<='9') //如果接收到的是数字, 转化16进制高位
- { Lchar=temchar-0x30;}
- else
- { Lchar=temchar-87;} //如果接收到的是字母, 转化16进制高位
-
- Saveflag=!Saveflag; //数据保存标志取反
- temchar=(Hchar<<4|Lchar); //高低四位合并
- if(IAP_cnt<0x0200)
- {
- Buftem[IAP_cnt]=temchar;//中断向量表临时保存
- //SBUF=Buftem[IAP_cnt];Delay1ms();
- }
- else
- {
- IapProgram(IAP_Code1[0]+IAP_cnt-0x0200, temchar); //写入实际数据内存地址
- //SBUF=IapRead(IAP_Code1[0]+IAP_cnt-0x0200);Delay1ms(); //读出 验证
- }
- IAP_cnt++;
- IAP_ChecksumAcc+=(u32)temchar;
-
- if(IAP_cnt==IAPLen&&IAP_ChecksumAcc==IAPChecksum) //写入长度和校验和都相同
- {
-
- for(m=0;m<0x0200;m++)
- {
- IapProgram(IAPadd+m, Buftem[m]); //最后写入中断向量文件,防止断电时无法回到基础版
- //SBUF=IapRead(IAPadd+m); //读出 验证
- Delay1ms();
- }
- // Delay1ms_cont(5000);//
- printf("\r\n_num_%bd_len=%u--",IAPnum,IAP_cnt); Delay1ms_cont(10);
- printf("\r\n_num_%bd_ChecksumAcc=%lu--",IAPnum,IAP_ChecksumAcc); Delay1ms_cont(10);
- printf("\r\n***********The driver upgrade is successful!*************\r\n"); Delay1ms_cont(10); //保存相关数据再重启
- // Save_PortTimes(); //保存端口时间数据
- IAP_CONTR |= 0x60;
- }
- }
- }
- }
- ET0=1;
- printf("\r\n_num_%bd_len=%u--\r\n",IAPnum,IAP_cnt); Delay1ms_cont(10);
- printf("_num_%bd_ChecksumAcc=%lu--\r\n",IAPnum,IAP_ChecksumAcc); Delay1ms_cont(10);
- Delay1ms_cont(1500);
- printf("{\"pc\":\"update\",\"mid\":\"%s\",\"res\":\"1\",\"softid\":\"%s\",\"num\":\"%bd\"}",AT_SIMID,&IAP_softid[0],IAPnum+1);
- return;
- }
复制代码
|