【STM32】IAP升级01 bootloader实现以及APP配置(主要)

APP程序以及中断向量表的偏移设置

前言

通过之前的了解 之前的了解,我们知道实现IAP升级需要两个条件:
1.APP程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;
2.APP程序的中断向量表相应的移动,移动的偏移量为 x;

1.APP 程序起始地址设置

默认条件下的起始地址

默认的条件下,图中 IROM1 的起始地址(Start)一般为 0x08000000,大小(Size)为 0x100000,即从 0x08000000 开始的 1024K 空间为我们的程序存储区。
在这里插入图片描述

设置APP起始地址

存储在flash上的APP起始地址设置方法

设置起始地址(Start)为 0x08010000,偏移量为 0x10000(64K 字节,即留给 BootLoader 的空间),因而,留给 APP 用的 FLASH 空间(Size)为 0x80000-0x10000=0x70000(448KB)。设置好 Start 和 Size,就完成 APP 程序的起始地址设置。IRAM 是内存的地址,APP 可以独占这些内存,我们不需要修改。

需要确保 APP 起始地址在 Bootloader 程序结束位置之后,并且偏移量为 0x200 的倍数即可
在这里插入图片描述

存储在SRAM上的APP起始地址设置方法

将 IROM1 的起始地址(Start)定义为:0x20001000,大小为 0x19000(100K 字节),即从地址 0x20000000 偏移0x1000 开始,存放 SRAM APP 代码。
STM32F407ZGT6 只有一个 128K(不算 CCM)的片内 SRAM,存放程序的位置与变量的加载位置不能重复,所以我们需要设置 IRAM1 中的地址(SRAM)的起始地址变为 0x2001A000,分配大小只有 0x6000(24K 字节)。整个 STM32F407ZGT6 的 SRAM(不含 CCM)分配情况为:最开始的 4K 给 Bootloader 程序使用,随后的 100K 存放 APP 程序,最后 24K,用作 APP 程序的内存。
在这里插入图片描述

2. 重新设置中断向量表的地址

SCB->VTOR寄存器存放的是中断向量表的起始地址。默认的情况它由 BOOT 的启动模式决定。重定向这个位置,这样就可以从 Flash 区域的任意位置启动代码。

是你跳转到app程序后,你后续得跑app的中断向量表。要实现这个就必须在刚进入app的时候重定向中断向量表

/* 设置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留 */SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);

baseaddr 为基地址(即 APP 程序首地址),Offset 为偏移量

比如 FLASH APP 设置中断向量表偏移量为 0x10000

SCB->VTOR = FLASH_BASE | ( 0x10000 & (uint32_t)0xFFFFFE00);

SRAM APP 的情况可以参考正点原子探索者开发指南V1.1。

3.设置keil生成bin文件

MDK 默认生成的文件是.hex 文件,并不方便用作 IAP更新,我们希望生成的文件是.bin 文件.

hex和bin文件的区别

简单来说:HEX文档是ascii码的文档。 是不能直接烧到单片机中的。 中间要有转换程序。 但是现在很多编程器都设计成直接可以导入hex文件烧录的,其实这是做了设计的。 bin文件是二进制文件,是可以直接烧到芯片中,中间不用转换的。

设置keil生成bin文件

在 MDK 点击 Options for Target→User 选项卡,在 After Build/Rebuild一栏中,勾选 Run #1,我们推荐使用相对地址,在勾选的同一行后的输入框并写入命令行:fromelf --bin -o …\Output@L.bin …\Output%L
在这里插入图片描述
D:\Keil_v5\ARM\ARMCC\bin\fromelf.exe是MDK 自带的格式转换工具 fromelf.exe的路径。

通过这一步设置,我们就可以在 MDK 编译成功之后,调用 fromelf.exe,
..\..\Output\%L 表示当前编译的链接文件(…\是相对路径,表示上级目录,编译器默认从工程文件*.uvprojx 开始查找,根据我的工程文件 Output 的位置就能明白路径的含义)
指令–bin –o …\Output@L.bin表示在 Output 目录下生成一个.bin 文件,
@L 在 Keil 的下表示 Output 选项卡下的 Name of Executable 后面的字符串,即在 Output 文件夹下生成一个 atk_f407.bin 文件。

在得到.bin 文件之后,我们只需要将这个 bin 文件传送给单片机,即可执行 IAP 升级。

