flash基本规划
为实现IAP升级,一般将flash规划成如上区域。
bootloader: 系统引导
app: 主程序运行的地方
update: 新的主程序固件存放的地方
这里给bootloader预留16K的空间,所以APP所在地址是0x08004000
,编译APP时,要注意将中断向量表的偏移地址设置为0x4000
bootloader要完成的功能
根据以上描述,bootloader应有以下功能
- 跳转功能,可以跳转到app区执行主程序
- 固件搬运功能,因为固件中断向量偏移是固定的
0x4000
,所以如果有新固件,需要将新固件转移到app区再跳转(APP区地址偏移需和中断向量偏移对应)
bootloader实现
根据上节分析,实现bootloader要有如下工作:
- 编写读写内部flash的接口
- 编写跳转到指定地址运行程序的代码
- 根据ST官方例程实现的flash操作接口:
擦除操作
// flash只能按页擦除。第一个参数是待擦除的起始页,第二个参数是要擦除的页数
uint32_t FLASH_If_Erase(uint32_t start_page, uint8_t pages)
{
uint32_t PageError = 0;
FLASH_EraseInitTypeDef pEraseInit;
HAL_StatusTypeDef status = HAL_OK;
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
pEraseInit.PageAddress = start_page;
pEraseInit.Banks = FLASH_BANK_1;
pEraseInit.NbPages = pages;
status = HAL_FLASHEx_Erase(&pEraseInit, &PageError);
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
HAL_FLASH_Lock();
if (status != HAL_OK)
{
/* Error occurred while page erase */
return FLASHIF_ERASEKO;
}
return FLASHIF_OK;
}
写flash
#define UPDATE_APP_ADDRESS (uint32_t)0x0802C000 /* ADDR_FLASH_PAGE_88 */
#define APP_ADDRESS (uint32_t)0x08004000
uint32_t FLASH_If_Write(uint32_t destination, uint32_t *p_source, uint32_t length)
{
uint32_t i = 0;
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
for (i = 0; (i < length) && (destination <= (USER_FLASH_END_ADDRESS-4)); i++)
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, destination, *(uint32_t*)(p_source+i)) == HAL_OK)
{
/* Check the written value */
if (*(uint32_t*)destination != *(uint32_t*)(p_source+i))
{
/* Flash content doesn't match SRAM content */
return(FLASHIF_WRITINGCTRL_ERROR);
}
/* Increment FLASH destination address */
destination += 4;
}
else
{
/* Error occurred while writing data in Flash memory */
return (FLASHIF_WRITING_ERROR);
}
}
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
HAL_FLASH_Lock();
return (FLASHIF_OK);
}
- 固件转移
void AppMove(void) {
// 新固件区的首个字是存放固件大小的
uint32_t file_size = *(uint32_t*)UPDATE_APP_ADDRESS;
uint32_t pages_use = 0;
uint32_t* data_ptr = NULL;
uint32_t file_size_word = 0;
if (file_size != 0xffffffff) {
printf("move app\n");
data_ptr = (uint32_t*)(UPDATE_APP_ADDRESS + 4);
// 计算需要擦除的页数
pages_use = file_size / FLASH_PAGE_SIZE + 1;
printf("size: %d pages_use: %d\n", file_size, pages_use);
file_size_word = file_size / 4;
FLASH_If_Erase(APP_ADDRESS, pages_use);
// 读取UPDATA区并写入APP跳转区域
FLASH_If_Write(APP_ADDRESS, data_ptr, file_size_word);
// 擦除UPDATA区
FLASH_If_Erase(UPDATE_APP_ADDRESS, pages_use);
} else {
printf("no new app\n");
}
}
- 程序跳转
typedef void (*pFunction)(void);
void AppJump(void) {
printf("jump\n");
if (((*(__IO uint32_t*)APP_ADDRESS) & 0x2FFE0000 ) == 0x20000000) {
uint32_t jump_addr = *(__IO uint32_t*)(APP_ADDRESS + 4);
pFunction jump_to_app = (pFunction)jump_addr;
__set_MSP(*(__IO uint32_t*)APP_ADDRESS);
__disable_irq();
jump_to_app();
}
}
- main.c
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
AppMove();
AppJump();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
bool就完成了 IAP参考这里