历史上的今天
今天是:2024年08月23日(星期五)
2019年08月23日 | 一步一步实现STM32-FOTA系列教程之Bootloader编写
2019-08-23 来源:eefocus
前言
上一篇文章《一步一步实现STM32-FOTA系列教程之FLASH静态区读写》实现了对FLASH静态区读写的操作,有了这部分功能之后,就可以实现一个非常简单的Bootloader代码了。
转载请注明出处
Bootloader 功能说明
这里提供的Bootloader功能就非常的简单了,就是在Bootloader启动之后,读取FLASH静态区的参数信息,然后判断启动分区标志位的值,然后进入相应的分区,运行该分区的程序。
注意这里的教程中没有在 Bootloader 中编写联网获取新版本的代码,这部分的实现会放到主分区和备份分区的代码中实现。
Bootloader 启动流程
这个启动流程之前已经说过了,这里贴出来,方便对比代码。

函数实现
FLASH 分区宏定义
// FLASH 分区 配置
#define FLASH_BASE_ADDR ((uint32_t)0x08000000)
#define NLEDBOOTLOADER_SIZE (64*1024) // Bootloader 大小为 64KB
#define FIRMWAR_ONE_SIZE (80*1024) // 固件1 大小为 80KB
#define FIRMWAR_TWO_SIZE (80*1024) // 固件2 大小为 80KB
#define NLED_CONFIG_PARAM_SIZE (224*1024)
#define BOOTLOADER_START_ADDR (FLASH_BASE_ADDR) //Bootloader 启动地址
#define FIRMWAR_ONE_START_ADDR (FLASH_BASE_ADDR + NLEDBOOTLOADER_SIZE ) // 固件 1 启动地址
#define FIRMWAR_TWO_START_ADDR (FLASH_BASE_ADDR + NLEDBOOTLOADER_SIZE +FIRMWAR_ONE_SIZE) // 固件 2 启动地址
#define CONFIG_PARAM_START_ADDR (FLASH_BASE_ADDR + NLED_CONFIG_PARAM_SIZE) // FLASH 静态区参数 起始地址
程序跳转代码
在之前的 FLASH静态区参数读写的基础上。增加了对启动分区的判断还有加载启动分区的代码。
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
for(int i = 0; i < 8; i++)
{
NVIC->ICER[i] = 0xFFFFFFFF; /* 关闭中断*/
NVIC->ICPR[i] = 0xFFFFFFFF; /* 清除中断标志位 */
}
jump2app(); //跳转到APP.
}
}
//判断进行固件升级还是跳转APP
void LoadingFirmware(void)
{
if(bootflag ==1)
{
printf("Jump To Firmware First 0x%08Xrn",FIRMWAR_ONE_START_ADDR);
iap_load_app(FIRMWAR_ONE_START_ADDR);
}
else if(bootflag ==2)
{
printf("Jump To Firmware First 0x%08Xrn",FIRMWAR_TWO_START_ADDR);
iap_load_app(FIRMWAR_TWO_START_ADDR);
}
else
{
printf("boot errorrn");
}
LED0 = 0;
}
主函数实现
int main()
{
ledInit();
uart1_init(9600);
delay_init();
printf("-----------------------------------rn");
printf("------------Bootloader-------------rn");
printf("-----------------------------------rn");
LOG_COMPILE();
printf("Version: V1.0rn");
LED0_Blink(100);
GetDeviceInfo();
LoadingFirmware(); /*判断加载启动分区*/
}
至此,如此简单的一个Bootloader 就编写完成,下面开始测试验证一下吧。
注意事项
1、由于Bootloader所占用的FLASH空间大小为64KB,如果在MDK中进行调试仿真时,注意修改MDK工程的ROM大小,如下图所示。

2、在使用JLINK仿真器下载代码的时候,注意下载时擦除 FLASH 扇区的方式不要选择全片擦除,这里选择擦除部分块,选择方式如下所示。

