文章分类

当前位置:首页>技术文章>MCS-51系统中断优先级的软扩展

MCS-51系统中断优先级的软扩展

众所周知,MCS-51 系统只提供“二级中断嵌套”,而大多数嵌入式系统希望有多于两级的优先级别。因为一般来说,系统都有掉电中断,且应置为最高优先级,这样所有其它中断只能共用一个最低优先级,如此,往往不能满足实际的逻辑需求。为了使系统具有多于两级的中断优先级别,可以利用 8259A 之类的中断控制芯片实现中断优先级的硬扩展,但却增加了系统的造价和复杂性。因复杂性的提高,系统的可靠性将受到影响。

本文提出一种扩展 MCS-51 系统中断优先级的纯软件方法,不需增加任何硬件,且所需的额外资源消耗也很小。实际应用表明这种方法是可行的和有效的。1 MCS-51 的中断系统简介 MCS-51 系列单片机允许有五个中断源,提供两个中断优先级,可实现二级中断嵌套。这两级优先级遵循下述规则:仅高优先级中断源可中断嵌套低优先级中断源。为实现这一规则,中断系统内部包含两个不可寻址的优先级状态触发器。当特定优先级的某中断源被响应时,相应的触发器即被置位,直到执行了 RETI 指令后,这个触发器才复位。

在此期间,同级和低级中断将被防止。中断源的中断请求能否得到响应,受中断允许寄存器 IE 的控制。每个中断源的优先级可通过对中断优先级寄存器 IP 编程来设定:或最低,或最高。同一优先级中的各中断源同时请求中断时,由内部查询逻辑确定响应次序。查询次序依次为:外部中断 0(X0)、定时器中断 0(T0)、外部中断 1(X1)、定时器中断 1(T1)、串口中断(S)。如果当前指令是 RETI 或是对 IE、IP 操作的指令,将封装 CPU 对中断的响应,且必须再执行完一条指令之后才会响应中断。

2 中断优先级软扩展的方法首先,给出软扩展的第一种方法,并分析其特点,指出其存在的缺陷。然后,基于对方法一的不足之处,给出不断完善的方法二、方法三。其中方法二是对方法一的完善,方法三是对方法二的完善,并最终解决了方法一、二中的缺陷,实现了真正的中断优先级的软扩展。

2.1 方法一此法仅使用和系统的中断允许寄存器 IE,通过中断屏蔽字机制,以使不同的中断源具有不同的逻辑中断优先级(下文中的“优先级”如不加说明即指“逻辑中断优先级”)。不失一般性,不妨令 8051 系统的五个中断源——外中断 0(X0)、定时器中断 0(T0)、外中断 1(X1)、定时器中断 1(T1)及串口中断(S),有如表 1 所列的优先级。(实际应用中,视具体情况,赋予不同中断源以适当的优先级。)

其中,“0”代表最高优先级,“4”代表最低优先级。首先,给设定了优先级的诸中断源赋以二级“物理中断优先级”:将优先级最高的中断源(X1)在中断优先级寄存器 IP 中的相应位(PX1)置 1,而令 IP 中的其它相关位(PT1、PT0、PS、PX0)为 0。其次,给设定了优先级的各中断源分配适当的“中断屏蔽字”。其基本思想是屏蔽同级和低级中断。具体分配过程如下:优先级为 k(0≤k≤N-1,N 为中断源数量)的中断源的“中断屏蔽字”为:优先级为 x(x∈[k,N-1],即同级和低级)的中断源在 IE 中的对应位置 0,优先级为 y(y∈[0,k-1],即高级)的中断源在 IE 中的相应位置 1 而得的位组字节。

当然,IE 的 EA 位(CPU 中断允许标志位)始终为 1.对于表 1 所列的中断优先级分配情况,各中断源的“中断屏蔽字”配置如表 2 所列。表 1 中断源的优先级分配表最后,给各中断源的 ISR(Interrupt Routine,中断服务例程)加以如下所示的外壳(Assembly 形式的)。不妨以定时器 0(T0)为例:CSEG AT 8×1+3;定义绝对段,设置断向量 JMP T0_ISR_SHELL?

PR?TO_ISR_SHELL?XX SEGMENT CODE ;声明再定位段 T0_ISR_SHELL:PUSH IE;保存 IEMOV IE,#TO_INT_MASK;设置当前中断屏蔽字 CALL ResetIntSys:复位中断系统 CALL T0_ISR:调用中断服务例程的主体 POP IE;恢复 IERET 这里,T0_ISR 为定时器 0(T0)的 ISR 的主体部分。其应以一般函数的形式,用汇编或 C 编写。

