本文主要是介绍【嵌入式】一种优雅的 bootloader 跳转APP 的方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
【嵌入式】一种优雅的 bootloader 跳转APP 的方式
- 0. 个人简介 && 授权须知
- 1. 前言
- 2. 干净的跳转
- 3.程序的 noinit 段
- 4. 利用noinit段实现优雅的跳转
- 4.1 检查栈顶地址是否合法
- 4.2 栈顶地址 +4
- 4.3 __set_MSP
- 5.OTA 过后的运行逻辑
0. 个人简介 && 授权须知
📋 个人简介
- 💖 作者简介:大家好,我是喜欢记录零碎知识点的菜鸟打工人。😎
- 📝 个人主页:欢迎访问我的博客主页🔥
- 🎉 支持我:点赞👍+收藏⭐️+留言📝
- 📣 系列专栏:嵌入式Linux开发 🍁 🍁
- 💬格言:写文档啊不是写文章,重要的还是直白!🔥
转载文章,禁止声明原创;不允许直接二次转载,转载请根据原文链接联系作者
若无需改版,在文首清楚标注作者及来源/原文链接,并删除【原创声明】,即可直接转载。
但对于未注明转载来源/原文链接的文章,我将保留追述的权利。作者:积跬步、至千里
1. 前言
一些需要OTA 的设备大多都是讲程序设计为 bootloader 和 app区域,以便在升级时更加方便。
程序从BT跳转到app:
- 小白:修改栈顶地址,直接 跳转到APP起始地址,在APP中在修改中断向量表
- 中级:
what the fxxk?
这么省事的吗?还需要做以下事情呢,包括但不限于:- 滴答定时器中断关掉、重置计数器
- 关闭时钟
- 关闭使能的外设,重新Deinit
- 关闭看门狗
- 设置栈顶地址
- 设置跳转地址
- 关闭全局中断
归根到底,关中断、外设、去初始化这些操作的本质就是给APP运行提供一个干净的环境
2. 干净的跳转
干净的跳转指的是MCU内部的寄存器没被污染,也即是初始上电状态,咋实现呢?
- 手动断电复位MCU??可以,但是不现实
- 启动软件复位,但是软件复位后所有的寄存器都复位了,程序中定义的变量也重新加载了
有时候,见多识广真的会打开思路。
在一篇文章中,看到了如下思路,但是以下思路中的第一条怎么实现?复位后,在程序中定义的变量不会被初始化??
- 定义一个标志,保证其不会被初始化
- 每次复位后就立即检查这个标志,如果是跳转标志就跳转,否则就继续运行
- 当升级完成后就更新跳转标志并软件复位
带着疑问,请看以下章节:
3.程序的 noinit 段
比如我在该段定义了一个变量A,程序在运行过程中A会累加,该段定义的变量可以实现
- 当程序【软复位】时重启时,该段的变量【不会】被初始化
- 只有当程序【掉电重启】时,该段的变量才【会】被初始化
在该段中定义的变量会被编译器分配到SRAM
中,SRAM 的地址可以手动设置,该段常用来保存一些敏感信息。
比如我想实现以下场景:
要是想监控程序由于看门狗超时复位而造成重启的次数,在不知道 noinit 之前我想到的就是,利用串口打印保存1个小时的打印 log ,然后观察,硬着头皮数重启次数。
就可以通过以上 noinit 段实现
4. 利用noinit段实现优雅的跳转
首先贴出代码:
#define FLASH_APP_ADDR 0x08010000 //应用程序起始地址/*在 noinit 段中定义一个标志位变量*/
__attribute__((section(".noinit"))) volatile uint32_t JumpFlag;void SetJumpFlagAndReset(void){JumpFlag = 0xA5A5A5A5;/* Setting of signs */NVIC_SystemReset();/* Software reset */
}void CheckJumpFlagAndJump(void){if(JumpFlag == 0xA5A5A5A5){JumpFlag = 0; /* Clear jump flag */iap_load_app(FLASH_APP1_ADDR);/* Loading the application */}
}void iap_load_app(uint32_t app_addr)
{typedef void (*jump_app_ptr)(void);jump_app_ptr jump2app;/* Check the top-of-stack address for legality */if(((*(volatile uint32_t *)app_addr) & 0x2FF00000) == 0x20000000){jump2app = (jump_app_ptr)(*(volatile uint32_t *)(app_addr + 4));__set_MSP(*(__IO uint32_t*)app_addr);/* Jump to APP program */jump2app();}
}
4.1 检查栈顶地址是否合法
if (((*(volatile u32*)app_addr) & 0x2FFE0000 ) == 0x20000000)
分析
app_addr
是app
应用程序的flash
首地址(volatile u32*)app_addr
是将该地址转换为u32
指针类型(*(volatile u32*)app_addr
是取出该地址的内容,假设为u32
类型的 变量P
- 变量
P
,是app
程序的堆栈地址 ,该地址指向RAM
,RAM
的起始地址为0x2000_0000
p & 0x2FFE_0000 = 0X2000_0000
,则p
的范围就是0x20000000~0x2001ffff
- 综上,
if
语句就是为了判断ram
是否在0x20000000~0x2001ffff
区间范围内,该区间大小为 128K,可以根据实际情况定义
4.2 栈顶地址 +4
栈顶地址 +4 存放的是 复位地址
4.3 __set_MSP
将用户定义的 flash
首地址存储的栈顶地址,通过 __set_MSP
语句告诉 ARM
内核,该地址就是APP
程序区域的栈顶地址
5.OTA 过后的运行逻辑
OTA
的最后运行函数SetJumpFlagAndReset()
, 将noinit
段的变量设置标志位并且软复位main
函数的初始下运行函数CheckJumpFlagAndJump()
- 标志位为
0xa5a5_a5a5
:表示是OTA
之后的启动,运行跳转函数 - 标志位为 0 ,表示是掉电启动的
- 标志位为
参考:
IAR下使用noinit段
怎么在Bootloader跳转前给APP提供干净的运行环境
这篇关于【嵌入式】一种优雅的 bootloader 跳转APP 的方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!