Bootloader程序的实现

1、APP文件的编写与通常编写一致,只需要设定好偏移地址
2、新的中断向量表的偏移设置好
这两点在前文已经写过,接下来就是bootloader的实现了。

Bootloader 和 app 属于两个独立的工程,不是一个工程!!!
相关资料:
1.写 STM32 内部 Flash 的功能用到 STM32 的 Flash操作。STM32片上Flash操作
2.设置bootloader的起始地址为复位之后第一个启动的工程。
这个例子中,bootloader从0x800 0000开始,APP从0x800 8000开始,执行完bootloader之后再执行APP。

BootLoader 的编译器设置
在这里插入图片描述
App 的编译器设置
在这里插入图片描述

1.bootloader程序也是一个app程序

是我们专门用来搬运APP程序的工具,所以也和是和普通程序一样的执行流程。
IAP升级程序(bootloader)启动流程
在这里插入图片描述
简单来说
初始化好时钟,GPIO,外设(依照升级方法来确定初始化对应的外设),那么这块MCU就有了可以和外部通信的能力。
在这里插入图片描述

例如,使用串口升级,那么步骤就是

bootloader的main之前

1.单片机复位,从0x800 0000开始执行。
2.执行0x0800 0004处的Rest_Handler复位中断向量函数
3. 复位中断向量函数执行
在这里插入图片描述
调用SystemInit函数。这个函数里面开启了外部晶振,设置了PLL,除能了所有中断,设置了时钟

_main 标号表示 C/C++标准实时库函数里的一个初始化子程序 _main的入口地址。该程序的一个主要作用是初始化堆栈(跳转_user_initial_stackheap标号进行初始化堆栈),并初始化映像文件,最后跳转到C程序中的main函数。这也正解释了为什么所有的C程序必须有一个main函数作为程序的起点,因为这是由C/C++标准实时库所规定的。
4.跳转到IAP升级程序(bootloader)的main函数。

bootloader的main之后

1.初始化

初始化和正常没有bootloader 的程序一下,该咋样就咋样,为了避免无效功耗损失,一般是需要什么就初始化什么。时钟初始化,串口初始化,定时器初始化等(如果有用到定时轮询或者定时器延时等可以初始化)

2.检查APP 程序是否完整

若出现某些情况导致APP程序不全,有可能导致程序跑飞,进入硬件错误。

初始化完成后,首先检测 APP 分区中最后地址的标志位是否符合(如是否等于0x5a5a,一般APP程序的末尾加入特殊字符,以检测完整性),如果符合,启动倒计时(时间大约几百毫秒就行,根据实际情况决定),等待进入 APP 程序,在此期间,也会等待接收升级的指令;否则,则一直等待接收升级的指令。
check收到的估计bin的最后四个字节是否为0x5A5A

int CheckIfAPPCanJump(void)
{uint32_t *pFlag = (uint32_t *)(APP_FLASH_CODE_BASE + APP_FLASH_CODE_SIZE - 4);if (*pFlag == 0x5A5A){ return 1; }return 0;
}
3.升级

接收到升级指令后,则启动升级流程,升级期间不允许进入 APP 程序。

4.跳转APP程序(IAP 如何实现跳转到用户程序)

升级完成后,需要跳转到 APP 程序(可以自动跳转,也可以增加跳转指令手动跳转),跳转前的准备:
1.检测 APP 程序是否存在,若存在,则继续往下执行APP升级
2.关闭全局中断(可选,放置升级中被打断)
3.清除所有中断标志位(可选,放置升级中被打断)

void Disable_irq(void)
{uint8_t i;__disable_irq(); // 关闭总中断//__enable_irq();/* 清除所有中断标志 */for(i= 0; i < CRS_IRQn; i++){NVIC_ClearPendingIRQ((IRQn_Type)i);}
}

4.跳转 APP 启动程序地址(当然,也能直接跳转到 APP 程序的 main 函数,但在跳转前需要重新初始化堆栈等内容,所以最好还是交给启动文件处理,跳转到resethandle)

4.1 设定跳转地址

