星期日, 四月 12, 2009

《电气传动系统软件设计》之五

4.2.3 中断处理(二)

4.2.3.5 ASM语言编写中断服务程序
用ASM语言编写中断服务程序时, 应确保中断服务程序的标号与中断向量表(或子中断向量表)中的名称对应. 下面以一个具体的汇编语言例子说明中断程序的处理. 本程序完成如下的操作: 当定时器1发生上溢、下溢和比较事件时采用中断方式分别翻转IOPB0、IOPB1和IOPB2引脚的状态.
; file: main.asm
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.include “240xA.h” ;变量和寄存器的定义
.include “vector.h" ;中断向量表的定义

KICK_DOG .macro ;看门狗复位宏
LDP #00E0H
SPLK #05555H,WDKEY
SPLK #0AAAAH,WDKEY
LDP #0
.endm

USE_PVECTOR .set 1 ;中断是否采用外设向量表

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.text ;主程序
_c_int0 LDP #0
SETC INTM ;关总中断
CLRC CNF ;B0配置为数据空间,
SPLK #0000H,IMR ;禁止所有中断
LACC IFR
SACL IFR ;清除所有中断标志
LDP #WDKEY>>7
SPLK #0004H,SCSR1 ;PLL四倍频, 允许EVA时钟
SPLK #006FH,WDCR ;禁止看门狗
KICK_DOG ;清看门狗
MAR *,AR7
LAR AR7,#7FH
LDP #0E1H
SPLK #0000000000000000B,MCRA
SPLK #1111111100000000B,PBDATDIR
;设置IOPB0~2为输出引脚
LDP #GPTCONA>>7
SPLK #0000000000000000B,GPTCONA
SPLK #0000000000000000B,T1CNT
SPLK #0000111101000010B,T1CON
;连续加/减计数, 128分频
;内部时钟, 计数器为0装载CMPR
;允许比较, 采用自己的周期寄存器
SPLK #1111111111111111B,T1PR
SPLK #0111111111111111B,T1CMPR
SPLK #0000011100000000B,EVAIMRA
;允许上溢、下溢和比较中断
SPLK #0000011100000000B,EVAIFRA
;清除外设中断标志
LDP #0
SPLK #0000000000000010B,IMR ;允许INT2中断
CLRC INTM ;允许所有中断
WAIT NOP ;循环等待中断
NOP
B WAIT
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GISR2 ;中断服务程序

MAR *,AR7 ;保护现场
SST #1,*-
SST #0,*-
SACH *-
SACL *
.if USE_PVECTOR ;如果采用外设向量表
LDP #PIVR>>7
LACC PIVR,1
ADD #PVECTORS
BACC
.else ;如果不采用外设向量表
LDP #PIVR>>7
LACL PIVR
XOR #002AH
BCND SISR2A,EQ ;T1上溢
LACL PIVR
XOR #0029H
BCND SISR29,EQ ;T1下溢
LACL PIVR
XOR #0028H
BCND SISR28,EQ ;T1比较
.endif ;结束条件编译
MAR *,AR7 ;恢复现场
LACL *+
ADD *+,16
LST #0,*+
LST #1,*
CLRC INTM
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SISR2A ;T1上溢中断服务程序
LDP #0E1H
LACC PBDATDIR
XOR #00001H
SACL PBDATDIR ;翻转IOPB0引脚的状态
LDP #GPTCONA>>7
LACC #0400H
SACL EVAIFRA ;清除T1上溢中断标志位
MAR *,AR7 ;恢复现场
LACL *+
ADD *+,16
LST #0,*+
LST #1,*
CLRC INTM ;允许所有中断
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SISR29 ;T1下溢中断服务程序
LDP #0E1H
LACC PBDATDIR
XOR #00002H
SACL PBDATDIR ;改变IOPB1引脚的状态
LDP #GPTCONA>>7
LACC #0200H
SACL EVAIFRA ;清除T1下溢中断标志位
MAR *,AR7 ;恢复现场
LACL *+
ADD *+,16
LST #0,*+
LST #1,*
CLRC INTM ;允许所有中断
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SISR28 ;T1比较中断服务程序
LDP #0E1H
LACC PBDATDIR
XOR #00004H
SACL PBDATDIR ;改变IOPB2引脚的状态
LDP #GPTCONA>>7
LACC #0100H
SACL EVAIFRA ;清除T1比较中断标志位
MAR *,AR7 ;恢复现场
LACL *+
ADD *+,16
LST #0,*+
LST #1,*
CLRC INTM ;允许所有中断
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GISR1 ;未使用的中断直接返回
GISR3
GISR4
GISR5
GISR6
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PHANTOM ;假中断中清看门狗后返回
KICK_DOG ;复位WD
RET
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.end
在该程序中T1周期寄存器T1PR设定为0FFFFH, T1比较寄存器T1CMPR设定为7FFFH, T1计数器T1CNT设定为加/减计数. 当T1CNT=7FFFH时产生比较中断, 进入比较中断服务程序SISR28, 在中断服务程序中IOPB2的引脚状态被改变, 从“0”变为“1”或从“1”变为“0”; 当T1CNT=0H时产生下溢中断, 进入下溢中断服务程序SISR29, 在中断服务程序中IOPB1的引脚状态被改变; 当T1CNT=0FFFFH时产生上溢中断, 进入上溢中断服务程序SISR2A, 在中断服务程序中IOPB0的引脚状态被改变.
CPU一旦接收到INT2的中断请求, CPU先在主中断向量表中找到INT2的中断向量入口GISR2. 在GISR2中进入一个特定的中断服务程序SISR, 可采用外设中断向量表, 也可以不采用外设中断向量表. 如果采用外设中断向量表, 外设中断向量表的建立稍显麻烦, 但程序实现比较简单, 只需读取外设向量寄存器PIVR, 加上外设向量表起始地址, 通过查外设向量表就可以转移到一个特定的外设中断服务程序SISR; 如果不采用外设中断向量表, 就不需要建立外设向量表, 在GISR2中通过PIVR的值来判断产生中断请求的外设事件, 从而转移到相应的外设中断服务程序SISR. 程序中通过条件编译伪指令来选择在是否采用外设向量表来实现中断.
在中断服务程序中除了中断事件的处理之外, 还包括中断现场的保护和恢复. 在这里中断现场主要包括累加器和两个状态寄存器, 在实际应用中可根据具体的需要来设定中断现场. 中断现场的保护在GISR中, 每个SISR中都有中断现场的恢复. 这是因为一个完整的中断服务程序是从GISR进入, 然后从其中一个SISR返回. 特别要注意在返回之前一定要清除被响应的外设中断标志位, 并且将INTM清“0”, 否则即使有中断请求, CPU也不会响应该中断请求. CPU的中断标志位IFR不需要中断服务程序处理, 在每次响应中断时, CPU会自动清除该标志位.
在这里, 假中断服务程序PHANTOM只是复位看门狗, 然后返回. 假中断用来保持中断系统的完整性, 以避免中断系统出现故障后程序运行紊乱.
4.1.1.6 C语言编写中断服务程序
用C语言编写中断服务程序时, 也应确保中断服务程序的函数名与中断向量表(或子中断向量表)中的名称对应. 汇编语言中若用到C语言中使用的名称(变量名、函数名等), 需要将该名称增加"_"(下划线)作为前缀, 例如C中有变量x, 汇编中应该使用_x来调用它[11].
TI文档中介绍了如下两种方式对中断服务函数进行定义[11].
1) 采用默认中断函数名c_intd
任何一个函数若名称为c_intd, d代表数字0~9, 它就默认是中断服务程序. 其中c_int0保留为系统重启中断服务程序, 不可作为其它函数使用. 例如:
void c_int1( )
{

}
2) 采用中断关键字interrupt
interrupt void gisr2( )
{

} // interrupt关键字在void之前或之后都是正确的

