文章目录

  • 一、单片机烧写程序的几种方法
  • 二、Bootloader如何加载启动App


一、单片机烧写程序的几种方法

![[Pasted image 20250704203159.png]]

在线应用编程,由开发者实现Bootloader功能,比如ARM单片机的Code分区中的Flash本是存储用户应用程序的区间(上电从此处执行用户代码),开发者可以将自己实现的Bootloader和应用程序都存放到Flash区间MCU上电启动先执行用户的Bootloader代码,该代码可为用户应用程序的下载、校验、升级、启动等提供支持,进而实现OTA远程升级功能。

一般用于给用户远程升级,或者是烧写程序不方便的时候。

![[Pasted image 20250709184234.png]]

复位以后进入Bootloader程序,

![[Pasted image 20250709195510.png]]

需要说明的是这个地方就是我们程序烧写的其实地址,

这是因为我们要把前面的空间流出来给BOOT程序。

ARM单片机启动流程(一)(详细解析)-CSDN博客 在本人的这篇文章里面可以看到我们的Main Flash的物理其实地址就是0x08000000,因此我需要把BOOT程序烧写到这里,因为CPU执行程序最先
CPU都是从0地址开始访问的,根据被引导到的地方,有可能直接跳转到Main Flash 0x0800 0000的原始存储空间;也有可能跳转到MCU厂商预置的bootloader开始于0x1FFF F000的原始存储空间。至于怎么跳转这两个以及默认是什么,连接文章在这一块进行了详细分析。

一般我们默认就是下面顺序:我们就是直接跳转到Main Flash 0x0800 0000的原始存储空间;
![[Pasted image 20250709200438.png]]

(CPU通过程序计数器(PC)获取下一条指令的地址。在无分支的情况下,PC自动增加(如 PC_new = PC_old + 指令长度),实现物理地址的顺序执行)

int main(void)
{	InitIrqAfterBoot();DrvInit();AppInit();while (1){TaskHandler();}
}void InitIrqAfterBoot(void)
{nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x3000);__enable_irq();
}#define NVIC_VECTTAB_FLASH          ((uint32_t)0x08000000) /*!< Flash base address */

值得注意的是在APP程序里面,需要配置中断向量表里面的初始地址,这是因为原本我们默认的程序初始地址是0x08000000,但是由于BOOT程序的作用导致我们的初始地址变成了((uint32_t)0x08000000)+偏移地址0x3000,中断向量表的初始位置。

如果我们常规的程序,是CPU直接加载的,就不需要我们初始化,就是内核会默认帮我们搞好,从0x08000000开始。

但是我们现在的程序是从BOOT里面启动的,那就意味着我们需要自己配置一个中断向量表初始地址,所以我们需要在应用程序里面做一些CPU需要做的事情,并且在BOOT程序里面一样需要做一些CPU需要做的事情,说白了就是手动干一些CPU的事情。

同样需要烂熟于心的还有0x3000表示12KB,0x1000表示4KB,也就是4096个字节(Byte)。

另外需要一点注意的:

        .property = R | W,.address = 0x0004,          // 触发系统复位 01 06 00 04 00 01.minValue = 0,.maxValue = 1,.WriteCb = ModbusResetSystem,static void ModbusResetSystem(uint16_t value)
{ResetToBoot();
}void ResetToBoot(void)
{__disable_irq();    //关闭所有中断NVIC_SystemReset(); //复位函数,需要一些执行的时间
}

在APP程序里面,触发复位的时候,我们一定要先关闭所有中断,如果不关闭中断可能会导致我们的BOOT程序会出现错误,因为BOOT里面也需要中断。

此外需要联系之前的知识在做一次对比:

代码段1:

