STM32实战之IAP代码升级

目录

1 IAP介绍  

2 内存分区

3 整体设计流程图

4 Boot Loader的代码编写

5 APP1代码编写

6 APP2代码编写

stm32内部flash操作相关函数


1 IAP介绍  

IAP(In Application Programming)即在应用编程, IAP 是用户自己的程序在运行过程中对User Flash 的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信接口对产品中的固件程序进行更新升级。通常实现 IAP 功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如 USB、 USART,蓝牙)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在 User Flash 中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:

  1. 检查是否需要对第二部分代码进行更新;
  2. 如果不需要更新则转到第二部分代码执行;
  3. 如果需要更新则执行更新操作;
  4. 跳转到第二部分代码执行;

第一部分代码的烧入必须通过其它手段,如 JTAG 或 ISP;第二部分代码可以使用第一部分代码 IAP 功能烧入,也可以和第一部分代码一起烧入,以后需要程序更新时再通过第一部分 IAP代码更新。

我们将第一个项目代码称之为 Bootloader 程序,第二个项目代码称之为 APP(功能代码) 程序,他们存放在 STM32 FLASH 的不同地址范围,一般从最低地址区开始存放 Bootloader,紧跟其后的就是 APP 程序。这样我们就是要实现 3 个程序:Bootloader 和 APP1和APP2。

       我们先来看看 STM32 正常的程序运行流程:

       STM32 的内部闪存(FLASH)地址起始于 0x08000000,一般情况下,程序文件就从此地址开始写入。此外 STM32 是基于 Cortex-M3 内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而这张“中断向量表”的起始地址是 0x08000004,当中断来临,STM32 的内部硬件机制会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。

STM32 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求,此时 STM32 强制将 PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。

当加入 IAP 程序之后,程序运行流程如图:

加入 IAP 之后程序运行流程图中,STM32 复位后,还是从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数,如图标号①所示,在执行完 IAP 以后(即将新的 APP 代码写入 STM32的 FLASH,灰底部分。新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,如图标号②和③所示,同样 main 函数为一个死循环,并且注意到此时 STM32 的 FLASH,在不同位置上,共有两个中断向量表。

在 main 函数执行过程中,如果 CPU 得到一个中断请求,PC 指针仍强制跳转到地址0X08000004 中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回 main 函数继续运行,如图标号⑥所示。

通过以上两个过程的分析,我们知道 IAP 程序必须满足两个要求:

  1.  新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;
  2. 必须将新程序的中断向量表相应的移动,移动的偏移量为 x;

2 内存分区

       我们写的代码最终都会被编译成二进制文件并保存在Flash中,那么我们就可以进一步对我们程序进行分区。STM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。

1)主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。注意,小容量和中容量产品则每页只有 1K 字节。从上图可以看出主存储器的起始地址就是0X08000000, B0、B1 都接 GND 的时候,就是从 0X08000000开始运行代码的。

2)信息块,该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 B0 接 V3.3,B1 接 GND 的时候,运行的就是这部分代码。

3)闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理;编程与擦除的高电压由内部产生。

       那么我们分区就是主要对FLASH的主存储器进行分区,STM32F103ZE共512K的Flash大小,我们将它分成三个区,BootLoader区、App1区、App2区(备份区)具体划分如下表:

BootLoader区存放启动代码

App1区存放应用代码

App2区存放暂存的升级代码   0x08000000---0x08007FFFF  

区域

大小

地址范围

BootLoader

12K    0x3000

0x08000000-0x08002FFF

APP1

250K    0x3E800

0x08003000-0x080417FF

APP2(备份区域)

250K    0x3E800

0x08041800-0x0807FFFF

3 整体设计流程图

先执行BootLoader程序, 先去检查APP2区有没有程序,如果有程序就将App2区(备份区)的程序拷贝到App1区, 然后再跳转去执行App1的程序。

然后执行App1程序, 因为BootLoader和App1这两个程序的向量表不一样, 所以跳转到App1之后第一步是先去更改程序的向量表,然后再去执行其他的应用程序。

在应用程序里面会加入程序升级的部分,这部分主要工作是拿到升级程序,然后将他们放到App2区(备份区),以便下次启动的时候通过BootLoader更新App1的程序。流程图如下图所示:

