IAP ( In Application Programming )功能为产品软件升级提供了一个方便快捷的接口。用户可以通过串口、USB、CAN总线[1]而无需使用编程器即可实现产品的软件更新,甚至可以通过以太网[2]或者无线网络[3]实现产品软件的远程升级,大大方便了产品的功能迭代,提升了产品的易用性。
Bootloader(以下简称Boot)俗称引导程序,是实现IAP功能的核心。Application(简称App)即用户应用程序,负责实现产品功能。Boot和App这两个独立的程序在同一芯片中运行,有各自的中断向量表,但中断号相同。在由Boot进入到App后,如何使App下的中断能正确响应?这是所有IAP都需要面对的问题,而解决问题的关键在于实现中断向量的重定位。不同MCU根据自身硬件特性不同,实现中断向量重定位的方法存在着重大区别。
1Cortex-M3 架构MCU中断向量重定位
Nested Vectored Interrupt Controller(NVIC)即嵌套向量中断控制器,是Cortex-M内核的一部分,它管理和处理所有的异常和中断。Cortex-M3中的NVIC,在地址0xE000 ED08处是一个Vector Table Offset Register(VTOR)即中断向量表偏移量寄存器,通过修改它的值就能重新定位向量表[4]。
进入App运行后,只要在使能中断功能前设置VTOR寄存器值为App起始地址即可实现中断向量表的重定位。参考文献[5]分析了Cortex-M3 单片机中断向量重定位的执行过程。Cortex-M4内核中也有VTOR寄存器,因而在这些内核MCU上实现中断向量的重定位十分便捷、高效。
2Cortex-M0 架构MCU中断向量重定位
适中的性能、极低的能耗、低廉的价格使得Cortex-M0架构MCU应用非常广泛,尤其是在物联网传感器、电动工具、电子测量、家电行业[6]。因为Cortex-M0中的NVIC没有VTOR寄存器,所以此类型的MCU中断向量重定位变得不容易。
2.1STM32F030中断向量重定位
STM32F030基于Cortex-M0架构,厂商为该系列的MCU赋予了一个特性:启动地址支持重映射。通过设置芯片Boot引脚电平,使得程序可从内部Flash或内部RAM启动。芯片的SYSCFG_CFGR1寄存器也保存了设置状态,通过修改这个寄存器的值,即可重新引导程序[7]。
基于地址可以重新映射的特性,在App使能中断功能前,将中断向量表复制到内部RAM的起始地址,再设置SYSCFG_CFGR1寄存器,使其从RAM中启动,即可以实现中断向量的重定位。
2.2普通Cortex-M0架构MCU实现IAP的困境
并非所有Cortex-M0内核的单片机都支持地址重映射,因为这并不是内核功能的一部分[8]。市场上存在大量使用Cortex-M0内核的专用MCU,如某些电机控制的专用MCU。它们不像STM32F030那样具备地址重新映射的特性,因此不能使用2.1中的方法实现中断向量的重定位。
因此,实现中断向量的重定位成为此类基于Cortex-M0架构MCU实现IAP功能的最大障碍。相关研究资料较少,参考文献[9]给出了一种基于RAM的中断跳转方法,不过效率有待提升。
3中断向量重定位的通用方法
本文给出了实现中断向量重定位的一种通用方法。该方法不依赖于MCU自身硬件特性,而是基于纯软件实现。同时结合STM32F030和Keil开发环境,详细介绍了该方法的原理,给出了相关代码。
3.1梯子函数
给每一个中断向量增加这样一段程序:该程序负责获取App中对应中断服务函数的入口地址,然后跳转到该地址运行,从而运行App的中断服务程序。这种功能的程序像是从Boot中断向量通向App中断服务函数的梯子,因此称它为“梯子函数”。Boot中断向量表中的每一个中断向量保存的是对应梯子函数的入口地址。
3.2中断向量重定位过程
图1给出了基于该方法进行中断向量重定位的跳转流程图。App运行期间产生中断,仍会从Boot中断向量表取得相应的中断向量。不过此时实际获取的是梯子函数的入口地址,然后借助梯子函数实现向App中断服务程序跳转的目标。
图1中断向量表重定位通用方法
以RTC中断为例,当App运行时产生了该中断,则会从Boot的中断向量表找到RTC中断的梯子函数入口地址并跳转到该地址运行,如箭头①和②所示。RTC中断的梯子函数则会从App中的RTC中断向量加载对应的RTC中断服务程序的入口地址,如箭头③所示。最后跳转到该地址,运行App的RTC中断服务程序,如箭头④所示。
3.3梯子函数的实现
梯子函数是实现从Boot中断向量到App中断服务程序跳转的关键。程序采用汇编代码实现,一个中断向量对应着一个梯子函数。部分梯子函数编写的示例如下:
同样以RTC_IRQHandler为例,0x0800 2000为App的起始地址,对应图1中APP_BASE的值。0x48为RTC中断向量的偏移地址。Line7将RTC中断向量偏移地址加载到r0寄存器。Line8将App的RTC中断处理函数地址加载到r1寄存器。Line9通过bx指令实现跳转到App的RTC_IRQHandler中断服务程序入口处的目标。
通过在Boot中为各个中断向量设置梯子函数,借助梯子函数的桥梁作用实现中断向量表的重定位。基于该方法在STM32F030上进行了IAP功能验证,App的中断功能运行正常,验证了这一方法的可行性。
4 中断向量重定位改进方法1
4.1存在的问题
一个梯子函数需要两条ldr指令和一条bx指令,分别需要2个和3个指令周期[10],因此一个梯子函数共需要7个指令周期。此外Flash的时钟频率一般比CPU时钟低很多。如STM32F030的CPU时钟频率高于24 MHz时,访问Flash需要插入一个等待周期[11]。
综上,进入App中断服务函数运行前,将会引入较大的时间开销。当要求中断能快速响应或者存在诸如ADC采样之类的高频中断,这种中断向量重定位方法会引发系统性能的明显下降,为此需要对中断向量重定位的方法进行改进。
4.2改进原理
改进分为两方面:一是减少梯子函数所需的指令,二是将梯子函数移到内部RAM中运行。
Boot与App中各有一份梯子函数用于中断向量的重定位。Boot中的梯子函数用于对无速度要求的中断向量重定位,其工作原理和执行效果与前文相同。App中的梯子函数为需要降低中断响应延迟,提高中断处理性能的中断重定位。改进方法1中断向量表重定位执行过程如图2所示。
图2改进方法1中断向量表重定位执行过程
仍以RTC_IRQHandler中断为例,此时该中断向量的梯子函数RTC_IRQHandler_App位于RAM中,且地址固定为0x2000 0001。同时Boot中对应RTC_IRQHandler中断向量的单元保存了该地址。当App运行过程中发生了RTC中断时,MCU会从Boot中的RTC_IRQHandler中断向量取得0x2000 0001地址,并跳转到App下RTC中断的梯子函数入口运行,对应箭头①和②。梯子函数会从App的中断向量表中取得RTC_IRQHandler中断服务函数地址,如箭头③所示,最终跳转到App的RTC中断服务程序入口处运行,如箭头④所示。
4.3App中的梯子函数
App所使用的梯子函数示例如下:
其中,Line1定义了名为“ladder_app”的代码段。Lin6定义了RTC_IRQHandler_App函数,共两条汇编指令。首先将App下的RTC_IRQHandler中断服务函数地址加载到R0寄存器中,接着跳转到该地址运行。相比于改进之前,此时只需要LDR和BX两条指令,总计只需要5个指令周期。同时该段代码在RAM中运行,不受Flash等待周期限制。
4.4App的分散加载设置
通过分散加载可使得APP中的梯子函数始终位于RAM固定地址,而与具体的应用程序无关。STM32F030在Keil下的分散加载文件如下:
其中Line10将“ladder_app”代码段加载到0x2000 0000地址,长度为128字节。若App中梯子函数较多,则预留空间需要相应增大,反之亦然。
梯子函数的入口地址可以从工程编译成功后的map文件获取。如图3所示,RTC_IRQHandler_App函数的入口地址为0x2000 0001,这就是前文Boot中RTC_IRQHandler向量保存的地址来源。
图3 梯子函数与中断服务函数入口地址
5中断向量重定位改进方法2
前文讨论了将梯子函数搬移到RAM中运行,从而大幅降低进入用户中断服务程序的时间开销的改进方法。本节讨论再改进的方法,进一步压缩梯子函数的时间开销,提高进入中断程序的效率。
5.1梯子函数的改进
经过改进的梯子函数代码如下:
可以看出,只需要通过一条B指令,就可以跳转到中断服务函数入口。该指令执行只需要3个时钟周期,进一步降低了梯子函数运行所需要的时间开销。
B指令是相对跳转指令,用于无条件跳转,其支持跳转地址范围为PC-2046~PC+2046[4]。因此中断服务函数必须也位于内部RAM中,且距对应的梯子函数的跳转指令地址必须在2046字节之内。这一条件是容易满足的,毕竟不是所有的中断服务程序都需要这么做,而只是个别时间敏感、高频的中断需要按照这种方法处理。
5.2中断服务函数约束条件
为实现中断处理函数的地址约束,在编写相关中断处理函数时,需要添加约束属性。通过为函数设置section属性将中断服务程序放入自定义名称为“ramfunc_isr”的输入段。除此之外,与普通的中断处理函数无其他差别。如在Keil环境下,对于RTC中断处理函数写法如下:
5.3分散加载文件
改进方法2的分散加载文件设置如下:
与改进方法1的分散加载文件相比,多了Line12的内容,即将“ramfunc_isr”段的内容从地址0x2000 0080开始放置。
图4给出了map文件的部分内容,可以看出,梯子函数RTC_IRQHandler_App入口地址为0x2000 0001,Timer3_IRQHandler_App入口地址为0x2000 0003。此时App的一个中断梯子函数占用存储空间大小仅为2字节。实际的中断处理函数的入口地址分别为0x200 00081和0x2000 008d,均位于RAM中,紧接在梯子函数之后,相对地址也远小于2046 B,满足该改进方法的使用要求。
图4 梯子函数与中断服务函数入口地址
6结 语
表1对文中提出的中断向量重定位的通用方法及其两种改进方法进行了对比,总结了3种方法的各自特点,实际上3三种方法可以混合使用。比如将高频的ADC采样中断使用改进方法2进行ADC中断向量的重定位,此时ADC中断额外的时间开销最小,同时由于该中断服务程序在RAM中运行,性能也将大幅提高。将中频的定时器中断使用改进方法1进行中断向量重定位,其他普通的中断向量采样通用方法进行重定位。
表1 中断向量重定位3种方法对比
基于本文提出的中断向量表重定位方法,在STM32F030和Keil开发环境下进行了验证,结果正确。此外,在其他品牌的基于Cortex-M0内核的MCU上进行了验证,也取得了成功。目前基于该方法实现的IAP功能已经在产品上批量使用,效果较好。
文中提出的中断向量重定位的方法虽然是以Cortex-M0内核的MCU为对象进行讨论和测试的,但是其不依赖于硬件的特殊性,因此也可以在其他内核的MCU上进行推广应用。
审核编辑:汤梓红
-
mcu
+关注
关注
146文章
17148浏览量
351193 -
控制器
+关注
关注
112文章
16361浏览量
178039 -
嵌入式
+关注
关注
5082文章
19126浏览量
305181 -
中断
+关注
关注
5文章
898浏览量
41495 -
Cortex-M0
+关注
关注
4文章
124浏览量
38692
原文标题:Cortex-M0中断向量重定位的高效方法
文章出处:【微信号:麦克泰技术,微信公众号:麦克泰技术】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论