Bootloader/IAP零基础入门(1.0) —— 设计一个Bootloader引导进入APP的程序,不含中断向量偏移

前言

(1)如果有嵌入式企业需要招聘湖南区域日常实习生,任何区域的暑假Linux驱动/单片机/RTOS的实习岗位,可C站直接私聊,或者邮件:zhangyixu02@gmail.com,此消息至2025年1月1日前均有效
(2)上一章节我们简单的科普了学习Bootloader/IAP所需的的前置知识,那么本章节主要是一个简单的Bootloader引导程序,将Bootloader引导进入APP程序中。
(3)上一章节博客地址:Bootloader/IAP零基础入门(0) —— Bootloader/IAP的前置知识
(4)系列教程仓库链接:GitHub仓库
(5)注意:本章节只是做一个Bootloader引导进入APP的程序教程。因此Bootloader会做的很简陋!不可以用于实际项目!

前期准备

制作APP程序

(1)因为我开发板上有两个LED,因此GPIO配置如下。设置SYSRCC的部分我不进行赘述。

下·

(2)生成工程之后,MDK中写入程序如下:

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
	HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
	HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
	HAL_Delay(1000);
  }
  /* USER CODE END 3 */

(3)程序烧录进去之后,我们能够看到开发板上两个LED每隔1s进行闪烁。

制作Bootloader程序

(1)首先我们打开串口1的初始化。设置SYSRCC的部分我不进行赘述。

在这里插入图片描述

(2)工程创建出来之后,打开微库。

在这里插入图片描述

(3)加入头文件,按Ctrl+F搜索Private includes,补充如下代码。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

(4)串口重映射,按Ctrl+F搜索Private user code,补充如下代码。

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f)
{
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}
/* USER CODE END 0 */

(5)添加应用代码,按Ctrl+F搜索USER CODE BEGIN 2,补充如下代码。

  /* USER CODE BEGIN 2 */
	printf("-------------------------------------\r\n");
	printf("---www.zyxbeyourself.blog.csdn.net---\r\n");
	printf("-------------------------------------\r\n");
	printf("--------------Bootloader-------------\r\n");
	printf("-------------------------------------\r\n");
	printf("--------zhangyixu02@gmail.com--------\r\n");
	printf("-------------------------------------\r\n");
  /* USER CODE END 2 */

(6)上机测试,复位之后能够看到串口打印如下数据

在这里插入图片描述

工程合成

Keil MDK的配置

(1)我们打算给Bootloader准备10KB的内存大小。因此我们将IROM1Size设置为0x2000

在这里插入图片描述

(2)因为Bootloader10KB,所以APP程序的IROM1Size设置为0x7E000Start设置为0x8002000

在这里插入图片描述

Bootloader需要添加的引导程序

(1)在Bootloader中加入引导程序。在前面的

uint32_t AppStartFlash = 0x8002000UL;       //app的flash起始地址
__set_MSP(*(volatile uint32_t *)AppStartFlash);  //设置APP栈顶地址(用户代码区的第一个字用于存放栈顶地址)
typedef void (*AppFunction)(void);       //定义一个函数指针类型
AppFunction AppEnter = (AppFunction)*(volatile uint32_t *)(AppStartFlash + 4);   //app的flash起始地址+4才是复位中断程序
AppEnter();

在这里插入图片描述

Bootloader代码讲解

设置APP栈顶指针的作用

栈的作用

(1)首先我们需要知道栈顶指针的作用,这样才能更好的知道明白为啥需要设置栈顶指针。
(2)我们先看下面的这段代码。我们会发现,Test_A()函数会调用Test_B()函数,而Test_B()函数里面存在一个10字节的数组,这个数组是局部变量。
<1>现在我们根据汇编代码进行分析。首先,Test_A()函数会调用BL指令跳转到Test_B()函数中。
<2>进入Test_B()函数,首先是使用PUSH命令将r1r2r3lr这四个寄存器的值压入栈中。这个过程就是保护上一次函数调用的环境。
<3>之后是MOVS指令,将r0寄存器的值清零。
<4>然后是3个STR指令,将r0的值存放进入栈地址偏移0,4,8的地址中。
<5>然后使用POP命令,栈中的r1r2r3lr这四个寄存器进行恢复。(因为第四步,所以会导致r1r2r3这三个寄存器被清零。因为STM32F103栈是向下生长的,当需要为函数调用或其他堆栈操作分配空间时,堆栈指针(SP)的值会减小)
(3)从这个例子中我们可以知道,当发生函数调用的时候,栈有两个作用:

  • 存储局部变量的值。
  • 函数调用的返回地址和一些必要的相关信息存储。
