关于STM32的IAP与APP互相跳转
关于STM32的IAP与APP互相跳转
之前做了一个不带系统的IAP与APP互相跳转,在网上找了资料后,很顺畅就完成了,后来在IAR集成开发环境下,IAP无系统,APP用UCOS系统做互相跳转出现了很多问题。现将IAP学习过程和实际遇到问题总结一下。
首先说一下什么是IAP。IAP(In Application Programming)即在应用编程,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。通常实现IAP功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。
以上内容摘自原子的开发指南。说的通俗一点,要做IAP功能(也可以说成是远程升级功能),需要有两段程序一个是IAP程序(也可以称为BootLoader),另一个是APP程序(主应用程序)。通过USB、串口、CAN等通讯方式向STM32发送要升级的程序文件数据(按自定的协议),IAP程序中将接收到的数据写到APP程序的地址实现将APP程序的升级。这是大致的流程。
此文档只做互相跳转的总结不包含接收数据、FLASH写入等操作。
说到IAP升级不得不说两个图(图片引自原子的开发指南)
第一个是正常运行时的流程图
STM32的FLASH地址起始于0x08000000,程序文件就从此地址开始写入。此外STM32内部通过“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,而“中断向量表”的起始地址是0x08000004,当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。
根据上图分析启动和运行过程,
① STM32在复位后,先从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,
② 在复位中断服务程序执行完之后,会跳转到的main函数(如使用KEIL MDK调试时一下载进程序,会发现需要运行几次下一步才会跳转到main函数的位置)
③ main函数一般都是超循环体(while(1)死循环),在main函数执行过程中,如果收到中断请求(发生重中断),此时STM32强制将PC指针指回中断向量表处
④ 根据中断源进入相应的中断服务程序,
⑤ 在执行完中断服务程序以后,程序再次返回main函数执行。
第二个是加入IAP功能后的流程图
根据上图分析加入IAP后的起动和运行过程
① STM32复位后,还是从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAP的main函数,如将IAP看作是一个APP的话,那么此部分和正常起动是一样的,此步相当于正常运行的①和②。
② 在执行完IAP以后(如需要将新的APP代码写入STM32的FLASH或没有更新直接跳转。APP的复位中断向量起始地址为0X08000004+N+M),跳转至APP的复位向量表
③ 取出APP的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至APP的main函数
④ 同样main函数为一个超循环,并且注意到此时STM32的FLASH,在不同位置上,共有两个中断向量表。在main函数执行过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0X08000004中断向量表处,而不是APP程序的中断向量表。
⑤ 程序再根据我们设置的中断向量表偏移量,跳转到对应中断源的APP中断服务程序中,
⑥ 在执行完中断服务程序后,程序返回main函数继续运行。
注意:IAP和APP跳转过程中,是通过PC指针定位进行跳转,所有的寄存器都保持原有状态,跳转过程中并不是做了复位。
下面是IAP和APP互相跳转程序
/******************************************************************************
IAP 跳转到 APP 之间跳转函数
******************************************************************************/
void IAP_APP_Jump (void)
{
INT32U SpInitVal; //要跳转到程序的SP初值.
INT32U JumpAddr; //要跳转到程序的地址.即,要跳转到程序的入口
void (*pFun)(void); //定义一个函数指针.用于指向APP程序入口
RCC_DeInit(); //关闭外设
NVIC_DeInit (); //恢复NVIC为复位状态.使中断不再发生.
SpInitVal = *(INT32U *)IAP_ADDR;//IAP_ADDR IAP的栈顶地址(0x08000000)
//跳转到APP时 APP_ADDR AAP的栈顶地址(如:0x08003800)
JumpAddr = *(INT32U *)( IAP_ADDR + 4); //设置复位中断向量(如上面流程分析)
__set_MSP(SpInitVal); //设置SP.,堆栈栈顶地址
pFun = (void (*)(void))JumpAddr; //生成跳转函数.将复位中断向量地址做为函数指针
(*pFun) (); //执行函数,实现跳转.不再返回.
}
在IAP和APP中都需要进行中断向量表的设置,如正常程序中设置一样。
再介绍一下在集成开发环境下APP程序起始地址设置
Keil MDK环境下点击Options for TargetàTarget选项卡
在IAP和APP均为无系统时,上面程序能够很好实现互相跳转。但在IAP无系统,而APP使用UCOS系统时出现了较多问题(IAR环境)。如下一一记录各种问题的解决过程。
1、在程序正常设置APP的程序和向量表时后,在IAP直接调用跳转APP时,程序没有正常跳转到APP中,跳入未区域,具体情况现已回忆不清。但分析是因为跳转后没有找到中断向量表,从而没有正常进入APP的main()函数。
但在BSP_Init()函数中确实有对复位中断向量进行了设置。
NVIC_SetVectorTable(NVIC_VectTab_FLASH,(APP1_ADDR+4));
说明一下,这里将中断向量表设置为 (APP1_ADDR+4)(偏移4个字节即为复位中断向量),而有的程序中设置为APP1_ADDR。实际上两种设置是一样的,在
NVIC_SetVectorTable()函数中执行下句
SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80); 所以可以看出是否偏移4字节是一样的。
经测试发现需要在调用OSStart()的前一句重新设置复位中断向量才能正确设置。
2、 经上一步修改后,IAP可以跳转到APP中,APP也能跳转到IAP中,但再重IAP就不能再跳回APP了。修改跳转函数
/***********************************************************************
函数功能:跳转到IAP函数
***********************************************************************/
void JumpToIAP(void)
{
INT32U IapSpInitVal;
INT32U IapJumpAddr;
void (*pIapFun)(void);
RCC_DeInit();
NVIC_DeInit();
__disable_irq(); //关中断()
// APP如跳转前关中断,跳转到IAP后,IAP初始化后要打开中断
IapSpInitVal = *(INT32U *)IAP_ADDR;
IapJumpAddr = *(INT32U *)(IAP_ADDR + 4);
__set_CONTROL(0);
//进入用户级线程模式 进入软中断后才可以回到特权级线程模式
//APP如使用系统如ucos必须要有此过程否则跳到IAP后,无法再次跳到APP
__set_MSP (IapSpInitVal);
pIapFun = (void (*)(void))IapJumpAddr;
(*pIapFun) ();
}
/***********************************************************************
IAP 跳转到 APP 函数
***********************************************************************/
void Jumpto_APP(void)
{
INT32U IapSpInitVal;
INT32U IapJumpAddr;
void (*pIapFun)(void);
RCC_DeInit();//关闭外设
NVIC_DeInit();
__disable_irq(); //关中断()如IAP关中断 APP如果没用UCOS系统,APP
//初始化后要开中断,用UCOS后,在起动任务后会开中断
IapSpInitVal = *(INT32U *)APP1_ADDR;
IapJumpAddr = *(INT32U *)(APP1_ADDR + 4);
if((IapSpInitVal & 0x2FFE0000)==0x20000000)//检查栈顶地址是否合法.
{
__set_MSP (IapSpInitVal);
pIapFun = (void (*)(void))IapJumpAddr;
(*pIapFun) ();
}
}
如此调整后可以正常互相跳转。
3、 将IAP增加3秒延时后再执行跳转APP函数(使用定时器update中断(更新中断)定时),但一执行跳转就进入APP的HardFault_Handler()。
因为在IAP有初始化定时器并始能了中断,而在APP中没有初始化定时器和初始化定时器的中断向量表,所以在APP中定时器仍然工作,产生中断后没有相应中断向量表就起出现错误。(自己理解,总之不管什么原因这样操作是错误的。)在执行跳转APP函数前将定时器更新中断失能,并且将定时器失能可解决!
/***************************************************************************
更能:关闭计时器
输入参数:num 定时器号
***************************************************************************/
void CloseTim(u8 num)
{
u32 tim = 0;
tim = (APB1PERIPH_BASE + (num-2)*0x0400); //计算定时器地址
TIM_ITConfig((TIM_TypeDef*)tim,TIM_IT_Update,DISABLE);
TIM_Cmd((TIM_TypeDef*)tim, DISABLE);
}
在用到STM32定时器的更新中断时,发现有些情形下只要开启定时器就立即进入一次中断。准确说,只要使能更新中断允许位就立即响应一次更新中断【当然前提是相关NVIC也已经配置好】。换言之,只要使能了相关定时器更新中断,不管你定时间隔多长甚至不在乎你是否启动了相关定时器,它都会立即进入一次定时器更新中断服务程序。这个问题比较容易忽视,有些情况下也无关紧要,但有些情况可能会给应用带来困扰。
解决办法是在使能定时器更新中断前先清除更新中断标志位。
在初始化定时器时使用如下代码:
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
TIM_ITConfig(TIM4,TIM_IT_Update, ENABLE);
TIM_Cmd(TIM4, ENABLE); //开启时钟
记录自己的工作中遇到的问题,一是方便自己查找学习,二是方便有初学者遇到相似问题,提供解决思路。以前工作或学习中遇到问题没有及时记录,后来再总结时,对当时的现象回忆不清。以后要及时截图记录,及时总结!
posted on 2016-07-24 10:58 石中玉smulngy 阅读(26178) 评论(3) 编辑 收藏
评论
#1楼
回复引用
SpInitVal = *(INT32U *)IAP_ADDR;//IAP_ADDR IAP的栈顶地址(0x08000000)
IAP_ADDR IAP的栈顶地址应该是(0x08010000)
#2楼[楼主]
回复引用
张来福和李富贵
iap的栈顶地址是存在flash0X08000000的地址(原文描述不准确)。0X08010000和0X08003800都是APP的设置,只是两个工程的截图(设置不同)。iap_app_jump的的注释也不对,看了一下这应该是app跳到iap的函数。有时间更正一下。
#3楼
回复引用
在app代码中如果发生中断,就会先取出向量表偏移量寄存器,然后再根据中断号计算得到中断服务函数的入口,然后跳转。
很多博客都是这样写的,都是因为原子这样写的。。希望博主改一改
关于STM32的IAP与APP互相跳转的更多相关文章
- 【转】关于IAP与APP互相跳转的实现
关于IAP与APP互相跳转的实现 首先,在您动手做这个实验之前,先要弄清除咱俩的软硬件有什么不同: 1. 我的CPU是STM32F103ZET6,里面有512K的FLASH,您的CPU如果是其它类型, ...
- STM32的IAP实现
STM32的IAP实现 2014年07月28日 16:31:06 Stylesen 阅读数:556 IAP,全称是“In-Application Programming”,中文解释为“在程序中编程 ...
- STM32的IAP方案
from: http://bbs.eeworld.com.cn/thread-294115-1-1.html 几乎所有的同类书籍都介绍综合性的应用示例如“万年历 + 温度显示 + 闹钟响铃 + 计 ...
- STM32串口IAP实验笔记
STM32的IAP功能确实方便,以前对此如何实现有所了解,但是一直没去测试,这两天来练了下,可谓困难重重,搞了两天问题也一一解决,下面做些简要的笔记 IAP就是在线应用编程,方便程序升级,可以不用打开 ...
- ios&h5混合开发项目仿app页面跳转优化
前言:本人原本是ios开发工程师,但由于现今H5的兴起,行内刮起了一阵混合开发的风气,趁着这股劲,我也学了前端开发,不说研究的多深,但也能胜任日常的开发工作.长话短说,现今的混合开发应该还处于摸索阶段 ...
- H5调取APP或跳转至下载
来源: 最近在配合移动端做几个详情页h5分享页面,需要调取App并跳转至app详情页, 如果没有安装App,需要判断引导至下载页面. 参考文档: https://juejin.im/post/5b7e ...
- 通过手机浏览器打开APP或者跳转到下载页面.md
目录 通过手机浏览器打开APP或者跳转到下载页面 添加 schemes 网页设置 参考链接 通过手机浏览器打开APP或者跳转到下载页面 以下仅展示最简单的例子及关键代码 由于硬件条件有限,仅测试了 A ...
- ios两个app之间跳转,传值的实现
两个APP之间的跳转是通过[[UIApplication sharedApplication] openURL:url]这种方式来实现的. 1.首先设置第一个APP的url地址 2.接着设置第二个AP ...
- Vue仿微信app页面跳转动画
10:14:11独立开发者在开发移动端产品时,为了更高效,通常会使用Web技术来开发移动端项目,可以同时适配Android.iOS.H5,稍加改动还可适配微信小程序. 在使用Vue.js开发移动端页面 ...
随机推荐
- Go语言 之TCP聊天室
服务端流程图如下: package main import ( "fmt" "net" ) // 客户端结构体 type Client struct { //用 ...
- [题解] [CF1037D] Valid BFS?
题面 题解 一个是模拟BFS的过程 还有一个是可以根据给出的BFS序构树, 再看两棵树是否相同 判断相同的话, 以同一个点为根, 看两棵树中1−
- THREE.js(一)
//创建场景 var scene = new THREE.Scene(); //透视摄像机(视野角度,长宽比,远剪切面,进剪切面,) var camera = new THREE.Perspectiv ...
- selenium 隐式等待报错 value must be a non-negative integer
笔者运行代码使用selenium的隐式等待时出现报错: from selenium import webdriver # 从selenium导入webdriver import time driver ...
- treeview所有节点递归解法及注意!!!!!!!!!!!!!!!!!
好吧 我把所有之前写的都删了,只为这一句话“所有变量切记小心在递归函数内部初始化”,包括:布尔,变量i,等等.至于为什么....递归就是调用自己,你初始化以后的变量,等再次调用的时候又回来了 bool ...
- apt如何列出所有已经安装的软件包
apt如何列出所有已经安装的软件包 转 https://www.helplib.com/ubuntu/article_155294 问题: 我想将所有已安装软件包的列表输出到文本文件中,以便我可以查看 ...
- Build Telemetry for Distributed Services之OpenTracing指导:C#
官网链接:https://opentracing.io/guides/ 官方微博:https://medium.com/opentracing Welcome to the OpenTracing G ...
- delphi中Treeview的使用介绍
今天重点学习了TreeView的使用方法,基本的已经写了,现在主要想说的是如何显示数据库的资料,今天只是做了个较简单的例子,一个父节点下显示数据库中某个field的值.代码如下: procedure ...
- RabbitMQ运转流程
生产者发送消息的过程 生产者连接到RabbitMQ Broker(相当于是一个RabbitMQ服务器),建立一个连接(Connection),开启一个信道(Channel). 生产者声明一个交换器(E ...
- 为Django添加图片验证码
可直接复制到Django项目中使用 # author:sunshine from django.http import HttpResponse from PIL import Image, Imag ...