3.3 升级程序Bootloader的设计 升级程序的好坏,在很大程度上取决于Bootloader设计的好坏。 一个优秀的IAP升级Bootloader,必须做好升级中出现故障等异常的处理。保证系统不会崩溃,即使升级失败,也能返回升级前的程序。
- 有升级指令,进行初始化工作(串口、定时器、看门狗)
- 接收升级数据包,检测帧头、长度、帧号、数据区校验,最大程度的保证升级数据的完整性、正确性。
- 实时检测接收状态,10 S内没有接收到数据或接收到的数据包都是错的,则退出升级,返回原程序。
- 接收的数据按照512字节一组写入Flash,写入后再读出与原数据进行对比校验,校验成功后,本次编程Flash成功。允许连续3次编程Flash,三次都不成功,退出升级程序,执行原程序。
- 升级成功后,更新当前程序运行区标志,跳转到新程序,同时原程序保存。
本设计的Bootload位于Flash的0x400开始的扇区0存储区内,使用分散加载机制,将程序的入口地址定位到0x00000400处。当用户程序接收到升级指令后,就会使用函数指针跳转到这个入口处。 3.3.1 使用IAP 图3-1 描述了使用IAP编程Flash所必须的步骤。
3.3.1.1 定义系统参数 在使用IAP前,需要定义一些系统参数,比如系统时钟、IAP中断入口、输入输出缓存。
#define IAP_CLK 11059200UL
#define IAP_LOCATION 0x7FFFFFF1
typedef void(*IAP)(uint32 [],uint32 []); //定义函数类型指针
IAP iap_entry=(IAP)IAP_LOCATION; //设置函数指针
unsigned long command[5] = {0,0,0,0,0};
unsigned long result[2]= {0,0};
3.3.1.2 选择扇区 在任何擦除和编程Flash之前,必须选中扇区,可以选中一个或多个。
/******************************************************************
* 名称:SelSector()
* 功能:IAP操作扇区选择,命令代码50。
* 入口参数:sec1 起始扇区
* sec2 终止扇区
* 出口参数:IAP返回值(paramout缓冲区) CMD_SUCCESS,BUSY,INVALID_SECTOR
*********************************************************************/
void SelSector(uint8 sec1, uint8 sec2)
{
paramin[0] = IAP_SELSECTOR; // 设置命令字
paramin[1] = sec1; // 设置参数
paramin[2] = sec2;
iap_entry(paramin, paramout); // 调用IAP服务程序
}
3.3.1.3 擦除扇区 在编程Flash前必须执行擦除操作,如果某个扇区已经擦除,就不需要再次擦除。可以一次擦除一个或多个扇区。
/******************************************************************
* 名称:EraseSector()
* 功能:扇区擦除,命令代码52。
* 入口参数:sec1 起始扇区
* sec2 终止扇区
* 出口参数:IAP返回值(paramout缓冲区) CMD_SUCCESS,BUSY,INVALID_SECTOR ************************************************************************/
void EraseSector(uint8 sec1, uint8 sec2)
{
paramin[0] = IAP_ERASESECTOR; // 设置命令字
paramin[1] = sec1; // 设置参数
paramin[2] = sec2;
paramin[3] = Fosc/1000; // 当不使用PLL功能时,Fcclk=Fosc
iap_entry(paramin, paramout); // 调用IAP服务程序
}
3.3.1.4 编程扇区 通过这个过程,数据可以从RAM中编程到片内Flash中。 注:
- 数据只能从片内SRAM编程到片内Flash。
- 片内Flash的地址必须512字节对齐。
- 片内RAM应位于局部总线,即USB或以太网的SRAM不可以使用。
- 每一次编程字节应该是512、1024、4096、8192中的一个。
/*********************************************************************
* 名称:RamToFlash()
* 功能:复制RAM的数据到FLASH,命令代码51。
* 入口参数:dst 目标地址,即FLASH起始地址。以512字节为分界
* src 源地址,即RAM地址。地址必须字对齐
* no 复制字节个数,为512/1024/4096/8192
* 出口参数:IAP返回值(paramout缓冲区) CMD_SUCCESS,SRC_ADDR_ERROR,DST_ADDR_ERROR,
SRC_ADDR_NOT_MAPPED,DST_ADDR_NOT_MAPPED,COUNT_ERROR,BUSY,未选择扇区
********************************************************************/
void RamToFlash(uint32 dst, uint32 src, uint32 no)
{
paramin[0] = IAP_RAMTOFLASH; // 设置命令字
paramin[1] = dst; // 设置参数
paramin[2] = src;
paramin[3] = no;
paramin[4] = Fosc/1000; // 当不使用PLL功能时,Fcclk=Fosc
iap_entry(paramin, paramout); // 调用IAP服务程序
}
3.3.1.5 比较数据 通过这个函数,可以检查写入Flash中的数据和RAM中的是否相同。 注意源地址、目标地址和字节数必须是4的倍数。可使用Keil MDK提供的关键字__align(n) 来指定n字节对齐。 [objc] view plain copy
- <code class="language-objc">/********************************************************************
- * 名称:Compare()
- * 功能:校验数据,命令代码56。
- * 入口参数:dst 目标地址,即RAM/FLASH起始地址。地址必须字对齐
- * src 源地址,即FLASH/RAM地址。地址必须字对齐
- * no 复制字节个数,必须能被4整除
- * 出口参数:IAP返回值(paramout缓冲区) CMD_SUCCESS,COMPARE_ERROR,ADDR_ERROR
- ******************************************************************/
- void Compare(uint32 dst, uint32 src, uint32 no)
- {
- paramin[0] = IAP_COMPARE; // 设置命令字
- paramin[1] = dst; // 设置参数
- paramin[2] = src;
- paramin[3] = no;
- iap_entry(paramin, paramout); // 调用IAP服务程序
- }</code>
|