中断处理是大多数DSP应用系统中不可缺少的一个重要环节。中断程序在大多数场合采用汇编语言编写,这是一件比较繁琐的工作,由于汇编语言与硬件结构息息相关,各公司提供的汇编语言也不尽相同,即使是同一公司的芯片,其芯片类型及产品各代也不相同,同时各DSP之间的汇编语言也存在一定的差异。因此,虽然用汇编语言开发的程序的执行效率很高,但其开发周期长,而且可移植性和可读性差。不仅如此,开发汇编语言程序还对开发人员有较高的专业要求。而C语言被大多数人所熟悉,另外,C语言的可读性、可移植性都非常好,因而能极大地缩短DSP软件开发的周期。而采用C语言编写中断处理程序,调用外围库所提供的中断处理函数及宏定义,同样可以有效地实现对中断控制寄存器的设置,且程序有较好的可读性,而且易于修改和维护。
用C语言编写的中断服务程序主要由以下四步组成:
(1)选择中断源并编写中断服务程序(interrupt service routine); (2)创建并初始化中断向量表(interrupt vectortable); (3)通过设置相关中断控制寄存器来使能中断;
(4)通过编写连接器命令文件(linker commandfile)来完成程序各个段的连接。 2中断处理基本概念
2.1中断服务表(IST)和指针寄存器(ISTP)
中断服务表IST(Interrupt Service Table)是包含中断服务代码取指包的一个地址表。当CPU开始处理一个中断时,一般要参照中断服务表进行。IST包含16个连续取指包,每个中断服务取指包都含有指令,每个单独的取指包恰好对应一个中断服务程序。图1所示是其中断服务取指包和中断服务表的示意图。
中断服务表指针寄存器ISTP(interrupt servicetable pointer)用于确定中断服务程序在中断服务表中的地址。ISTP中的字段ISTB确定IST的地址的基值,另一字段HPEINT确定特定的中断,并给出这一特定中断取指包在IST中的位置。 2.2中断服务取指包(ISFP)
中断服务取指包ISFP(Interrupt Service FetchPacket)是用于服务中断的取指包。为了使中断结束后能够返回主程序,取指包中包含有一条跳转到中断返回指针所指地址的指令。因为跳转指令有5个延迟间隙,所以在跳转指令后需加上一条"NOP5"指令,否则CPU将会在跳转之前开始执行下一个ISFP中的5个执行包。
2.3创建中断向量表(interrupt vectortable)
因为中断向量表中的每个向量都必须分布在取指包的边界上,所以中断向量表通常都用汇编语言编写。每个取指包必须包含32位指令,每个向量中既可以包含跳转到中断服务程序的指令,也可包含完整的中断服务程序。
连接时如果选择“-c”或“-cr”选项,C编译器实时支持库将自动创建函数:“c int00”。这个函数对应C程序的入口处。因此,复位中断向量(reset)必须包含一条跳转到“c int00”的指令。另外,在连接时,还需要在汇编语言文件中使用“.sect”指令为中断向量表保留空间,并告诉连接器中断向量表安装在内存中的具体位置。 3基于C语言的中断编程 3.1 C语言中断服务程序
在典型的DSP系统中,一旦中断发生,处理器将从当前程序跳转至中断服务表基地址并执行相应的中断服务程序。
对于C编译器,声明中断服务程序有以下两种方法:
(1)通过关键字“interrupt”告诉C编译器,函数是一个特殊的中断服务程序。用“interrupt”声明过的函数将遵循特定的寄存器保护规则,同时应能返回主程序以继续执行被暂时中断的程序。不过,必须把用“interrupt”声明过的函数定义成无返回值和无参数型。在中断服务函数内部既可使用本地变量,也可使用堆栈或全局变量。如:
(2)通过使用“INTERRUPT”编译标志来指定中断服务程序。这个编译标志可直接用C语言代码来处理中断,参数“func”是中断函数名。编译语法为:
dler是一个中断服务函数)。 3.2用C语言设置中断控制寄存器
在TMS320 C62x中有8个中断控制寄存器。控制状态寄存器(CSR)和中断使能寄存器(IER)用于使能或禁止中断处理;中断标志寄存器(IFR)用于指示中断状态或指出挂起的中断,中断设置寄存器(ISR)和中断清除寄存器(ICR)被用来人工设置或清除IFR中的标志位。同时,还有3个指针寄存器。中断服务表指针(ISTP)通常指向中断服务表的起始地址,不可屏蔽中断返回指针(NRP)和可屏蔽中断返回指针(IRP)分别包含从不可屏蔽中断和可屏蔽中断返回的地址。 处理一个中断,应依照以下几步进行:
(1)初始化中断服务表指针。具体应参考连接器命令文件(*.cmd),连接器命令文件包含着向量表中能找到具体的指针值。中断服务表指针应指向中断服务表的起始地址,而中断服务表则是在连接命令文件的“.vec”段之后定义的。
(2)将中断源选择号(ISN)映射到CPU中断序号上。TMS320C6x外设有16种中断源,但是CPU只能利用其中的12个。
(3)通过在ICR的特定位写1来手动清除被选择的中断,这样能确保在ICR的位域中没有不想要的数据。
(4)挂接编写的中断服务程序和CPU中断信号。
(5)使能非屏蔽中断NMI。因为NMI的优先级比其它任何一个可屏蔽中断的优先级都要高,所以如果要处理其它可屏蔽中断的话,则应先使能NMI中断。
(6)使能在第2步映射到CPU中断并在第4步被挂上的中断。
(7)通过设置CSR中的GIE位来全局使能所有的可屏蔽中断。如果GIE位非1,则其它剩下的中断将被禁止。
下面的例程将具体说明如何通过TMS320 C6x指令来实现以上7个步骤,具体函数和宏定义可参考有关文献
[2]
。此例中设置了与CPU中断14信号相对应的一个中断服务函数“timerISR”。
序开始处理“timerISR”函数。如果想禁止一个中断,则必须把中断使能寄存器(IER)的相应位清零,可以通过调用宏“INTR_DISABLE(bit)”(#include<intr.h>)或“SET REG(IER,val)”(#include<regs.h>)来设置IER的相应位以实现中断禁止。在宏“INTR DISABLE”中,bit(0≤bit≤15)参数对应于特定中断在CPU中断(0-15)中的序号。宏“SET REG”中的IER参数则表示可对中断使能寄存器(IER)进行操作,而val参数则表示把IER设成与val参数等同的一个值,也就是说,通过val参数可间接禁止中断,不过,调用宏时,必须先将val参数值转换成十进制形式。
如果要禁止所有的可屏蔽中断,则应调用宏“INTR GLOBAL DISABLE”,然后通过它将控制状态寄存器(CSR)的GIE位清零来禁止所有的可屏蔽中断。 3.3编写连接器命令文件(linker command file)
当连接中断程序时,还需要用到外围支持库中提供的一些文件(如intr.h,intr_.asm,regs.h等)。因此,在连接时,应使用“-l”连接选项把“dev6x.lib”库文件连接到“ldev6x.lib”上。 除此此外,在连接器命令文件中,应当还包含以下代码段:
连接器命令文件在内部程序存储区(IPM)专门开辟了一个长度为200h的VECTORS区域,以存放中断向量“.vec”段,应用时可根据具体情况进行适当的修改。 3.4程序示例
下面给出一段程序源代码,中断源是DSP的EXTINT7信号,而INT7 ISR是举例用的中断服务函数名(仅实现打印一句话的功能,编程时可根据具体需要用别的代码来代替)。软件环境是TI公司的CCS2.0,DSP采用TI的TMS320C6x系列芯片。执行程序时,主函数对DSP及外围初始化后一直执行循环(注意:初始化程序在以下代码中没有体现,编程时应根据具体需要进行添加),一旦外部中断7的信号出现,程序便进入中断服务函数并执行,以打印出指定内容。需要注意的是,在添加库文件时,务必要把外围支持库dev6x.lib添加到工程之中。其具体的实现代码如下: