IAR环境下STM32+IAP方案的实现

--基于STM32F103ZET6的UART通讯实现

一、什么是IAP,为什么要IAP

      IAP即为In Application Programming(在应用中编程),一般情况下,以STM32F10x系列芯片为主控制器的设备在出厂时就已经使用J-Link仿真器将应用代码烧录了,如果在设备使用过程中需要进行应用代码的更换、升级等操作的话,则可能需要将设备返回原厂并拆解出来再使用J-Link重新烧录代码,这就增加了很多不必要的麻烦。站在用户的角度来说,就是能让用户自己来更换设备里边的代码程序而厂家这边只需要提供给用户一个代码文件即可。

      而IAP却能很好的解决掉这个难题,一片STM32芯片的Code(代码)区内一般只有一个用户程序。而IAP方案则是将代码区划分为两部分,两部分区域各存放一个程序,一个叫bootloader(引导加载程序),另一个较user application(用户应用程序)。bootloader在出厂时就固定下来了,在需要变更user application时只需要通过触发bootloader对userapplication的擦除和重新写入即可完成用户应用的更换。如图1-1所示

图 1-1

      在程序执行初始进入bootloader,在bootloader里面检测条件是否被触发(可通过按键是否被按下、串口是否接收到特定的数据、U盘是否插入等等),如果有则进行对user application进行擦除和重新写入操作,如果没有则直接跳转到user application执行应用;如果有则进行擦除用户代码并重新写入新的用户代码。

 

二、STM32F103ZET6硬件条件

      STM32F103ZET6的启动方式有三种:内置FLASH启动、内置SRAM启动、系统存储器ROM启动,通过BOOT0和BOOT1引脚的设置可以选择从哪中方式启动,这里选择内置的FLASH启动。其FLASH的地址为0x08000000—0x0807ffff,共512KB,这些都能从芯片数据手册中直接得到。而这里首要的一个问题是中断的问题。正常情况下发生中断的过程为:发生中断(中断请求)à到中断向量表查找中断函数入口地址à跳转到中断函数à执行中断函数à中断返回。也就是说在STM32的内置的Flash中有一个中断向量表来存放各个中断服务函数的入口地址,内置Flash的分配情况大致如下图2-1。

图2-1

 

在只有一个程序的情况下,程序执行的走向应该如图2-2所示(借用网友的原图)。

图2-2

      STM32F10x有一个中断向量表,这个中断向量表存放在代码开始部分的后4个字节处(即0x08000004),代码开始的4个字节存放的是堆栈栈顶的地址,当发生中断后程序通过查找该表得到相应的中断服务程序入口地址,然后再跳到相应的中断服务程序中执行。上电后从0x08000004处取出复位中断向量的地址,然后跳转到复位中断程序的入口(标号①所示),执行结束后跳转到main函数中(标号②所示)。在执行main函数的过程中发生中断,则STM32强制将PC指针指回中断向量表处(标号③所示),从中断向量表中找到相应的中断函数入口地址,跳转到相应的中断服务函数(标号④所示),执行完中断函数后再返回到main函数中来(标号⑤所示)。

      若在STM32F103x中使用IAP方案,则内置的Flash分配情况大致如下图2-3。

图2-3

在内置的Flash里面添加一个BootLoader程序,BootLoader程序和user application各有一个中断向量表,假设BootLoader程序占用的空间为N+M字节,则程序的走向应该如图2-2所示(借用网友的原图并做改动,其中虚线部分为原图步骤④⑤的走向,本人改为指向灰色部分)。

图2-2

      上电初始程序依然从0x08000004处取出复位中断向量地址,执行复位中断函数后跳转到IAP的main(标号①所示),在IAP的main函数执行完成后强制跳转到0x08000004+N+M处(标号②所示),最后跳转到新的main函数中来(标号③所示),当发生中断请求后,程序跳转到新的中断向量表中取出新的中断函数入口地址,再跳转到新的中断服务函数中执行(标号④⑤所示),执行完中断函数后再返回到main函数中来(标号⑥所示)。

      对于步骤④⑤,网友认为是:“在main执行的过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0x08000004中断向量表处,而不是新的中断向量表,如图标号④所示,程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示”。我对此的理解是:“当发生中断后,程序从0x08000004(旧)处的中断向量表中得到相应的中断服务函数入口地址,继而跳转到相应的中断服务程序”。但是旧的中断向量列表里边存放的是IAP程序中断函数的入口地址,它是如何得到user程序中断函数的入口地址呢?所以我觉得此种说法是错误的。“当发生中断时PC指针强制会跳转到0x08000004处”这种说法并没有错,只是忽略了后续的一些知识要点而导致这个说法出现矛盾。

      对于步骤④⑤我认为的是,在main函数的执行过程中,如果CPU得到一个中断请求,PC指针本来应该跳转到0x08000004处的中断向量表,由于我们设置了中断向量表偏移量为N+M,因此PC指针被强制跳转到0x08000004+N+M处的中断向量表中得到相应的中断函数地址(待求证),再跳转到相应新的中断服务函数,执行结束后返回到main函数中来。

 

三、实现过程

      STM32F103ZET6的Flash地址为0x08000000—0x0807ffff共512KB,把这512KB的空间分为两块,第一块大小为32KB存放BootLoader程序,剩余的空间存放用户程序(根据实际情况分配这两块空间的大小,BootLoader程序占用的空间越小越好,则BootLoader地址为0x08000000—0x08007fff,用户程序地址为0x08008000—0x0807ffff。BootLoader流程图大致应该如下:

1、初始化时钟。

2、初始化中断向量表地址。

3、初始化按键。      (使用按键触发方式,上电时如果按键被按下则进行用户程序更新操作)

4、初始化串口。

5、检测按键是否被按下,是则执行步骤6,否则执行步骤10。

6、擦除用户程序(擦除0x08008000—0x0807ffff地址空间Flash)。

7、从串口读取新的用户代码数据,把代码写入用户程序空间。

8、检测串口数据接收完毕?是则执行步骤9,否则跳回步骤7。

9、用户程序更新完毕,等待重新上电或硬件复位。

10、跳转到用户程序(强制将PC指针跳转到0x08008000+4处)。

     

到这里首先要解决的问题就有:

1、如何进行对STM32的Flash进行擦除和写入操作。

2、中断向量表偏移如何设置。

3、如何改变代码存放的地址空间(因为BootLoader要存放在0x08000000处,用户程序要存放在0x08008000处,而默认的代码存放的地址空间为0x08000000)。

4、怎么进行PC指针的强制跳转,跳转时需要做些什么。

5、串口接收的用户代码数据是什么样的代码数据,是一种什么样的文件。

 

问题的解决:

1、使用STM32的固件库函数,只需调用几个库函数即可轻松解决,使用的固件库为stm32f10x_flash.c文件,对Flash的操作过程简要为:Flash解锁àFlash擦除àFlash写入àFlash上锁。(对Flash编程的更详细操作参考STM32F10xxx闪存编程手册)

①解锁:

FLASH_Unlock();            //解锁Flash

FLASH_SetLatency(FLASH_Latency_2);            //因为系统时钟为72M所以要设置两个时钟周期的延时

②擦除:

for(i=0;i<240;i++)

{

  if(FLASH_ErasePage(FLASH_ADDR+i*2048) != FLASH_COMPLETE)      //一定要判断是否擦除成功

    return ERROR;

}

说明:FLASH_ErasePage(uint32_t Page_Address)即为Flash擦除操作,按页擦除,每页2KB,Page_Address为页的起始地址,如0x08000000是第一页起始地址,0x08000800为第二页起始地址,这里的操作擦除了0x08008000—0x0807ffff地址空间的Flash。

③写入:

unsigned char buf[1024];            //假设待写入的代码数据

unsigned short temp;            //临时数据

for(i=0;i<512;i++)

{

      temp = (buf[2*i+1]<<8) | buf[2*i];            //2个字节整合为1个半字

      if(FLASH_ProgramHalfWord(ADDR,temp) != FLASH_COMPLETE)      //判断是否写入成功

      {

            Return ERROR;

      }

ADDR +=2;      //地址要加2,因为每次写入的是2个字节(1个半字)

}

说明:因为STM32的Flash写入为双字节(1个半字)写入,FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)函数即为对地址为Address写入1个半字的Data,每次写入完成后地址要加2。

