嵌入式MCU BootLoader开发配置详细笔记教程

目录

一、BootLoader基础

二、BootLoader原理及配置

三、BootLoader程序

bootloader.h

bootloader.c

四、Application1 用户程序

application1.h

application1.c

五、Application2 用户程序

application2.h

六、程序运行效果

七、工程文件Demo


一、BootLoader基础

        对于接触过嵌入式Linux系统开发的开发者们,想必对BootLoader是不陌生的,因为定制化移植Linux系统,最先接触的就是BootLoader程序。但如果是从单片机MCU起步的开发者,可能对BootLoader就不是那么熟悉了,因为单片机开发最先接触的往往是GPIO外设的驱动开发。但不管是嵌入式Linux的MPU开发,还是嵌入式单片机的MCU开发,BootLoader的功能都是一样的。它是嵌入式系统中一种特殊的软件程序,它在系统加电或复位后最先执行,负责初始化硬件设备、设置系统环境,并最终引导加载操作系统内核或用户指定的应用程序。BootLoader 起到了桥梁的作用,连接了硬件启动与高级软件运行之间的环节,确保系统能够从一个初始、裸机的状态过渡到一个完整的、可操作的运行环境。

        当然,除了嵌入式开发者们,对于喜欢DIY装机的极客们,对于BootLoader应该也是不陌生的,并且经常接触到它,如电脑在安装或开启Windows时的BIOS界面,本质上也是一个BootLoader引导程序。如下图为常见的BootLoader显示的GUI界面图。

如下例举的是一些BootLoader 的主要功能和作用:

①、硬件初始化:

  • 开启和配置基本的硬件模块,如CPU、时钟、内存控制器、中断控制器、串口、GPIO等,使它们进入工作状态。
  • 设置堆栈、中断向量表等关键数据结构,为后续软件执行做好准备。

②、内存管理:

  • 建立内存空间映射图,识别可用的RAM区域及其大小,为操作系统内核分配合适的运行空间。
  • 对于使用MMU(Memory Management Unit)的系统,可能还需要设置内存分页和映射规则。

③、引导加载操作系统:

  • 从非易失性存储器(如Flash、EEPROM、NAND/NOR Flash等)中读取并验证操作系统的内核映像。
  • 将内核映像加载到RAM中指定的位置,并按照内核所需的特定格式设置启动参数和环境变量。

④、固件升级:

  • 提供一种安全机制,允许在运行时通过网络、串口、USB等接口接收新的固件映像,并将其写入非易失性存储器,实现设备的远程或本地固件更新。

⑤、系统诊断与恢复:

  • 可能包含简单的故障检测和恢复机制,如硬件自检、低级别固件修复、安全模式启动等功能,帮助在系统启动失败时进行故障排查和恢复。

⑥、多重引导支持:

  • 在某些系统中,BootLoader 可能支持选择加载不同的操作系统版本或应用程序,提供多启动选项,增强系统的灵活性和可定制性。

        目前BootLoader 的应用广泛存在于各种嵌入式系统中,如消费电子领域中,随处可见的智能手机,智能电脑,智能手表、路由器等。在汽车电子领域的车载系统ADAS(Advanced Driver Assistance Systems)模块、ECU(Electronic Control Units)等,及医疗设备领域的便携式医疗仪器、监护设备、植入式医疗器械等。基本上有电子产品的地方,都能看到BootLoader的身影,因此掌握BootLoader是从事嵌入式开发的一项非常基本的技能。

