1、环境
stm32f103zet6
MDK 5.28
2、芯片
2.1 Flash大小
我用的是stm32f103zet6属于高容量产品,flash大小512KB,每个Page2KB大小,一共256页(这个可以根据自己的芯片去ST官网查询文档),如下图所示:
我们这边对这个flash再做个划分(需要根据固件大小和bootloader程序大小划分,bootloader程序出厂就不会改变了,所以要预留考虑好分区是否放的下程序):
- bootloader大小是前128KB,从0x08000000~0x0801FFFF
- APP程序区是192KB,从0x08010000~0x0804FFFF
- UpdateFirmware区是192KB,从0x08050000~0x0807FFFF
UpdateFirmware
这个区域我是把这个区域第一页用来存放是否升级,文件大小等信息等。
第二页开始也才开始存放真正意义上的固件。
内部flash详细分区
3、IAP升级流程
bootloader程序设计流程
APP程序设计流程
4、Bootloader程序设计
要实现IAP升级,需要先实现几个功能擦写内部flash、计算校验和、程序跳转和软件复位系统的功能。
擦除/写flash
flash解锁和上锁
ST对内部flash做了保护如果要擦写需要先解锁,使用结束后我们也会锁上。
这里使用的是HAL库提供的函数接口实现的:
- flash解锁
HAL_StatusTypeDef HAL_FLASH_Unlock(void) //解锁
- flash上锁
HAL_StatusTypeDef HAL_FLASH_Lock(void) //上锁
- flash擦除
/**
* @brief Erase the specified FLASH memory sector
* @param Sector FLASH sector to erase
* The value of this parameter depend on device used within the same series
* @param VoltageRange The device voltage range which defines the erase parallelism.
* This parameter can be one of the following values:
* @arg FLASH_VOLTAGE_RANGE_1: when the device voltage range is 1.8V to 2.1V,
* the operation will be done by byte (8-bit)
* @arg FLASH_VOLTAGE_RANGE_2: when the device voltage range is 2.1V to 2.7V,
* the operation will be done by half word (16-bit)
* @arg FLASH_VOLTAGE_RANGE_3: when the device voltage range is 2.7V to 3.6V,
* the operation will be done by word (32-bit)
* @arg FLASH_VOLTAGE_RANGE_4: when the device voltage range is 2.7V to 3.6V + External Vpp,
* the operation will be done by double word (64-bit)
*
* @retval None
*/
void FLASH_Erase_Sector(uint32_t Sector, uint8_t VoltageRange)
- flash写入
/**
* @brief Program byte, halfword, word or double word at a specified address
* @param TypeProgram Indicate the way to program at a specified address.
* This parameter can be a value of @ref FLASH_Type_Program
* @param Address specifies the address to be programmed.
* @param Data specifies the data to be programmed
*
* @retval HAL_StatusTypeDef HAL Status
*/
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
- 等待flash操作完成
/**
* @brief Wait for a FLASH operation to complete.
* @param Timeout maximum flash operationtimeout
* @retval HAL Status
*/
HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout)
更新APP区代码如下
HAL_FLASH_Unlock();
// 擦除应用程序区
for(uint8_t i=0;i<APP_SECTOR_TOTAL_NUM;i++)
{
FLASH_WaitForLastOperation(0xffff);
FLASH_Erase_Sector(APP_FIRST_SECTOR_NUM+i,FLASH_VOLTAGE_RANGE_2);//APP_FIRST_SECTOR_NUM定义成APP开始地址
}
for(uint32_t i=0;i<firmSize;i++)
{
dat = *(__IO uint16_t*)(FIRMWARE_START_FLASH_ADDR + i*2);//FIRMWARE_START_FLASH_ADDR 定义固件区起始地址
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,APP_START_FLASH_ADDR + i*2,dat);//将新固件写入APP区
}
// 擦除固件升级信息
FLASH_WaitForLastOperation(0xffff);
FLASH_Erase_Sector(UPDATE_PLAN_SECTOR_NUM,FLASH_VOLTAGE_RANGE_2);//将升级计划擦除
// 擦除固件缓存区
for(uint8_t i=0;i<FIRMWARE_SECTOR_TOTAL_NUM;i++)
{
FLASH_WaitForLastOperation(0xffff);
FLASH_Erase_Sector(FIRMWARE_FIRST_SECTOR_NUM+i,FLASH_VOLTAGE_RANGE_2);
}
HAL_FLASH_Lock();//上锁
FLASH_WaitForLastOperation(0xffff);//等待flash操作结束
校验
功能:确保固件在flash中是完全正确的
这个可以自己设计,和校验和CRC校验都可以。需要添加校验和到固件的末端或者头部。然后根据计算方法验证flash中的固件是否正确,与添加到固件中的校验和比对,若正确认为固件完整且正确。为了提高效率只有申请固件升级后才会对固件进行验证工作。
程序跳转
void Jump(uint32_t addr)
{
__disable_irq() ; //关闭总中断
/* Jump to user application */
if (((*(volatile uint32_t*)addr) & 0x2FFE0000 ) == 0x20000000)
{
JumpAddress = *((__IO uint32_t*) (addr + 4));
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) addr);
Jump_To_Application();
}
}
从bootloader程序跳转到APP程序。
注:这里关闭了中断,所以跳转结束后,APP程序需要打中断否则会出现进不了中断的现象。
系统软复位
5、APP程序设计
flash起始位置的修改
中断向量表偏移
SCB->VTOR = FLASH_BASE | 0x20000; /* Vector Table Relocation in Internal FLASH */
注:
Q:为什么只有APP程序中需要设置中断向量偏移量?
A:bootloader一般写在FLASH的头部,开机直接启动,所以使用默认的向量偏移地址就可以。
App程序一般写在BootLoader的后面或者外部FLASH中,程序头部不在0x8000000,其中断向量表基地址也不在0x8000000。
Q:HAL库的
下载程序设计(也可以设计在Bootloader中)
Ymodem协议可以看另外一篇文章这里不过多介绍了。
flash的写入
与Bootloader中的flash擦写一致。
完成固件接收之后还需要将升级计划更新,以便于下次启动时更新新的的固件。