typedef  void (*pFunction)(void);       //定义一个pFunction类型 这是个函数指针
pFunction Jump_To_Application;          //Jump_To_Application设置为pFunction类型/*设定跳转地址,FLASH_USER_START_ADDR是APP程序的起始地址*/
JumpAddress = *(__IO uint32_t*) (FLASH_USER_START_ADDR + 4);
Jump_To_Application = (pFunction) JumpAddress;//将APP程序的中断向量表的Rest_Handler地址赋值给Jump_To_Application
__set_MSP(*(__IO uint32_t*) FLASH_USER_START_ADDR); //这里是将把应用程序起始地址设为栈顶指针
Jump_To_Application(); //设置PC指针为复位地址,你可以理解为跳转到应用程序的函数

设定跳转地址,FLASH_USER_START_ADDR是APP程序的起始地址,+4是因为中断向量表每四个字节代表一个中断函数的地址。所以JumpAddress指向APP中断向量表的Rest_Handler.

将跳转地址JumpAddress强制转换pFunction类型,可以理解为编译器将其编译成一个函数

2.官方的demo,以stm32f10x为例

IAP/src/main.c int main(void)
{/* Flash unlock *//*flash解锁,因为需要操作flash*/FLASH_Unlock();/* Initialize Key Button mounted on STM3210X-EVAL board *//*初始化按键,demo中的升级触发条件为按键按下*/         STM_EVAL_PBInit(BUTTON_KEY, BUTTON_MODE_GPIO);   /* Test if Key push-button on STM3210X-EVAL Board is pressed */if (STM_EVAL_PBGetState(BUTTON_KEY)  == 0x00){ /* If Key is pressed *//*如果按键按下,即触发升级,进行升级*//* Execute the IAP driver in order to re-program the Flash *//*初始化串口,demo里面使用的是usart1 + Y-MODe协议*/IAP_Init();SerialPutString("\r\n================================================================");SerialPutString("\r\n=          (C) COPYRIGHT 2010 STMicroelectronics           =");SerialPutString("\r\n=                                                          =");SerialPutString("\r\n=  In-Application Programming Application  (Version 3.3.0) =");SerialPutString("\r\n=                                                          =");SerialPutString("\r\n=                       By MCD Application Team            =");SerialPutString("\r\n============================================================");SerialPutString("\r\n\r\n");/*升级菜单,用户自己实现*//*升级中可选关闭所有中断,防止升级被打断,但是在跳转到APP程序后要第一时间打开总中断*/Main_Menu ();}/* Keep the user application running */else//不升级 进入APP{/* Test if user code is programmed starting from address "ApplicationAddress" *//*升级条件不满足,跳转到用户程序处执行用户程序*/if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)//检测栈顶指针{ /* Jump to user application *//*ApplicationAddress为用户程序的栈地址,+4便为用户程序的复位中断向量地址*/  JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);Jump_To_Application = (pFunction) JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) ApplicationAddress);/*执行用户空间的复位中断向量函数,里面主要是进行系统时钟配置,执行用户空间的main函数数*/  Jump_To_Application();}}while (1){}
}

检测栈顶指针

升级菜单demo,更具需求裁剪

void Main_Menu(void)
{uint8_t key = 0;/* Get the number of block (4 or 2 pages) from where the user program will be loaded *//*计算IAP占用的flash页数*/  BlockNbr = (FlashDestination - 0x08000000) >> 12;/* Compute the mask to test if the Flash memory, where the user program will beloaded, is write protected */
#if defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
#else /* USE_STM3210E_EVAL */if (BlockNbr < 62){UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));}else{UserMemoryMask = ((uint32_t)0x80000000);}
#endif /* (STM32F10X_MD) || (STM32F10X_MD_VL) *//* Test if any page of Flash memory where program user will be loaded is write protected *//*检测flash中用户空间的写保护锁是否开启*/ if ((FLASH_GetWriteProtectionOptionByte() & UserMemoryMask) != UserMemoryMask){FlashProtection = 1;}else{FlashProtection = 0;}while (1){SerialPutString("\r\n================== Main Menu ============================\r\n\n");SerialPutString("  Download Image To the STM32F10x Internal Flash ------- 1\r\n\n");SerialPutString("  Upload Image From the STM32F10x Internal Flash ------- 2\r\n\n");SerialPutString("  Execute The New Program ------------------------------ 3\r\n\n");if(FlashProtection != 0){SerialPutString("  Disable the write protection ------------------------- 4\r\n\n");}SerialPutString("==========================================================\r\n\n");key = GetKey();if (key == 0x31){/* Download user application in the Flash *//*下载程序*/  SerialDownload();}else if (key == 0x32){/* Upload user application from the Flash */SerialUpload();}else if (key == 0x33){JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);/* Jump to user application */Jump_To_Application = (pFunction) JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) ApplicationAddress);Jump_To_Application();}else if ((key == 0x34) && (FlashProtection == 1)){/* Disable the write protection of desired pages */FLASH_DisableWriteProtectionPages();}else{if (FlashProtection == 0){SerialPutString("Invalid Number ! ==> The number should be either 1, 2 or 3\r");}else{SerialPutString("Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r");} }}
}