应注意, 当进入C语言中断服务程序后, 默认是关总中断状态, 即寄存器状态INTM=1. 如果用户希望实现中断嵌套, 应在C语言中断服务程序中重新打开中断, 即执行INTM=0的操作.
应注意, 任何C中断服务程序都必须定义为void无参数型, 并且还应注意编译器设置条件中不能选择"oe"选项, 如图4 2所示, 也就是保持中断指针的编译操作.

图4 2 关于中断的编译优化选项
下面以一个具体的C语言例子说明中断程序的处理, 它完成和上一节中ASM中断服务程序相同的操作.
// file: main.c
/*****************************************************************************/
void main
{
asm(" setc INTM "); //关总中断
asm(" clrc CNF "); //B0被配置为数据存储空间
*IMR = 0x0000; //禁止所有中断
*IFR = 0xffff; //清除所有中断标志
*SCSR1 = 0x0004; //PLL四倍频, 允许EVA时钟
*WDCR = 0x006f; //禁止看门狗
*WDKEY = 0x5555;
*WDKEY = 0xaaaa; //清看门狗

*MCRA = 0x0000;
*PBDATDIR = 0xff00; //设置IOPB0~2为输出引脚

*GPTCONA = 0x0000;
*T1CNT = 0x0000;
*T1CON = 0x0f42; //连续加/减计数,128分频,内部时钟,下溢装载CMPR,允许比较
*T1PR = 0x0ffff;
*T1CMPR = 0xefff;

*EVAIMARA = 0x0700; //允许上溢、下溢和比较中断
*EVAIFARA = 0x0700; //清除外设中断标志
*IMR = 0x0002; //允许INT2中断
asm(" clrc INTM "); //允许所有中断

for(; ;); //循环等待中断
}

// file: intfuns.h
/*****************************************************************************/
#define SISR2A 0x002A //T1上溢中断
#define SISR29 0x0029 //T1下溢中断
#define SISR28 0x0028 //T1比较中断

// file: intfuns.c
/*****************************************************************************/
interrupt void gisr2( ) //中断服务程序
{ //在vector.asm中应该用_gisr2引用该函数
int ipivr;

ipivr = *PIVR;
switch (ipivr ){
case SISR2A: //T1上溢中断
*PBDATDIR ^= 0x0001; //翻转IOPB0引脚的状态
*EVAIFRA = 0x0400; //清除T1上溢中断标志位
break;
case SISR29: //T1下溢中断
*PBDATDIR ^= 0x0002; //翻转IOPB1引脚的状态
*EVAIFRA = 0x0200; //清除T1下溢中断标志位
break;
case SISR28: //T1比较中断
*PBDATDIR ^= 0x0004; //翻转IOPB2引脚的状态
*EVAIFRA = 0x0100; //清除T1比较中断标志位
break;
default:
break;
}
}
interrupt void phantom( ) //假中断服务程序
{ //在vector.asm中应该用_phantom引用该函数
*WDKEY = 0x5555;
*WDKEY = 0xaaaa; //清看门狗
}

1 条评论:

  1. 好像是写的书里面的啊 不错
    要多写啊 我们才有学习的机会啊

    回复删除