__Vectors0x08000000:    20000428    (..     DCD    5368719760x08000004:    08000145    E...    DCD    1342180530x08000008:    0800014d    M...    DCD    1342180610x0800000c:    0800014f    O...    DCD    1342180630x08000010:    08000151    Q...    DCD    1342180650x08000014:    08000153    S...    DCD    1342180670x08000018:    08000155    U...    DCD    1342180690x0800001c:    00000000    ....    DCD    00x08000020:    00000000    ....    DCD    00x08000024:    00000000    ....    DCD    00x08000028:    00000000    ....    DCD    00x0800002c:    08000157    W...    DCD    1342180710x08000030:    08000159    Y...    DCD    1342180730x08000034:    00000000    ....    DCD    00x08000038:    0800015b    [...    DCD    1342180750x0800003c:    0800015d    ]...    DCD    1342180770x08000040:    0800015f    _...    DCD    1342180790x08000044:    0800015f    _...    DCD    1342180790x08000048:    0800015f    _...    DCD    1342180790x0800004c:    0800015f    _...    DCD    1342180790x08000050:    0800015f    _...    DCD    1342180790x08000054:    0800015f    _...    DCD    1342180790x08000058:    0800015f    _...    DCD    134218079

代码段2:

    __Vectors0x08003000:    20000738    8..     DCD    5368727600x08003004:    08003145    E1..    DCD    1342303410x08003008:    08003d21    !=..    DCD    1342333770x0800300c:    08003965    e9..    DCD    1342324210x08003010:    08003c69    i<..    DCD    1342331930x08003014:    0800356b    k5..    DCD    1342314030x08003018:    08004531    1E..    DCD    1342354410x0800301c:    00000000    ....    DCD    00x08003020:    00000000    ....    DCD    00x08003024:    00000000    ....    DCD    00x08003028:    00000000    ....    DCD    00x0800302c:    0800402d    -@..    DCD    1342341570x08003030:    0800365b    [6..    DCD    1342316430x08003040:    0800315f    _1..    DCD    1342303670x08003044:    0800315f    _1..    DCD    1342303670x08003048:    0800315f    _1..    DCD    1342303670x0800304c:    0800315f    _1..    DCD    1342303670x08003050:    0800315f    _1..    DCD    1342303670x08003054:    0800315f    _1..    DCD    134230367

可以明显看出初始的中断向量地址就是0x08003000:开始的,跟之前的0x08000000:有明显的地址偏移量,而这个地址偏移量就是我们自己设计的0x3000

二、Bootloader如何加载启动App

在这里插入图片描述
首先我们看一下ROM空间的分布,

相当于前面12KB划分给了BOOT,后面的500KB空间给了APP。并且本例程使用的GD32这个单片机一共的FLASH空间就是512KB。

首先是读取 0x08000000:获取栈顶地址,

接着是读取 0x08000004:获取复位函数的地址

然后跳转到复位函数地址 0x08000004:执行复位函数的代码指令,这些是CPU自动完成的。

这里需要说明的为什么CPU需要执行复位函数?
建立可预测的初始状态,消除不确定性,确保系统行为可预测。
CPU上电或复位时,寄存器、程序计数器(PC)、状态标志等内部状态是随机的或残留前次运行的错误值。复位函数会强制将其清零或设为预设值(如PC指向复位向量地址0xFFFF0),使CPU从已知起点开始执行。
程序计数器(PC)清零​:复位后PC指向固定的启动地址(如ARM的0x00000000或x86的0xFFFF0),加载第一条指令
寄存器初始化​:通用寄存器、状态寄存器(如EFLAGS)恢复默认值,避免残留数据干扰新程序。

此外当系统遇到致命错误时,复位是恢复运行的终极手段:

  • 软件错误​:如堆栈溢出、死循环、空指针访问等,通过看门狗定时器触发复位。看门狗超时未清零则强制复位,脱离卡死状态。
  • 硬件故障​:内存错误、总线冲突、电源欠压等触发复位以保护硬件。例如,STM32的欠压检测(VBOR)会直接复位CPU。
  • 中断与死锁​:多核系统中核心间死锁可通过内核复位(如ARM的VECTRESET)局部恢复,避免全系统重启。

那如何启动APP?

我们使用BOOT启动APP的时候也是需要干这些动作,只不过这些动作不是CPU帮助我们自动完成了,而是需要我们自己手动完成。

代码详细解析:

static void BootToApp(void)
{uint32_t stackTopAddr = *(volatile uint32_t*)APP_ADDR_IN_FLASH; if (stackTopAddr > RAM_START_ADDRESS && stackTopAddr < (RAM_START_ADDRESS + RAM_SIZE)) //判断栈顶地址是否在合法范围内{__disable_irq();__set_MSP(stackTopAddr);uint32_t resetHandlerAddr = *(volatile uint32_t*) (APP_ADDR_IN_FLASH + 4);/* Jump to user application */pFunction Jump_To_Application = (pFunction) resetHandlerAddr; // int *p = (int *)0x8003145/* Initialize user application's Stack Pointer */Jump_To_Application();}NVIC_SystemReset();
}

文章源码获取方式:
如果您对本文的源码感兴趣,欢迎在评论区留下您的邮箱地址。我会在空闲时间整理相关代码,并通过邮件发送给您。由于个人时间有限,发送可能会有一定延迟,请您耐心等待。同时,建议您在评论时注明具体的需求或问题,以便我更好地为您提供针对性的帮助。

【版权声明】
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议。这意味着您可以自由地共享(复制、分发)和改编(修改、转换)本文内容,但必须遵守以下条件:
署名:您必须注明原作者(即本文博主)的姓名,并提供指向原文的链接。
相同方式共享:如果您基于本文创作了新的内容,必须使用相同的 CC 4.0 BY-SA 协议进行发布。

感谢您的理解与支持!如果您有任何疑问或需要进一步协助,请随时在评论区留言。

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

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

相关文章

C语言基础教程--从入门到精通

C语言基础教程–从入门到精通&#xff08;总体概括&#xff09; 接下来会对每一个章节进行详细的总结与整理&#xff0c;希望对大家有用&#xff01;大家一起学习&#xff01; 目录C语言基础教程--从入门到精通&#xff08;总体概括&#xff09;**接下来会对每一个章节进行详细…

单细胞分析教程 | (二)标准化、特征选择、降为、聚类及可视化

在完成质控&#xff08;QC&#xff09;后&#xff0c;我们已经过滤掉了低质量细胞、双细胞和低表达基因&#xff0c;获得了较为干净的单细胞数据集单细胞分析教程 | &#xff08;一&#xff09;Python单细胞质控全流程。接下来&#xff0c;我们将进行以下关键步骤&#xff1a; …

大模型 Agent(智能体)技术简介

大模型 Agent&#xff08;智能体&#xff09;技术 是当前人工智能领域的前沿方向&#xff0c;它赋予大型语言模型&#xff08;LLM&#xff09;自主感知、规划、决策和行动的能力&#xff0c;使其不再局限于“被动应答”&#xff0c;而是能主动完成复杂任务。简单来说&#xff0…

OneCode 3.0架构深度剖析:工程化模块管理与自治UI系统的设计与实现

引言 OneCode 3.0作为新一代低代码开发平台&#xff0c;其架构设计围绕"工程模块化"与"UI自主化"两大核心目标展开。本文将从底层接口到上层应用&#xff0c;全面解析OneCode 3.0的技术架构&#xff0c;包括核心工厂类、工程管理接口、数据仓库设计以及动态…

功耗校准数据PowerProfile测试方法建议

场景步骤版本:xxxxA1A2结果&#xff08;mA&#xff09;screen,full1.打开飞行模式&#xff0c;灭屏时间最长&#xff0c;其他的基础功能关2.进入到日历应用界面3.将亮度设置至最大&#xff08;4095&#xff09;&#xff0c;待电流稳定后&#xff0c;测试5分钟&#xff0c;记录电…

[附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+vue实现的供电公司安全生产考试管理系统,推荐!

摘 要 使用旧方法对安全生产考试信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在安全生产考试信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。 这次开发的供电公…

输入框过滤选项列表,el-checkbox-group单选

需求&#xff1a;根据输入的文本动态过滤选项列表&#xff0c;并在下方显示匹配到的选项。当用户勾选匹配到的选项时&#xff0c;把该选项的值赋值给输入框中绑定的值。当用户取消选择时&#xff0c;输入框中的字段可以随意编辑。组件&#xff1a;el-input、el-checkbox-group、…

身份认证缺陷

Authentication Bypasses审计创建AccountVerificationHelper实例&#xff0c;用于处理账户验证逻辑parseSecQuestions函数的作用是从请求体中遍历参数名&#xff0c;找到包含secQuestion的参数&#xff0c;将其值存入Map中并返回这里直接把AccountVerificationHelper整个分析一…

火山引擎:字节跳动的技术赋能初解

火山引擎是字节跳动旗下的企业级智能技术服务平台&#xff0c;于2020年6月正式上线。它通过开放字节跳动在大数据、人工智能、视频云等领域的核心技术&#xff0c;助力企业实现数字化转型与业务增长。火山引擎界面核心能力与技术亮点:1.全栈云服务公有云与混合云&#xff1a;提…

VUE 带有搜索功能的穿梭框(简单demo)

一、template/ 组件代码<el-dialog :title"title" :visible.sync"dialogVisible" width"60%" :before-close"handleClose" class"custom-dialog-line" ><div style"text-align: center ; width: 100%; height…

写个扫雷小游戏

1.test.c&#xff08;测试源文件&#xff09;2.game.c&#xff08;游戏源文件&#xff09;3.头文件

【Linux庖丁解牛】— system V共享内存!

1. 什么是system VSystem V IPC&#xff08;Interprocess Communication&#xff0c;进程间通信&#xff09;是Unix系统中一种经典的进程间通信机制&#xff0c;由AT&T在System V.2版本中引入&#xff0c;并广泛应用于Linux等现代操作系统中。它通过三种核心机制实现进程间…

从输入到路径:AI赋能的地图语义解析与可视化探索之旅(2025空间智能全景)

​​摘要​​在空间智能爆发的2025年&#xff0c;地图系统已从静态导航工具进化为​​实时决策中枢​​。本文深度解析AI如何重构地理信息处理全链路&#xff1a;通过​​多模态语义理解​​&#xff08;文本/语音/图像→空间意图&#xff09;、​​动态路网建模​​&#xff0…

安全运维新趋势:AI 驱动的自动化威胁检测

在数字化浪潮中&#xff0c;网络攻击正从 “单点突破” 进化为 “链状打击”&#xff1a;2024 年某金融机构遭遇供应链攻击&#xff0c;恶意代码通过运维通道潜伏 3 个月&#xff0c;传统规则引擎因未识别 “正常运维指令中的异常参数”&#xff0c;导致数据泄露损失过亿。这背…

数据库复合索引设计:为什么等值查询列应该放在范围查询列前面?

前言作为后端开发工程师&#xff0c;我们经常会遇到数据库查询性能问题。在一次系统优化中&#xff0c;我发现一个简单的索引顺序调整竟然让查询速度提升了10倍&#xff01;这让我意识到复合索引列顺序的重要性。今天&#xff0c;我就来分享一下这个经验&#xff0c;希望能帮助…

【PMP备考】每日一练 - 2

1、一个建筑项目的项目经理发现&#xff0c;他管理的项目所在地附近正在新建一条新的水管线。公司政策要求&#xff0c;在他的团队继续完成这个项目之前&#xff0c;必须先填写一系列有关城市环境变化的表格。这是那两种情况的例子&#xff1f;&#xff08;选2个选项&#xff0…

【三】ObservableCollection 与 List 的区别

文章目录前言一、核心概念简介ObservableCollectionList二、关键差异对比三、典型使用场景ObservableCollection 的适用场景List 的适用场景四、在Community Toolkit MVVM中使用ObservableCollection<Data>和List<Data>场景1&#xff1a;动态列表&#xff08;Obser…

网安-SSRF-pikachu

目录 SSRF:Server-Side Request Forgery PHP curl PHP 可能引起SSRF的函数 PHP其他函数 CURL其他协议 SSRF利用&#xff1a; SSRF的发现 工具 SSRF的防御 pikachu-SSRF 一&#xff1a;curl 1.访问连接&#xff1a; 2.读取本地文件 3.dict协议扫描主机端口 二&…

在Centos系统上如何有效删除文件和目录的指令汇总

CentOS系统是一款开源的类Unix操作系统&#xff0c;极其亲和程序员和技术人员。这个系统最大的优势就是其高度自由化的特性&#xff0c;世界各地的开发者可以依照实际需求去修改和运行。在这个操作系统中&#xff0c;如果你想删除文件和目录&#xff0c;你可以使用各式各样的命…

Spring(四) 关于AOP的源码解析与思考

Spring&#xff08;四&#xff09; 关于AOP的源码解析与思考 每种语言都有其独特的机制和特点&#xff0c;那么说到Java你可能会首先想到反射&#xff0c;反射是Java语言提供的一种能够在程序运行时动态操作类或对象的能力&#xff0c;比如获取某个对象的类定义、获取类声明的属…