在自行研制单片机应用系统过程中,经常会碰到这样的问题:硬件没有任何故障,软件调试也都成功,但在运行时却出现有时正常,有时死机的情形。
这类故障大多是由于编程中的中断保护不完善所致。下面以MCS—51系列单片机应用系统为例,谈谈中断保护问题。
1、MCS—51单片机的中断机构简介
MCS—51单片机共有5个中断源,按查询顺序的先后分别为:外部中断0:INT0——,定时器中断0:T0—,外部中断1:INT1——,定时器中断1:T1—,串口发送/接收中断T/R。五个中断源都有各自的控制位和标志位,程序员可以通过指令对每个中断源的控制位进行改写,以实现对每个中断源的允许/禁止控制。CPU靠内部硬件按先后顺序不断地对各中断标志位进行查询,一旦查到某个中断标志位被置1,则立即响应该中断请求( CPU的中断应答过程细节可查阅有关资料)。实际上MCS—51单片机通过控制字可将这五个中断源分成两个优先级,即高优先级和低优先级。设置为高优先级的中断源可以在任何时刻打断低优先级的中断服务,使CPU立即响应高优先级的中断请求(这里的“立即响应”说法并不十分严格,仅为定性说明问题)。
MCS—51单片机的这种中断结构特点告诉我们,在编写相应的软件时必须考虑多级中断嵌套时的现场保护。
2、需要采取中断保护措施的几种情形。
在MCS—51系统中,中断响应后CPU能自动完成的保护内容仅是PC指针,即中断现场的地址。其它内容一概由编程者根据中断服务程序中所需要使用的公用资源自行保护。
公共资源有下列几种情形。
(1)通用寄存器。一般有A累加器,B寄存器,R0~R7寄存器,外部数据指针DPTR等。这些寄存器不一定都要保护,要编程者根据中断服务程序是否用到它们来决定。
(2)公用数据区。这里所说的公用数据区主要是指不同数据块可能使用到的数据区,例如某个子程序所用的存储区,而该子程序有可能在主程序及中断服务程序中都要调用,如码变换子程序,数据处理子程序,数据传送子程序等,都有可能在多种场合下调用。
(3)某些I/O口线用作片选线时,该输出口线一定要保护。例如P1.7线的“0”、“1”两种状态分别用来作外部数据存储区和外部扩展的I/O口选择时,则一定要对P1.7进行保护。另外还有些特殊情形需要保护,如某个扩展的输出口(带锁存)或某个存储单元进行保护,这里就不一一列举了。
3、常用的几种中断保护措施
MCS—51单片机系统中,实现中断现场保护大致有以下几种途径。
(1)利用堆栈进行中断现场保护。利用堆栈进行中断现场保护是各种微机系统通用的一种保护措施。MCS—51单片机和其它CPU一样,都设有专门的堆栈操作指令,即PUSH、POP指令。它们可以直接对寄存器进行操作,因此利用堆栈操作实现对寄存器的保护十分方便。使用堆栈时要注意栈顶的溢出问题。MCS—51单片机的堆栈指示器复位值为SP=07H,也可以用指令对SP赋值,例如 MOV SP,#4FH。此时可用作堆栈区的空间为50H~7FH。任何时刻,若出现SP超过7FH,将产生堆栈溢出,导致程序运行错误。在多重中断嵌套时尤其要注意堆栈溢出错误。
(2)利用寄存器来保护寄存器。MCS—51单片机中有4个区的R0~R7,共32个寄存器,它们都可以作通用寄存器使用,且寻址方式灵活多样,程序中具体使用第几区的R0~R7,要由控制率PSW的第3、4位来确定。例如我们可以在主程序中使用0区寄存器,在低优先级中断服务程序中使用1区寄存器,在高优先级中断服务程序中使用2区寄存器,这样就不会发生数据冲突,起到了R0~R7的保护作用。顺便说一句,当利用R0~R7来传递参数时,要注意寄存器区的改变会影响参数传递,这也是初学者易忽视的地方。上述两种中断现场的保护途径比较通用,也很容易掌握,就不具体举例说明了。
(3)利用专用数据区进行保护。利用数据存储区来完成中断现场的保护,对程序员来说相对要麻烦些,有时不容易考虑周到。下面以两个具体例子来说明这一过程。例1、在某个系统监控程序中,有一码变换子程序BIND,实现二进制到十进制数代码转换。该子程序占用8031内部RAM 30H~3AH作临时数据区。假如在主程序中调用BIND实现二进制时钟的处理,在低中断优先级的INT0——采集处理服务程序中也调用了BIND子程序实现数据代码的转换,而在设置为高中断优先级的串行通信服务程序中又调用了BIND子程序实现代码转换。此时就有可能出现这种情况:当主程序正在调用BIND子程序时,INT0——有中断请求,CPU响应中断,运行中断服务程序,恰好在INT0——也调用BIND子程序时,又发生串行通信的中断服务请求而再次调用BIND,此时就会导致30H~3AH数据被破坏。这种巧合的几率虽然不大,一旦碰上,就会导致错误,轻者数据出错,严重时出现死机或系统失控。由此可见,公用数据区30H~3AH必须采取有效保护措施。最好的办法是编一数据传输子程序,在中断服务程序调用BIND之前,先将30H~3AH的数据搬至另外一个安全处,中断服务完毕,再还原30H~3AH的内容。此外还必须保证,在两个不同优先级的中断服务程序中,应将30H~3AH搬至不同数据区。
例2、 P1口用作数据存储区和I/O口地址片选时P1口的保护。如图1所示,当P1.7=0时,选通I/O口,当P1.7=1时选通存储器芯片62256。让我们来考虑这样一种情形:当主程序执行存储器操作(P1.7=1)时发生中断请求,而中断服务程序中又执行了I/O口操作(P1.7=0),P1.7的状态发生了变化。此时若不对P1.7加以保护,则返回到主程序后就会出现名为读写存储器操作,而实为读写I/O口的操作。同理可知,两个不同优先级的中断源发生中断嵌套时也有可能发生上述情形。最有效的保护方法是设置两个数据位来跟踪P1.7的状态,P1.7每改变一次,使相应数据位也改写一次,中断返回前再将该数据位回送给P1.7。假定设00H位在主程序中跟踪P1.7,设0lH位在低优先级中断服务程序中跟踪P1.7,则相应的程序结构如图2所示。(周振安)

