赞
踩
IAP,即In Application Programming,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写。简单来说,就是开发者代码出bug了或者添加新功能了,能够利用预留的通讯接口,对代码进行升级
UART、SPI、IIC、USB等等,当然还有wifi、4G、蓝牙等无线通讯手段,都可以作为IAP升级的方式,今天主要介绍如何使用串口对固件进行升级
要想设计IAP,首先需要对MCU的代码启动过程有个了解,先来看看STM32的代码启动过程是怎样的吧
此部分参考:https://www.cnblogs.com/gulan-zmc/p/12248509.html
在《Cortex-M3权威指南》有讲述:芯片复位后首先会从向量表里面取出两个值(下图来自Cortex-M3权威指南):
从0x0000 0000地址取出MSP(主堆栈寄存器)的值
从0x0000 0004地址取出PC(程序计数器)的值
然后取出第一条指令执行
;******************** (C) COPYRIGHT 2011 STMicroelectronics ******************** ;* File Name : startup_stm32f10x_hd.s ;* Author : MCD Application Team ;* Version : V3.5.0 ;* Date : 11-March-2011 ;* Description : STM32F10x High Density Devices vector table for MDK-ARM ;* toolchain. ;* This module performs: ;* (上电复位后会做下面的几件事情) ;* - Set the initial SP(设置堆栈,就是设置MSP的值) ;* - Set the initial PC == Reset_Handler(设置PC的值) ;* - Set the vector table entries with the exceptions ISR address(设置中断向量表的地址) ;* - Configure the clock system and also configure the external (设置系统时钟;如果芯片外部由挂载SRAM,还需要配置SRAM,默认是没有挂外部SRAM的) ;* SRAM mounted on STM3210E-EVAL board to be used as data ;* memory (optional, to be enabled by user) ;* - Branches to __main in the C library (which eventually (调用C库的__main函数,然后调用main函数执行用户的) ;* calls main()). ;* After Reset the CortexM3 processor is in Thread mode, ;* priority is Privileged, and the Stack is set to Main. ;* <<< Use Configuration Wizard in Context Menu >>> ;******************************************************************************* ; ------------------分配栈空间---------------- Stack_Size EQU 0x00000400 ;EQU指令是定义一个标号;标号名是Stack_Size; 值是0x00000400(有点类似于C语言的#define)。Stack_Size标号用来定义栈的大小 AREA STACK, NOINIT, READWRITE, ALIGN=3 ;AREA指令是定义一个段;这里定义一个 段名是STACK,不初始化,数据可读可写,2^3=8字节对齐的段(详细的说明可以查看指导手册) Stack_Mem SPACE Stack_Size ;SPACE汇编指令用来分配一块内存;这里开辟内存的大小是Stack_Size;这里是1K,用户也可以自己修改 __initial_sp ;在内存块后面声明一个标号__initial_sp,这个标号就是栈顶的地址;在向量表里面会使用到 ; <h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h> ; ------------------分配堆空间---------------- ;和分配栈空间一样不过大小只是512字节 Heap_Size EQU 0x00000200 AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base ;__heap_base堆的起始地址 Heap_Mem SPACE Heap_Size ;分配一个空间作为堆空间,如果函数里面有调用malloc等这系列的函数,都是从这里分配空间的 __heap_limit ;__heap_base堆的结束地址 PRESERVE8 ;PRESERVE8 指令作用是将堆栈按8字节对齐 THUMB;THUMB作用是后面的指令使用Thumb指令集 ; ------------------设置中断向量表---------------- ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY ;定义一个段,段名是RESET的只读数据段 ;EXPORT声明一个标号可被外部的文件使用,使标号具有全局属性 EXPORT __Vectors ;声明一个__Vectors标号允许其他文件引用 EXPORT __Vectors_End ;声明一个__Vectors_End标号允许其他文件引用 EXPORT __Vectors_Size ;声明一个__Vectors_Size标号允许其他文件引用 ;DCD 指令是分配一个或者多个以字为单位的内存,并且按四字节对齐,并且要求初始化 ;__Vectors 标号是 0x0000 0000 地址的入口,也是向量表的起始地址 __Vectors DCD __initial_sp ;* Top of Stack 定义栈顶地址;单片机复位后会从这里取出值给MSP寄存器, ;* 也就是从0x0000 0000 地址取出第一个值给MSP寄存器 (MSP = __initial_sp) ;* __initial_sp的值是链接后,由链接器生成 DCD Reset_Handler ;* Reset Handler 定义程序入口的值;单片机复位后会从这里取出值给PC寄存器, ;* 也就是从0x0000 0004 地址取出第一个值给PC程序计数器(pc = Reset_Handler) ;* Reset_Handler是一个函数,在下面定义 ;后面的定义是中断向量表的入口地址了这里就不多介绍了,想要了解的可以参考《STM32中文手册》和《Cortex-M3权威指南》 DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler .....由于文件太长这里省略了部分向量表的定义,完整的可以查看工程里的启动文件 DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1 DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2 DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3 DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5 __Vectors_End ;__Vectors_End向量表的结束地址 __Vectors_Size EQU __Vectors_End - __Vectors ;定义__Vectors_Size标号,值是向量表的大小 AREA |.text|, CODE, READONLY ;定义一个代码段,段名是|.text|,属性是只读 ;PROC指令是定义一个函数,通常和ENDP成对出现(标记程序的结束) ; Reset handler Reset_Handler PROC ;定义 Reset_Handler函数;复位后赋给PC寄存器的值就是Reset_Handler函数的入口地址值。也是系统上电后第一个执行的程序 EXPORT Reset_Handler [WEAK] ;*[WEAK]指令是将函数定义为弱定义。所谓的弱定义就是如果其他地方有定义这个函数, ;*编译时使用另一个地方的函数,否则使用这个函数 ;*IMPORT 表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似 IMPORT __main ;*__main 和 SystemInit 函数都是外部文件的标号 IMPORT SystemInit ;* SystemInit 是STM32函数库的函数,作用是初始化系统时钟 LDR R0, =SystemInit BLX R0 LDR R0, =__main ;* __main是C库的函数,主要是初始化堆栈和代码重定位,然后跳到main函数执行用户编写的代码 BX R0 ENDP ; Dummy Exception Handlers (infinite loops which can be modified) ;下面定义的都是异常服务函中断服务函数 NMI_Handler PROC EXPORT NMI_Handler [WEAK] B . ENDP .....由于文件太长这里省略了部分函数的定义,完整的可以查看工程里的启动文件 SysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP Default_Handler PROC EXPORT WWDG_IRQHandler [WEAK] EXPORT PVD_IRQHandler [WEAK] .....由于文件太长这里省略了部分中断服务函数的定义,完整的可以查看工程里的启动文件 EXPORT DMA2_Channel2_IRQHandler [WEAK] EXPORT DMA2_Channel3_IRQHandler [WEAK] EXPORT DMA2_Channel4_5_IRQHandler [WEAK] WWDG_IRQHandler PVD_IRQHandler TAMPER_IRQHandler .....由于文件太长这里省略了部分标号的定义,完整的可以查看工程里的启动文件 DMA2_Channel1_IRQHandler DMA2_Channel2_IRQHandler DMA2_Channel3_IRQHandler DMA2_Channel4_5_IRQHandler B . ENDP ALIGN ;四字节对齐 ;******************************************************************************* ; User Stack and Heap initialization ;******************************************************************************* ;下面函数是初始化堆栈的代码 IF :DEF:__MICROLIB ;如果定义了__MICROLIB宏编译下面这部分代码,__MICROLIB在MDK工具里面定义 ;这种方式初始化堆栈是由 __main 初始化的 EXPORT __initial_sp ;栈顶地址 (EXPORT将标号声明为全局标号,供其他文件引用) EXPORT __heap_base ;堆的起始地址 EXPORT __heap_limit ;堆的结束地址 ELSE ;由用户初始化堆 ;否则编译下面的 IMPORT __use_two_region_memory ;__use_two_region_memory 由用户实现 EXPORT __user_initial_stackheap __user_initial_stackheap LDR R0, = Heap_Mem ;堆的起始地址 LDR R1, =(Stack_Mem + Stack_Size);栈顶地址 LDR R2, = (Heap_Mem + Heap_Size);堆的结束地址 LDR R3, = Stack_Mem ;栈的结束地址 BX LR ALIGN ENDIF END ;******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE*****
1、上电复位后,从 0x0000 0000 地址取出栈顶地址赋给MSP寄存器(主堆栈寄存器),即MSP = __initial_sp。这一步是由硬件自动完成的
2、从0x0000 0004 地址取出复位程序的地址给PC寄存器(程序计数器),即PC = Reset_Handler。这一步也是由硬件自动完成调用SystemInit函数初始化系统时钟
3、跳到C库的__main函数初始化堆栈(初始化时是根据前面的分配的堆空间和栈空间来初始化的)和代码重定位(初始RW 和ZI段),然后跳到main函数执行应用程序
大体分为两部分设计,bootloader、APP代码设计,bootloader用于检查APP区代码是否需要更新,以及跳转到APP区执行APP程序
下面这个流程比较通用一些,大概是下图所示升级流程:
是以STM32L431KBU6为主控做的flash分区,主要功能:
bootloader区:0x0800 0000 到 0x0800 5000 地址的flash块划分给bootloader,用于升级固件,大小是20kb
APP区:0x0800 5000 到 0x0801 1800 的flash块划分为APP区 ,(application)用于存放用户功能应用代码,大小是50Kb
APP缓存区: 0x0801 1800 到 0x0801 E000 的flash块划分为APP缓存区 (update region),用于暂存下发的固件,大小跟应用程序区一样 50kb
用户参数区+未定义:0x0801 E000 到 0x0802 0000 的flash块划分为用户参数区(parameters),用于存储用户的一些参数,大小是8Kb,flash块划分未定义区,可以根据具体用途定义
Flash擦除:
/* 功能:擦除用户地址所在页 参数:EraseAddr 需要擦除的Flash地址 NumberPage 擦除的页数 */ HAL_StatusTypeDef MY_FLASH_Erase(uint32_t EraseAddr,uint8_t NumberPage) { uint8_t page; uint32_t PAGEError=0; FLASH_EraseInitTypeDef FLASH_EraseInitStruct; /** 计算用户编程地址在FLASH中哪个页(每页2K字节) **/ page = (EraseAddr - FLASH_BASE) / FLASH_PAGE_SIZE;//编程地址在Bank1上用此公式 // page = (addr - (FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;//地址在Bank2 FLASH_EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; //擦除方式:页擦除 FLASH_EraseInitStruct.Banks = FLASH_BANK_1; //擦除页所在的区域:BANK1 FLASH_EraseInitStruct.Page = page; //擦除页的编号 FLASH_EraseInitStruct.NbPages = NumberPage; //擦除页的数量 return HAL_FLASHEx_Erase(&FLASH_EraseInitStruct,&PAGEError); }
写入Flash:
/* 功能:写入Flash 参数:WriteNumber 写入Flash的数据 addr 写的地址 */ void Flash_WriteNumber(uint32_t addr,uint32_t WriteNumber) { uint8_t page; uint32_t PageError = 0; FLASH_EraseInitTypeDef FlashSet; HAL_StatusTypeDef status; /* 擦除Flash */ /** 计算用户编程地址在FLASH中哪个页(每页2K字节) **/ page = (addr - FLASH_BASE) / FLASH_PAGE_SIZE;//编程地址在Bank1上用此公式 // page = (addr - (FLASH_BASE + FLASH_BANK_SIZE)) / FLASH_PAGE_SIZE;//地址在Bank2 FlashSet.TypeErase = FLASH_TYPEERASE_PAGES; //擦除方式:页擦除 FlashSet.Banks = FLASH_BANK_1; //擦除页所在的区域:BANK1 FlashSet.Page = page; //擦除页的编号 FlashSet.NbPages = 1; //擦除页的数量 //解锁Flash操作 HAL_FLASH_Unlock(); status = HAL_FLASHEx_Erase(&FlashSet, &PageError);//擦除flash HAL_FLASH_Lock(); if(status != HAL_OK) { printf("erase fail, PageError = %d\r\n", PageError); } else printf("erase success\r\n"); /* 写入新的数据 */ HAL_FLASH_Unlock();//解锁 status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, (uint64_t)WriteNumber);//写入Flash if(status != HAL_OK) { printf("Flash write error,0x%08x\r\n",addr); } else printf("Flash write success\r\n"); HAL_FLASH_Lock();//上锁 }
读取FLASH:
/* 功能:读取Flash内容 参数:addr 需要读取的Flash地址 size 读取字节大小 */ uint32_t* Flash_ReadNumber(uint32_t addr,uint16_t size) { static uint32_t ReadData[] = {0}; /* 读取Flash内容 */ for(uint32_t i=0;i<size;i++) { ReadData[i] = (*(uint32_t*)(addr+4*i)); printf("Flash read data:%d\r\n",ReadData[i]); } return ReadData; }
//CAN1中断服务函数 void CAN1_RX0_IRQHandler(void) { HAL_CAN_IRQHandler(&hcan1); } /** * \brief 函数功能:CAN中断回调函数 * 此函数会被CAN_Receive_IT()调用 * \param[in] hcan:CAN句柄 * \return * */ void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) { // 可以使用rxHeader和rxData来获取消息的ID和数据内容 CAN_RxHeaderTypeDef RxHeader; uint8_t rxData[8]; // 检查是否有新的CAN消息到达 if(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, rxData) == HAL_OK) { // 在这里对接收到的CAN消息进行处理 // 可以使用rxHeader和rxData来获取消息的ID和数据内容 if(RxHeader.StdId == 0x666) { CAN_RxData = 0; CAN_RxData= ((uint64_t)rxData[7]<<56)|((uint64_t)rxData[6]<<48)|((uint64_t)rxData[5]<<40)|((uint64_t)rxData[4]<<32) \ |((uint64_t)rxData[3]<<24)|((uint64_t)rxData[2]<<16)|((uint64_t)rxData[1]<<8)|((uint64_t)rxData[0]<<0); if(CAN_RxData == 0x8888888800000000)//固件包发送完毕 { Rx_Complete_State = 0xF;//0-未接收完固件 F-固件全部接收完成 1-单次接收完成 } else { Rx_Complete_State = 0x1; } } } }
bootloader代码设计:
#include "main.h" uint8_t Rx_Complete_State = 0; //0-未接收完固件 F-固件全部接收完成 1-单次接收完成 volatile uint8_t recv_tcp_complete = 0; //接收到平板发送的一帧tcp命令 1:接收到 处理完成后需要把此变量清零 #define Fifo_Buf_Size_MAX 2048 uint8_t Fifo_Buf[Fifo_Buf_Size_MAX] = {0}; /* 定义变量 ----------------------------------------------------------*/ #define APP_BASE_ADDR (uint32_t)0x08005000 //定义应用程序的起始地址 #define APP_CODE_MAX (uint32_t)50*1024 //定义APP程序最大长度 50K #define UPDATE_CODESAVE_ADDR (uint32_t)0x08011800 //定义升级程序待存放的起始地址 #define PAPER_FLAG_UPSTART_ADDR (uint32_t)0x0801F800 //升级标志存放的地址 #define PAPER_BIN_LEN_ADDR (uint32_t)0x0801F808 //bin文件大小存放的地址 uint32_t Updatepara = 0; //定义更新参数在flash中的位置 typedef void (*piapfunc)(void); //定义一个函数类型的参数. void iap_load_app(uint32_t JumpAddress) { piapfunc Jump_To_Application; __disable_irq(); /* Check if the top address of the stack is legal*/ if (((*(__IO uint32_t*)APP_BASE_ADDR)&0x2FFE0000)==0x20000000) { DEBUG("goto APP Start...\r\n"); // HAL_Delay(100); JumpAddress = *(__IO uint32_t*)(APP_BASE_ADDR + 4); /* Jump to user application */ Jump_To_Application = (piapfunc)JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) APP_BASE_ADDR); __enable_irq(); Jump_To_Application(); } else { printf("\r\n No APP found!!!\r\n"); } } int main(void) { uint32_t bin_total_len = 0; //定义固件总大小 // uint32_t recv_total_bin_len = 0; //需要接收的总固件的大小 uint32_t recv_bin_len = 0; //已经接收的固件大小 /* USER CODE BEGIN SysInit */ HAL_Init(); SystemClock_Config(); LL_mDelay(500); /* 初始化外围配置 */ MX_GPIO_Init(); MX_USART2_UART_Init(); printf("\r\n Bootloader function!!\r\n"); CAN1_Config(); HAL_FLASH_Unlock(); /* Clear All pending flags 清除所有错误标志(如果不清除会导致写失败)*/ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); HAL_FLASH_Lock(); uprintf("p_Updatepara1:0x%x\r\n\r\n",*((uint32_t*)PAPER_FLAG_UPSTART_ADDR)); uprintf("PAPER_BIN_LEN:0x%x--%dk\r\n\r\n ",*((uint32_t*)(PAPER_FLAG_UPSTART_ADDR+8)),*((uint32_t*)(PAPER_FLAG_UPSTART_ADDR+8))/1024); Updatepara = *((uint32_t*)PAPER_FLAG_UPSTART_ADDR); uprintf("Updatepara3:0x%08x\r\n\r\n ",Updatepara); if(Updatepara == 0x66668888)//需要更新固件,接收固件保存至备份区 { /* 保存bin文件长度 */ bin_total_len = *((uint32_t *)(PAPER_BIN_LEN_ADDR)); printf("\r\n bin_total_len:%d\r\n",bin_total_len); if(bin_total_len > APP_CODE_MAX) //如果固件长度超过存储空间 发送error类 擦除升级标志 复位 { // SendError(ERR_BIN_LARGE,40); //固件太大 存不下 printf("\r\n Updating firmware file too large \r\n");//更新文件过大 goto pp1; } /* 更新flash中固件升级标志位状态改为正在升级 */ HAL_FLASH_Unlock(); /* Clear All pending flags 清除所有错误标志(如果不清除会导致写失败)*/ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); MY_FLASH_Erase(PAPER_FLAG_UPSTART_ADDR,1); HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,PAPER_FLAG_UPSTART_ADDR,0x11112222); HAL_FLASH_Lock(); /* 擦除备份区flash数据 */ printf("\r\n Start erase falsh!\r\n"); HAL_FLASH_Unlock(); /* Clear All pending flags 清除所有错误标志(如果不清除会导致写失败)*/ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); MY_FLASH_Erase(UPDATE_CODESAVE_ADDR,APP_CODE_MAX/2048); HAL_FLASH_Lock(); printf("\r\n Erase compelte!\r\n"); printf("\r\n Waiting to receive updated firmware package \r\n"); /* 发送Bootloader准备接收数据标志 */ // CAN1_Send_Msg();//发送Bootloader准备完毕,可以发送固件包 /* 等待接受更新固件包并写入备份区 */ while(1) { if(Rx_Complete_State == 1)//接收到一次固件包 { Rx_Complete_State = 0; HAL_FLASH_Unlock(); /* 接收到一次写入备份区 */ uint64_t data_to_write = 0; printf("\r\n receive CAN_RxData: 0x%16llx\r\n\r\n",CAN_RxData); // data_to_write = ((CAN_RxData & 0x00000000ffffffff) << 32) | (CAN_RxData >> 32); data_to_write = CAN_RxData; printf("data_to_write: 0x%16llx\r\n\r\n",data_to_write); HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,UPDATE_CODESAVE_ADDR+recv_bin_len,data_to_write); HAL_FLASH_Lock(); printf("\r\n Program addr: 0x%08x\r\n",UPDATE_CODESAVE_ADDR+recv_bin_len); /* 校验接收数据错误或flash写入错误 */ uint64_t CheckData = ((uint64_t)(*(uint32_t*)(UPDATE_CODESAVE_ADDR+recv_bin_len+4)) << 32) | (*(uint32_t*)(UPDATE_CODESAVE_ADDR+recv_bin_len)) ; if(CheckData != CAN_RxData) { printf("\r\n Check data fail,addr:0x%08x\r\n",UPDATE_CODESAVE_ADDR+recv_bin_len); printf("CheckData:0x%llx \r\n data_to_write: 0x%llx\r\n",CheckData,CAN_RxData); //发送error类 数据出错 需要重发 // SendError(ERR_CALC_FAIL,CLASS_UPDATE_DATA_INDEX); } recv_bin_len = recv_bin_len+8;//一次接受8个字节(64位) printf("\r\n recv cuccess,recv_total_bin_len:%d\r\n",recv_bin_len); uint8_t Return_Buf[] = {0xFF,0x4A,0x5A,0xA5,0xFF,0x4A,0x5A,0xA5}; CAN1_Send_Msg(0x68,Return_Buf,8);//告诉上位机单次接收完成,准备接受下次数据 } else if(Rx_Complete_State == 0xF)//固件包全部接收完成 /* 拷贝备份区数据至app */ { // Rx_Complete_State = 0; HAL_FLASH_Unlock(); /* Clear All pending flags 清除所有错误标志(如果不清除会导致写失败)*/ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); MY_FLASH_Erase(APP_BASE_ADDR,APP_CODE_MAX/2048); /* 拷贝备份区固件至app */ uint64_t data_to_write = 0; printf("\r\n Copy the firmware of the backup area to the app\r\n"); for(uint32_t i=0;i<recv_bin_len;i+=8) { data_to_write = ((uint64_t)*((uint32_t *)(UPDATE_CODESAVE_ADDR+i+4)) << 32) | ((uint64_t)*((uint32_t *)(UPDATE_CODESAVE_ADDR+i))); HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,APP_BASE_ADDR+i,data_to_write); } for(uint32_t i=0;i<recv_bin_len;i+=4) { printf("\r\n addr:0x%08x data:0x%08x \r\n",APP_BASE_ADDR+i,*((uint32_t *)(APP_BASE_ADDR+i))); } /* Clear All pending flags */ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); MY_FLASH_Erase(PAPER_FLAG_UPSTART_ADDR,1); HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,PAPER_FLAG_UPSTART_ADDR,0x88886666);//跟新完之后这里改变标志位 HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,(PAPER_FLAG_UPSTART_ADDR+8),0x8000); HAL_FLASH_Lock(); printf("\r\n Successfully copied to the app \r\n"); printf("FLAG_UPSTART:%x\r\n",*((uint32_t*)PAPER_FLAG_UPSTART_ADDR)); HAL_Delay(100); printf("\r\n iap_load_app! \r\n"); iap_load_app(APP_BASE_ADDR); //复位 运行bootloaderr // printf("\r\n system reset!\r\n"); // NVIC_SystemReset(); } } } else if(Updatepara == 0x88886666)//不需要更新固件 直接运行app { printf("\r\n iap_load_app! \r\n"); iap_load_app(APP_BASE_ADDR); } else { iap_load_app(APP_BASE_ADDR); } pp1: HAL_FLASH_Unlock(); /* Clear All pending flags */ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); MY_FLASH_Erase(PAPER_FLAG_UPSTART_ADDR,1); HAL_FLASH_Lock(); HAL_Delay(100); //复位 运行bootloaderr // printf("\r\n system reset!\r\n"); // NVIC_SystemReset(); iap_load_app(APP_BASE_ADDR); }
bin文件生成
$K\ARM\ARMCC\bin\fromelf.exe --bin --output=Bin\@L.bin !L
APP代码设计
APP代码是用户功能代码,实现业务逻辑,本次测试用的比较简单,接收到APP代码之后,会自动重启,更新APP区代码
SCB->VTOR = FLASH_BASE | 0x5000;//设置中断偏移
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。