二、BootLoader原理及配置

        本文主要是针对单片机MCU设备进行BootLoader的配置讲解,目标设备为STM32G431,开发平台是MDK KEIL V5以上

        如下图所示是FLASH中的数据分布图,可见FLASH的用户代码区域的起始程序为BootLoader引导程序,然后紧接着的是应用程序APP1和应用程序APP2。

        其中,BootLoader和APP1及APP2都是完整的用户代码程序,但因BootLoader只起一个引导跳转APP启动操作,所以占用的FLASH内存空间较小。

        提醒:App的个数可以根据实际需求进行设置,只要不超过FLASH的内存空间大小限制即可,为了效果展示,在本文中设置了一个BootLoader程序,两个APP应用程序进行切换。

        如下图所示的是,Bootloader、App1、App2在FLASH中的内存地址映射图。在BootLoader的程序配置好后,根据触发条件的不同,会自动跳转到不同的APP应用程序。

        BootLoader和APP应用程序的启动跳转切换,原理上就是内存地址的切换,当BootLoader程序接收到对应的操作触发条件时,会进行相应的地址跳转切换,及一些其它的附加操作,然后执行该地址空间上的用户程序。但一般来说,BootLoader中会进行CPU工作模式、配置内存控制器、初始化外设等工作,为后续程序运行创建一个稳定的硬件环境。所以在APP中可以节省掉BootLoader中已经进行过的硬件环境配置。

        如下图所示为《STM32G4系列微控制器参考手册》官方文件中截图下来的,STM32的FLASH中是按块进行读写操作的,所以内存空间配置时,必须以块为最小单元分配

如何配置BootLoader及APP应用程序的下载烧录?

①、单击魔术棒

②、选择Target

③、修改IROM1中的Start地址数据及Size数据

        修改Size空间的大小时,需要先确定程序编译后的内存大小是多少,如果内存空间配置不够,会导致编译及下载报错。如下图所示为查看程序编译后需要的内存空间大小的方式。     

三、BootLoader程序

bootloader.h

#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H#include "main.h"#define FLASH_BASE_ADDR    (uint32_t)(0x08000000)//BootLoader   预留10KB的FLASH空间 --- (0x0800 0000 --> 0x0800 27FF)
//Application1 预留20KB的FLASH空间 --- (0x0800 2800 --> 0x0800 77FF)
//Application2 预留20KB的FLASH空间 --- (0x0800 7800 --> 0x0800 C7FF)#define BOOT_BASE_ADDR    FLASH_BASE_ADDR
#define APP1_BASE_ADDR    (uint32_t)(0x08002800)
#define APP2_BASE_ADDR    (uint32_t)(0x08007800)#define KEY_DOWN    GPIO_PIN_RESET
#define KEY_UP        GPIO_PIN_SET#define LED1    LED1_LCD8_Pin
#define LED2    LED2_LCD9_Pin
#define LED3    LED3_LCD10_Pin
#define LED4    LED4_LCD11_Pin
#define LED5    LED5_LCD12_Pin
#define LED6    LED6_LCD13_Pin
#define LED7    LED7_LCD14_Pin
#define LED8    LED8_LCD15_Pin#define LED_ON    GPIO_PIN_RESET
#define LED_OFF    GPIO_PIN_SETextern volatile uint8_t key1_flag;
extern volatile uint8_t key2_flag;void BootLoader_Code(void);void Key_San(void);
void LED_Control(int led, int state);
void LED_Close_All(void);#endif

bootloader.c