进入APP

APP 代码起始设置见前文
APP中断向量表需要偏移是你跳转到app程序后,你后续得跑app的中断向量表。要实现这个就必须在刚进入app的时候重定向中断向量表

注意在 mian 函数起始处重新设置中断向量表(寄存器 SCB->VTOR)的偏移量,否则 APP 无法正常运行

void SetVectorTable(void)
{uint8_t i;SCB->VTOR = APP_FLASH_CODE_BASE;/* 清除所有中断标志 */for(i= 0; i < CRS_IRQn; i++){NVIC_ClearPendingIRQ((IRQn_Type)i);}/* 在BOOT中跳转之前若关闭了全局中断, 此需要重新打开 */__enable_irq();
}

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

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

相关文章

ROS2 中的轻量级、自动化、受控回放

一、说明 这篇文章描述了一种在 ROS2 中实现受控重播器的轻量级方法。用以测试中将现象重新播放一遍&#xff0c;以实现调参或故障定位的目的。所有源代码都可以在这里找到。该帖子也可在此处获得。 二、问题&#xff1a;不同步重播 任何曾经认真开发过 ROS2 的人都会知道这个问…

cloudCompare教程:一、可视化、点、线编辑

依据高度等准则(都在Scalar Fields中)渲染点云&#xff08;首先要打开Tools -> Projection -> Export coordinate to SF&#xff09; 在上述准则之外的&#xff0c;设置为不显示&#xff1a; 软件的显示设置&#xff08;首先打开右边的彩色柱状图&#xff0c;点击左边属性…

ECharts多个数据视图进行自适应大小的解决方案

项目场景&#xff1a; 在制作数据视图时经常会遇到多个数据视图的情况&#xff0c;在多个数据视图的情况下做自适应是比较麻烦的&#xff0c;这里就详细的分析一下该如何去制作&#xff0c;分享一下我的解决办法及思路。 定义 DOM 容器 这里需要注意一个地方&#xff0c;在定…

idea Springboot 校园助学贷款系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 校园助学贷款系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统 具有完整的源代码和数据库&…

2023.09.30使用golang1.18编译Hel10-Web/Databasetools的windows版

#Go 1.21新增的 log/slog 完美解决了以上问题&#xff0c;并且带来了很多其他很实用的特性。 本次编译不使用log/slog 包 su - echo $GOPATH ;echo $GOROOT; cd /tmp; busybox wget --no-check-certificate https://go.dev/dl/go1.18.linux-amd64.tar.gz;\ which tar&&am…

C++核心编程--继承篇

4.6、继承 继承是面向对象三大特征之一 有些类与类之间存在特殊的关系&#xff0c;例如下图中&#xff1a; ​ 我们发现&#xff0c;定义这些类的定义时&#xff0c;都拥有上一级的一些共性&#xff0c;还有一些自己的特性。那么我们遇到重复的东西时&#xff0c;就可以考虑使…

用go实现http服务端和请求端

一、概述 本文旨在学习记录下如何用go实现建立一个http服务器&#xff0c;同时构造一个专用格式的http客户端。 二、代码实现 2.1 构造http服务端 1、http服务处理流程 基于HTTP构建的服务标准模型包括两个端&#xff0c;客户端(Client)和服务端(Server)。HTTP 请求从客户端…

泰国数字加密平台Bitkub创始人到访上海和数集团

2023年9月21日&#xff0c;泰国数字加密货币交易平台Bitkub创始人兼首席执行官&#xff08;CEO&#xff09;Jirayut Srupsrisopa (Topp)先生到访上海和数集团总部。董事长唐毅先生热情会见了来宾&#xff0c;双方进行了友好深入的交流。 和数集团国际部经理晋松&#xff1b;苏州…