④上锁:

FLASH_Lock();      //Flash 上锁,一个固件库函数即可实现。

 

2、关于中断向量表的偏移设置,对于BootLoader程序只需设置中断向量表的指向在0x08000000处,对于用户程序需要设置中断向量表的指向在0x08008000处即可。

①在BootLoader程序的中断向量表指向设置中应有这么一句:

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);      //设置中断向量表指向

其中NVIC_VectTab_FLASH是个宏定义,的值为0x08000000。

②在用户程序的中断向量表指向设置用应有这么一句:

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000);      //设置中断向量表指向

 

3、确认代码存放的地址空间,在IAR和在Keil中的设置是不同的,网上有在Keil中设置的方法,设立介绍在IAR软件环境下的设置方法。

①在固件库目录\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template\EWARM下找到一个stm32f10x_flash.icf文件,将其复制到工程目录中来,在打开IAR工程,将配置文件添加到工程中,如下图3-2所示

图3-1

②在工程中打开stm32f10x_flash.icf该文件,修改两个参数即可改变代码存放的地址空间,图下图3-2所示。

图3-2

 

4、关于PC指针的强制跳转,想在BootLoader程序中将PC指针跳转到用户代码处,可选择下面的操作

typedef        void (*pFunction)(void);     

pFunction       Jump_To_Application;

uint32_t       JumpAddress;

#define       ApplicationAddress       0x08008000

 

 

if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)       //--------①

{

  JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);                  //--------②

  Jump_To_Application = (pFunction) JumpAddress;                              //--------③

  __set_MSP(*(__IO uint32_t*) ApplicationAddress);                             //--------④

  Jump_To_Application();                                                                 //--------⑤

}

①因为用户程序开始位置(0x08008000处)的前4个字节存放的是堆栈的地址,堆栈地址必定是指向RAM空间的,而STM32的RAM空间起始地址为0x20000000,所以要进行判断。

②程序跳转地址的确认,前面已经说过0x08008004处的4个字节存放的是复位函数的入口地址,该句的意思为获得(ApplicationAddress + 4)地址处的数据,即为获得新的复位函数入口地址。

③令Jump_To_Application这个函数指针指向复位函数入口地址。

④堆栈的初始化,重新设定栈顶代地址,把栈顶地址设置为用户代码指向的栈顶地址。

⑤跳转到新的复位函数。

 

5、通过串口来接收代码数据,就是PC机通过串口将代码数据发送到STM32中去。这里就涉及到两个问题:

①数据怎么得来。

②数据传输的过程需要遵循的协议,什么时候开始,什么时候结束。

解决①:一般我们就将*.hex文件使用JFlash-ARM打开再通过Jlink仿真器烧录到STM32芯片中,但是*.hex文件里边包含的数据不纯粹是代码数据还有一些别的东西,而*.bin文件数据就全部是代码数据。

在IAR软件环境中打开一个用户工程,先设置好中断向量表偏移和代码存放的地址空间后(前面已介绍过这两种方法)。设置工程如下图3-3所示,确认后重新编译工程,在工程的\Debug\Exe目录下会相应生成一个xxx.bin文件,这就是所需要的代码文件。

图3-3

②数据通过串口来传输文件常用的协议有XModem、YModem、ZModem这三种协议,在PC端使用这些协议传输文件只需要PC的超级终端或者终端工具SecureCRT即可,但是在STM32这边的编程会增加一些困难(因为要先去读懂、解析这些协议,在通过编程来实现)。也可选择自己定义一套简单的传输协议,但同样会有一些困难(因为要在PC端进行文件和串口编程)。总之不管通过什么办法都行,只要能将xxx.bin文件数据通过串口全部发送到STM32并且STM32能够全部接收到这些数据并写入Flash即可(我选择后者,自定义传输协议并用VC进行文件和串口编程)。

 

四、结束语

      总的来说STM32的IAP方案实现需要在进行用户程序之前加一段Bootloader程序,BootLoader程序的作用就是:

①什么都不做,直接跳转到用户程序。

②删除原有的用户程序,读取*.bin文件数据并将数据重新写入新的用户程序。

对于用户程序相比普通的编程只需要做三步改动即可