#include "bootloader.h"typedef  void (*pFunction)(void);pFunction Boot_Jump_to_App;uint32_t jump_addr;#if 0
//初始化用户栈指针汇编程序
__ASM void __set_MSP(uint32_t mainStackPointer)
{msr msp, r0bx lr
}
#endif/*** @brief  BootLoader程序* @param  None* @retval None*/
void BootLoader_Code(void)
{LED_Close_All();printf("------------Hello BootLoader V 1.0---------\r\n");printf("------------Editor:牛马大师兄--------------\r\n\r\n");printf("------------Press KEY1 Boot APP1-----------\r\n");printf("------------Press KEY2 Boot APP2-----------\r\n");//扫描按键状态,根据按键跳转到相应的APP程序while(1){Key_San();if(key1_flag==1||key2_flag==1){break;}}if(key1_flag == 1){//检查用户代码1的栈顶地址,是否位于0x20000000~0x2001ffff内。if (((*(volatile uint32_t*)APP1_BASE_ADDR) & 0x2FFE0000 ) == 0x20000000){printf("\r\n-------------- APP1 Starting --------------\r\n");//屏蔽所有中断,防止跳转过程中,中断干扰__disable_irq();//用户代码的第二个字,为程序开始地址(复位地址)jump_addr = *(volatile uint32_t*)(APP1_BASE_ADDR+4);Boot_Jump_to_App = (pFunction)jump_addr;//初始化用户栈指针__set_MSP(*(volatile uint32_t*) APP1_BASE_ADDR);//用户程序跳转Boot_Jump_to_App();}}else if(key2_flag == 1){//检查用户代码2的栈顶地址,是否位于0x20000000~0x2001ffff内。if (((*(volatile uint32_t*)APP2_BASE_ADDR) & 0x2FFE0000 ) == 0x20000000){printf("\r\n-------------- APP2 Starting --------------\r\n");//屏蔽所有中断,防止跳转过程中,中断干扰__disable_irq();//用户代码的第二个字,为程序开始地址(复位地址)jump_addr = *(volatile uint32_t*)(APP2_BASE_ADDR+4);Boot_Jump_to_App = (pFunction)jump_addr;//初始化用户栈指针__set_MSP(*(volatile uint32_t*) APP2_BASE_ADDR);//用户程序跳转Boot_Jump_to_App();}}
}volatile uint8_t key1_flag = 0;
volatile uint8_t key2_flag = 0;/*** @brief  按键扫描程序* @param  None* @retval None*/
void Key_San(void)
{if(HAL_GPIO_ReadPin(GPIOB, KEY1_Pin) == KEY_DOWN){HAL_Delay(10);if(HAL_GPIO_ReadPin(GPIOC, KEY1_Pin) == KEY_DOWN){key1_flag = 1;printf("\r\n--------------- KEY 1 PRESS ---------------\r\n");LED_Control(LED1, LED_ON);}}if(HAL_GPIO_ReadPin(GPIOB, KEY2_Pin) == KEY_DOWN){HAL_Delay(10);if(HAL_GPIO_ReadPin(GPIOC, KEY1_Pin) == KEY_DOWN){key2_flag = 1;printf("\r\n--------------- KEY 2 PRESS ---------------\r\n");LED_Control(LED2, LED_ON);}}
}
/*** @brief  LED控制程序* @param  led:操作的LED灯* @param  state:LED的状态* @retval None*/
void LED_Control(int led, int state)
{HAL_GPIO_WritePin(GPIOC, led, state);    HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_RESET);
}
/*** @brief  关闭全部的LED指示灯* @param  None* @retval None*/
void LED_Close_All(void)
{HAL_GPIO_WritePin(GPIOC, LED6_LCD13_Pin|LED7_LCD14_Pin|LED8_LCD15_Pin|LED1_LCD8_Pin|LED2_LCD9_Pin|LED3_LCD10_Pin|LED4_LCD11_Pin|LED5_LCD12_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_RESET);
}

        主函数中对BootLoader程序进行调用

        修改BootLoader程序烧录地址及内存空间大小

四、Application1 用户程序

application1.h

#ifndef __APPLICATION_H
#define __APPLICATION_H#include "main.h"#define APP1_VECT_ADDR_OFFSET    0x2800void App1_Code(void);#endif

application1.c

