TMS320C6000系列中断设置的简明方法

2019-07-22 13:01发布

一、 简述
本文介绍TMS320C6000系列中断设置的简明方法。通过示例定时器中断,MCBSP串口接收中断及外部中断这三种中断实现过程,介绍如何实现中断各个寄存器的配置,中断向量表书写以及中断服务函数。最后提供一个简要的示例程序可供大家下载使用。此示例在DSK6416的TI官方实验板上通过测试。由于定时器和串口工作模式较繁,因此对中断无关部分不做介绍。
二、 实现DSP中断需要做哪些通用工作
设置允许哪些非屏蔽中断
设置各个允许的非屏蔽中断的中断来源
设置开启总中断
设计中断向量表
将中断向量表通过cmd文件挂载到指令内存
提供中断处理函数
如果中断向量表首地址挂载的不是地址,那么需要设置中断向量表地址寄存器
对于不同的中断源,需要做各个自己的工作,比如如果是外部中断,那么需要设置管脚极性,即由高->低产生中断抑或反之。
为了照顾知识较少的读者,下面将从一个新工程出发,引导大家建立一个中断示例程序。
如果您对建立工程很熟悉,可以跳过此步。
三、 建立新工程
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.1C6000cgtoolslib ts6400.lib
如果您使用的是其他器件类型,请在lib文件夹内选择其他器件库。
添加源文件,选择File->New->Source File,保存为main.c到工程路径下。
在此文件内书写主函数。

[mw_shl_code=c,true]void main(void)
{
while(1);
}[/mw_shl_code]
最后通过如2步骤添加此文件到工程。
3.添加寄存器别名定义头文件。在本示例中,对需要用到的寄存器定义别名后,构成global.h文件,内容在后文逐步介绍。在此可以建立一个空文件,并在main.c中包括它。
[mw_shl_code=c,true]#i nclude "global.h"[/mw_shl_code]
到此,一个DSP的新工程框架制作完毕。
4.添加cmd链接文件
为了实现链接时内存配置,我们需要提供一个cmd文件,为了方便,可以从官方的示例程序中拷贝一份,再加以修改。
在安装目录下D:CCStudio_v3.1 utorial器件类型hello1示例下,会找到一个hello1.cmd,
将其拷贝到本工程目录下,并将其改名为link.cmd,最后将其添加到工程中。
由于此文件没有声明stack和heap,会产生警告,如果动态数据较多也容易溢出。因此我们最好在此文件提供stack和heap的大小,其值可根据实际情况调整,修改后,此文件内容类似为:
[mw_shl_code=c,true]-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
}[/mw_shl_code]
至此,工程建立完毕,可以编译一遍,观察是否正常。
---------------------------  intexample.pjt - Debug  ---------------------------
[main.c] "D:CCStudio_v3.1C6000cgtoolsincl6x" -g -fr"D:/intexample/Debug" -d"_DEBUG" -mv6400 -@"Debug.lkf" "main.c"[Linking...] "D:CCStudio_v3.1C6000cgtoolsincl6x" -@"Debug.lkf"
<Linking>Build Complete,
  0 Errors, 0 Warnings, 0 Remarks.
