一步一步实现STM32-FOTA系列教程之Bootloader编写

发布者:EnchantedBreeze最新更新时间:2019-08-23 来源: eefocus关键字:STM32  FOTA系列  Bootloader编写 手机看文章 扫描二维码
随时随地手机看文章

前言

上一篇文章《一步一步实现STM32-FOTA系列教程之FLASH静态区读写》实现了对FLASH静态区读写的操作,有了这部分功能之后,就可以实现一个非常简单的Bootloader代码了。


转载请注明出处


Bootloader 功能说明

这里提供的Bootloader功能就非常的简单了,就是在Bootloader启动之后,读取FLASH静态区的参数信息,然后判断启动分区标志位的值,然后进入相应的分区,运行该分区的程序。


注意这里的教程中没有在 Bootloader 中编写联网获取新版本的代码,这部分的实现会放到主分区和备份分区的代码中实现。


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大小,如下图所示。

Bootloader-ROM配置

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

jlink


测试验证

验证说明

本次测试验证主要是验证该 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固件的设置如下。


Firmware1固件MDK配置设置

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固件的设置如下。

Firmware2固件MDK配置设置

验证步骤

首先将 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...



关键字:STM32  FOTA系列  Bootloader编写 引用地址:一步一步实现STM32-FOTA系列教程之Bootloader编写

上一篇:一步一步实现STM32-FOTA系列教程之FLASH静态区读写
下一篇:stm32控制舵机旋转到不同角度

推荐阅读最新更新时间:2024-11-08 16:34

stm32 FreeRTOS中如何创建任务
#include config.h #include global.h #include stdio.h #include PC.h #include FreeRTOS.h #include task.h #include list.h void vLED1Task(void *pvParameters) { while(1) { GPIO_Reverse (); vTaskDelay(1000/portTICK_RATE_MS); } } void vLED2Task(void *pvParameters) { portTickType xLastWakeTime; //xLastWakeTi
[单片机]
【STM32+W5500】 21,移植说明,以太网关键函数,
0.移植说明,w5500移植文件 Ethernet和Internet里面包含的文件就是以太网五层模型中的应用层 应用层协议:HTTP,FTP,SMTP,TFTP,Telent, MQTT 支撑协议:DNS,SNMP HTTP使用TCP而不是UDP. MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和制动器(比如通过Twitter让房屋联网)的通信协议。 0-1,Ethernet可以认为是对W5500芯片硬件的配置,全部添加
[单片机]
【STM32+W5500】 21,移植说明,以太网关键函数,
STM32CUBEIDE打印浮点数问题
IDE不像MDK5那样默认就可以使用串口输出浮点数。 解决办法: 右键你的工程,在最下面点击 properties 在弹出来的框中点击 C/C++ Build 下拉框 在下拉框中点击 Settings 在弹出来的框中最底部找到 Miscellaneous 并点击 在other flags 中点击 图标 输入 -u_printf_float ,然后点击ok 最后点击 Apply and close,就可以使用串口打印浮点数了。
[单片机]
STM32 之 SysTick
感觉定时1秒还是有点不准,仅为目测,下次用示波器去测量下。 包含文件: (1)Main C语言: Codee#14620 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 实验平台 : ST 官方三合一套件 + 硬件 : STM32F103C8T6 + 开发平台 : IAR For ARM 5.40 + 仿真器 : J-Link + 日期 : 2010-10-26 + 频率 :HSE = 8MHz ,主频 = 72MHz +++++++++++++++++++++++++++++++++++
[单片机]
<font color='red'>STM32</font> 之 SysTick
STM32 通用定时器相关寄存器
TIMx_CR1(控制寄存器1) 9-8位:CKD 时钟分频因子,定义在定时器时钟(CK_INT)频率与数字滤波器(ETR,TIx)使用的采样频率之间的分频比例。 定义:00(tDTS = tCK_INT),01(tDTS = 2 x tCK_INT),10(tDTS = 4 x tCK_INT)11:保留 7位:ARPE:自动重装载预装载允许位,定义:0(TIMx_ARR寄存器没有缓冲),1(TIMx_ARR寄存器被装入缓冲器) 6-5位:CMS 选择中央对齐模式,定义:00:边沿对齐模式。计数器依据方向位(DIR)向上或向下计数。 01(中央对齐模式1。计数器交替地向上和向下计数。配置为输出的通道(TIMx_CC
[单片机]
STM32之独立看门狗
实验现象: 开始LED1亮,LED2熄灭,若不隔时间按KEY1则发现LED2因独立看门狗的作用使系统复位而不断闪烁,若间断的按KEY1则发现LED2不会闪烁,表明没有复位。 实验平台: 基于STM32F103C8T6的彩屏开发板 操作步骤: 1)向IWDG_KR 写入0X5555。 通过这步,我们取消IWDG_PR 和IWDG_RLR 的写保护,使后面可以操作这两个寄存器。 设置 IWDG_PR 和IWDG_RLR 的值。 这两步设置看门狗的分频系数,和重装载的值。由此,就可以知道看门狗的喂狗时间(也 就是看门狗溢出时间),该时间的计算方式为: Tout=((4 2^prer) rlr) /40 其中Tout 为看门
[单片机]
STM32 ADC 采样频率的确定
(4)16.7 可编程的通道采样时间 ADC 使用若干个ADC_CLK 周期对输入电压采样,采样周期数目可以通过 ADC_SMPR1 和ADC_SMPR2 寄存器中的SMP 位而更改。每个通道可以以 不同的时间采样。 总转换时间如下计算: TCONV = 采样时间+ 12.5 个周期 例如: 当ADCCLK=14MHz 和1.5 周期的采样时间 TCONV = 1.5 + 12.5 = 14 周期 = 1 s SMPx :选择通道x的采样时间 这些位用于独立地选择每个通道的采样时间。在采样周期中通道选择位必须保持不变。 000:1.5周期 100:41.5周期 001:7.5周期 101:55.5周期
[单片机]
STM32 在温度监控系统中的应用
引言 现代工业控制领域通常要测量很多信号,将其转化为计算机可以识别的二进制信号,并利用计算机监视和记录各种测量的信号。这个过程就要涉及到信号的采集和处理。CAN 总线是一种串行多主总线,它卓越的特性、极高的可靠性和独特的设计,特别适合工业过程监控设备的互连,因此,越来越受到工业界的重视,并已公认为最有前途的 现场总线 之一。本文介绍了基于 STM32 和 CAN 总线的温度监控系统的设计,通过上位机与下位机的通信,实现对温度数据的监控,并经初步实验达到了设计的要求。 1 系统总体方案概述 系统总体框图如图 1 所示,本系统采用主站+从站的结构,CAN 主站主要实现温度数据的存储以及 CAN 总线协议和串口协议之间的 桥接 ,CA
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved