大家好,欢迎来到IT知识分享网。
目录
前言
主要介绍IAP,以及怎么实现
一、IAP简介
这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:
二、Bootloader编写
2.1 Flash读写
由于程序最终烧写到内部flash里,所以IAP涉及到flash的读写
#define STM32_FLASH_SIZE 256 //所选STM32的FLASH容量大小(单位为K) #define STM32_FLASH_BASE 0x0 //STM32 FLASH的起始地址 #if STM32_FLASH_SIZE<256 #define STM_SECTOR_SIZE 1024 //字节 #else #define STM_SECTOR_SIZE 2048 #endif u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节 //从指定地址开始读出指定长度的数据 //ReadAddr:起始地址 //pBuffer:数据指针 //NumToWrite:半字(16位)数 void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead) {
u16 i; for(i=0;i<NumToRead;i++) {
pBuffer[i]=*(vu16*)ReadAddr;//读取2个字节. ReadAddr+=2;//偏移2个字节. } } //不检查的写入 //WriteAddr:起始地址 //pBuffer:数据指针 //NumToWrite:半字(16位)数 void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) {
u16 i; for(i=0;i<NumToWrite;i++) {
FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]); WriteAddr+=2;//地址增加2. } } //从指定地址开始写入指定长度的数据 //WriteAddr:起始地址(此地址必须为2的倍数!!) //pBuffer:数据指针 //NumToWrite:半字(16位)数(就是要写入的16位数据的个数.) void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite) {
u32 secpos; //扇区地址 u16 secoff; //扇区内偏移地址(16位字计算) u16 secremain; //扇区内剩余地址(16位字计算) u16 i; u32 offaddr; //去掉0X0后的地址 if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址 FLASH_Unlock(); //解锁 offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址. secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6 secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.) secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小 if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围 while(1) {
STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容 for(i=0;i<secremain;i++)//校验数据 {
if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除 } if(i<secremain)//需要擦除 {
FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区 for(i=0;i<secremain;i++)//复制 {
STMFLASH_BUF[i+secoff]=pBuffer[i]; } STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区 }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. if(NumToWrite==secremain)break;//写入结束了 else//写入未结束 {
secpos++; //扇区地址增1 secoff=0; //偏移位置为0 pBuffer+=secremain; //指针偏移 WriteAddr+=secremain; //写地址偏移 NumToWrite-=secremain; //字节(16位)数递减 if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完 else secremain=NumToWrite;//下一个扇区可以写完了 } }; FLASH_Lock();//上锁 }
2.2 IAP程序
#define FLASH_APP1_ADDR 0x0 //第一个应用程序起始地址(存放在FLASH) typedef void (*iapfun)(void); //定义一个函数类型的参数. iapfun jump2app; u16 iapbuf[1024]; //appxaddr:应用程序的起始地址 //appbuf:应用程序CODE. //appsize:应用程序大小(字节). void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize) {
u16 t; u16 i=0; u16 temp; u32 fwaddr=appxaddr;//当前写入的地址 u8 *dfu=appbuf; for(t=0;t<appsize;t+=2) {
temp=(u16)dfu[1]<<8; temp+=(u16)dfu[0]; dfu+=2;//偏移2个字节 iapbuf[i++]=temp; if(i==1024) {
i=0; STMFLASH_Write(fwaddr,iapbuf,1024); fwaddr+=2048;//偏移2048 16=2*8.所以要乘以2. } } if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去. } //跳转到应用程序段 //appxaddr:用户代码起始地址. void iap_load_app(u32 appxaddr) {
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x) //检查栈顶地址是否合法. {
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址) __set_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) jump2app(); //跳转到APP. } }
IAP运行和跳转,其中USART_RX_BUF为串口接收APP缓存,applenth为APP长度
if(((*(vu32*)(0X+4))&0xFF000000)==0x0)//判断是否为0X08XXXXXX. {
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码 printf("固件更新完成!\r\n"); } printf("开始执行FLASH用户代码!!\r\n"); if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x0)//判断是否为0X08XXXXXX. {
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码 }
2.3 Bootloader程序配置
三、APP程序配置
app部分如果实验用,可以就用点灯尝试
3.1 APP 程序起始地址设置
3.2 中断向量表的偏移量设置
在系统启动的时候,会首先调用 SystemInit 函数初始化时钟系统,同时SystemInit 还完成了中断向量表的设置,如下所示
#ifdef VECT_TAB_SRAM SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */ #else SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */ #endif
VTOR寄存器存放的是中断向量表的起始地址,可以在 FLASH APP 的main函数最开头处添加如下代码实现中断向量表的起始地址的重设:
SCB->VTOR = FLASH_BASE | 0x10000; /* Vector Table Relocation in Internal FLASH. */
3.3 设置编译后生成.bin 文件
点击Options for Target→User选项卡,在After Build/Rebuild 栏,勾选 Run #1,并写入下行脚本,这样就可以使用MDK自带的fromelf.exe 转换工具,将.axf 文件转换成.bin 文件。
fromelf.exe --bin -o "$L@L.bin" "#L"
四、IAP补充
上面是M4内核的方法,M0要修改
4.1 GD32修改方法
- 重印射基地址
#define SYSCFG_MemoryRemap_Flash ((uint8_t)0x00) #define SYSCFG_MemoryRemap_SystemMemory ((uint8_t)0x01) #define SYSCFG_MemoryRemap_SRAM ((uint8_t)0x03) #define CPUID_STM32F0xx 0x410cc200 #define CPUID_STM32E230xx 0x411cd200 #define VECTOR_SIZE 0xB4 #define DEFAULT_APP_START_ADDR 0x0 //默认APP起始地址 #define DEFAULT_APP_MAX_SIZE 0x2800 //默认APP空间54K static void GD32E230_VTORRemap() {
if(SCB->CPUID == CPUID_STM32F0xx) {
//注:F0系列单片机没有VOTR寄存器,故此处将中断向量表映射在SRAM处 //memcpy((void*)0x, (void*)( FLASH_BASE | 0x02000), VECTOR_SIZE); //SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); memcpy((void*)0x, (void*)(DEFAULT_APP_START_ADDR), VECTOR_SIZE); SYSCFG->CFGR1 |= ((uint32_t)0x00000003); } else SCB->VTOR = DEFAULT_APP_START_ADDR; //GD32E230有SCB->VTOR寄存器 } / * @brief 控制程序跳转到指定位置开始执行 。 * @param Addr 程序执行地址。 * @retval 程序跳转状态。 */ void JumpToApplication(uint32_t Addr) {
static pFunction Jump_To_Application; __IO uint32_t JumpAddress; /* Test if user code is programmed starting from address "ApplicationAddress" */ if (((*(__IO uint32_t *)Addr) & 0x2FFE0000 ) == 0x) //检查栈顶地址是否合法. {
// RCC_DeInit(); //关闭外设 // DMA_DeInit(DMA1_Channel5); // DMA_DeInit(DMA1_Channel4); // USART_DeInit(USART1); // TIM_DeInit(TIM3); /* 关闭滴答定时器且禁止中断 */ SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk; /* Jump to user application */ JumpAddress = *(__IO uint32_t *) (Addr + 4); //用户代码区第二个字为程序开始地址(复位地址) Jump_To_Application = (pFunction) JumpAddress; __set_PRIMASK(1); //关闭所有中断 /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t *)Addr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) Jump_To_Application(); } else {
Uart.Fault = fu_no_app; } }
- 函数调用
void main(void) {
GD32E230_VTORRemap(); //条件成立执行以下 JumpToApplication(DEFAULT_APP_START_ADDR); }
4.2 STM32F0修改方法
- 创建函数
#define APPLICATION_ADDRESS (uint32_t)0x0800A000 #define SYSCFG_MemoryRemap_Flash ((uint8_t)0x00) #define SYSCFG_MemoryRemap_SystemMemory ((uint8_t)0x01) #define SYSCFG_MemoryRemap_SRAM ((uint8_t)0x03) #if (defined ( __CC_ARM )) __IO uint32_t VectorTable[48] __attribute__((at(0x))); #elif (defined (__ICCARM__)) #pragma location = 0x __no_init __IO uint32_t VectorTable[48]; #elif defined ( __GNUC__ ) __IO uint32_t VectorTable[48] __attribute__((section(".RAMVectorTable"))); #elif defined ( __TASKING__ ) __IO uint32_t VectorTable[48] __at(0x); #endif //IAP函数申明 typedef void (*pFunction)(void); pFunction Jump_To_Application; void APP_Restart(); //IAP地址修改 static void STM32F0xx_VectorRemap() {
uint32_t i = 0; /* Relocate by software the vector table to the internal SRAM at 0x */ /* Copy the vector table from the Flash (mapped at the base of the application load address 0x0800A000) to the base address of the SRAM at 0x. */ for(i = 0; i < 48; i++) {
VectorTable[i] = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2)); } /* Enable the SYSCFG peripheral clock*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); /* Remap SRAM at 0x00000000 */ SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); } //IAP函数 void APP_Restart() {
uint32_t JumpAddress; /* Jump to user application */ JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4); Jump_To_Application = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); /* Jump to application */ Jump_To_Application(); }
- 主函数调用
void main(void) {
STM32F0xx_VectorRemap(); /*一系列初始化*/ while(1) {
if(/*条件成立*/) APP_Restart(); } }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/136274.html