题目:有关TI DSP的一些东西(整理一些网络资源及手册资料)--外设寄存器和CPU控制寄存器、数据类型、中断的使用
===================================================================
IER和IFR在并没有定义地址,因为它是CPU是CPU寄存器,只有存储器才会有明确的地址。
只需如此在头文件中如此定义:
extern cregister volatile unsigned int IFR;
extern cregister volatile unsigned int IER
在使用时要注意IER 可采用直接付值的方式,而IFR为只读如果要改变值 只可用&= 和|=
转自:http://bbs.eeworld.com.cn/thread-111092-1-9.html
===================================================================
看顾老师的手把手教你学DSP,每个例程里面main()中有下面几行代码:DINT; IER = 0x0000; IFR = 0x0000;......IER |= M_INT1 ; 刚开始不知道其中的原理是什么,为什么可以直接使用IER和IFR这两个寄存器,因为头文件并没有给出IER,IFR两个CPU寄存器的地址,不像其它寄存器那样,通过#define 寄存器名字 地址定义之后,我们便可以直接操作寄存器了。 看了TMS320C28x Optimizing CC++ Compiler.pdf P130 之后才明白其中的原因。
The compiler extends the C language by adding the cregister keyword to allow high level language access to control registers.
When you use the cregister keyword on an object, the compiler compares the name of the object to a subset of standard control registers for the TMS320C28x . If the name matches, the compiler generates the code to reference the control register. If the name does not match, the compiler issues an error.
The cregister keyword can only be used in file scope. The cregister keyword is not allowed on any declaration within the boundaries of a function. It can only be used on objects of type integer or pointer. The cregister keyword is not allowed on objects of any floating-point type or on any structure or union objects.
Once you have declared the register, you can use the register name directly. Note that IFR is read only and can only be set using the| (or) operation with an immediate.
For example: IFR | = 0x4; The IFR can only be cleared using the& (AND) operation with an immediate. Any other use of the IFR register will result in an error. 疑问:那上面的IFR = 0x0000 直接赋值清除中断标志时怎么回事?
转自:http://www.hellodsp.com/bbs/forum.php?mod=viewthread&tid=183888
====================================================================
分清楚外设寄存器(peripheral register)和CPU控制寄存器(CPU Control Registers),外设寄存器是有地址的,通过地址直接仿问,而CPU控制寄存器没有地址,访问时直接extern cregister volatile unsigned int声明即可,详细可参见以上两段转载内容。
针对TMS320C6416T的外设寄存器可参见文档《[sprs226m]TMS320C6414T, TMS320C6415T, TMS320C6416T FIXED-POINT DIGITAL SIGNAL PROCESSORS》的Table4~Table22,针对CPU控制寄存器详见文档《[spru186v]TMS320C6000 Assembly Language Tools v7p3 User's Guide》的Table3-2 ,有关CPU控制寄存器的详细描述详见文档《[spru732j]TMS320C64x_C64x+ DSP CPU and Instruction Reference Guide》的2.8节,另外该文档的第5章较大篇幅的讲述了DSP的中断。
有关中断的三个外设寄存器MUXH、MUXL、EXTPOL说明详见文档《[spru646a]TMS320C6000 DSP Interrupt Selector Reference Guide》 。
使用中断时经常要调用intr.h文件,但内部的以下三个函数是没有源代码的,它们属于外设支持库中的函数,详细说明可以参考文档《[spru273b]TMS320C6x Peripheral Support Library Programmer’s Reference》。
void intr_reset(void);
void intr_init(void);
void intr_hook(void (*fp)(void),int cpu_intr);
DSP的数据类型说明如下,来自文档《[spru198k]TMS320C6000 Programmer’s Guide》 :
char 8 bits
short 16 bits
int 32 bits
float 32 bits
long 40 bits
long long 64 bits
double 64 bits
以上主要说了一些内容对应哪个文档,也可以在CCS下help---contents,搜索相应的关键字,比如要查看IER这个寄存器,直接index IER即可。
=========================================================
DSP中断使用:(摘自http://embed.chinaitlab.com/DF/750409_2.html)
下面以响应外部中断4为例,给出具体的中断处理流程:(1)初始化中断intr_init(),初始化ISTP,其值为中断服务表的首地址;(2)将外部中断源4映射到CPU中断4上:intr map(CPU INT4,ISN EXT INT4),置中断选择寄存器相应位置处值为中断服务号4;(3)装载中断服务子程序:intr_hook(fp,CPU_INT4),函数指针指向中断处理表中的相应表项;(4)手动清除中断标志寄存器IFR中的中断标志位:INT_CLR_FLAG(CPU_INT4),保证IFR任何比特域中没有不必要的值;(5)使能非屏蔽中断NMI,否则其余中断皆不能处理,中断使能寄存器IER中的NMI E位置1:INTR_ENABLE(CPU INT NMI);(6)使能CPU中断4,中断使能寄存器IER中的相应位置1:INTR_ENABLE(CPU INT4);(7)通过设置控制状态寄存器CSR中的GIE位,全局使能所有可屏蔽中断:INTR_GL OBAL_ENABLE()。
=========================================================
一、 简述
本文介绍TMS320C6000系列中断设置的简明方法。通过示例定时器中断,MCBSP串口接收中断及外部中断这三种中断实现过程,介绍如何实现中断各个寄存器的配置,中断向量表书写以及中断服务函数。最后提供一个简要的示例程序可供大家下载使用。此示例在DSK6416的TI官方实验板上通过测试。由于定时器和串口工作模式较繁,因此对中断无关部分不做介绍。
二、 实现DSP中断需要做哪些通用工作
设置允许哪些非屏蔽中断
设置各个允许的非屏蔽中断的中断来源
设置开启总中断
设计中断向量表
将中断向量表通过cmd文件挂载到指令内存
提供中断处理函数
如果中断向量表首地址挂载的不是0地址,那么需要设置中断向量表地址寄存器
对于不同的中断源,需要做各个自己的工作,比如如果是外部中断,那么需要设置管脚极性,即由高->低产生中断抑或反之。
为了照顾知识较少的读者,下面将从一个新工程出发,引导大家建立一个中断示例程序。
如果您对建立工程很熟悉,可以跳过此步。
三、 建立新工程
1.点击 Project->New,设置Project Name为intexample,Project Type为Executable,Target选择您需要的器件,在此由于本人使用的是DSK6416评估板。因此选择TMS320C64XX。
2.添加标准库rts6400.lib,以便自动产生c_int00等函数。右击当前工程,选择“Add Files to Project”,选择库所在路径,一般为CCS安装自带,可参考本CCS3.1版本的路径地址:\CCStudio_v3.1\C6000\cgtools\lib\rts6400.lib
如果您使用的是其他器件类型,请在lib文件夹内选择其他器件库。
添加源文件,选择File->New->Source File,保存为main.c到工程路径下。
在此文件内书写主函数。
void main(void)
{
while(1);
}
最后通过如2步骤添加此文件到工程。
3.添加寄存器别名定义头文件。在本示例中,对需要用到的寄存器定义别名后,构成global.h文件,内容在后文逐步介绍。在此可以建立一个空文件,并在main.c中包括它。
#i nclude "global.h"
到此,一个DSP的新工程框架制作完毕。
4.添加cmd链接文件
为了实现链接时内存配置,我们需要提供一个cmd文件,为了方便,可以从官方的示例程序中拷贝一份,再加以修改。
在安装目录下D:\CCStudio_v3.1\tutorial\器件类型\hello1示例下,会找到一个hello1.cmd,
将其拷贝到本工程目录下,并将其改名为link.cmd,最后将其添加到工程中。
由于此文件没有声明stack和heap,会产生警告,如果动态数据较多也容易溢出。因此我们最好在此文件提供stack和heap的大小,其值可根据实际情况调整,修改后,此文件内容类似为:
-stack 0x1000
-heap 0x1000
MEMORY
{
ISRAM : origin = 0x0, len = 0x1000000
}
SECTIONS
{
.vectors > ISRAM
.text > ISRAM
.bss > ISRAM
.cinit > ISRAM
.const > ISRAM
.far > ISRAM
.stack > ISRAM
.cio > ISRAM
.sysmem > ISRAM
}
至此,工程建立完毕,可以编译一遍,观察是否正常。
--------------------------- intexample.pjt - Debug ---------------------------
[main.c] "D:\CCStudio_v3.1\C6000\cgtools\bin\cl6x" -g -fr"D:/intexample/Debug" -d"_DEBUG" -mv6400-@"Debug.lkf" "main.c"
[Linking...] "D:\CCStudio_v3.1\C6000\cgtools\bin\cl6x" -@"Debug.lkf"
Build Complete,
0 Errors, 0 Warnings, 0 Remarks.
四、 定时器中断设计
首先,我们先实现一个定时器中断,因为它不受外部影响,容易测试。
在global.h文件中,加入控制寄存器和中断寄存器别名定义,另外为了使用定时器1,也应对其别名进行定义:
/*定义控制寄存器*/
extern cregister volatile unsigned int AMR; /* Address Mode Register */
extern cregister volatile unsigned int CSR; /* Control Status Register */
extern cregister volatile unsigned int IFR; /* Interrupt Flag Register */
extern cregister volatile unsigned int ISR; /* Interrupt Set Register */
extern cregister volatile unsigned int ICR; /* Interrupt Clear Register */
extern cregister volatile unsigned int IER; /* Interrupt Enable Register */
extern cregister volatile unsigned int ISTP; /* Interrupt Service Tbl Ptr */
extern cregister volatile unsigned int IRP; /* Interrupt Return Pointer */
extern cregister volatile unsigned int NRP; /* Non-maskable Int Return Ptr*/
extern cregister volatile unsigned int IN; /* General Purpose Input Reg */
extern cregister volatile unsigned int OUT; /* General Purpose Output Reg */
/* 定义中断选择寄存器 */
#define MUXH 0x019C0000
#define MUXL 0x019C0004
#define EXTPOL 0x019C0008
/*定义定时器1寄存器*/
#define CTL1 0x01980000 //Timer1 control register
#define PRD1 0x01980004 //Timer1 period register
#define CNT1 0x01980008 //Timer1 counter register
之后,在main函数中对定时器进行初始化,在此我们使用Timer1,参数初始化函数如下:
void Timer1_Init(void)
{
*( volatile unsigned int* )CTL1= 0x00000201; //计数器功能设置
*( volatile unsigned int* )PRD1= 0x1000; //计数器周期值
*( volatile unsigned int* )CTL1|= 0x000000C0; //计数器清零,启动
}
并在主函数中调用它。
随后我们设置中断寄存器参数。
DSP支持1个RESET中断,1个NMI(不可屏蔽中断),12个可屏蔽中断(INT4-15),它们具有优先级顺序,INT4最高,INT15最低。每个中断号都可以设置任何中断来源。在此我们选择中断INT10,即开启中断10,并设置其中断来源为定时器1,即在MUXH或MUXL中指定位上填写中断来源选择码:
中断来源选择码定义如下:(此内容可以通过帮助中搜索INTSEL得到)
INTSEL(Interrupt Selection Number Deion)
00000b DSPINT Host port host to DSP interrupt
00001b TINT0 Timer 0 interrupt
00010b TINT1 Timer 1 interrupt
00011b SD_INT EMIF SDRAM timer interrupt
00100b EXT_INT4 External interrupt 4
00101b EXT_INT5 External interrupt 5
00110b EXT_INT6 External interrupt 6
00111b EXT_INT7 External interrupt 7
01000b EDMA_INT EDMA channel (0-15) interrupt
01001-01011b Reserved
01100b XINT0 McBSP0 transmit interrupt
01101b RINT0 McBSP0 receive interrupt
01110b XINT1 McBSP1 transmit interrupt
01111b RINT1 McBSP1 receive interrupt
10000-11111b Reserved
从中得到定时器1的中断选择码为00010。
MUXH和MUXL的寄存器定义如下:(也可以通过帮助得到)
MUXH
位 中断来源
30-26 INTSEL15
25-21 INTSEL14
20-16 INTSEL13
14-10 INTSEL12
9-5 INTSEL11
4-0 INTSEL10
31,15位保留,填0
MUXL
位 中断来源
30-26 INTSEL9
25-21 INTSEL8
20-16 INTSEL7
14-10 INTSEL6
9-5 INTSEL5
4-0 INTSEL4
31,15位保留,填0
因此,我们设置MUXH的第4-0位为定时器1的中断选择码00010,其余位可以任意设置(在此可以填1)。转换为16进制后,设置如下:
*( volatile unsigned int* )MUXH=0x7fff7fe2;
MUXL可以不设置。
开启中断到IE10,使能全局中断:
IER |= 0x00000402; // IE10=1
CSR |= 0x00000001; // 全局中断使能
以上就完成了中断参数的配置,中断启动并且可以进入了。下面是中断的处理过程。主要分为设计中断向量表和中断处理函数。
我们可以从DSP CCS的示例中复制一份向量表的雏形。例如\CCStudio_v3.1\tutorial\dsk6416\hello1\vectors.asm
将其拷贝到本工程目录下并加入工程中。
中断向量表包含了16个中断处理单元,每个单元限制必须是8条指令。如果不够8条,可以用nop填充,(但nop 4算1条语句),如果服务程序过多,那么可以制作专门的中断服务程序,此时此表只起到跳转作用,这样CPU就可以正确寻址找到正确的中断服务入口。
首先分析一下此文件。
文件开始定义了一个宏,用于处理未用到的中断。
unused .macro id
.global unused:id:
unused:id:
b unused:id: ; nested branches to block interrupts
nop 4
b unused:id:
nop
nop
nop
nop
nop
它的做法是让程序进入死循环,我认为这种做法未必最优,因此我建议使用直接返回的方式。返回到被中断地址,对于可屏蔽中断为b irp,因此将此宏部分替换成,注意一定要凑够8条。
unused .macro id
.global unused:id:
unused:id:
b irp
nop
nop
nop
nop
nop
nop
nop
.endm
这样,即使我们误开启了此中断,也会顺利返回。当然,如果我们确信的确没有开启,那么其内容是无意义的。
代码的正文部分用了一系列unused n来插入此宏,起到占地的作用。
由于NMI的返回与可屏蔽中断不同,它在向量表中位于RESET之下,即unused 1,我们将其删除,替换为
NMI: b nrp
nop
nop
nop
nop
nop
nop
nop
为了实现定时器1中断的处理,我们将unused 10删除,替换为我们自己的中断跳转程序,如下:
INT10:
stw b0,*--b15
mvkl _xint0_isr,b0
mvkh _xint0_isr,b0
b b0
ldw *b15++,b0
nop 3
nop
nop
另外,需要和语句:
.ref _c_int00 ; C entry point
类似,添加处理程序的引用
.ref _xint0_isr ; timer 1 interrupt handler
由于中断向量表的位置需要特定指明,且应对齐到400H,在此文件中,已经定义了段名:
.sect ".vectors"
因此我们需要将此.vector代码段挂载到专门的一段指定内存区域。
修改link.cmd 链接文件,加入INT区域,起点为0地址。其大小为400H,将原先的ISRAM起始点修改。并将SECTIONS中的.vector指向自己定义的内存区域。
MEMORY
{
INT : origin = 0x00000000, len = 0x0000400
ISRAM : origin = 0x00000400, len = 0x1000000
}
SECTIONS
{
.vectors > INT
…
}
中断向量表设置、安装完毕。
最后,设计中断服务函数,在main.c中添加:
interrupt void xint0_isr(void)
{
}
注意,一定要标识interrupt关键字,用于产生中断返回语句b irp,同时,此函数的入口参数和出口参数应为void。如果需要更新变量,可以通过全局变量的方式。
另外,C语言函数名称与汇编相差一个“_”,请在设计中断向量表时注意添加。
经过上述步骤,整个定时器中断的制作过程就完成了。此时可以在interrupt void xint0_isr(void)上添加一个断点,运行后应该停在此处。如果进入失败,可以先在vector.asm的INT10:stw b0,*--b15一句上设置断点,如果没有进入此处,证明中断没有进来,可以检查是否在参数设置上出现了问题。
五、 外部中断设计
DSP6000系列提供了INT4-7四个中断输入管脚,因此可以通过此四个管脚的输入电平变化实现外部中断。对于电平变化的极性,分为高到低,低到高两种,因此,DSP采用寄存器EXTPOL来设置。EXTPOL只有低4位有效,分别代表INT4-7,对于每个位有:
0:低->高产生中断
1:高->低产生中断
因此设置它即可完成极性变化。
下面,以设置外部端口INT7中断,并将其挂载到12号中断为例,简述实现过程:
将12号中断设置为外部中断7,即MUXH(4:0)=00111,此时MUXH设置为:
*( volatile unsigned int* )MUXH=0x7fff7ce2;//0111 1100 1110 0010
将IER的第12位开启。
IER |= 0x00001402; // IE10=1 IE12=1
对vectors.asm的unused 12替换为:
INT12:
stw b0,*--b15
mvkl _extint7_isr,b0
mvkh _extint7_isr,b0
b b0
ldw *b15++,b0
nop 3
nop
nop
并添加引用
.ref _extint7_isr
在main.c中加入服务函数:
interrupt void extint7_isr(void)
{
}
在硬件上,对INT7/GPIO7管脚产生一个低->高的信号,则可以触发出中断。
若改变此极性,可以设置EXTPOL第四位为1:
*( volatile unsigned int* )EXTPOL|= 0x00000008;
此时,一个高->低的信号可以产生中断。
需要注意的是,如果你对GPIO进行过初始化,一定要保证GPEN的中断引脚相应位为1。如全部使能:
*(volatile unsigned int* )GPEN = 0x000000F0;
六、 MCBSP串口接收中断设计
在实际应用过程中,经常需要通过中断接收串口数据。在此假设添加MCBSP0接收中断到11号。
首先,将MCBSP0别名添加到global.h文件。
设置MCBSP0参数并启用,其初始化函数为:
void MCBSP0_Init(void)
{
*( volatile unsigned int* )McBSP0_SPCR = 0x00000000;
*( volatile unsigned int* )McBSP0_SRGR = 0x200000FF;
*( volatile unsigned int* )McBSP0_PCR = 0x00000800;
*( volatile unsigned int* )McBSP0_XCR = 0x000100A0;
*( volatile unsigned int* )McBSP0_RCR = 0x000100A0;
*( volatile unsigned int* )McBSP0_MCR = 0x00000000;
*( volatile unsigned int* )McBSP0_SPCR |= 0x00C10001;
}
并在main函数中调用。
开启中断11:
IER |= 0x00001C02; // IE10=1 IE11=1 IE12=1
并将MUXH(9:5)=01101,综合以上三个中断,此时MUXH为:
*( volatile unsigned int* )MUXH=0x7fff1da2;//0001 1101 1010 0010
当然,如果只考虑现在的中断,MUXH可以设置为:
*( volatile unsigned int* )MUXH=0x7fff7dbf;//0111 1101 1011 1111
制作中断服务程序,将数据取出:
interrupt void rint0_isr(void)
{
int DotRev;
DotRev=*( volatile unsigned int *)McBSP0_DRR;
}
修改vectors.asm,替换unused 11为:
INT11:
stw b0,*--b15
mvkl _rint0_isr,b0
mvkh _rint0_isr,b0
b b0
ldw *b15++,b0
nop 3
nop
nop
添加引用:
.ref _rint0_isr ; mcbsp 0 receive interrupt handler
这时,所有的任务完成了,可以通过设置断点观察一下接收的数值。
另外需要注意一定要在服务程序中将数据取出,否则会停止接到新的数据。
七、 其他话题
1.设置中断向量表起始位置
上文讨论的都是将中断向量表放置在0地址,如果需要放置到任意地址(以400H对齐),那么就需要提供向量表起始地址。
比如我们的终端向量位置:INT设置为:
MEMORY
{
INT : origin = 0x00000400, len = 0x0000400
ISRAM : origin = 0x00000800, len = 0x1000000
}
那么我们在初始化中断时,应设置:
ISTP=0x00000400;
2.查看现在的中断位图
可以查看中断标志寄存器IFR相应位(15:0)看是否有中断到达。
3.清除/设置原先的中断
如果需要清除原先的中断,可以通过对ICR寄存器相应位置位。如果希望人工触发中断,可以设置ISR寄存器相应位置位,它们将更新IFR位图。
比如,我们在定时器中断服务程序中,通过设置ISR的第12位,人工触发外部INT7的12号中断。
interrupt void xint0_isr(void)
{
ISR=0x00001000;
}
那么CPU将执行extint7_isr(void)处理此中断。
又比如,在上例的外部中断中,有时会出现刚一开机,没有发送信号就有中断进来的情况,那么怎样克服呢?可以通过ICR克服。对ICR置位可以清除可屏蔽中断。对应位有效。比如在设置中断初始化时清除所有原先的中断。那么可以加入语句:
ICR =0xffffffff;
4.DSP/BIOS下的中断设置
在DSP/BIOS管理下,我们不需要自己设定中断向量表,以及中断初始化等等,一切通过BIOS的图形化设置即可完成。
添加一个DSP/BIOS
选择File->New,在本测试下选择DSK6416,读者可根据自己实际需要选取。保存为Configuration1.cdb。
将其添加到工程。
如上例需求,选择HWI的10,11,12号中断,右键选择Properties分别填写如下参数:
HWI_INT10 interrupt source=Timer_1 =_xint0_isr(注意下划线!)
HWI_INT11 interrupt source=MCSP_0_Receive =_rint0_isr
HWI_INT11 interrupt source=External_Pin_7 =_extint7_isr
在main函数中可以通过同样的方法启动中断。
IER |= 0x00001C02; // IE10=1 IE11=1 IE12=1
CSR |= 0x00000001; //全局中断使能
至此配置完毕。
5.中断进不来怎样检查?
首先检查是否设置IER相应位开启,CSR最低位置位,其次看看中断向量表地址是否设置正确。如果确认无误。在向量表中断应当进入的位置设定断点。运行看是否执行到断点。如果有,那么看看中断服务程序有没有执行到。如果中断只进来一次后就再也无法进入了,可以查看中断向量表是否能返回到原程序,如果不能返回,查看是否是8条语句。另外可以通过跟踪查看b irp语句是否被执行。如果可以正常返回到原程序,例如串口接收,看看是否没有取值导致阻塞。如果是这样需要将原先值取出才有新的中断。
6.中断若干寄存器的说明到哪里去找?
可以通过Help->Contents,搜索关键字的方法得到。也可以参考官方文档。
八、 附带程序
本教程以附件提供三个示例:
1. onlytimerint——仅包含了定时器的示例
2. int——包含了完整的3种中断
3. intbios——提供了DSP/BIOS下的此3种中断。
九、 参考文档
《TMS320C64x/C64x+ DSP CPU and Instruction Set Reference Guide》Chapter5 interrupts