四、 定时器中断设计
首先,我们先实现一个定时器中断,因为它不受外部影响,容易测试。
在global.h文件中,加入控制寄存器和中断寄存器别名定义,另外为了使用定时器1,也应对其别名进行定义:
[mw_shl_code=c,true]/*定义控制寄存器*/
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 */[/mw_shl_code][mw_shl_code=c,true]/* 定义中断选择寄存器*/
#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[/mw_shl_code]之后,在main函数中对定时器进行初始化,在此我们使用Timer1,参数初始化函数如下:
[mw_shl_code=c,true]void Timer1_Init(void)
{
*( volatile unsigned int* )CTL1= 0x00000201;  //计数器功能设置
*( volatile unsigned int* )PRD1= 0x1000;   //计数器周期值
*( volatile unsigned int* )CTL1|= 0x000000C0;  //计数器清零,启动[/mw_shl_code]上句的注释:[mw_shl_code=c,true]
*( volatile unsigned int* )CTL1=CTL1|0x000000C0}[/mw_shl_code]
并在主函数中调用它。随后我们设置中断寄存器参数。
DSP支持1个RESET中断,1个NMI(不可屏蔽中断),12个可屏蔽中断(INT4-15),它们具有优先级顺序,INT4最高,INT15最低。每个中断号(INT4-INT15)都可以设置任何中断来源。在此我们选择中断INT10,即开启中断10,并设置其中断来源为定时器1,即在MUXH或MUXL中指定位上填写中断来源选择码:
中断来源选择码定义如下:(此内容可以通过帮助中搜索INTSEL得到)
I
NTSEL(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                              保留,填

MUXL
位                                   中断来源
30-26                                 INTSEL9
25-21                                 INTSEL8
20-16                                 INTSEL7
14-10                                 INTSEL6
9-5                                    INTSEL5
4-0                                    INTSEL4
31,15                               保留,填

因此,我们设置MUXH的第4-0位为定时器1的中断选择码00010,其余位可以任意设置(在此可以填1)。转换为16进制后,设置如下:
[mw_shl_code=c,true] *( volatile unsigned int* )MUXH=0x7fff7fe2;
MUXL可以不设置。
开启中断到IE10,使能全局中断:
IER |= 0x0000 0402;   // IE10=1
CSR |= 0x00000001;   // 全局中断使能[/mw_shl_code]以上就完成了中断参数的配置,中断启动并且可以进入了。下面是中断的处理过程。主要分为设计中断向量表和中断处理函数。


我们可以从DSP CCS的示例中复制一份向量表的雏形。
例如CCStudio_v3.1 utorialdsk6416hello1vectors.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 irp
  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区域,起点为地址。其大小为400H,将原先的ISRAM起始点修改。并将SECTIONS中的.vector指向自己定义的内存区域。[mw_shl_code=c,true]
MEMORY
{
   INT         : origin = 0x00000000,  len = 0x0000400
   ISRAM      : origin = 0x00000400,  len = 0x1000000
}

SECTIONS
{
        .vectors > INT

}[/mw_shl_code]
中断向量表设置、安装完毕。
最后,设计中断服务函数,在main.c中添加:
[mw_shl_code=c,true]interrupt void xint0_isr(void)
{
}[/mw_shl_code]
注意,一定要标识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,对于每个位有:
:低->高产生中断
1:高->低产生中断

因此设置它即可完成极性变化。
下面,以设置外部端口INT7中断,并将其挂载到12号中断为例,简述实现过程:
将12号中断设置为外部中断7,即MUXH(14:10)=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中加入服务函数:
[mw_shl_code=c,true]interrupt void extint7_isr(void)
{
}[/mw_shl_code]
在硬件上,对INT7/GPIO7管脚产生一个低->高的信号,则可以触发出中断。
若改变此极性,可以设置EXTPOL第四位为1:
  *( volatile unsigned int* )EXTPOL|= 0x00000008;

此时,一个高->低的信号可以产生中断。
需要注意的是,如果你对GPIO进行过初始化,一定要保证GPEN的中断引脚相应位为1。如全部使能:
   *(volatile unsigned int* )GPEN = 0x000000F0;

六、 MCBSP串口接收中断设计
在实际应用过程中,经常需要通过中断接收串口数据。在此假设添加MCBSP0接收中断到11号。
首先,将MCBSP0别名添加到global.h文件。
设置MCBSP0参数并启用,其初始化函数为:[mw_shl_code=c,true]
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;
}[/mw_shl_code]
并在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

制作中断服务程序,将数据取出:[mw_shl_code=c,true]
interrupt void rint0_isr(void)
{
int DotRev;
DotRev=*( volatile unsigned int *)McBSP0_DRR;
}[/mw_shl_code]
修改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

这时,所有的任务完成了,可以通过设置断点观察一下接收的数值。
另外需要注意一定要在服务程序中将数据取出,否则会停止接到新的数据。
0条回答

一周热门 更多>