4 Boot Loader的代码编写

       在BootLoader程序中,我们主要实现对APP2区域的中的标志检查,读取APP2内部的程序,写入APP1地址程序,代码执行跳转程序。

       具体的代码执行过程,我们参考示例代码来掌握,整个执行流程如下图所示:

5 APP1代码编写

       在整个APP代码的编写上,我们首先修改向量表, 因为本程序是由BootLoader跳转过来的, 不修改向量表后面会出现问题;需要在APP的基本功能上加入串口接收数据并保存到APP2(备份区)的功能代码。

       具体的代码执行过程,我们参考示例代码来掌握,整个执行流程如下图所示:

       在APP程序的编译时,我们需要修改地址属性,具体如下图所示:

  

6 APP2代码编写

       APP2的代码,理论上来说和APP1的代码除了基本功能不同外,在代码上级上的功能一样。只是APP2的代码,我们可以直接将编译后的二进制文件使用串口等方式发送(也可以无线远程发送,SD卡等方式存储)到芯片内部,并存储在APP2的地址区域内。事实上接收和存储的过程是运行的APP1的程序,然后将接收到的数据存放到APP2的地址区域内。

       APP2的编译地址和APP1保持一致,但是我们发送的文件并不是hex文件,而是编译后的bin文件,那如何生成bin文件呢?需要修改配置属性:

D:\Keil_v5\Arm\ARMCC\bin\fromelf.exe --bin -o .\Objects\weather.bin .\Objects\weather.axf

E:\keil\ARM\ARMCC\bin\fromelf.exe--bin -o .\Objects\test.bin .\Objects\test.axf

其中:D:\Keil_v5\Arm\ARMCC\bin\fromelf.exe 是一个keil自带的生成bin文件的工具绝对路径。

通过这一步设置,我们就可以在 MDK 编译成功之后,调用 fromelf.exe(注意,我的 MDK 是安装在 D盘文件夹下,如果你是安装在其他目录,请根据你自己的目录修改fromelf.exe 的路径),根据当前工程的 weather.axf(如果是其他的名字,请记住修改,这个文件存放在 Objects 目录下面,格式为 xxx.axf),生成一个 RTC.bin 的文件。并存放在 axf 文件相同的目录下,即工程的 Objects 文件夹里面。在得到.bin 文件之后,我们只需要将这个 bin 文件传送给单片机,即可执行 IAP 升级。(我们也可以将bin文件无线发送,存放在SD卡内,存放在外部FLASH内等等方式进行代码升级,其中无线发送的形式叫OTA)。

       我们把APP2生成的bin文件,通过串口,发送到APP1的运行设备上,就会自动的保存APP2的代码数据到对应的Flash地址下,那么按下复位按键后(也可以软件复位),再次运行bootloader代码,就会加载APP2的数据到APP1的地址下,并运行新的程序。

stm32内部flash操作相关函数

//读取指定地址的半字(16位数据)
//faddr:读地址(此地址必须为2的倍数!!)
//返回值:对应数据.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
	return *(vu16*)faddr; 
}

//不检查的写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数   
void STMFLASH_Write_NoCheck(u32 WriteAddr, u16 *pBuffer, u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0; i<NumToWrite; i++)
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
	  WriteAddr += 2;//地址增加2.
	}  
} 


//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Read(u32 ReadAddr, u16 *pBuffer, u16 NumToRead)   	
{
	u16 i;
	for(i=0; i<NumToRead; i++)
	{
		pBuffer[i] = STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr += 2;//偏移2个字节.	
	}
}


//startAddr地址必须是2K的整数倍
//numToErase 要擦除的内存大小,必须是2K的整数倍
uint8_t STMFLASH_Erase(u32 startAddr, u32 eraseSize) 
{
	uint16_t i=0;
	uint16_t len = eraseSize/STM_SECTOR_SIZE;
	if(startAddr<STM32_FLASH_BASE||(startAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))
		return -1;//非法地址
	if((startAddr % STM_SECTOR_SIZE))
		return -2;//非法地址
	FLASH_Unlock();//解锁
	for(i=0; i<len; i++) {
		FLASH_ErasePage(startAddr+i*STM_SECTOR_SIZE);//擦除这个扇区
	}
	FLASH_Lock();//上锁	
	return 0;
}

