STM32实战之IAP代码升级

这篇具有很好参考价值的文章主要介绍了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实战之IAP代码升级,stm32,嵌入式硬件,IAP升级

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

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

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

STM32实战之IAP代码升级,stm32,嵌入式硬件,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的程序。流程图如下图所示:

STM32实战之IAP代码升级,stm32,嵌入式硬件,IAP升级

4 Boot Loader的代码编写

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

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

STM32实战之IAP代码升级,stm32,嵌入式硬件,IAP升级

5 APP1代码编写

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

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

STM32实战之IAP代码升级,stm32,嵌入式硬件,IAP升级

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

STM32实战之IAP代码升级,stm32,嵌入式硬件,IAP升级

  

6 APP2代码编写

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

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

STM32实战之IAP代码升级,stm32,嵌入式硬件,IAP升级

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)。

STM32实战之IAP代码升级,stm32,嵌入式硬件,IAP升级

       我们把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的数据存储和擦除操作。文章来源地址https://www.toymoban.com/news/detail-789258.html

到了这里,关于STM32实战之IAP代码升级的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【嵌入式学习笔记】嵌入式基础9——STM32启动过程

    程序段交叉引用关系(Section Cross References):描述各文件之间函数调用关系 删除映像未使用的程序段(Removing Unused input sections from the image):描述工程中未用到被删除的冗余程序段(函数/数据) 映像符号表(Image Symbol Table):描述各符号(程序段/数据)在存储器中的地址、类

    2024年02月15日
    浏览(81)
  • stm32嵌入式实验考核

    STM32 实验考核题目 1. 利用 STM32 小板实现:控制外接 LED 灯每隔 3 秒钟亮暗变换,同 时在 PC 机上显示 MCU 的计时时间,MCU 的初始时间由 PC 机 方设置。 2. 利用 STM32 小板实现:利用导线外接 GPIO 口模拟 2 个按键输入, 根据输入组合的四种情况,分别控制三色灯四种流水灯效果

    2024年02月03日
    浏览(49)
  • STM32串口通信详解(嵌入式学习)

    时钟信号在电子领域中是指用于同步和定时电路操作的周期性信号。它在数字系统和通信系统中起着至关重要的作用,用于协调各个组件之间的数据传输和操作。 时钟信号有以下几个重要的方面: 频率:时钟信号的频率是指单位时间内信号周期的数量。它通常以赫兹(Hz)为

    2024年02月09日
    浏览(67)
  • STM32的时钟系统(嵌入式学习)

    时钟是指用于计量和同步时间的装置或系统。时钟是嵌入式系统的脉搏,处理器内核在时钟驱动下完成指令执行,状态变换等动作,外设部件在时钟的驱动下完成各种工作,例如:串口数据的发送、AD转换、定时器计数等。因此时钟对于计算机系统是至关重要的,通常时钟系

    2024年02月16日
    浏览(47)
  • 嵌入式 STM32 通讯协议--MODBUS

    目录 一、自定义通信协议 1、协议介绍 2、网络协议 3、自定义的通信协议  二、MODBUS通信协议 1、概述 2、MODBUS帧结构  协议描述 3、MODBUS数据模型   4、MODBUS事务处理的定义 5、MODBUS功能码  6、功能码定义   7、MODBUS数据链路层 8、MODBUS地址规则  9、MODBUS帧描述 10、MODBUS两种

    2024年02月11日
    浏览(62)
  • 嵌入式——新建STM32工程(标准库)

    目录 一、初识标准库 1.CMSIS标准及库层级关系 2.库文件介绍 (1)Libraries文件夹 ①CMSIS文件夹 ②STM32F10x_Std_Periph_Driver文件夹 ③ 在用库建立一个完整的工程时,还需要添加stm32f10x_it.c、 stm32f10x_conf.h 和 system_stm32f10x.c文件 (2)Project文件夹 (3)Utilities文件夹 3.库各文件之间的关

    2024年01月23日
    浏览(53)
  • 嵌入式C语言基础(STM32)

    前言:一条混迹嵌入式3年的老咸鱼,想到自己第一次接触到stm32的库函数时,c语言稀碎,痛不欲生的场景,该文章为萌新指条明路。 位操作在嵌入式中常用于直接对芯片的寄存器进行操作,当时作为初学者的我看着一脸懵逼,至于为什么这样修改,下面好好分析一下。  一

    2024年02月02日
    浏览(56)
  • STM32的中断系统详解(嵌入式学习)

    中断是处理器中的一种机制,用于响应和处理突发事件或紧急事件。当发生中断时,当前正在执行的程序会被暂时中止,处理器会跳转到中断处理程序(也称为中断服务例程),对中断事件进行处理。处理完中断后,处理器再返回到被中断的程序继续执行。 中断可以分为内部

    2024年02月12日
    浏览(69)
  • 嵌入式学习笔记——STM32的时钟树

    在之前的所有代码编程的过程中,似乎每次都绕不开一个叫做时钟使能的东西,当时我们是在数据手册上直接看其挂接在那条时钟线上的,那么STM32内部的时钟到底是怎么一个构型呢,本文来对此做一个介绍。 老规矩,一个新的名词出现,首先需要搞清楚它是个啥,下图中对

    2024年02月02日
    浏览(53)
  • [蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解

    最近,我报名了今年的蓝桥杯嵌入式比赛,为此刷了一下以往的真题。以下是我对十二届蓝桥杯省赛真题的一些思路和心得,还有一些具体代码的实现。 1、相关模块 第十二届比赛主要用到的模块包括:LED、KEY、LCD、TIM、USART 2、重难点分析 这道题主要目的是做一个停车管理

    2024年01月18日
    浏览(95)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包