①改变中断向量表。

②改变代码存放的地址空间

③修改生成*.bin文件

  

   使用通过UART的IAP方案并不是很好的选择,这只是IAP方案的一个机制,因为能使用PC机通过串口升级程序,同样能通过Jlink烧写程序,并且自定义的串口通讯协议在没有校CRC校验的情况下不能及时发现数据传输过程发生的错误。这里推荐使用SD卡(或U盘)进行用户程序更新,将*.bin文件复制到SD卡(或U盘)中,STM32再通过读取SD卡(或U盘)的*.bin文件进行用户程序更新,这也避免了STM32与PC笨重的通讯,只需插一个SD卡(或U盘)更显得人性化一些,但需要去弄懂STM32如何与SD卡(或U盘)的通讯。

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

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

相关文章

BM34 判断是不是二叉搜索树

1.题目描述 给定一个二叉树根节点&#xff0c;请你判断这棵树是不是二叉搜索树。 二叉搜索树满足每个节点的左子树上的所有节点均小于当前节点且右子树上的所有节点均大于当前节点。 例&#xff1a; 图1 图2 数据范围&#xff1a;节点数量满足 1≤&#x1d45b;≤104 1≤n≤104…

SpringMVC的底层工作原理?

1.用户发送请求至前端控制器DispatcherServlet. 2.DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器 3.HandlerMapping找到具体的处理器(可以根据 xml 配置、注解进行查找&#xff09;&#xff0c;生成处理器及处理器拦截器(如果有则生成)一并返回给DispatcherSe…

SystemUI默认去掉底部导航栏

一、背景 在Android系统中&#xff0c;SystemUI负责管理系统的状态栏、导航栏等用户界面元素。若要在SystemUI中默认去掉底部导航栏&#xff0c;可以通过以下几种方法实现&#xff1a; 1. 修改布局文件 在Android的SystemUI源代码中&#xff0c;底部导航栏的布局文件通常…

AI赋能下的人体摔倒识别技术:深度解析与应用前景

引言 随着人工智能技术的快速发展&#xff0c;AI赋能的解决方案在各行各业中展现出巨大的潜力。特别是在安全监控和健康护理领域&#xff0c;AI技术的应用不仅提高了效率&#xff0c;还极大地提升了安全性。本文将深入探讨思通数科&#xff08;南京&#xff09;信息技术有限公…

【Matlab】RBF径向基神经网络回归预测算法(附代码)

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/89564332 资源合集&#xff1a; https://download.csdn.net/download/vvoennvv/89564427 目录 【Matlab】BP 神经网络回归预测算法 【Matlab】CNN-LSTM回归预测 卷积神经网络-长短期记忆神经网络组合模型 …

昇思学习打卡-23-生成式/CycleGAN图像风格迁移互换

文章目录 模型介绍网络结构数据集可视化网络的其他细节模型推理 模型介绍 CycleGAN(Cycle Generative Adversarial Network) 即循环对抗生成网络&#xff0c;实现了一种在没有配对示例的情况下学习将图像从源域 X 转换到目标域 Y 的方法。 该模型一个重要应用领域是域迁移(Do…

vue 侧边锚点外圆角

环境&#xff1a;uniapp、vue3、unocss、vant4 效果&#xff1a; 代码 主要是&#xff1a;pointTop 、pointCentent 、pointBottom&#xff0c;这三个样式 html <div v-show"!showPoint" class"fixedLeftDiv"><div><div class"pointT…

XXE:XML外部实体引入

XXE漏洞 如果服务器没有对客户端的xml数据进行限制&#xff0c;且版本较低的情况下&#xff0c;就可能会产生xxe漏洞 漏洞利用流程 1.客户端发送xml文件&#xff0c;其中dtd存在恶意的外部实体引用 2.服务器进行解析 3.服务器返回实体引用内容 危害&#xff1a;任意文件读…

代码:前端与数据库交互的登陆界面

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>登录</title> </head> <body>…

【Linux 14】进程间通信概念

文章目录 &#x1f308; 一、进程间通信的目的&#x1f308; 二、进程间通信的理解&#x1f308; 三、进程间通信的分类 &#x1f308; 一、进程间通信的目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程资源共享&#xff1a;多个进程之间共享同样的资源。通…

Java GC(垃圾回收)机制详解

Java GC&#xff08;垃圾回收&#xff09;机制详解 1、GC触发的条件2、GCRoots的对象类型 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java的世界里&#xff0c;内存管理是自动进行的&#xff0c;其中垃圾回收&#xff08;Garbage Col…

【JAVA多线程】Future,专为异步编程而生

目录 1.Future 2.CompletableFuture 2.1.为什么会有CompletableFuture&#xff1f; 2.2.使用 2.2.1.提交任务获取结果 2.2.2.回调函数 2.2.3.CompletableFuture嵌套问题 1.Future Java中的Future接口代表一个异步计算。其提供了一组规范用来对异步计算任务进行管理控制…

MySQL8的备份方案——增量备份(CentOS)

MySQL8的增量备份 一、安装备份工具二、备份数据三、准备恢复所需的备份数据四、 恢复备份文件 点击跳转全量(完全)备份 点击跳转差异备份 点击跳转压缩备份 一、安装备份工具 官网 下载地址 备份所用工具为percona-xtrabackup 如果下方安装工具的教程失效&#xff0c;请点击…

PACS-医学影像信息管理系统,全影像科室PACS源码,内置包括MPR、CMPR、VR等三维处理功能

PACS系统可以覆盖医院现有放射、CT、MR、核医学、超声、内镜、病理、心电等绝大部分DICOM和非DICOM检查设备&#xff0c;支持从科室级、全院机、集团医院级乃至到区域PACS的平滑扩展&#xff0c;能够与医院HIS、集成平台的有效集成和融合&#xff0c;帮助医院实现了全院医学影像…

Qt Style Sheets-使用样式表自定义 Qt 部件

使用样式表自定义 Qt 部件 在使用样式表时&#xff0c;每个小部件都被视为具有四个同心矩形的框&#xff1a;边距矩形、边框矩形、填充矩形和内容矩形。框模型对此进行了更详细的描述。 盒模型 以下是四个同心矩形在概念上的呈现方式&#xff1a; 边距超出边框。边框绘制在边…

【深入C++】二叉搜索树

文章目录 什么是二叉搜索树二叉搜索树的接口1.查找操作2.插入操作3.中序遍历4.删除操作 所有代码总结 什么是二叉搜索树 二叉搜索树&#xff08;Binary Search Tree, BST&#xff09;是一种特殊的二叉树&#xff0c;其每个节点最多有两个子节点&#xff0c;分别称为左子节点和…

C++实现数组中是否存在递增三元组的巧妙方法【奇思妙想】

C实现数组中是否存在递增三元组的巧妙方法 在解决数组问题时&#xff0c;尤其是涉及到子序列的查找&#xff0c;我们需要考虑时间复杂度和空间复杂度&#xff0c;以确保算法的效率。我们将介绍一种高效的解决方案&#xff0c;详细讲解其思路和实现。 问题描述 给你一个整数数…

Linux环境下dockes使用MongoDB,上传zip文件如何解压并备份恢复到MongoDB数据库中

1、准备 Docker 和 MongoDB 容器 建议主机端口改一下 docker run --name mongodb -d -p 27018:27017 mongo 2. 创建一个工作目录并将 zip 文件上传到dockers容器中 docker cp data.zip mongodb:/data.zip 3. 在 MongoDB 容器中解压 zip 文件&#xff08;也可以解压完再复制…

C++仿函数

在C中&#xff0c;我们经常需要对类中的元素进行比较&#xff0c;例如在排序、查找等操作中。为了使类更加灵活&#xff0c;我们可以通过自定义比较函数来实现不同的比较方式。在本文中&#xff0c;我们将探讨如何在类中使用仿函数和 Lambda 表达式来定义自定义比较函数。 1. …

《C++并发编程实战》笔记(三)

三、线程间共享数据的保护 多个线程同时访问修改共享的数据时&#xff0c;如果不加以控制&#xff0c;可能会造成未知的错误&#xff0c;为了解决这个问题&#xff0c;需要采取特殊的手段保证数据在各个线程间可以被正常使用。 这里介绍使用互斥量保护数据的方法。 3.1 使用互…