目录
基于STM32f103芯片的应用程序在线升级功能框架的实现
一、原理简介
二、KEIL软件主要设置
三、应用程序app部分
四、Bootloader部分
五、补充部分
基于STM32f103芯片的应用程序在线升级功能框架的实现
一、原理简介
我们在使用stm32的过程中,如果需要对芯片的软件程序进行在线升级,一般会将软件程序分为bootloader程序以及应用程序app。 在bootloader程序中,系统上电后,通过进行条件判断确定是否需要对硬件板卡进行软件升级,如果不需要进行升级就直接跳转到app程序进行执行。如果需要进行软件升级,那么就在bootloader中完成对软件的升级操作。这里以stm32f103ze系列进行说明。
二、KEIL软件主要设置
这里将软件程序分为bootloader和app两部分。 其中bootloader代码部分的起始地址为:0x08000000,大小为0x4000(16KB); app部分的代码起始地址为:0x08004000,大小为0x7C000(512KB-16KB)。 //芯片flash总大小为512KB 在keil软件的魔术棒下的Target设置框下,按照以上信息在IROM1中分别设置好软件程序对应的起始地址和大小即可。
三、应用程序app部分
1、固定版本号信息 在应用程序app中,可以通过固化app程序版本号的位置作为后续判断硬件板卡是否需要升级的条件。具体代码如下: //在app中固定硬件板卡版本号V1.00的存储位置为0x0800F800。 const unsigned char Ver[6] __attribute__ ((at(0x0800F800)))= {'V','1','.','0','0'}; 固定好硬件板卡的版本号信息后,我们后续在bootloader中通过读取该位置的版本号信息进行判断是否需要进行升级操作。
2、升级前擦除版本号所在的flash页内容。 关于对stm32的flash进行擦除的操作,不同的芯片操作方式可能有所不同。有的芯片是按页进行擦除有的芯片是按照扇区进行擦除。这里使用的stm32f103是采用页擦除的方式。具体代码如下:void RunUpdateCommand() {uint8_t *p;p = (uint8_t *)((uint32_t)0x0800F800);printf("擦除前FLASH地址0x0800F800存放的字符为:%c\n",*p);FLASH_Unlock();if(FLASH_ErasePage(0x0800F800) == FLASH_COMPLETE){FLASH_Lock();printf("擦除后FLASH地址0x0800F800存放的字符为:%c\n",*p); }printf("================进入bootloader程序===============\n");NVIC_SystemReset();while(1); } 这里在app程序中,进入bootloader程序执行升级操作前首先会擦除原来的版本号所在的flash页内容。然后使用函数NVIC_SystemReset()进行复位操作从而进入到bootloader程序中。
3、app的主函数部分代码 应用程序app主函数main的主要代码如下:void main() {SystemInit(); //系统初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置优先级分组NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x4000); //设置app程序的向量表偏移位置__disable_irq();/* 在这里进行硬件外设的初始化操作*/__enable_irq();while(1){//应用程序的代码部分 } }
四、Bootloader部分
bootloader部分的核心代码如下: typedef void (*pFunction)(void); //定义函数指针pFunction,用于实现函数的跳转 //定义APP版本号地址,需要和app中的存储地址对应 #define APP_VERSION_ADDR ((uint32_t)0x0800F800) //定义APP的起始地址,需要和keil软件中的设置对应并且和app主函数中的向量表的偏移地址对应。 //这里可知偏移地址为0x08004000 - 0x08000000 = 0x4000。 #define Application_Address ((uint32_t)0x08004000) uint32_t JumpAddress; pFunction Jump_To_Application; void main() {uint8_t ch1;SystemInit(); //系统初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置优先级分组__disable_irq();/* 在这里进行硬件外设的初始化操作*//*对于仅仅用于升级流程的初始化操作也可以放到while(1)前进行初始化*///通讯串口初始化//调试串口初始化等等__enable_irq();//================================================================================ch1 = *(uint8_t *)(APP_VERSION_ADDR)if(ch1 == 'V') {JumpAddress = *(__IO uint32_t *)(Application_Address + 4); //app程序复位的地址Jump_To_Application = (pFunction)JumpAddress;__set_MSP(*(__IO uint32_t *)Application_Address); //初始化app的栈指针printf("================进入应用程序APP===============\n");Jump_To_Application(); //跳转到app程序while(1);}//备注:双线之间的程序是通过读取应用程序存储在0x0800F800地址的版本号V1.00中的字符'V'//是否存在来判断是否需要执行程序升级的。//如果字符'V'依旧存在那就不需要升级直接跳转进入app程序即可。//如果字符'V'不存在(因为在进入boorloader升级之前在app中已经把对应页擦除掉了),//表示app程序需要进行升级操作,那么就不再跳转进入app程序,而是接着执行升级程序的操作。//================================================================================//这里可以初始化那些仅仅用于升级流程的初始化操作,比如初始化队列,或者初始化系统指示灯等。while(1){/*升级程序的代码实现操作*/ //注意在升级完成后,同样可以使用函数NVIC_SystemReset()进行复位操作//使得系统重新由bootloader跳转到app中。 } }
五、补充部分
补充一:对于bootloader中升级程序的代码实现思路,我这边是通过串口与上位机通讯进行操作的,在串口接收中断中将上位机发来的数据存放到队列中,然后根据升级流程从队列中读取相关内容。整个升级流程大致分为:1、握手(联机)2、擦除app的FLASH空间内容3、对之前擦除的FLASH写入新的升级程序数据,该步骤又可以细分为:①接收每次写入flash的起始地址②接收每次写入flash的数据长度③接收每次写入的具体数据并进行数据校验④调用接口将升级数据写入flash4、升级成功,调用函数接口NVIC_SystemReset()重启系统由bootloader进入app程序。以上的各步骤都可以和上位机之间添加应答机制。进一步保证通讯的可靠性。补充二:如果在bootloader程序跳转进入到app程序后发现在app中无法进入中断,可能是因为app主函数中的以下代码的顺序有问题:SystemInit(); //系统初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置优先级分组NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x4000); //设置app程序的向量表偏移位置 在app的main函数中要确保SystemInit()系统初始化函数放在NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x4000)设置向量表偏移位置函数之前。
完结。。。