#include "application1.h"/*** @brief  App1应用程序* @param  None* @retval None*/
void App1_Code(void)
{//设置中断向量偏移表SCB->VTOR = FLASH_BASE | APP1_VECT_ADDR_OFFSET;//使能全局中断--不使能会出现异常__enable_irq();//APP1中的业务代码程序printf("--------- Welcome to Application 1 --------\r\n");LCD_Init();LCD_SetBackColor(Black);LCD_SetTextColor(White);LCD_Clear(Black);HAL_Delay(200);LCD_DisplayStringLine(Line4, (unsigned char *)"    Application 1    ");    }

         主函数中对App1程序进行调用

        修改App1程序烧录地址及内存空间大小

五、Application2 用户程序

application2.h

#ifndef __APPLICATION_H
#define __APPLICATION_H#include "main.h"#define APP2_VECT_ADDR_OFFSET    0x7800void App2_Code(void);#endif

application2.c

#include "application2.h"/*** @brief  App2应用程序* @param  None* @retval None*/
void App2_Code(void)
{//设置中断向量偏移表SCB->VTOR = FLASH_BASE | APP2_VECT_ADDR_OFFSET;//使能全局中断--不使能会出现异常__enable_irq();//APP2中的业务代码程序printf("--------- Welcome to Application 2 --------\r\n");LCD_Init();LCD_SetBackColor(Black);LCD_SetTextColor(White);LCD_Clear(Black);HAL_Delay(200);LCD_DisplayStringLine(Line4, (unsigned char *)"    Application 2    ");    
}

         主函数中对App2程序进行调用

        修改App2程序烧录地址及内存空间大小

六、程序运行效果

开发板实物演示图,按下KEY1按键,启动APP1;按下KEY2按键,启动APP2。

        

上位机串口输出数据演示图

七、工程文件Demo

        本文关于BootLoader讲解演示的3个工程文件可查阅下面的链接访问,文件已上传至CSDN平台的文件资源仓库。

【免费】嵌入式MCUBootLoader开发配置工程Demo资源-CSDN文库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/816742.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity 中消息提醒框

Tooltip 用于ui布局 using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; using UnityEngine.UI;[ExecuteInEditMode()] // 可以在编辑模式下运行public class Tooltip : MonoBehaviour {public TMP_Text header; // 头部文本publi…

MCU最小系统的电源模块设计和复位模块的设计

最小操作系统就是一个电路,这个电路里面必须要的东西(如人需要喝水吃饭温度等情况,才能或者) 现在我们要解决这三个问题 这里V开头的,都是电源管脚 这里解释一下: 这里要注意哪些是电路电压,哪…

简历上写熟悉Linux下常用命令?直接寄

大家写简历技术栈时,都觉得越多越好,其中一条,熟悉Linux下常用命令?其实开发中Linux不是必备考点,除了运维,真正用的多的仅仅cd ls mkdir等,但当面试官问到上面命令时,是不是就傻眼了…

【网安小白成长之路】6.pikachu、sql-labs、upload-labs靶场搭建

🐮博主syst1m 带你 acquire knowledge! ✨博客首页——syst1m的博客💘 🔞 《网安小白成长之路(我要变成大佬😎!!)》真实小白学习历程,手把手带你一起从入门到入狱🚭 &…

oracle 19c 主备 补丁升级19.22

补丁升级流程 备库升级 备库备份$ORALCE_HOME du -sh $ORACLE_HOME ​​​​​​​ 备份目录将dbhome_1压缩 cd $ORACLE_HOME cd .. Ls tar -cvzf db_home.tar.gz db_home_1 /opt/oracle/product/19c ​​​​​​​​​​​​​​ 关闭监听关闭数据库查看sq…

计算机网络 实验指导 实验17

实验17 配置无线网络实验 1.实验拓扑图 Table PC0 和 Table PC1 最开始可能还会连Access Point0,无影响后面会改 名称接口IP地址网关地址Router0fa0/0210.10.10.1fa0/1220.10.10.2Tablet PC0210.10.10.11Tablet PC1210.10.10.12Wireless互联网220.10.10.2LAN192.16…

[Kubernetes[K8S]集群:Slaver从节点初始化和Join]:添加到主节点集群内

文章目录 操作流程:上篇主节初始化地址:前置:Docker和K8S安装版本匹配查看0.1:安装指定docker版本 **[1 — 8] ** [ 这些步骤主从节点前置操作一样的 ]一:主节点操作 查看主机域名->编辑域名->域名配置二&#x…

数据库系统工程师笔记(一)计算机系统

文章目录 一、计算机系统1.1运算器1.2控制器1.3指令1.4存储器与总线1.5输入输出技术1.6计算机软件程序数据相关文档。1.7操作数1.8计算机硬件的典型结构1.9CPU由运算器和控制器组成1.10指令执行的过程1.11CPU的基本功能1.12计算机体系结构和计算机组成的区别1.13计算机体系结构…

【uniapp】vscode安装插件、ts校验、允许json文件注释

1、vscode安装的插件: uni-create-viewuni-hlperuniapp小程序扩展 2、ts校验 安装插件: pnpm i -D types/wechat-miniprogram uni-helper/uni-app-types配置tsconfig.json {"extends": "vue/tsconfig/tsconfig.json","compi…

大家都在用的一款高颜值免费在线SCI绘图工具~~~

ImageGP从2017年推出后,稳定运行7年,因其使用简单方便,深受广大朋友们喜欢。 独立IP访问60万次,日均使用1000次。 如果您对编程不熟悉,使用ImageGP可以快速出图。 只需轻轻点2两下,下面的美图就出来了 如果…

系统架构最佳实践 -- 金融企业的资损防控

一、资损产生的原因 由于支付行业的特殊性与复杂性(主要处理资金相关业务),支付公司处于资损的风口浪尖,最容易发生资损,可以说资损风险无处不在。 常规来说,资损原因主要可以分为以下三类: 1…

软考128-上午题-【软件工程】-白盒测试

一、白盒测试(结构测试) 白盒测试也称为结构测试,根据程序的内部结构和逻辑来设计测试用例,对程序的路径和过程进行测试,检查是否满足设计的需要。 白盒测试常用的技术是:逻辑覆盖、循环覆盖和基本路径测…

redis-plus-plus的安装与使用

本文参考自 redis-plus-plus 官方文档 一、安装 因为redis-plus-plus是基于hiredis封装的,所以需要先安装hiredis; 第一步:安装hiredis # 使用git下载源代码 git clone https://github.com/redis/hiredis.git # 进入源代码主目录 cd hired…

Servlet测试1

通过按钮提交get,post请求,并且后端响应数据,显示到前端 当点击get按钮时 是发起Get请求 后端接收到Get请求后,把数据写入到body内 当点击pst按钮时 是发起Post请求 后端接收到Post请求后,把数据写入到body内 之后前端就从bod…

基于机器学习的人脸发型推荐算法研究与应用实现

1.摘要 本文主要研究内容是开发一种发型推荐系统,旨在识别用户的面部形状,并根据此形状推荐最适合的发型。首先,收集具有各种面部形状的用户照片,并标记它们的脸型,如长形、圆形、椭圆形、心形或方形。接着构建一个面部…

HarmonyOS实战开发-状态管理、通过使用页面级的状态变量 和应用级的状态变量 来实现应用的状态管理。

介绍 本示例通过使用页面级的状态变量 和应用级的状态变量 来实现应用的状态管理。 效果预览 使用说明 1.点击首页中的基本类型进入对应页面,点击按钮可以更改圆形的颜色;点击查看源码可以展示基本类型功能效果的源码。 2.点击首页中的数组类型进入对…

LeetCode-31-下一个排列问题

题目说明 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 必须原地修改,只允许使用额外常数空间。…

Python数据分析案例41——基于CNN-BiLSTM的沪深300收盘价预测

案例背景 虽然我自己基于各种循环神经网络做时间序列的预测已经做烂了.....但是还是会有很多刚读研究生或者是别的领域过来的小白来问这些神经网络怎么写,怎么搭建,给我一篇论文看看感觉很厉害的样子。我一看:普刊、单变量时间序列预测、一个…

CentOS7离线升级OpenSSH_8.8p1

一、环境 centos7.9,升级openssh到8.8p1最新版本 二、下载升级包 # openssl和zlib为相关依赖 wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-8.8p1.tar.gz wget https://www.openssl.org/source/openssl-1.1.1j.tar.gz wget http://www.zlib.net/zl…

YOLOV5 + 双目相机实现三维测距(新版本)

文章目录 YOLOV5 双目相机实现三维测距(新版本)1. 项目流程2. 测距原理3. 操作步骤和代码解析4. 实时检测5. 训练6. 源码下载 YOLOV5 双目相机实现三维测距(新版本) 本文主要是对此篇文章做一些改进,以及解释读者在…