完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
近期看到一篇关于bootloader的博文,亲手也尝试了一下,这里做下笔记,方便日后查看,根据项目修改可以加个DTU,用服务器远程升级一下程序。
一、工程配置 使用的是stm32f103vet6芯片,512的ROM,64kRAM,外部flash用的是8M芯片W25Q64,分成三部分,每部分1Mb,第一块(0x000000-0x100000)存放升级应用程序,第二块(0x100000-0x200000)存放备份程序(即当前应用程序),第三块用来存放标志位(新旧程序校验码、旧程序备份标志、新程序更新标志)。 1.BootLoader程序工程配置 2.Application程序工程配置 二、大体流程,细节需完善 1.需要注意对flash写操作前,先擦除对应的扇区,外部flash一次查出一扇区4kb,stm32大容量一次擦除2kb,像stm32f103c8t6这种一次擦除1kb。
三、代码 1.BootLoader main.c /** ****************************************************************************** * @file main.c ****************************************************************************** */ #include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./led/bsp_led.h" #include "./flash/bsp_spi_flash.h" #include "app_update.h" typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus; __IO uint32_t DeviceID = 0; __IO uint32_t FlashID = 0; __IO TestStatus TransferStatus1 = FAILED; void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); } int main(void) { LED_GPIO_Config(); /* 配置串口为:115200 8-N-1 */ USART_Config(); printf("rn 这是一个bootloader程序 rn"); /* 8M串行flash W25Q64初始化 */ SPI_FLASH_Init(); /* 获取 Flash Device ID */ DeviceID = SPI_FLASH_ReadDeviceID(); Delay( 200 ); /* 获取 SPI Flash ID */ FlashID = SPI_FLASH_ReadID(); printf("rn FlashID is 0x%X, Manufacturer Device ID is 0x%Xrn", FlashID, DeviceID); /* 检验 SPI Flash ID */ if (FlashID == sFLASH_ID) { printf("rn 检测到串行flash W25Q64 !rn"); // 读取更新标志 SPI_FLASH_BufferRead(&Prog_Status_Flag.Backup_Flag,PROG_FLAG_ADDR,sizeof(struct _Prog_Status_Flag)); // 判断是否需要备份程序,新的 FLASH 读出来都是 0xFF if(Prog_Status_Flag.Backup_Flag != 0xA5) { LED_GREEN; // 如果检测到是新的 Flash 或者是第一次开机,则备份当前的程序, // 备份后在进行校验,校验OK后将校验码写入 PROG_FLAG_ADDR 开始的地址中 Backup_Current_Program(); } // 判断是否需要更新程序 if(Prog_Status_Flag.Update_Flag == 0x55) { LED_BLUE; // 显示屏显示提示,正在更新程序.... Backup_Current_Program(); Updata_New_Program(); } } else { LED_RED; printf("rn 获取不到 W25Q64 ID!nr"); } Load_App(STARTADDR); while(1); } /*********************************************END OF FILE**********************/ app_update.c /** ****************************************************************************** * @file app_update.c * @author ****************************************************************************** */ #include "stm32f10x.h" #include "./usart/bsp_usart.h" #include "./led/bsp_led.h" #include "./flash/bsp_spi_flash.h" #include "app_update.h" struct _Prog_Status_Flag Prog_Status_Flag; uint32_t JumpAddress; // 跳转地址 u8 ReadBuf[2048]; // 缓存,2048 , 每次更新 2048 2k typedef void (*pFunction)(void); // 申明一个函数指针 pFunction Jump_To_Application; // 备分当前程序 void Backup_Current_Program(void) { unsigned int i,j; uint32_t ADD_CheckSum = 0; unsigned int XOR_CheckSum = 0; unsigned int Addr; JumpAddress = STARTADDR; // 当前程序的地址 Prog_Status_Flag.Old_Prog_ADD_CheckSum = 0; Prog_Status_Flag.Old_Prog_XOR_CheckSum = 0; // 开始备份当前程序 Addr = OLD_PROG_ADDR; // 要备份到外部 Flash 的地址 /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 这里擦除496k,即一个扇区496/4,擦除的最小单位是扇区*/ for(int m=0;m<496/4;m++) { SPI_FLASH_SectorErase(OLD_PROG_ADDR+m*4096); } for(i=0;i<496/2;i++) // // 使用的是 STM32F103VET6,内部 Flash 是 512K,4K用来做 Bootload,所以只有 496K { for(j=0;j<2048;j++) // 每次更新 2K { ReadBuf[j] = *(__IO uint8_t*)JumpAddress++; // 如果是 0x00,或者是 0xFF if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00)) { // 计算校验和 Prog_Status_Flag.Old_Prog_ADD_CheckSum += ReadBuf[j]; // 计算异或校验 Prog_Status_Flag.Old_Prog_XOR_CheckSum ^= ReadBuf[j]; } } // 写入到外部 Flash 中 SPI_FLASH_BufferWrite(ReadBuf,Addr,2048); Addr += 2048; } // 校验程序 Addr = OLD_PROG_ADDR; for(i=0; i<496/2; i++)// { // 从 Flash 中读取程序 SPI_FLASH_BufferRead(ReadBuf,Addr,2048); for(j=0; j<2048; j++) { // 计算校验码 if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00)) { ADD_CheckSum += ReadBuf[j]; XOR_CheckSum ^= ReadBuf[j]; } } Addr += 2048; } #ifdef PROG_VERIFY // 判断程序是否相同 if((Prog_Status_Flag.Old_Prog_ADD_CheckSum == ADD_CheckSum) &&(Prog_Status_Flag.Old_Prog_XOR_CheckSum == XOR_CheckSum)) { // 备份当前程序成功 Prog_Status_Flag.Backup_Flag = 0xA5; Prog_Status_Flag.Update_Flag = 0x00; // 将校验码写入外部 Flash PROG_FLAG_ADDR 开始的地址中 SPI_FLASH_SectorErase(PROG_FLAG_ADDR);//擦一个扇区4k,写之前一定要擦 SPI_FLASH_BufferWrite(&Prog_Status_Flag.Backup_Flag,PROG_FLAG_ADDR,sizeof(struct _Prog_Status_Flag)); } #endif } // 更新新程序 /** 注意如果你使用的是 STM32F103RC 内部有 256 Kbytes Flash 的 MCU, 则需要注意一点, FLASH_ErasePage 每次擦除不是 1024 byte, 而是 2048 byte, 我实测是这个样子,所以这段程序要改成* u8 ReadBuf[2048]; // 缓存,1024 , 每次更新 1024, 改成 2048 // 擦除并写入内部 Flash if(FLASH_ErasePage(WriteAddr) == FLASH_COMPLETE) // 擦除并写入 { p = (uint32_t*)ReadBuf; for(j=0; j<512; j++) // 256 改成 512 { FLASH_ProgramWord(WriteAddr,*p++); // 每次写入 32 bit WriteAddr += 4; } Addr += 2048; // 1024 改成 2048 } */ void Updata_New_Program(void) { unsigned int i,j; uint32_t ADD_CheckSum = 0; unsigned int XOR_CheckSum = 0; unsigned int Addr; unsigned int WriteAddr; uint32_t *p; FLASH_Unlock(); // MCU 内部 Flash 擦写解除锁定 Addr = NEW_PROG_ADDR; WriteAddr = STARTADDR; Prog_Status_Flag.New_Prog_ADD_CheckSum = 0; Prog_Status_Flag.New_Prog_XOR_CheckSum = 0; JumpAddress = STARTADDR; for(i=0; i<496/2; i++) // 496 K,每次擦2k写2k { SPI_FLASH_BufferRead(ReadBuf,Addr,1024*2); // 从外部 Flash 读取程序 for(j=0; j<1024*2; j++) // 2k { if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00)) { ADD_CheckSum += ReadBuf[j]; XOR_CheckSum ^= ReadBuf[j]; } } // 擦除并写入内部 Flash if(FLASH_ErasePage(WriteAddr) == FLASH_COMPLETE) // 擦除并写入 { p = (uint32_t*)ReadBuf; for(j=0; j<512; j++) { FLASH_ProgramWord(WriteAddr,*p++); // 每次写入 32 bit WriteAddr += 4; } Addr += 2048; } else { // 显示屏显示提示 Flash Error } } // 校验写入的程序 for(i=0;i<496/2;i++) // 496k { for(j=0;j<2048;j++) // 2k { ReadBuf[j] = *(__IO uint8_t*)JumpAddress++; // 如果是 0x00,或者是 0xFF if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00)) { // 计算校验和 Prog_Status_Flag.New_Prog_ADD_CheckSum += ReadBuf[j]; // 计算异或校验 Prog_Status_Flag.New_Prog_XOR_CheckSum ^= ReadBuf[j]; } } } #ifdef PROG_VERIFY // 判断是否相同 if((Prog_Status_Flag.New_Prog_ADD_CheckSum == ADD_CheckSum) &&(Prog_Status_Flag.New_Prog_XOR_CheckSum == XOR_CheckSum)) { // 程序更新成功 Prog_Status_Flag.Update_Flag = 0x00;//更新标志清0 // 将校验码写入外部 Flash PROG_FLAG_ADDR 开始的地址中 SPI_FLASH_SectorErase(PROG_FLAG_ADDR);//擦一个扇区4k,写之前一定要擦 SPI_FLASH_BufferWrite(&Prog_Status_Flag.Backup_Flag,PROG_FLAG_ADDR,sizeof(struct _Prog_Status_Flag)); } #endif } //跳转到应用程序段,appxaddr:用户代码起始地址. void Load_App(u32 appxaddr) { // 跳转到用户程序 if (((*(__IO uint32_t*)appxaddr) & 0x2FFE0000 ) == 0x20000000) { /* Jump to user application */ JumpAddress = *(__IO uint32_t*) (appxaddr + 4); Jump_To_Application = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ /*后加*/ __set_BASEPRI((0xFFU >> 2));//关闭全局中断 NVIC_SetVectorTable(NVIC_VectTab_FLASH,(appxaddr-0X08000000));//设置中断向量表偏移量 __set_BASEPRI(0);//打开全局中断 /**/ __set_MSP(*(__IO uint32_t*) appxaddr); Jump_To_Application(); } } app_update.h #ifndef __APP_UPDATE_H #define __APP_UPDATE_H #include "stm32f10x.h" // 定义几个常量 // 外部 SPI Flash 地址,我使用的是 8M 外部 Flash, // 所以空间很大,分了三块出来,每块 1M 的空间 #define NEW_PROG_ADDR (0x000000) // 新程序存放的地址 #define OLD_PROG_ADDR (0x100000) // 旧程序存放的地址 #define PROG_FLAG_ADDR (0x200000) // 程序标志位 #define STARTADDR 0x08004000 // 用户程序地址 #define PROG_VERIFY // 程序是否需要校验,取消 xPROG_VERIFY 的 x 就可以添加校验功能 // 程序更新标志位 struct _Prog_Status_Flag { u8 Backup_Flag; // 程序是否已经备份 u32 Old_Prog_ADD_CheckSum; // 备份程序(旧程序)校验和 u8 Old_Prog_XOR_CheckSum; // 备份程序(旧程序)异或校验 u32 New_Prog_ADD_CheckSum; // 要更新的程序(新程序)校验和 u8 New_Prog_XOR_CheckSum; // 要更新的程序(新程序)异或校验 u8 Update_Flag; // 更新标志位,有更新这个值为0x55,更新完成校验后清0 }; extern struct _Prog_Status_Flag Prog_Status_Flag; extern void Backup_Current_Program(void); extern void Updata_New_Program(void); extern void Load_App(u32 appxaddr); #endif 因为手里没有写关于串口接收这部分程序,这里先将应用程序复制到外部flash第一块(0x000000-0x100000)中做测试的升级程序,后期有项目再修改。 app程序中进行中断向量偏移: DownloadCodeToOutFlash.c /** ****************************************************************************** * @file DownloadCodeToOutFlash.c * @author ****************************************************************************** */ #include "DownloadCodeToOutFlash.h" #include "./flash/bsp_spi_flash.h" struct _Prog_Status_Flag Prog_Status_Flag; uint32_t JumpAddress; // 跳转地址 u8 ReadBuf[2048]; // 缓存,2048 , 每次更新 2048 2k // 将当前程序下载到外部flash的第一部分 void Download_Current_Program(void) { unsigned int i,j; uint32_t ADD_CheckSum = 0; unsigned int XOR_CheckSum = 0; unsigned int Addr; JumpAddress = STARTADDR; // 当前程序的地址,正常情况应该取串口接收的程序数据头地址 Prog_Status_Flag.New_Prog_ADD_CheckSum = 0; Prog_Status_Flag.New_Prog_XOR_CheckSum = 0; // 开始下载当前程序到外部flash,正常情况是将从串口来的数据程序下载到外部flash Addr = NEW_PROG_ADDR; // 要下载到外部 Flash 的地址 /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 这里擦除496k,即一个扇区496/4,擦除的最小单位是扇区*/ for(int m=0;m<496/4;m++) { SPI_FLASH_SectorErase(NEW_PROG_ADDR+m*4096); } for(i=0;i<496/2;i++) // // 使用的是 STM32F103VET6,内部 Flash 是 512K,4K用来做 Bootload,所以只有 496K { for(j=0;j<2048;j++) // 每次更新 2K { ReadBuf[j] = *(__IO uint8_t*)JumpAddress++; // 如果是 0x00,或者是 0xFF if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00)) { // 计算校验和 Prog_Status_Flag.New_Prog_ADD_CheckSum += ReadBuf[j]; // 计算异或校验 Prog_Status_Flag.New_Prog_XOR_CheckSum ^= ReadBuf[j]; } } // 写入到外部 Flash 中 SPI_FLASH_BufferWrite(ReadBuf,Addr,2048); Addr += 2048; } // 校验程序 Addr = NEW_PROG_ADDR; for(i=0; i<496/2; i++)// { // 从 Flash 中读取程序 SPI_FLASH_BufferRead(ReadBuf,Addr,2048); for(j=0; j<2048; j++) { // 计算校验码 if((ReadBuf[j] != 0xFF)&&(ReadBuf[j] != 0x00)) { ADD_CheckSum += ReadBuf[j]; XOR_CheckSum ^= ReadBuf[j]; } } Addr += 2048; } #ifdef PROG_VERIFY // 判断程序是否相同 if((Prog_Status_Flag.New_Prog_ADD_CheckSum == ADD_CheckSum) &&(Prog_Status_Flag.New_Prog_XOR_CheckSum == XOR_CheckSum)) { // 下载当前(更新的)程序到外部flash第一部分成功 Prog_Status_Flag.Update_Flag = 0x55; // 将校验码写入外部 Flash PROG_FLAG_ADDR 开始的地址中 SPI_FLASH_SectorErase(PROG_FLAG_ADDR);//擦一个扇区4k,写之前一定要擦 SPI_FLASH_BufferWrite(&Prog_Status_Flag.Backup_Flag,PROG_FLAG_ADDR,sizeof(struct _Prog_Status_Flag)); } #endif NVIC_SystemReset(); // 复位 } DownloadCodeToOutFlash.h #ifndef __DownloadCodeToFlash_H #define __DownloadCodeToFlash_H #include "stm32f10x.h" // 定义几个常量 // 外部 SPI Flash 地址,我使用的是 8M 外部 Flash, // 所以空间很大,分了三块出来,每块 1M 的空间 #define NEW_PROG_ADDR (0x000000) // 新程序存放的地址 #define OLD_PROG_ADDR (0x100000) // 旧程序存放的地址 #define PROG_FLAG_ADDR (0x200000) // 程序标志位 #define STARTADDR 0x08004000 // 用户程序地址 #define PROG_VERIFY // 程序是否需要校验,取消 xPROG_VERIFY 的 x 就可以添加校验功能 // 程序更新标志位 struct _Prog_Status_Flag { u8 Backup_Flag; // 程序是否已经备份 u32 Old_Prog_ADD_CheckSum; // 备份程序(旧程序)校验和 u8 Old_Prog_XOR_CheckSum; // 备份程序(旧程序)异或校验 u32 New_Prog_ADD_CheckSum; // 要更新的程序(新程序)校验和 u8 New_Prog_XOR_CheckSum; // 要更新的程序(新程序)异或校验 u8 Update_Flag; // 更新标志位,有更新这个值为0x55,更新完成校验后清0 }; extern struct _Prog_Status_Flag Prog_Status_Flag; extern void Download_Current_Program(void); #endif |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1574 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1519 浏览 1 评论
949 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
672 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1565 浏览 2 评论
1852浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
622浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
508浏览 3评论
514浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
494浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-15 02:20 , Processed in 0.731874 second(s), Total 79, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号