ResetIntSys 为仅含一条中断返回指令(IRET)的函数,即 ResetIntSys:RETI。其用于复位中断系统,以使在相应 ISR 执行过程中,系统仍可响应其它中端源提出的中断请求,以便实现中断嵌套。这样就达到了防止同级和低级优先级中断的目高优先级的中断源可以提出中断请求,但未必会被立即响应。因为在当前策略下,尚不能实现真正的“中断嵌套”(即高优先级的中断服务例程可中断低优先级的中断服务例程而嵌套执行),而仅有最高优先级的中断(X1)才可以实现这种真正的“中断嵌套”。

因为在 8051 系统里,中断能否嵌套仅取决于其相应的“物理中断优先级”(各中断源的物理中断优先级由中断优先级寄存器 IP 中的相应位决定,且仅有二级)。下面分三种情况说明方法一的特点和不足:①当外部中断 1(X1,其具有最高的逻辑中断优先级和最高的物理中断优先级)提出中断请求时,系统将立即响应,而不管系统此时忙否。如果此时系统正在执行其它中断的 ISR,X1 的 ISR 将以嵌套形式执行,因为其它中断湖泊的物理中断优先级都为最低(51 系统仅有两级物理优先级:最高或最低)。

②当定时器 0(T0,其优先级为 2)的中断请求正被响应时,来自串口(S,其优先级为 3)和外部中断 0(X0,其优先级为 4)的中断请求将被禁止;而只允许外部中断 1(X1,其优先级为 0)和定时器 1(T1,其优先级为 1)提出中断请求。如果是 X1 提出中断请求,则 X1 的 ISR 将立即嵌套执行;如果是 T1 提出,尽管其优先级高于当前中断 T0,但因其物理中断优先级与 T0 一样(同为最低),故而将不会像 X1 那样被系统立即响应,并嵌套执行,而只能等待,直到 T0 的中断服务例程执行完毕。

③如果在串口(S,其优先级为 3)中断正被响应过程中,定时器 1(T1,其优先级为 1)与定时器 0(T0,其优先级为 2)分别提出中断请求。由于它们有高于 S 的优先级,所以系统允许它们提出中断请求;但因其物理优先级与 S 一样,故而直到 S 的中断服务例程执行完毕,系统才会受理 T1 与 T0 的中断请求。逻辑上,由于 T1 具有高于 T0 的优先级,所以 T1 应先为系统响应。但因物理优先级相同时,中断请求的响应次序取决于内部查询顺序,而 T0 先于 T1,所以实际上 T0 先系统响应,即出现了“优先级反转”的问题。

可见,方法一虽然可以部分地达到“扩充中断优先级”的目的,但其存在两个问题:*某些高优先级中断不能中断嵌套低优先级中断;*会出现“优先级中反转”。方法二和方法三是针对方法一的这两个不足而提出的,并最终实现对 51 系统的中断优先级的真正扩展。

2.2 方法二该方法是在方法一基础上,为解决“优先级反转”的问题,而实施的简单策略而得。根据方法一中对“优先级反转”问题的分析可知,出现该问题的原因是:各中断源的逻辑优先级与其内部查询顺序不一致。只要在系统设计时,兼顾中断源相关事件的紧迫程度与中断源的内部查询逻辑:将最紧迫的事件(如掉电)赋以最高的优先级 0,并使其与系统中的最先被查询的中断源(外部中断 0)相关联;使次紧迫的事件的优先为 1,并使之与系统内第二个被查询的中断源(定时器 0)相关联,即 51 系统内的各中断源应有表 3 所列的优先级。如此,即可解决“优先级反转”的问题。

2.3 方法三本法是在方法一、二的基础上,针对“某些高优先级中断不能中断嵌套低优级中断”的问题,引入相应的策略,以实现对 51 系统中断优先级的“真正”扩展优先级软扩展的函数库实现为了真正扩展 51 系统的优先级,各中断源的优先级、优先级屏蔽字、中断屏蔽字应是确定的,如表 3、4 所列。C51 编写断服务例程时,应给出相应的中断源编号(中断号)。特定中断源有特定的中断号,而此中断号恰与各中断应有的优先级一致。