//半字数据写入
void STMFLASH_WriteHalfWord(u32 startAddr, u16 data)
{
	FLASH_Unlock();						//解锁
	FLASH_ProgramHalfWord(startAddr, data);
	FLASH_Lock();//上锁
}

这段代码是针对STM32单片机的FLASH操作的函数实现。主要包括读取指定地址的半字数据、不检查的写入数据、从指定地址开始读出指定长度的数据、擦除指定地址范围内的FLASH数据以及半字数据的写入。

具体分析如下:

  1. ​STMFLASH_ReadHalfWord​​函数用于读取指定地址的半字(16位数据),通过将地址转换为指针,然后读取指针对应的数据。
  2. ​STMFLASH_Write_NoCheck​​函数用于不进行检查的写入数据,通过循环将数据逐个写入指定地址,并递增地址。
  3. ​STMFLASH_Read​​函数用于从指定地址开始读出指定长度的数据,通过循环将每个地址对应的半字数据读取到指定的数据缓冲区中。
  4. ​STMFLASH_Erase​​函数用于擦除指定地址范围内的FLASH数据,先判断地址的合法性,然后对每个扇区进行擦除操作。
  5. ​STMFLASH_WriteHalfWord​​函数用于将半字数据写入指定地址,先解锁FLASH,然后进行半字数据的写入,最后上锁FLASH。

这段代码主要实现了对STM32单片机的FLASH进行读取、写入和擦除操作的功能。可以通过调用这些函数来实现对FLASH的数据存储和擦除操作。

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

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

相关文章

机器学习周刊第二期:300个机器学习应用案例集

大家好 前文&#xff1a;机器学习项目精选 第一期 继续分享我最近看过并觉得非常硬核的资源&#xff0c;包括Python、机器学习、深度学习、大模型等等。 1、Python编程挑战 地址&#xff1a;https://github.com/Asabeneh/30-Days-Of-Python 30天Python编程挑战是一个逐步学…

c++比Java更不容易失业吗?

c比Java更不容易失业吗&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01…

SpringBoot 如何 返回页面

背景 RestController ResponseBody Controller Controller中的方法无法返回jsp页面&#xff0c;或者html&#xff0c;配置的视图解析器 InternalResourceViewResolver不起作用&#xff0c;返回的内容就是Return 里的内容。 Mapping ResponseBody 也会出现同样的问题。 解…

SpringCloud系列篇:入门讲解Spring Cloud是什么

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于SpringCloud的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Spring Cloud是什么 二.Spring …

java基于vue的音乐播放器的设计与实现论文

摘 要 当下&#xff0c;如果还依然使用纸质文档来记录并且管理相关信息&#xff0c;可能会出现很多问题&#xff0c;比如原始文件的丢失&#xff0c;因为采用纸质文档&#xff0c;很容易受潮或者怕火&#xff0c;不容易备份&#xff0c;需要花费大量的人员和资金来管理用纸质文…

ThinkPHP5多小区物业管理系统源码(支持多小区)

基于 ThinkPHP5 Bootstrap 倾力打造的多小区物业 管理系统源码&#xff0c;操作简单&#xff0c;功能完善&#xff0c;用户体验良好 开发环境PHP7mysql 安装步骤: 1.新建数据库db_estate,还原数据db_estate.sql 2.修改配置文件&#xff1a;application/database.php 3.运…

【已解决】You have an error in your SQL syntax

报错讯息 java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘desc,target_url,sort,status,create_by,modify_by,created,last_update_time FROM…

基于JAYA算法优化的Elman神经网络数据预测 - 附代码

基于JAYA算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于JAYA算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于JAYA优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针…

2024年阿里云、腾讯云、华为云、LightNode、硅云服务器如何选?怎么买最划算?[最新价格表]

很多小伙伴都有一颗上云的心&#xff0c;包括我自己 有事没事的折腾一下自己的小破站&#xff0c;也挺有意思的&#xff01; 那么&#xff0c;云服务器哪家好&#xff1f;优惠力度哪家大&#xff1f;活动入口哪里进&#xff1f;云服务器如何配置&#xff1f;如何选型&#xf…

