IAP远程在线升级
在上一篇中实现了LWIP网口通讯,那么肯定要加个在线升级功能,这个功能所占用的资源很少,但在物联网中很重要也很实用。在线升级就是像手机一样,先下载好系统,然后点击升级~然后就没然后了。
网上有很多IAP的教程,为了方便演示,大多数都在Bootloader引导代码中添加了选择显示功能,并且通过串口接收固件。这对于教学来说非常好,但在实际使用中,这是不可取的。通常,Bootloader代码尽量做得越小越好,它只需要实现把存储器中的代码烧录到Flash中,然后跳转到APP的地址执行就OK了。至于接收固件和选择显示等功能应该由APP代码来实现,APP代码在升级功能中主要负责通过通讯把接收到的数据存到存储器中。
一下就分享我实现IAP远程升级功能的经验,驱动程序主要还是基于原子的教程改写的。
读取单片机内flash代码,可有可无,这个可以方便做出厂固件保存。
void Flash_To_W25Q(unsigned int addr, unsigned int len) { u32 i; u32 secpos; u32 secremain; secpos = len/SECTION; //分多少次缓存 secremain = len%SECTION; //最后剩余字节 for(i=0; i<secpos; i++) { STMFLASH_Read(FLASH_APP1_ADDR+SECTION*i,(u32*)Data_W,SECTION);//读取Flash代码 W25QXX_Write(Data_W,addr+SECTION*i,SECTION); W25QXX_Read(Data_R,addr+SECTION*i,SECTION); } STMFLASH_Read(FLASH_APP1_ADDR+(SECTION*i),(u32*)Data_W,secremain);//读取Flash代码 W25QXX_Write(Data_W,addr+SECTION*i,secremain); }
读取存储芯片内的固件,并写入到单片机,这个固件就是APP接收到并保存的。
void W25Q_To_Flash(unsigned int addr, unsigned int len) { u32 i; u32 secpos; u32 secremain; secpos = len/SECTION; //分多少次缓存 secremain = len%SECTION; //最后剩余字节 for(i=0; i<secpos; i++) { W25QXX_Read(Data_R,addr+SECTION*i,SECTION); iap_write_appbin(FLASH_APP1_ADDR+(SECTION*i),Data_R,SECTION);//写入到Flash } W25QXX_Read(Data_R,addr+SECTION*i,secremain); iap_write_appbin(FLASH_APP1_ADDR+(SECTION*i),Data_R,secremain); }
跳转到APP地址,之所以做个判断,是可以识别APP代码中是否有程序。
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX. { iap_load_app(FLASH_APP1_ADDR);//跳转到APP地址 }
APP主函数开始加入地址偏移
SCB->VTOR = FLASH_BASE | 0x10000; 地址偏移0x10000
APP工程中也不要忘了编译时偏移地址
生成.bin文件,加入这句 fromelf.exe --bin -o "$L@L.bin" "#L"
最后分享一下我个人的观点。我使用的是网络通讯接收固件,因为单片机运行内存有限,必须要把固件拆包发送接收。按理说在接收完全部数据后应该要进行MD5校验才能升级,而Bootloader在取出数据时也应该进行一次MD5校验,而我就懒得做了~也算是个隐患,在后期优化还是会考虑做。出厂的时候非常建议对固件进行自动备份存储,如果有条件的话还可以再开辟一个存储区域用来存储上一次升级的数据用来回滚。另外,有人也许会想问,在线升级有两个代码,那出厂的时候岂不是要烧两个程序,对于我这种懒到家的人来说,烧一个程序都够烦的了,要要烧两次,我才不干。这时候可以使用UltraEdit编辑器对bin文件进行合并,因为这两个代码的flash地址是不冲突的,具体教程可以参考https://blog.csdn.net/xinghuah/article/details/82145192