此方法前提是你得有一个EEPROM
我用的单片机是STM32F103ZET6 , 此单片机FLASH容量为512KB;
在此单片机里面FLASH的起始地址是0X8000000,BOOT作为引导加载程序一般都是从这个地址开始,单片机一上点默认会从这个地址开始运行,所以将自己的BOOT程序放在这个地址。
其实boot也是个程序,只是他的主要功能是处理升级包。
我将FLASH分成三个块,第一块是BOOT区,第二块是程序缓存区,第三块是APP区
BOOT
我的boot代码大概33KB左右,所以我预留了40KB作为BOOT区
CODE
这个区是代码缓存区,一共160KB,将串口接收到的代码先存放在这个位置,等全部接受完成了,再把code区的代码移到APP区,然后跳转到APP就算完成bootloader
APP
这一块是我的主要代码运行区,我预留了312KB
程序运行逻辑
BOOT启动-----》读取EEPROM,判断有没有更新:
如果有---》将CODE区的代码移到APP区,然后跳转到APP区;
如果没有-----》等待一段时间,如果接收到新的升级包,先把数据存放到CODE区,等全部接受完成了移到APP区。如果没有接收到升级包,直接跳转到APP
APP启动--------》等待接受升级包,如果接收到升级包,重启单片机,回到BOOT
BOOT程序里判断EEPROM状态代码
这个放在main函数里。运行此段之前先初始化串口和EEPROM
uint32_t readAddr ; //程序缓存区地址
uint32_t app_wrinte_addr; //APP启动地址
uint32_t app_size; //升级包大小
uint8_t read_write_buf[1200];
uint8_t BOOTloader_ST=0;
//检查EEPROM
if(AT24CXX_Check()==0)//检查器件EEPROM已经初始化
{
USART3_Send_Str("------------------------------------------------------\r\n");
USART3_Send_Str("---------BOOTLOADER程序正在运行正常--------------------\r\n");
USART3_Send_Str("EEPROM硬件正常\r\n");
//初始化bootloader状态
BOOTloader_ST = AT24CXX_ReadOneByte(999);//升级标志位在最后面
switch(BOOTloader_ST)
{
//无需升级
case 0XA0: //等待10个心跳时间后如果还是没有收到升级包跳转APP
//直接跳过
USART3_Send_Str(" 等待接受升级程序\r\n");
// iap_load_app(0X8032000);
break;
//准备升级
case 0XA1: //擦除FLASH,准备接受
AT24CXX_WriteOneByte(999,0XA0); //初始化时等于0XA1说明接受失败
USART3_Send_Str("准备升级--升级失败--等待接受升级程序\r\n");
break;
//正在接受数据
case 0XA2: //等待接受完成,超时跳转APP
AT24CXX_WriteOneByte(999,0XA0); //初始化时等于0XA2说明接受失败
USART3_Send_Str("接受中断--升级失败--等待接受升级程序\r\n");
break;
//等待升级命令
case 0XA3: //等待开始升级命令
AT24CXX_WriteOneByte(999,0XA0); //初始化时等于0XA3说明接受失败
USART3_Send_Str("接受完成---等待接受升级命令\r\n");
break;
//接受失败
case 0XA4:
AT24CXX_WriteOneByte(999,0XA0); //初始化时等于0XA4说明接受失败
BOOTloader_ST = 0XA0;
USART3_Send_Str("---接受中断--升级失败--等待接受升级程序-----\r\n");
break;
//开始升级
case 0XA5: //擦除APP区,缓存的升级包移到APP区,跳转到APP区
AT24CXX_WriteOneByte(999,0XA6); //升级
USART3_Send_Str("---------接受完成---开始升级----------\r\n");
app_size = (AT24CXX_ReadOneByte(1000)<<24) | (AT24CXX_ReadOneByte(1001)<<16)| (AT24CXX_ReadOneByte(1002)<<8) |AT24CXX_ReadOneByte(1003);
app_wrinte_addr = 0X8032000; //APP地址
readAddr = 0X800A000; //代码缓存区地址
while (app_size> 0)
{
if (app_size>=1024)
{
System_ReadInteriorFlash (readAddr, read_write_buf, 1024) ;
System_WriteInteriorFlash(app_wrinte_addr,read_write_buf, 1024);
readAddr+=1024;
app_size-=1024;
app_wrinte_addr+= 1024; //写入地址增加
}
else
{
System_ReadInteriorFlash (readAddr, read_write_buf,app_size);
System_WriteInteriorFlash(app_wrinte_addr,read_write_buf,app_size);
readAddr+=app_size;
app_size-=app_size;
app_wrinte_addr+= 1024; //写入地址增加
}
}
USART3_Send_Str("--------------即将跳转APP----------\r\n");
iap_load_app(0X8032000); //跳转到APP
break;
//升级成功,无需
case 0XA6: //回到无需升级
USART3_Send_Str("--------------升级成功-----------\r\n");
AT24CXX_WriteOneByte(999,0XA0); //等待新更新
break;
default : //可能是第一次上电,默认回到0XA0状态
AT24CXX_WriteOneByte(999,0XA0);
break;
}
}
在串口接受协议里面运行的代码
//下面这个几个变量是全局变量,不能用局部变量
framne_all = uart485buff[6]<<8 | uart485buff[7]; //包总数
frame_num = uart485buff[8]<<8 | uart485buff[9]; //包序号
writesize = uart485buff[10]<<8 | uart485buff[11]; //数据大小
if(frame_num==0) //第一包
{
writeaddr = 0x800A000;
AT24CXX_WriteOneByte(999,0XA2); //正在接受数据
System_WriteInteriorFlash(writeaddr, &uart485buff[12] , writesize) ; //写入到内部FLASH
writeaddr += writesize;
}
else if(frame_num == (framne_all-1)) //最后一包
{
BOOTloader_ST = 0XA5;
AT24CXX_WriteOneByte(999,0XA5); //开始升级
System_WriteInteriorFlash(writeaddr, &uart485buff[12] , writesize) ; //写入到内部FLASH
writeaddr += writesize;
AT24CXX_WriteOneByte(999,0XA5); //开始升级
USART3_Send_Str("\r\n");
USART3_Send_Str("---升级包全部接受完成--即将重启--\r\n");
writeaddr-=0x800A000;
AT24CXX_WriteOneByte(1000,((u8)(writeaddr>>24)&0xFF));
AT24CXX_WriteOneByte(1001,((u8)(writeaddr>>16)&0xFF));
AT24CXX_WriteOneByte(1002,((u8)(writeaddr>>8)&0xFF));
AT24CXX_WriteOneByte(1003,((u8)(writeaddr)&0xFF));
reboot(); //重启或者跳转到0X8000000地址
}
else
{
System_WriteInteriorFlash(writeaddr,&uart485buff[12] , writesize) ; //写入到内部FLASH
writeaddr += writesize;
}