【InternLM】Lagent智能体工具调用实践浦语·灵笔(InternLM-XComposer)图文理解创作Demo练习

目录 前言一、Lagent智能体工具1-1、什么是智能体&#xff1f;1-2、Lagent智能体 二、InternLM-XComposer&#xff08;图文理解创作模型介绍&#xff09;三、Lagent调用实践3-0、环境搭建3-1、创建虚拟环境3-2、导入所需要的包3-3、模型下载3-4、Lagent安装3-5、demo运行 四、I…

篇三:让OAuth2 server支持密码模式

由于Spring-Security-Oauth2停止维护&#xff0c;官方推荐采用 spring-security-oauth2-authorization-server&#xff0c;而后者默认不支持密码授权模式&#xff0c;本篇实战中采用的版本如下&#xff1a; <dependency><groupId>org.springframework.security<…

冒泡排序数据结构实验报告

实验目的&#xff1a; 理解冒泡排序算法的原理和基本思路。熟悉冒泡排序在实际应用中的场景和优化方法。 实验内容&#xff08;实验题目与说明&#xff09; 编写一个双向冒泡排序算法&#xff0c;即在排序过程中以交替的正、反两个方向进行遍历。若第一趟把关键字最大的记录…

2024年低压电工证模拟考试题库及低压电工理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年低压电工证模拟考试题库及低压电工理论考试试题是由安全生产模拟考试一点通提供&#xff0c;低压电工证模拟考试题库是根据低压电工最新版教材&#xff0c;低压电工大纲整理而成&#xff08;含2024年低压电工证…

7款实用的SQLite数据库可视化管理工具

前言 俗话说得好“工欲善其事&#xff0c;必先利其器”&#xff0c;合理的选择和使用可视化的管理工具可以降低技术入门和使用门槛。今天推荐7款实用的SQLite数据库可视化管理工具(GUI)&#xff0c;帮助大家更好的管理SQLite数据库。 什么是SQLite&#xff1f; SQLite是一个…

centos通过yum安装redis

1. 安装yum添加epel源(此步根据环境&#xff0c;如果有源则可跳过&#xff0c;在阿里去可跳过&#xff09; yum install epel-release 2 使用yum安装Redis yum install redis 出现如下图所示的内容 3 Redis配置 vim /etc/redis.conf :set number(显示行号) 61行&#x…

代码随想录-刷题第四十九天

121. 买卖股票的最佳时机 题目链接&#xff1a;121. 买卖股票的最佳时机 思路&#xff1a;动态规划五步曲 dp[i][0] 表示第i天持有股票所得最多现金&#xff0c;dp[i][1] 表示第i天不持有股票所得最多现金。 一开始现金是0&#xff0c;那么加入第i天买入股票&#xff0c;现金…

19道ElasticSearch面试题(很全)

1. elasticsearch的一些调优手段 1、设计阶段调优 &#xff08;1&#xff09;根据业务增量需求&#xff0c;采取基于日期模板创建索引&#xff0c;通过 roll over API 滚动索引&#xff1b; &#xff08;2&#xff09;使用别名进行索引管理&#xff1b; &#xff08;3&…

优雅的通过Shell脚本生成Go的程序包

概要 本文将介绍如何使用 Shell 脚本打包来优雅地生成Go的程序包。我们将创建一个简单的脚本&#xff0c;用于构建、测试和部署 Golang 项目。 前言 随着Go语言的普及&#xff0c;越来越多的开发人员选择使用Go编写代码。虽然越来越多的公司项目已使用持续集成/持续部署&…

android 倒计时控件

效果&#xff1a;&#xff08;可不设置 之前、之后文字&#xff09; /*** 倒计时秒数** desc : 时分秒倒计时view* * 布局里引用后&#xff0c;* private fun testMethod(){* binding.test.setCDownStarText("之前的文字")* binding.test.setCDo…

听GPT 讲Rust源代码--compiler(28)

File: rust/compiler/rustc_codegen_llvm/src/llvm/mod.rs 文件rust/compiler/rustc_codegen_llvm/src/llvm/mod.rs是Rust编译器的LLVM代码生成模块的一个文件。该文件定义了一些用于与LLVM交互的结构体、枚举和常量。 此文件的主要作用是&#xff1a; 定义编译器和LLVM之间的接…