测试验证
验证说明
本次测试验证主要是验证该 Bootloader 能否加载指定分区中的程序,由于还没有编写远程获取固件的代码,因此这里采用调试编译仿真的方式将主分区和备份分区的代码烧写到 FLASH 中。
为了测试方便,这里仅仅提供两个最简单的分区代码,其中主分区的代码会在串口1循环打印Firmware1 running…,循环间隔为1秒;备份分区的代码则会在串口1循环打印Firmware2 running…,循环间隔为2.5秒。
Firmware1固件main文件实现
#include #include "sys.h" #include "delay.h" #include "led.h" #include "usart.h" #include "logdebug.h" #include "fotaprotocol.h" //运行指示灯 void LED0_Blink(int xms) { LED0 =0; delay_ms(xms); LED0 =1; delay_ms(xms); LED0 =0; delay_ms(xms); LED0 =1; delay_ms(xms); LED0 =0; delay_ms(xms); } int main() { NVIC_SetVectorTable(FIRMWAR_ONE_START_ADDR,0); delay_init(); ledInit(); uart1_init(9600); printf("-----------------------------------rn"); printf("------------Firmware1--------------rn"); printf("-----------------------------------rn"); LOG_COMPILE(); printf("Firmware-1-Version: V1.0rn"); while(1) { printf("Firmware1 running...rn"); LED0_Blink(200); } } 注意,代码编写完之后,还要修改MDK工程中ROM区域的设置,Firmware1固件的设置如下。 Firmware2固件main文件实现 #include #include "sys.h" #include "delay.h" #include "led.h" #include "usart.h" #include "logdebug.h" #include "fotaprotocol.h" //运行指示灯 void LED0_Blink(int xms) { LED0 =0; delay_ms(xms); LED0 =1; delay_ms(xms); LED0 =0; delay_ms(xms); LED0 =1; delay_ms(xms); LED0 =0; delay_ms(xms); } int main() { NVIC_SetVectorTable(FIRMWAR_TWO_START_ADDR,0); delay_init(); ledInit(); uart1_init(9600); printf("-----------------------------------rn"); printf("------------Firmware2--------------rn"); printf("-----------------------------------rn"); LOG_COMPILE(); printf("Firmware-2-Version: V1.0rn"); while(1) { printf("Firmware2 running...rn"); LED0_Blink(500); } } 注意,代码编写完之后,还要修改MDK工程中ROM区域的设置,Firmware2固件的设置如下。 验证步骤 首先将 Bootloader 利用 Jlink 烧写到 FLASH 中。 然后将 Firmware1 和 Firmware2 固件分别按照同样的方法烧写到FLASH中。 烧写完成后,按复位按钮查看启动日志。 烧写完成后,正常的测试现象是,首先启动 Bootloader 打印 Bootloader 相关信息,然后根据启动分区标志位加载不同的固件。 注意,由于在 Bootloader中加入了交替修改启动分区标志位的代码,因此每次重启单片机,Bootloader就会从不同的分区执行代码。这样也达到了测试Bootloader加载启动分区的效果了。 测试日志记录 下面附上测试验证的日志,注意单片机重启是手动硬件复位的,日志如下。 ----------------------------------- ------------Bootloader------------- ----------------------------------- Compile Time: Nov 13 2018,19:24:50 Version: V1.0 Static Params Address :0x08038000 start to boot firmware one Testing... set updateflag 2 Jump To Firmware First 0x08010000 ----------------------------------- ------------Firmware1-------------- ----------------------------------- Compile Time: Nov 13 2018,19:39:26 Firmware-1-Version: V1.0 Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... Firmware1 running... ----------------------------------- ------------Bootloader------------- ----------------------------------- Compile Time: Nov 13 2018,19:24:50 Version: V1.0 Static Params Address :0x08038000 start to boot firmware two Testing... set updateflag 1 Jump To Firmware First 0x08024000 ----------------------------------- ------------Firmware2-------------- ----------------------------------- Compile Time: Nov 13 2018,19:33:58 Firmware-2-Version: V1.0 Firmware2 running... Firmware2 running... Firmware2 running... Firmware2 running... Firmware2 running... Firmware2 running... Firmware2 running...

史海拾趣
|
各位,我最近调试一个东东,LPC2114用串口Uart0与电脑通讯。上位机程序是VC++6.0编的。工作顺序如下:首先电脑定时发送8字节读数指令,2114收到后返回8字节数据。问题是无论2114内的串口程序是中断的还是轮询的,电脑收 ...… 查看全部问答> |
|
WinCE6.0 和 Wince5.0 差别很大,请介绍一下WinCE6.0 (包括: 目录结构....) WinCE6.0 和 Wince5.0 差别很大,请介绍一下WinCE6.0 (包括: 目录结构....)… 查看全部问答> |
|
最近弄一个窗口,是有标题栏的;但是鼠标拖动窗口的标题栏,则窗口会跟着鼠标移动,我不想让窗口移动,要让他固定在某个位置;这个该怎么弄啊?那位大哥给点代码让我试一下;我也试过:ON_WM_NCLBUTTONDOWN()和ON_WM_NCHITTEST();但是一编译就 ...… 查看全部问答> |
|
我的中断时使用两个,一个事外部中断PA8,另一个timer3产生10ms中断,设置如下:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//配置为:先占优先级(0-1),从优先级(0-7)// Enable the EXTI9_5 Interrupt NVIC_Ini ...… 查看全部问答> |
|
用MSP430F2101开发的系统,定时器由ACLK作为时钟,能工作。因发现ACLK使用的32768振荡器太弱,极易受到干扰,故打算使用外接晶振来解决,即用外部晶振电路然后由XIN脚接入,用示波器看,振荡很大,可是系统就是不工作,不知何故,请高手帮忙。… 查看全部问答> |
|
双11除了让人剁手,还能让人跺脚。11号发出的,今天才拿到,整整9天。 另外,这个网络图片就是能忽悠人,看着挺大,结果到手一看,好迷你啊,就是半个鼠标那么大嘛。 整块板子也很简单,从BOM看,就是两个芯片:blueNRG和eeprom。 这个eeprom是s ...… 查看全部问答> |