/*--- C代码 ---*/
void Test_B()
{
	volatile uint8_t arr[10] = {0};
	//...省略
}
void Test_A()
{
	Test_B();
}
/*--- 汇编代码 ---*/
i.Test_A
Test_A
	BL   Test_B
i.Test_B
Test_B
	PUSH     {r1-r3,lr}
	MOVS     r0,#0
	STR      r0,[sp,#0]
	STR      r0,[sp,#4]
	STR      r0,[sp,#8]
	POP      {r1-r3,pc}

(4)除了上面我说的两个作用。在程序发生中断的时候,我们需要保护当前程序的环境。对于M3M4内核的芯片,会自动将寄存器的值压入栈中,保护当前的环境。
(5)在OS中,发生任务调度,也需要栈来保存任务的执行状态。(其实任务调度本质上就是利用的中断,不过加了一些复杂的处理机制)
(6)因此,我们可以得出结论栈有四大作用:

  • 存储局部变量的值。
  • 函数调用的返回地址和一些必要的相关信息存储。
  • 用于中断的现场保护。
  • OS中保存任务的执行状态。

Bootloader的栈顶和APP的栈顶区别是什么

(1)上面我们知道了栈的作用之后,我们就需要考虑一个问题。在上一章节博客中,我们不是说M3M4内核启动的时候,会自动从0地址(STM32F1030x08000000,这个涉及内存映射的知识,自行了解)取出4字节数据,作为栈顶指针吗?既然Bootloader已经设置好了栈顶指针,而且BootloaderAPPRAM都是从0x20000000开启,为什么我们这里还需要重新设置一次栈顶指针呢?
(2)在前文我们说了,芯片启动,完成芯片的系统初始化之后,其实是跳转到__main函数,而不是main函数。在__main函数中会进行数据段的搬运和BSS段的清零。BootloaderAPPRAM虽然都是从0x20000000开启,但是他们的数据段和BSS段大小都不一样,值也不同。因此需要重新设置一次栈顶指针的值。
(3)扩展知识:这个栈顶指针我们是怎么知道的呢?
<1>栈顶指针的值其实是由编译器给出的,编译器在编译过程中,会产生一个__initial_sp变量,这个变量存放着栈顶指针的值。我们在.s文件中能够看到下面这行代码,也就是将__initial_sp这个变量的值存入首地址。

__Vectors       DCD     __initial_sp               ; Top of Stack

<2>知道__initial_sp值的方法有两种,一种是看反汇编的Flash起始地址的4字节数据。另外一种是看map文件,如下:

在这里插入图片描述

__set_MSP()函数的作用

(1)有了上面的基础,我们就知道了设置栈的作用。现在还需要科普一个知识。对于M3M4内核而言,为了对RTOS做支持,因此存在两个堆栈指针。对于裸机程序,我们只需要知道我们只用到了MSP这个堆栈指针。
(2)然后上一章节在讲解STM32F103的启动流程的时候,我也说了,程序的首地址存放的是栈顶指针地址。AppStartFlash作为APPFlash首地址,因此该地址存放着APP的栈顶地址值。于是我们通过一下方法取出APP的栈顶地址值,然后将这个值赋予给MSP寄存器。

uint32_t AppStartFlash = 0x8002000UL;     //app的flash起始地址
__set_MSP(*(volatile uint32_t *)AppStartFlash);  //设置APP栈顶地址(用户代码区的第一个字用于存放栈顶地址)

从Bootloader引导进入APP程序分析

(1)这一部分需要一定的函数指针的知识。首先,我们先定义一个无返回值,无需传入参数的函数指针AppFunction()
(2)然后上一章节,讲解STM32F103的启动流程的时候说了,首地址的4字节存放栈顶指针地址,紧接着的4字节就是复位函数的地址。如果我们需要跳转进入APP程序,就需要跳转进入APP的复位中断函数地址处。于是我们这里取出AppStartFlash偏移4字节地址的值,然后跳转过去即可。

typedef void (*AppFunction)(void);       //定义一个函数指针类型
AppFunction AppEnter = (AppFunction)*(volatile uint32_t *)(AppStartFlash + 4);   //app的flash起始地址+4才是复位中断程序
AppEnter();

参考

(1)B站:STM32的IAP技术,基于CAN总线的STM32F103 BootLoader设计
(2)CM3权威指南

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

重磅消息!《大模型面试宝典》(2024版) 正式发布!

2022 年11月底&#xff0c;OpenAI 正式推出 ChatGPT &#xff0c;不到两个月的时间&#xff0c;月活用户就突破1亿&#xff0c;成为史上增长最快的消费者应用。 目前国内已发布的大模型超过200个&#xff0c;大模型的出现彻底改变了我们的生活和学习方式。 现在只要你想从事 A…

优化选址问题 | 模拟退火算法求解物流选址问题含Matlab源码

目录 问题代码问题 模拟退火算法(Simulated Annealing, SA)是一种概率性的全局优化算法,用于求解大规模组合优化问题。在物流选址问题中,模拟退火算法可以用来寻找成本最低、效率最高的仓库或配送中心位置。下面是一个简化的模拟退火算法求解物流选址问题的描述,并附带有…

阿里云OSS分布式存储

目录 &#x1f9c2;1.OSS开通 &#x1f32d;2.头像上传整合OSS &#x1f68d;2.1.引入依赖 &#x1f68d;2.2添加配置 &#x1f68d;2.3创建配置类 &#x1f68d;2.4添加实现类 &#x1f68d;2.5controller调用接口 &#x1f68d;2.6postman测试 1.OSS开通 1.登…

力扣---最长有效括号---动态规划,栈

动态规划思路&#xff1a; 最长xxxx的问题&#xff0c;从动态规划的角度去考虑&#xff0c;我们会将 g[i] 定义为以 第 i 位 结尾的小问题。在本道题中&#xff0c;我们将 g[i] 定义为以第 i 位为结尾的最长有效括号子串的长度。从头去遍历每一位&#xff0c;我们会发现只有s[i…

我的创作纪念日——在CSDN的三年

我的创作纪念日——在CSDN的三年 个人简介机缘收获个人成就学习成就墙文章专栏 日常成就憧憬 个人简介 &#x1f3d8;️&#x1f3d8;️个人主页&#xff1a;以山河作礼。 &#x1f396;️&#x1f396;️:Python领域新星创作者&#xff0c;CSDN实力新星认证&#xff0c;CSDN内…

134. 加油站(力扣LeetCode)

文章目录 134. 加油站题目描述暴力枚举&#xff08;超时&#xff09;代码一代码二&#xff08;优化&#xff09; 贪心算法方法一方法二 134. 加油站 题目描述 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&…

【机器学习入门 】支持向量机

系列文章目录 第1章 专家系统 第2章 决策树 第3章 神经元和感知机 识别手写数字——感知机 第4章 线性回归 第5章 逻辑斯蒂回归和分类 前言 支持向量机(Support Vector Machine) 于1995年发表&#xff0c;由于其优越的性能和广泛的适用性&#xff0c;成为机器学习的主流技术&…

【11】工程化

一、为什么需要模块化 当前端工程到达一定规模后,就会出现下面的问题: 全局变量污染 依赖混乱 上面的问题,共同导致了代码文件难以细分 模块化就是为了解决上面两个问题出现的 模块化出现后,我们就可以把臃肿的代码细分到各个小文件中,便于后期维护管理 前端模块化标准…

活用 C语言之union的精妙之用

一、union的基本定义 Union的中文叫法又被称为共用体、联合或者联合体。它的定义方式与结构体相同,但意义却与结构体完全不同。下面是union的定义格式: union 共用体名 {成员列表}共用体变量名;它与结构体的定义方式相同,但区别在于共用体中的成员的起始地址都是相同的,…

Android Studio Gradle设置查看全部task

如果你在 Android Studio 的 Gradle 窗口中看不到所有的任务&#xff0c;你可以尝试以下步骤来解决这个问题 android studio 版本&#xff1a; Android Studio Iguana | 2023.2.1 Build #AI-232.10227.8.2321.11479570, built on February 22, 2024 打开 Android Studio 的设置…

【CSP】2020-12-3 带配额的文件系统 100分完整代码 最长的大模拟 使用指针优化数据结构

2020-12-3 带配额的文件系统 最长的大模拟 使用指针优化数据结构 索引2020-12-3 带配额的文件系统 最长的大模拟 使用指针优化数据结构思路遇到的问题(学到的东西)40分stl代码acwing 15/15 csp官网40分代码100分完整代码 索引 历年CSP认证考试真题题解总汇持续更新 2020-12-3…

框架结构模态分析/动力时程分析Matlab有限元编程 【Matlab源码+PPT讲义】|梁单元|地震时程动画|结果后处理|地震弹性时程分析| 隐式动力学

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

Bytebase 2.14.1 - 分支 (Branching) 功能支持 Oracle

&#x1f680; 新功能 分支 (Branching) 功能支持 Oracle。为 SQL 编辑器添加了项目选择器。 新增 SQL 审核规范&#xff1a; 禁止混合 DDL、DML 语句。禁止对同一张表进行不同类型的 DML 变更 (UPDATE,INSERT,DELETE)。 &#x1f514; 重大变更 工作空间设置中的「数据访问…

【Linux操作系统】命令的运行原理

文章目录 shell命令以及运行原理Linux系列学习目录 shell命令以及运行原理 Linux严格意义上说的是一个操作系统&#xff0c;我们称之为“核心&#xff08;kernel&#xff09;“ &#xff0c;但我们一般用户&#xff0c;不能直接使用kernel。而是通过kernel的“外壳”程序&…

【网站项目】294火车票订票系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Data Interpreter: An LLM Agent For Data Science 论文解读

论文地址&#xff1a;https://arxiv.org/abs/2402.18679 Github&#xff1a;MetaGPT: The Multi-Agent Framework 数据解释器&#xff08;Data Interpreter&#xff09;是一个基于大型语言模型&#xff08;LLM&#xff09;的代理&#xff0c;专门为解决数据科学问题而设计。它…

主干网络篇 | YOLOv8更换主干网络之MobileNetV3

前言:Hello大家好,我是小哥谈。MobileNetV3是一种轻量级的卷积神经网络架构,用于图像分类和目标检测任务。它是MobileNet系列的第三个版本,旨在在保持高准确性的同时减少模型的计算量和参数数量。MobileNetV3引入了一些新的设计思想和技术,以提高模型的性能。其中一项重要…

抖音用户主页如何打开词令抖音小程序?

抖音用户主页如何打开词令抖音小程序&#xff1f; 1、打开抖音主页&#xff0c;点击右上角的「搜索」&#xff1b; 2、使用抖音搜索找到小程序名称&#xff1a;词令的用户&#xff0c;并点击头像名称进入&#xff1b; 3、进入词令抖音用户主页&#xff0c;并到抖音小程序的图标…

Tensorflow 2.0 常见函数用法(一)

文章目录 0. 基础用法1. tf.cast2. tf.keras.layers.Dense3. tf.variable_scope4. tf.squeeze5. tf.math.multiply 0. 基础用法 Tensorflow 的用法不定期更新遇到的一些用法&#xff0c;之前已经包含了基础用法参考这里 &#xff0c;具体包含如下图的方法&#xff1a; 本文介…

Springboot解决跨域问题方案总结(包括Nginx,Gateway网关等)

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 前言 解决跨域问题方案 1.Spring Boot 中解决跨域 1.1 通过注解跨域 1.2 通…