无中断向量重定位单片机中实现IAP和APP中断的方法

0 引言

在实际的生产工作中,从经济和实用性方面考虑,选择嵌入式系统MCU控制器时常常会使用到一些性能相对较弱,功能配置也不太强大的单片机,这类单片机有时候仅仅够用,无法从根本上满足多元化的需求。如果需要更多的功能就需要选择更高端的单片机,同时也意味着成本的增加。本文在项目过程中选用的MCU即为这种类型。在产品的整个生命周期中,需要对应用程序进行及时的升级换代,这就用到了IAP功能[1-2],而该款MCU不具备硬件级的中断向量重定位功能,因此只能另辟蹊径,以软件的方式解决该问题。

1 IAP系统介绍

在传统的单片机应用开发中,程序员将写好的代码通过IDE编译链接后生成可执行的二进制文件下载到单片机Flash中即可[3],功能的修改或者错误的修正需要程序员在完成代码修改后,再次通过专用调试器下载到单片机中才能实现。在简单小批量产品功能的实现中,这已经可以满足要求。如果是已经量产的产品,要想再修改功能几乎不可能。但是面对越来越复杂的应用环境,越来越多的应用要求,一次性调试好代码并下载运行的情况已经变得越来越难,而且随着代码量的增多,隐藏的BUG也越不容易被发现,需要在后期的应用中不断完善。

IAP(In Application Programming)功能即是为了应对这种情况应运而生的。IAP是一段完整的程序,它与应用程序(APP,即具体实现功能的单片机代码)是相互独立和分开的两部分,它的功能是在产品全生命周期中随时根据需要对应用程序部分代码进行擦除和烧写,目的是为了在产品发布后可以方便地通过预留的通信接口对产品中的功能进行更新升级[4]

IAPAPP是同时存在于单片机上的两个完整的代码段。IAP实现对APP段代码的擦除和重新烧写,APP则负责实现具体的功能任务,当需要时跳转至IAP进行程序更新。它们分别烧写在单片机Flash的两个区域。对于本文所使用的华芯微特SWM240D8U7,它拥有64 KB的片上Flash(0x0——0xFFFF),就可以划分16 KBIAP(0x0——0x3FFF),余下48 KB留给APP(0x4000——0xFFFF)。需要特别注意的是IAP代码和APP代码编译后的大小不能超过所分配的空间大小。

2 IAPAPP程序运行流程

本文使用的SWM240单片机在上电后固定地从Flash地址0处开始执行代码[5],因本文将IAP程序放于此处,因此首先运行IAP程序,在IAP程序中通过检查预先设置的标志信息来确定APP代码段是否正常,若符合要求就跳转到APP段开始执行,否则进入等待升级烧写状态。

ARMCortex M0单片机将代码的最初几十个字作为中断向量入口,该区域存放的是对应的中断处理程序代码所在的开始位置。中断发生时由硬件将PC指针拉到对应的中断入口处,取得地址后跳转到中断处理程序,完成完整的中断处理过程[6]。在IAPAPP共存的理想情况下,如果在APP中发生中断,中断指针应该指向APP入口处的中断向量;同样,如果在IAP中发生中断,中断指针就应该指向IAP入口处的中断向量。可实际情况是,Cortex M0系列没有像其他M3/M4/M0+系列核心所具备的中断矢量表重定位寄存器[7],其中断发生时会始终会指向从0x0开始的某入口向量处,即本文存放IAP程序的向量位置。那么当APP中发生中断时,取得的是IAP中的处理程序地址如图1所示。

1 IAP启动流程及中断流程

3 解决思路

由于每次中断发生时指针都指向从IAP开始的入口地址[8],因此可以考虑将IAP的所有可屏蔽中断(或者只是用到的中断)程序都写成一个跳转程序,根据当前所处的位置来取得不同的跳转地址,这在单片机的启动文件中可以通过汇编语言来实现。跳转地址的取得可以有多种方法。这里通过将向量地址拷贝到RAM指定位置来实现。在IAP进程时内存存储的是IAP中断向量地址,当跳转到APP时,在APP中将该内存地址存储内容改写为APP的中断向量地址。同样当再次跳转回IAP时也会执行同样的操作。这样无论是在哪个运行空间,虽然中断都是跳回IAP向量位置,但是获取的中断地址却是与运行空间对应,如图2所示。

2 修改后的中断处理流程

4 实现步骤

首先要实现的是将原IAP入口处的中断函数改写为跳转函数。在ARM的启动文件中定义了与硬件相对应的中断函数名称,如图3所示。通过修改启动文件中的函数内容,使用LDRBX两条指令来实现取值和跳转。

3 启动文件定义的中断函数名

用户如果在自己的代码空间中实现了该函数,并且在启动文件中EXPORT了该函数,中断后就会跳转到用户函数中。可以修改为自定义的跳转指令。以GPIOA0外部中断函数为例,图4显示的是未修改前在启动文件中的代码段,它的作用是当发生该中断时,使程序跳转到GPIOA0_Handler函数去执行(函数名其实就是一个地址)。图5显示的是修改以后的代码段,它的作用是当发生中断时,使程序跳转到GPIOA0_ISR_FUN_ADD所指的地址去执行。这里__GPIOA0_ISR_FUN_ADD是强行指定的RAM地址,该地址的内容根据是在IAP还是在APP有所不同,如图6所示。

4 修改前的中断函数

5 修改后的中断函数

6 指定存储中断地址的内存位置

其次,在完成跳转函数后,需要分别在IAPAPP代码段将自己空间的中断函数地址填入对应的RAM位置。做法是在代码中定义全局变量,并将变量指定在RAM的固定位置,实现代码如下:

        uint32_t VectorTable[4] __attribute__((at(0x20000000)));

此处只使用了4个中断并将该数组固定在RAM起始位置。最后,需要分别在IAPAPP代码初始化阶段,将自己运行空间的处理函数地址填入对应的数组中即可。实现代码如下:

        VectorTable[0] = (uint32_t)GPIOA0_Handler;

        VectorTable[1] = (uint32_t)TIMR0_Handler;

        VectorTable[2] = (uint32_t)GPIOC0_Handler;

        VectorTable[3] = (uint32_t)TIMR1_Handler;

需要说明的是,在编写代码时,由于IAPAPP是两个独立的工程,分别都有自己的启动文件,而单品机发生中断时起作用的是IAP中的启动文件,因此只需要将IAP中的启动文件进行修改即可,APP中的不做改动。另外,在IAPAPP初始化过程中,文中所述获取中断处理函数地址的方法,APP中还可以通过直接拷贝对应中断向量表中的内容来得到,但是在IAP中必须按照上面的方法,如果拷贝中断向量表的内容,得到的就是启动代码中XXX_Handler PROC的地址,这将导致程序进入死循环。

经过上面的设置后,分别将IAPAPP烧入对应的Flash空间,就可以实现两个空间的中断。如果用户有多个运行空间,也可以按照此方法来进行处理。

5 运行与测试

按照本文所述,在某国产SWM240D8U7单片机上分别建立了基于CAN通信的IAPAPP程序。程序上电后首先按照硬件默认设置从0x0处运行代码,该处代码完成MCU的启动,中断向量的定位,APP应用标志的检测(检查APP代码段标志是否正常),如果一切正常则跳转到应用程序段执行,否则等待接收从CAN通信接口传送过来的升级数据包。经测试文章所述方法完全满足要求,在IAPAPP之间相互跳转的效果和基于硬件设置的单片机效果一致,达到了设计初衷。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值