BUUCTF reverse wp 76 - 80

[CISCN2018]2ex 四处游走寻找关键代码 int __fastcall sub_400430(int a1, unsigned int a2, int a3) {unsigned int v3; // $v0int v4; // $v0int v5; // $v0int v6; // $v0unsigned int i; // [sp8h] [8h]unsigned int v9; // [sp8h] [8h]int v10; // [spCh] [Ch]v10 0;for…

在 Python 中列出虚拟环境

文章目录 在Python中列出虚拟环境使用lsvirtualenv命令使用Conda命令使用workon命令 总结 虚拟环境是一个独立的环境&#xff0c;我们可以在其中安装库、包、脚本和Python解释器。如果你的项目需要不同版本的库或Python解释器&#xff0c;你可以为每个项目创建单独的虚拟环境。…

2.索引操作

1. 创建索引 创建索引就等于创建数据库&#xff0c;ES使用put操作创建索引&#xff0c;我们创建一个students的索引&#xff0c;只需要发生put请求&#xff1a;http://127.0.0.1:9200/students 2. 查看索引 2.1 查看所有索引&#xff1a; 使用http://127.0.0.1:9200/_cat/ind…

Firefox 开发团队对 Vue 3 进行优化效果显著

Mozilla 官方博客近日发表文章《Faster Vue.js Execution in Firefox》&#xff0c;介绍了 Firefox 开发团队对 Vue 3 进行的优化。 文章写道&#xff0c;在使用 Speedometer 3 对 Firefox 进行基准测试时&#xff0c;他们发现 Vue.js test 的测试结果从 Vue 2 升级到 Vue 3 后…

ElasticSearch 同步数据变少了

一、前言 这几天对接ES遇到几个坑&#xff0c;我们将一张库存表同步到ES发现Docs Count和我们表中的数据对不上&#xff0c;需要加上Docs deleted才对得上&#xff0c;也不知道批量写入数据为什么有些数据就会成 Docs deleted。 二、ID和版本号 ES中每一个Document都有一个_…

大规模语言模型--中文 LLaMA和Alpaca

中文LLaMA 尽管 LLaMA 和 Alpaca 在 NLP 领域取得了重大进展&#xff0c; 它们在处理中文语言任务时&#xff0c; 仍存在一些局限性。这 些原始模型在字典中仅包含数百个中文 tokens (可以理解为单词)&#xff0c;导致编码和解码中文文本的效率受到了很大 影响。 之前已经对…

数据结构--队列

一、队列是什么 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;队列是一种操作受限制的线性表。进行插入操作的端称为队尾&…

GEO生信数据挖掘(一)数据集下载和初步观察

检索到目标数据集后&#xff0c;开始数据挖掘&#xff0c;本文以阿尔兹海默症数据集GSE1297为例 目录 GEOquery 简介 安装并加载GEOquery包 getGEO函数获取数据&#xff08;联网下载&#xff09; 更换下载数据源 对数据集进行初步观察处理 GEOquery 简介 GEOquery是一个…

第1篇 目标检测概述 —(4)目标检测评价指标

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。目标检测评价指标是用来衡量目标检测算法性能的指标&#xff0c;可以分为两类&#xff0c;包括框级别评价指标和像素级别评价指标。本节课就给大家重点介绍下目标检测中的相关评价指标及其含义&#xff0c;希望大家学习之后…

【中秋国庆不断更】HarmonyOS对通知类消息的管理与发布通知(上)

一、通知概述 通知简介 应用可以通过通知接口发送通知消息&#xff0c;终端用户可以通过通知栏查看通知内容&#xff0c;也可以点击通知来打开应用。 通知常见的使用场景&#xff1a; 显示接收到的短消息、即时消息等。显示应用的推送消息&#xff0c;如广告、版本更新等。显示…

【中秋国庆不断更】OpenHarmony多态样式stateStyles使用场景

Styles和Extend仅仅应用于静态页面的样式复用&#xff0c;stateStyles可以依据组件的内部状态的不同&#xff0c;快速设置不同样式。这就是我们本章要介绍的内容stateStyles&#xff08;又称为&#xff1a;多态样式&#xff09;。 概述 stateStyles是属性方法&#xff0c;可以根…

机器人中的数值优化(十九)—— SOCP锥规划应用:时间最优路径参数化(TOPP)

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…