单片机微控制器中断处理算法改进
2013-10-17王丽娟
王 婷,王丽娟
(中国空空导弹研究院 11所,河南洛阳 471009)
单片机是一种集成电路芯片,采用超大规模集成电路技术将处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功能集成到同一芯片上,构成了一个小且完善的计算机系统[1-2]。由于其功能灵活、集成度高、可靠性好,因此在工业控制领域有广泛的应用。在使用某型微控制器对电源进行控制和检测时发现,软件中断处理算法中存在的问题,本文对这些问题进行分析,并提出解决方法,为今后深入应用提供参考。
1 概述
文中采用NXP的LPC2132单片机完成电源控制、检测、通讯等功能,该单片机基于ARM7核心,具有运行速度快、外设丰富、使用简单等优点。
在该电源系统中,该单片机主要完成以下工作:利用UART串口接受控制指令输入并输出状态信号和测试数据、对电压进行 A/D采样、完成控制算法、用PWM脉宽调制技术控制稳压电源的变换和输出,接收其他外部控制指令完成点火控制、故障处理等功能。
为实现上述功能,采用如下中断算法:
(1)开启串口接收和发送中断,在中断服务程序中完成串行数据的收发,数据包的整合、校验、拆解。
(2)开启定时器0周期中断,在定时器周期中断中完成控制指令的获取、数据分析、完成控制算法和控制信号的输出。
(3)开启4路外部事件中断,用于获得外部脉冲控制信号和故障处理。
(4)开启A/D采样中断,自动完成各通道信号的采集,并将数据存入内存缓冲区,供控制算法调用。
在以上程序中,定时器0中断作为主任务完成整个系统的任务调度,其他外部中断不断触发各种任务,完成指令和数据的更新,供主任务调度。
最初上述中断算法能够满足要求,随着功能的增加,如要求电源能够对外部控制信号做出快速反应;更高要求的串行通讯的可靠性;更加精确快速要求的电源控制算法的实现使得上述中断算法的不足逐渐突现,需进行改进。
2 算法分析
由于采用LPC2132主频较低,没有DSP核,运算能力较弱,进行功能、串行通信和算法的增加和扩充后,原算法越来越无法满足要求。
在上述的中断算法中,LPC2132处理器轮流执行定时器、A/D、串口、外部中断等任务程序。与其他大多数处理器一样,在响应一个中断任务的同时,处理器自动屏蔽中断使能,只在完成当前中断服务程序后,才响应另一个中断。由于定时器0周期中断程序需完成各种任务和控制算法,其执行周期较长,在其运行时间内,处理器无法响应串行数据中断,如果在定时器0周期中断程序执行的时间内收到2 Byte以上UART数据,则可能造成通讯数据的丢失。同样当系统执行定时器0周期中断和串行通讯中断时,也无法响应外部中断事件,使得外部控制信号的响应时间被延迟,系统功能以及算法越复杂,延迟时间越长。当有紧急外部事件时将不能响应,如故障处理。这些是不允许的,在程序执行中需确保这类紧急事件能得到及时响应。
上述问题主要是由于某个任务中断服务程序执行时间过长,此期间关闭了其他中断,导致其他紧急任务的中断无法被触发执行。要该问题,除更换更快速的DSP处理器外,还应优化软件结构。
软件上考虑采用以下方法[3-5]:
(1)尽量缩短每个中断服务程序的执行时间。在中断中仅通知系统事件发生,而事件的处理交给一个独立的任务处理,在该任务处理中,中断系统是打开的,不会阻塞其他外设响应。但当任务较多时,如何管理、组织这些任务,使系统尽快执行紧急的任务,则需要引入嵌入式实时操作系统来调度任务。而对于电源这类简单的系统,引入操作系统显得过于复杂。
(2)使中断可重入,即在一个执行较长的中断处理程序中,再次打开中断,使处理器能够在该中断服务程序中,随时被更紧急的中断事件打断,优先执行紧急的中断服务程序,执行完毕后,再继续本中断服务程序。某些处理器能够硬件处理中断的重入,而LPC2132是基于ARM7结构,其硬件不具备中断重入功能,因此只能靠软件实现。
软件可以使用嵌入式实时操作系统完成中断的重入,但该方法过于复杂,所以这里采用一种简单的方法实现:
当进入中断服务时,系统自动将全局中断使能屏蔽,导致无法响应新的中断,那么在某个中断服务程序中,清除当前中断源、重新使能全局中断就可以使系统响应其他中断,初步代码如下:
VICIntEnClr=1≪14;//清除当前中断源VICVectAddr=0x00;//通知系统中断变化IRQEnable();//使能IRQ中断
在实际的操作中,该方法能够在中断服务中,重新打开系统中断,使CPU能响应新的中断。但有时软件会跑飞,没有正常完成中断嵌套和返回,需进一步改进。
3 算法改进
中断服务程序要想不破坏主程序状态并能正确返回,必需正确进行现场的保护和恢复。在汇编语言中,这些任务由编程者手动实现,在C语言中,这些任务由编译器自动完成。
C语言编程中,一个典型中断服务程序如下:
_irq声明该函数为中断服务程序,keil编译器自动在函数内部增加了中断现场保护的代码,编译器增加的中断现场保护和恢复的代码如下:
但在这部分代码中,并没有完整的保护现场,程序的返回地址并没有保存。
一般的单片机如AVR,中断时C语言将下一条指令地址PC+1保存入堆栈SP中,返回时将弹出地址弹出到PC,只要堆栈足够大这样的嵌套中断不会有问题,但LPC2132不同。
ARM7处理器在进入IRQ异常处理时,内核硬件自动执行以下工作:
(1)将返回地址保存到r14_irq。中断处理程序可以利用r14_irq-4写入PC来返回。
(2)将CPSR当前值存入SPSR_irq。返回时可以通过SPSR_irq恢复CPSR。
(3)设置CPSR为irq模式,同时置位中断禁止控制位。
(4)设置PC为相应中断处理程序入口地址,跳转到中断处理程序。
因此在中断处理程序中,软件应当执行:1)将r14_irq-4写入PC来返回;2)将SPSR值复制回CPSR,同时清零中断禁止控制位。
可以看出,由硬件完成了部分现场保存工作,但__irq声明中断服务程序时编译器不对R14_irq进行保护,因此当再次打开中断时,R14_irq会被新发生中断的断点值覆盖,这时被打断的中断服务程序将会出错。假设有下面一种情况:
在上述程序,IRQ处理函数IRQ_Handler()中调用了函数Fun1(),若在IRQ_Handler()里中断可重入,则当新中断请求恰好在“BL Fun1”指令执行完成后发生时,LR寄存器的值将调整为BL指令的下一条指令(ADD)地址,这样程序能从Fun1()正确返回;但这时发生了新的中断请求,需要进行新中断的响应,此时处理器为了能使新中断处理完成后正确返回,也将进行LR_irq保存。这两次对LR_irq的操作将发生冲突,当新中断返回后执行STMFD指令时,压栈里的LR不是原ADD指令地址,子程序Fun1()将无法正确返回。
由上述分析可知,仅靠_irq声明中断服务程序不能完成嵌套下的中断服务程序,需手动完成相应的保护。由于中断时刻LR_irq的保护是硬件实现的,所以可以在重新使能中断之前改变处理器的模式,使程序中的“BL Fun1”指令不在IRQ模式下运行。这样当新中断发生时就不会对LR寄存器重复操作[1-2]。
手动完成中断现场的保存和恢复,无需编译器自行生成保护代码和_irq声明,中断服务程序用C语言的普通函数实现中断嵌套。
用这种方法改进后,经调试程序可以实现中断的嵌套运行,但程序会死机、出错。需进一步分析解决。
当某个中断发生时,LPC2132的CPU会自动地去取对应的中断向量,并检查对应的中断是否使能并挂起,这个过程和一般CPU对中断的处理过程类似,由硬件自动进行。但LPC2132不同的是:CPU除完成以上工作外,还要检查这个中断在这么短的时间里,中断条件是否满足,若不满足,则认为是伪中断。以上造成了程序的死机和出错。
控制器LPC2132采用的是ARM7处理器,该处理器为3级流水线设计,内核没有配备外设中断处理器,且向量中断控制器(VIC)集成在内核外,造成中断处理有异步特性。当中断源发出请求后,流水线上还有两条指令未执行,要等这两条指令执行完后,才能处理中断的请求,若这两条指令中有关于中断处理的,例如关中断,或在执行这两条指令时中断源被清除、VIC的状态发生改变,这就会触发伪中断。
以上整个过程可能经过以下步骤:(1)VIC判断是否有IRQ中断。若有,则向内核发送IRQ信号。(2)内核保存IRQ状态。(3)执行流水线的多个周期的处理。(4)内核从VIC中装入IRQ地址。
若在执行到步骤(3)时,向量中断控制器的状态改变,那么就将发生伪中断。笔者碰到两种伪中断情况:
情况1 在步骤(3)时执行了关中断指令。这将导致中断服务程序入口处CPSR中的中断是禁止的,并被保存到SPSR。这样当中断返回恢复SPSR,中断还是禁止的,造成此后的程序不正常。
情况2 向向量中断控制器发送IRQ信号的中断标志丢失。当进入伪中断,VIC将不能装载到VICVectaddr中的中断服务程序入口地址,装载到的是VICDefVectAddr里的默认地址,而这个默认地址在复位后会被置为0x00000000,导致出现假复位。
为防止上述情况的出现,需要采取以下两种方法来预防伪中断的产生:1)编写程序来防止伪中断的产生;2)正确设置和检测VIC的默认处理程序。
在之前采用修改后的周立功uc/os-2在LPC2000上的移植代码解决中断嵌套和重入问题程序的基础上,对于情况1,拟采用在中断服务程序入口处判断中断发生时中断标志位是否被禁止来解决。若被禁止则说明出现情况1,应立刻离开此中断服务程序,此时不进行中断标志的清除,中断将会在下一次中断使能时处理。在程序中加入如下代码:
该方法简单地解决了情况1遇到的伪中断问题。
对于情况2,在本控制器程序中,在串行通讯当中,当UART0/UART1的RDA/CTI中断允许时就可能发生。在UART0中,当UART0 Rx的FIFO到达寄存器U0FCR7∶6所定义的触发点(比如接收4 bit)时,将发生RDA中断。当UART0 Rx FIFO的深度低于触发点时,RDA中断标志被清除。当UART0 Rx FIFO包含至少1 bit,且在接收3.5~4.5 bit的时间内没有发生UART0 Rx FIFO动作时,将发生CTI中断。若此时UART0 Rx FIFO有任何动作(读或写UART0 RSR),有字符进来,CTI中断标志被清除,此时向量中断控制器无法识别是谁产生了中断,CTI伪中断发生。
同样,RDA伪中断也是这样发生的。以接收4 bit发生RDA中断为例,当UART0 Rx FIFO已接收3 bit,且超过了3.5~4.5 bit的时间,发生CTI中断。在系统正确处理CTI中断时,恰好有一个字符进来,使得UART0 Rx FIFO中的字符数正好为4,于是发生RDA中断。但是由于先处理CTI中断,CTI中断程序先读取了其中的字符,使UART0 Rx FIFO内的字符数小于4,因此RDA中断标志就被清除了,等到系统处理RDA中断时,伪中断就发生了。
为解决CTI/RDA伪中断,在本程序中,采用修改默认中断处理程序VicDefVectAddr的方法解决。在程序跳转到默认中断处理程序时,尽快读出UART0的接收数据,解决串行通讯中的伪中断问题。这种方法简单易行,效果良好。
4 进一步改进
经过上述分析,本控制程序已经基本解决了中断嵌入和伪中断问题,在实际应用中发现仍有问题需进一步改进。
4.1 中断优先级问题
在上述简单的重新打开中断允许位的方法中,有可能会使能全部中断,从而导致一个正在运行的中断被任何其他中断打断。此时可以采用中断优先级管理的方法,只允许更紧要的任务中断打断当前中断。由于LPC2132不具备中断优先级管理功能,无法直接为各中断设置优先级。本程序中通过中断管理器对32个中断源进行优先级的管理。每个中断都有一个单独的使能位,在中断服务程序中手动将更低优先级的中断使能位清除,以避免打扰当前中断,服务程序结束时再将其恢复,就可以实现只允许更高优先级的中断重入,达到优先级管理。该方法在硬件无法实现的情况下,由软件实现了中断优先级管理,完善了中断嵌套算法。本程序的示例代码如下:
4.2 可重入问题
另外在软件的编写中发现,由于采用了中断嵌套,A/D中断和定时器0周期中断可能同时调用一个函数,该函数第一遍尚未执行完,所占用的变量还未释放,就被再次执行。若该函数中使用了全局变量,两次调用同时改变了全局变量的内容,将导致程序出错。故在考虑中断嵌套使用时,要考虑可重入性的设置,对于一段可能会被同时访问的程序或者变量,应按照可重入的要求编写可重入性以避免此类问题。
5 结束语
本文对电源所用控制器LPC2132中断程序设计中的中断算法问题进行了分析,提出了简单易行的改进方法,并分析了ARM处理器中断的特点,同时给出了中断程序设计中中断嵌套问题、伪中断问题和中断优先级等中断算法问题的有效地解决方法及简单的代码,进一步完善和优化中断控制算法。
[1]周立功.ARM嵌入式系统基础教程[M].2版.北京:北京航空航天大学出版社,2008.
[2]周立功.深入浅出 ARM7-LPC213x/214x[M].北京:北京航空航天大学出版社,2006.
[3]JEAN J L.嵌入式实时操作系统μcos-Ⅱ[M].2版.邵贝贝,译.北京航空航天大学出版社,2003.
[4]LI Qing.Real-time concepts for embedded systems[M].USA:CMP Books,2003.
[5]任哲.嵌入式实时操作系统μcos-Ⅱ原理 及应用[M].北京:北京航空航天大学出版社,2005.