TI C54xx DSP 十天速成讲义 <三>
[replyview]实验三 中断
中断的概念应该不陌生,指的是当某个事件发生时,暂停当前的操作,转向中断服务程序,执行完后再返回继续原来的操作。这使得DSP能够处理多个任务。
DSP有许多中断源,可以设置中断控制寄存器来确定响应哪些中断而不理会哪些中断。本实验介绍最常用的定时器中断和外部中断的使用方法,并介绍中断向量表和中断向量指针。
实验3.1 定时器中断:方波发生器
实验目的:学习定时器中断的设计方法
回顾一下实验一控制LED的闪烁实际就是一个简单的方波发生器。但不足的是延时的方法定时不精确,另外还有一个缺点是在执行延时的过程中DSP就无法执行其它指令,这时就可以用定时器来改进。
使用定时器首先要对它初始化,基本步骤如下:
1.关掉中断
2.停止定时器运行。
3.设定时器的定时长度
4.允许定时器中断
5.运行定时器
6.打开中断
现以简单的方波程序为例:
;==============================================
; fangbo1.asm
; 利用定时器Timer0在XF脚产生周期2ms的的方波
;==============================================
.title "fangbo1.asm"
.mmregs
.def codestart ;程序入口
.def TINT0_ISR ;Timer0中断服务程序
STACK .usect "STACK",10H ;分配堆栈空间
;设定定时器0控制寄存器的内容
K_TCR_SOFT .set 0B<<11 ;TCR第11位soft=0
K_TCR_FREE .set 0B<<10 ;TCR第10位free=0
K_TCR_PSC .set 0B<<6 ;TCR第9-6位,可设TDDR一样,也可不设自动加载
K_TCR_TRB .set 1B<<5 ;TCR第5位TRB=1此位置1,PSC会自动加载的
K_TCR_TSS .set 0B<<4 ;TCR第4位TSS=0
K_TCR_TDDR .set 1001B<<0 ;TCR第3-0位TDDR=1001B
K_TCR .set
K_TCR_SOFT|K_TCR_FREE|K_TCR_PSC|K_TCR_TRB|K_TCR_TSS|K_TCR_TDDR
K_TCR_STOP .set 1B<<4 ;TSS=1时计数器停止
.data
DATA_DP: ;数据区指针
XF_Flag: .word 1 ;当前XF的电平标志,如果XF_Flag=1,则XF=1
;================================================
;主程序:
;================================================
.text
CodeStart:
STM #STACK+10H,SP ;设堆栈指针SP
LD #DATA_DP,DP ;设数据地址DP
STM #XF_Flag,AR2 ;AR指向XF标志
;改变中断向量表位置
K_IPTR .set 0080h ;指向0080H,默认是FF80
LDM PMST,A
AND #7FH,A ;保留低7位,清掉高位
OR #K_IPTR,A ;
STLM A,PMST
*初始化定时器0
*f=50MHz,定时2ms时:
*根据定时器长度计算公式:Tt=T*(1+TDDR)*(1+PRD)
*给定TDDR=9,PRD=9999,CLKOUT主频f=40MHz,T=25ns
*Tt=20ns*(1+9)*(1+9999)=2000us=2ms
*f=100Mhz,定时最大是:10ns*2^4*2^16=10ms,
PERIOD .set 9999 ;定义计数周期
STM K_TCR_STOP,TCR ;停止计数器0
; STM #PERIOD,TIM ;可设成跟PRD一样,也可不设自动加载
STM #PERIOD,PRD ;设定计数周期
STM #K_TCR,TCR ;开始Timer0
stm #0008h,IMR ;允许Timer0中断
STM #0008h,IFR ;清除挂起的中断
rsbx intm ;开中断
end: nop
B end
;================================================
;Timer0中断服务程序:TIN0_ISR
;================================================
TINT0_ISR:
PSHM ST0 ;本中断程序影响TC,位于ST0中
;判断当前XF状态并作电平变化
BITF *AR2,#1 ;IF XF_Flag=1 then TC=1 else TC=0
BC ResetXF,TC ;IF TC=1 then XF=0 else XF=1
setXF:
SSBX XF ;置XF为高电平
ST #1,*AR2 ;相应修改标志
B Next
ResetXF:
RSBX XF ;;置XF为高电平
ST #0,*AR2 ;相应修改标志
Next:
POPM ST0
RETE
.end
有时定时的长度不能满足需要,比如DSP工作频率50Mhz时,定时最大值是:20ns*2^4*2^16=20ms。如果需要更长的定时,就要在定时器中断子程序中再加一个计数器,直到产生一定次数的定时中断后再执行相应的操作。程序只需要稍作修改,见附盘的fangbo2.asm
技巧提示:寄存器的不同位通常有不同的含义,初始化时单独设定寄存器的每一位可以增加程序可读性,容易让其它人看懂具体每一位设置的含义,并且易于修改。如果代码太长可以自己写一个初始化的子程序,需要时修改一下调定时再调用。当然如果对寄存器各个位的含义相当熟悉,直接整个初始化也行。可以自行选择这些不同的编程风格。
设计指导:
1.中断向量表
中断向量表是DSP程序的重要组成部分,当有中断发生并且处于允许状态时,程序指针跳转到中断向量表中对应的中断地址。由于中断服务程序一般较长,通常中断向量表存放的是一个跳转指令,指向实际的中断服务程序。下面是5402中断向量表的一个范例,可以作为模板,使用时稍作修改就行:
*****************************************************************
*5402Vectors.asm
*完整的5402中断向量表示例
*5402共有30个中断向量,每个向量占4个字的空间。
*使用向量一般用一条跳转指令转到相应中断服务子程序,其余空位用NOP填充
*未使用的向量直接用RETE返回,是为了防止意外进入未用中断。
*****************************************************************
.sect ".vectors" ;开始命名段.vecotrs
.global CodeStart ;引用程序入口的全局符号定义
;。。。引用其它中断程序入口的全局符号定义
.align 0x80 ; 中断向量表必须对齐128字的页边界
RESET: B CodeStart ; Reset中断向量,跳转到程序入口
NOP ;用NOP填充表中其余空字
NOP ;B指令占了两个字,所以要填两个NOP
NMI: RETE ;不可屏蔽中断
NOP
NOP
NOP
; 软件中断
SINT17 .space 4*16 ;软件中断使用较少,简单起见用0填充
SINT18 .space 4*16
SINT19 .space 4*16
SINT20 .space 4*16
SINT21 .space 4*16
SINT22 .space 4*16
SINT23 .space 4*16
SINT24 .space 4*16
SINT25 .space 4*16
SINT26 .space 4*16
SINT27 .space 4*16
SINT28 .space 4*16
SINT29 .space 4*16
SINT30 .space 4*16
INT0: RETE ;外部中断INT0
NOP
NOP
NOP
INT1: RETE ;外部中断INT1
NOP
NOP
NOP
INT2: RETE ;外部中断INT2
NOP
NOP
NOP
TINT: RETE ;Timer0中断
NOP
NOP
NOP
BRINT0: RETE ;McBSP #0 接收中断
NOP
NOP
NOP
BXINT0: RETE ;McBSP #0 发送中断
NOP
NOP
NOP
DMAC0: RETE ;无定义(默认)DMA0中断
NOP
NOP
NOP
TINT1: RETE ;Timer1中断(默认)或DMA1中断.
NOP
NOP
NOP
INT3: RETE ;外部中断3
NOP
NOP
NOP
HPINT: RETE ;HPI中断
NOP
NOP
NOP
BRINT1: RETE ;McBSP #1接收中断(默认)或DMA2中断
NOP
NOP
NOP
BXINT1: RETE ;McBSP #1发送中断(默认)或DMA3中断
NOP
NOP
NOP
DMAC4: RETE ;DMA4中断
NOP
NOP
NOP
DMAC5: RETE ;DMA5中断
.end
在本实验中只要把在开头加上中断子程序标号的引用,并在中断表的TINT部分换成跳转指令就行了:
*******************************************************
*vectors.asm for 方波发生器
*******************************************************
.sect ".vectors" ;开始命名段.vecotrs
.global CodeStart ;引用程序入口的全局符号定义
.global TINT0_ISR ;引用Timer0中断子程序
<节省篇幅,中间省略>
TINT: B TINT0_ISR ;Timer0中断
NOP
NOP
BRINT0: RETE ;McBSP #0 receive interrupt
<节省篇幅,下略>
技巧提示:只有第一个中断(Reset中断)是每个程序都应该有的,在不需要其它中断的情况下,可以只用这一部分,后面可以省略。如果只需要部分中断也可以按需设置,但必须保证所用中断在中断向量表的位置不变。不熟悉中断向量表的情况下最好还是用这个完整中断向量表样例。
另外C5400系列中不同型号DSP的中断向量数量和在中断向量表中的位置有所不同,程序移植时需要查相应datasheet确认。
2.中断向量指针
中断向量表的位置并没有强制的位置,可以在内部存贮器,也可以在外部存贮器。但有一个要求:中断量表必须放在80H字长存贮块的起始处,即中断向量表的首地址的低7位必须全为0。DSP
的寄存器PMST的高9位是中断向量表的指针IPTR。其上电时默认是在FF80H处,这是为了运行固化在内部ROM的上电加载程序(见实验八的程序加载部分)。由于FF80H是只读的,加载用户自定义的中断向量表时会报错。这样需要重新设置IPTR的值,本书一般把它重定义到0080H(也可以用自定义的地址),并在程序开头重新设置一下IPTR的值:
;改变中断向量表位置
K_IPTR .set 0080h ;指向0080H,默认是FF80
LDM PMST,A
AND #7FH,A ;保留低7位,清掉高位
OR #K_IPTR,A ;将新值传到高9位
STLM A,PMST ;修改PMST寄存器
技巧指示:由于这段代码几乎每个程序都需要,可以单独存成一个文件:IPTR0080H.asm,然后在程序需要的地方用.copy或.include指令:
.copy “IPTR0080H.asm”
或: .include “IPTR0080H.asm”
编译时就会自动把这段代码嵌到相应位置。稍微要注意的是由于这一小段代码要用到累加器A,所以最好保证执行这段代码之前不要使用累加器A。
其它还有一些经常重复的代码,如初始化SP、DP、IPTR的代码都可以写在一个文件里include/copy进来。
注1:.copy和.inlucde指令效果是一样的,只是在生成程序列表时,.copy会把代码复制过来,而.include不会。
注2:文件名可以用路径,如果不用,则编译器会按下面的循序搜索:当前目录、编译选项指定的目录、环境变量指定的目录。
更多参考:
1.关于中断:SPRU131 TMS320C54x DSP Reference Set, Volume 1: CPU and
Peripherals,6.10 Interrupts
2.关于定时器:SPRU131 TMS320C54x DSP Reference Set, Volume 1: CPU and
Peripherals,8.4 Timer
实验3.2 外部中断:频率计
DSP有4个外部中断INT0-INT3,下降沿触发,实验箱的频率计使用的是INT3。
频率计的设计原理是:在设定时间下计外部中断INT3的次数,除以定时器的定时周期(也就是乘以定时器中断的触发频率),就得到外部脉冲频率。实验箱上配有1.024k-262.144k共8档频率源,也可以外接频率源。用跳线冒选择频率源,并接到INT3上。下面的例程是定时器定时1s,在INT3中断服务子程序中计脉冲个数,到时则关闭中断。脉冲计数结果显示到数码管上,即为以单位为Hz的频率值
**********************************************
*频率计
**********************************************
.mmregs
.global CodeStart
.global TINT1_ISR
.global INT3_ISR
.include "../DefineIO.asm"
.data
DATA_DP:
PulseCounter: .word 0 ;脉冲计数器
Display: .word 0FH,0FH,0FH,0FH,0FH,0FH;存放数据管显示值,值F在数码管上不显示
DotData: .word 000000B ;数码管的dot point
Number10: .word 10 ;十六进制转BCD所除的10
.text
CodeStart:
.copy "../SP_DP_IPTR.asm" ;初始化SP、DP和IPTR的代码段
STM #99,AR1 ;10ms计数后再100分频
STM #Display,AR3 ;定义数据管显示存贮区指针
LD #0,A ;A用来计脉冲数
SSBX INTM ;关中断
CALL Timer1Init ;初始化Timer1
STM #110000000B,IMR ;允许Timer1和INT3中断
STM #0FFH,IFR ;清除挂起的中断
RSBX INTM ;开中断
wait:
B wait;
***************************************
*外部中断子程序
***************************************
INT3_ISR:
ADD #1,A ;计中断次数
RETE
***************************************
*定时器中断子程序
***************************************
TINT1_ISR:
BANZ GoOnCount,*AR1- ;测量次数计数器减1,次数为0就中止计数数,
;结束计数
STM #0,IMR ;取消所有中断
HEX2BCD: ;把计数结果转成BCD码
RPT #15
SUBC Number10,A
STH A,*AR3+
AND #0FFFFH,A
BC HEX2BCD,ANEQ
;在数码管上显示结果
STM #Display,AR3
PORTW *AR3+,Digital0
PORTW *AR3+,Digital1
PORTW *AR3+,Digital2
PORTW *AR3+,Digital3
PORTW *AR3+,Digital4
PORTW *AR3+,Digital5
PORTW DotData,DotPoint
RETE
GoOnCount: ;继续计数
STM #1100001B,IFR ;清除挂起的中断
RETE
***************************************
*定时器初始化
***************************************
Timer1Init:
;定时器1的寄存器地址
TIM1 .set 0030h ;减1计数器
PRD1 .set 0031h ;存放定时时间常数
TCR1 .set 0032h ;定时器状态及控制寄存器
;F=50MHz, T=20ns*(1+15)*(1+3124)=20ns*16*31250=10ms
STM #010,TCR1 ;TSS置位停止Timer
STM #31249,PRD1
STM #2FH,TCR1
RET
.end
简单起见本例只能测一次,可以做一些改进,比如每隔1-2S自动重新测量,或者用按键来触发测量。
[/replyview]
关键词: C54xx 十天 速成 讲义 中断 程序 定时器
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
与电子爱好者谈读图四被打赏50分 | |
与电子爱好者谈读图二被打赏50分 | |
【FRDM-MCXN947评测】Core1适配运行FreeRtos被打赏50分 | |
【FRDM-MCXN947评测】双核调试被打赏50分 | |
【CPKCORRA8D1B评测】---移植CoreMark被打赏50分 | |
【CPKCORRA8D1B评测】---打开硬件定时器被打赏50分 | |
【FRDM-MCXA156评测】4、CAN loopback模式测试被打赏50分 | |
【CPKcorRA8D1评测】--搭建初始环境被打赏50分 | |
【FRDM-MCXA156评测】3、使用FlexIO模拟UART被打赏50分 | |
【FRDM-MCXA156评测】2、rt-thread MCXA156 BSP制作被打赏50分 |