控制/MCU
在STM32应用中经常会需要基于用户程序的做代码更新、升级,即In Applicaton Programming,简称IAP。这个过程往往需要程序从不同执行区做跳转,最常见的自然是从启动区跳往应用程序区,即从BOOT区跳往APP区。这个跳转过程中,经常有人遇到跳转失败的问题。虽是个老话题,但关于这方面的问题咨询可谓经久不息。我这里以STM32G4系列芯片及开发板做下相关实验,分享些应用提醒,以供参考。
为了保证在跳转过程不出异常,主要注意两点:
第一点,即将跳转到程序区的内存地址、中断矢量表地址。在STM32库例程里,中断矢量表地址的修改一般采用基地址加偏移量的代码写法。
第二点,也是非常重要的一点。在做跳转前,做好准备工作。执行跳转前,当前程序区不能存在尚未处理的中断请求,要对开启过的中断使能全面地逐个清零关闭,切不可简单地只是调用一个所谓关总中断函数的做法,即调用 __disable_irq()函数。该函数只是临时关闭中断响应,不会阻止中断事件的发生及相应中断标志的生成。这个做法极不可取,隐患很多。
一般来讲,自己开启了哪些中断大致是清楚的。对于STM32用户来讲,如果基于CubeMx创建工程,SYSTICK定时器中断默认开启,担当Cube库函数的滴答时基,很多延时相关都使用到它。我们在做跳转前,记得将其计数器停掉或关闭它的中断请求能力。使用下面三句的任意一句都可以令其丧失中断请求能力【下面是基于STM32库函数的代码写法】:
SysTick->CTRL&=~SysTick_CTRL_TICKINT_Msk;
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->LOAD = 0 ;
我这里以STM32G474芯片为蓝本,划分三个区,分别称之为BOOT区、APP1区、APP2区。三个区代码内容基本一样,用到外设完全一样,主要是UART2、TIM1。基于TIM1周期性事件通过USART向串口终端提示当前程序运行区间。
程序在3个区间按下面示意图来回跳转执行,永不停息。
经过简单的编程即可达到上面目的。下图是串口助手显示的输出结果的部分截图。
这里实际上有3个工程,每个工程做跳转时跳转地址不一样。这里不妨以从BOOT区跳往AAP1区为例,看看跳转前做的哪些准备工作。
跳转前的准备工作如果像上面那样,多数情况下跳转是没有啥问题的。不过,这还不能保证跳转总成功,是否成功跟具体应用场景有关。因此,我们强烈建议针对使用过的外设做复位操作,这就比较保险了。毕竟前面的准备工作,侧重跳转过程中避免产生中断事件或停止外设的运行,但不能保证跳转过程中不会出现一些不确定的状态。所以,建议跳转前对开启过的外设做复位,让他们彻底静默下来,待到新的程序执行区根据实际情况再行初始化。
从main()函数开始的地方不难看到,目前开启的外设主要是下面几个。
我们在跳转前的准备工作里加上针对上面外设复位的操作,见下面橙色方框内代码,分别针对TIM1、USART2和相关GPIO外设做了强制复位。
显然,如果之前开启的外设较多的话,这样一个个添加强制初始化代码也挺啰嗦的。这里再推荐一个等效做法。在STM32 HAL库有一个专门用来对所有外设进行复位的函数,它就是HAL_Deinit()。下面是其函数体内的具体内容。
这些复位操作将让相应总线上的外设得以强制复位。这样一来,我们就可以将前面跳转前的那一堆逐个针对ST外设的操作代码改成HAL_DeInit()这一句即可。经实际验证也是可行的。参考下面代码的写法,代码一下变得精简、清爽很多。
最后补充两点,上面实验代码中从APP2区跳回BOOT区,通过调用系统复位函数也是可行的。另外,上面内容不完全适用于STM32F0系列。
好,关于程序在不同代码区跳转执行的话题就聊到这里,下次再聊!
审核编辑:黄飞
全部0条评论
快来发表一下你的评论吧 !