本文用 C51,以函数库的形式实现方法三所述的策略,其包含两个文件:ExtIntPri.H、ExtIntPri.C。须要指出,为使优先级的设置和恢复具有原子性以防出现混乱,应对 SetPriority()和 ResetPriority()作临界处理,以使其不被“再入”访问。另外,应对系统栈作调整。如图 1 所示,其中“1”代表 SetPriority()所作的调整,其将 IP、IE 保存于系统栈中;“2”代表 ResetPriority()所作的调整,其从系统栈中恢复 IE、IP;“HAddr”、“LAddr”分别代表当前函数返回地址的高位字节和低字节(栈中的地址是以小端字节序方式存储,这是 C51 中唯一的例外,而所有其它多字节数据则皆以大端字节序方式存储)。

如果不这样做,而是定义两个全局变量来保存 IE、IP,由于 SetPriority()和 ResetPriority()都要访问这两个全局变量,而这两个函数又应在 ISR 的开关和结尾处被分别调用,从而使 ISR 成为临界区,而不可被其它 ISR 中断,这将使优先级的存在失去意义。//ExtIntPri.Hextern void SetPriority(unsigned char);extern void SetPriority(unsigned char);extern void ResetPriority(void);//ExtIntPri.C#pragma src#include "ExtIntPri.H"#include//静态(局部)函数声明 static void ResetIntSys(void);//仅含一条指令:RETI//宽两个宏用作“临界区”的进入区和退出区#define ENTER_CRITICAL()EA=0//关中断,以防临界再入#define EXIT_CRITICAL() EA=1//中断屏蔽字和优先级屏蔽字的宏定义,如表 3 所列。

#define S_INT_MASK 0x8F//;1-01111B//…#define S_PRI_MASK 0x0F//;---01111B//…//先调整系统栈以保存 IP、IE,其过程如图 1 所示,再为给定中断//(prio 也是中断号)设置优先级 void SetPriority(unsigned char prio){ENTER_CRITICAL();//关中断#pragma asmPOP ACC //弹出返回地址的高位字节 HAddrPOP B //弹出返回地址的低位字节 LaddrPUSH IPPUSH IE //EA= =0PUSH B //LAddr 进栈 PUSH ACC //HAddr 进制#pragma endasmswitch(prio){case 0:IP=X0_PRI_MASK;IE=X0_INT_MASK;case 0:IP=X0_PRI_MASK;IE=X0_INT_MASK;break;//…case4:IP=S_PRI_MASK;IE=S_INT_MASK;break;}ENTER_CRITICAL();//这里中断被打开,故再关中断 ResetIntSys();EXIT_CRITICAL();//开中断}//从系统栈中恢复 IE、IP,其过程如图 1 所示。

该函数应在退出 ISP 时调用 void ResetPriority(void){ENTER_CRITICAL();#pragma asmPOP ACC //弹出返回地址的高位字节 HAddrPOP B //弹出返回地址的低位字节 LAddrPOP IE //EA= =0POP IPPUSH B //LAddr 进栈 PUSH ACC //Haddr 进栈#pragma endasmEXIT_CRITICAL();//开中断}//仅含一条指令:RETI,用以复位中断系统,以便系统在 ISR 执行过程中可响应其它中断 void ResetIntSys(void){char code reti=0x32; //32H 为 RETI 的机器码(((void)(code*)(void))(&reti))();//将 reti 的地址强制转化为函数指针}使用时,只需将 ExtIntPri.H 头文件用#include 加入相应源文件(当然,应将 ExtIntPri.C 的目标文件、库文件或汇编源文件加入当前工程)。

不妨以定时器 0(T0)为例,其中断号为 1,故优先级亦应为 1,如下所示:////Test.C#include "ExtIntPri.H"//…void T0_ISR(void)interrupt 1 using 2{SetPriority(1);//…ResetPriority();}如此,T0 即有了次最高优先级——1。结语使用本文所述的“软扩展”方法,可以将 MCS-51 系统的中断优先级扩展到 5 级。

如果所用 51 系统的中断源个数为 N(N≤8),只须对上述方法稍作修改即可将其优先级扩展到 N 级。该方法不需增加任何硬件,且所需的额外资源消耗很小,使用也非常简单,不会给用户增加编程负担。实际应用表明,这种方法是可行的和有效的。


© 星光电子技术工作室 2021 All Rights Reserved.
如有任何问题、建议、业务、合作,请联系手机/微信:13713820066