Microsoft Word - 54DSP十天速成.pdf
实验一 新手上路] 初学者编写的第一个程序通常是控制 XF 引脚的变化,然后用示波器测量 XF 脚波形或观察与相接的 LED。 这个程序也常常用来测度一下 DSP 能否正常工作。 实验 1.1 最简单的程序控制 XF 引脚周期性变化 实验目的通过简单的程序了解 DSP 程序的结构,熟悉 CCS 开发环境。 ************************************************************* *最简单的程序TestXF1.asm *循环对 XF 位置 1 和清 0,用示波器可以在 XF 脚检测到电平高低周期性变化 *常用于检测 DSP 是否工作。 ************************************************************* .mmregs ;预定义的寄存器 .def CodeStart ;定义程序入口标记 .text ;程序区 CodeStart ;程序入口 SSBX XF ;XF 置 1 RPT 999 ;重复执行 1000 次空指令产生延时 NOP RSBX XF ;XF 清 0 RPT 999 ;重复执行 1000 次空指令产生延时 NOP B CodeStart ;跳转到程序开头循环执行 .end NOP 指令执行时间为一个时钟周期,设 DSP 工作频率是 50MHz,可以估算出 XF 引脚电平的变化频率约为5 0M/200025kHz 在没有示波器的情况下,就要将程序 1.1 稍作改进,增加延时,用一个延时子程序将 XF 脚电平变化频率降 到肉眼可分辨的程度,就可以用 LED 来显示电平的变化,程序如下 实验 1.2 子程序调用 实验目的学习子程序的调用 ************************************************************* *TestXF2.asm *对 TestXF1.asm 稍作改进,用延时子程序设置较长的延时, *可以用试验板上的 LED 看到 XF 引脚电平的变化 ************************************************************* .mmregs ;预定义的寄存器 .def CodeStart ;定义程序入口标记 .text ;程序区 CodeStart ;程序入口 SSBX XF ;XF 置 1 CALL Delay ;调用延时程序 RSBX XF ;XF 清 0 CALL Delay ;调用延时程序 B CodeStart ;跳转到程序开头循环执行 ************************************************************** *延时子程序Delay *用两级减一计数器来延时。调整 AR1 和 AR2 的大小 LED 闪烁的频率不同 ************************************************************** Delay STM 999,AR1 ;循环次数 1000 LOOP1 STM 4999, AR2 ;循环次数 5000 LOOP2 BANZ LOOP2,*AR2- ;如果 AR2 不等于 0,AR2 减 1,再判断 BANZ LOOP1,*AR1- ;如果 AR1 不等于 0,AR1 减 1,跳转到 LOOP1 RET .end ************************************************************** *注意这种延时方法并不精确,需要精确定时必须用定时器。 *按此法延时的近似公式为4*AR21*AR11*时钟周期 *当 DSP 工作在 50MHz时钟周期 20ns,AR1999, AR24999 时 *延时约为 400ms,则 LED 闪烁的周期为 800ms,频率 1.25Hz ************************************************************** 设计指导 1.源代码书写格式 源代码的书写有一定的格式,初学者往往容易忽视。简单归纳如下 1.每一行代码分为三个区标号区、指令区和注释区。标号区必须顶格写,主要是定义变量、常量、程序 标签时的名称。指令区位于标号区之后,以空格或 TAB 格开。如果没有标号,也必须在指令前面加上空格 或 TAB,不能顶格。注释区在标号区、程序区之后,以分号开始。注释区前面可以没有标号区或程序区。 另外还有专门的注释行,以*打头,必须顶格开始。 2.一般区分大小写,除非加编译参数忽略大小写。 3.标点符号有时不注意会打成中文全角字符导致错误。 书写格式的要求在很多 DSP 书里都没有提,初学者往往只把书上的代码输入进去,编译时得到错误的提示, 而不知所措。其中最容易犯的错误指令顶格写,不过一般经提示后不会犯第二次。 有些格式 CCS 并没有做要求,但注意养成良好的代码书写风格,增加代码的可读性。以上两个例子的书写 风格可作参考,但不是硬性规定 1.标号区占 3 个 TAB 的间隔,即 12 个字符 2.指令中的指令码占两个 TAB 间隔,然后是操作数。 3.每一行的尾注能对齐的尽量对齐 4.标明一段程序功能的注释以*号打头顶格写,如果功能说明的注释较多,用分格线框起来。 此外其它编程语言的编程风格也可以借用过来,比如标示符命名规则、程序说明的要求等。如果项目组有 规定,则按规定执行。 本书的代码尽量保持一定的风格,不过读者可以发现前面的代码注释较多,后面随着学习的深入,一般不 会对每一条指令加注释,只注明程序段的功能。另外代码贴到 word 里后,格式有些错位,无法一一纠正。 2.链接配置文件 一个完整的 DSP 程序至少包含三个部分程序代码、中断向量表、链接配置文件*.。这里介绍一下链 接配置文件文件,对本次试验影响不大的中断向量表将在后文介绍。 连接配置文件的确定了程序链接成最终可执行代码时的选项,其中有很多条目,实现不同方面的选项,其 中最常用的也是必须的有两条1.存贮器的分配 2.标明程序入口。以本次实验为例,下面的简单的链接配置文件就够用了 /* TestXF. */ -e CodeStart /*程序入口,必须在程序中定义相应的标号*/ MEMORY { page 0 PRAM org0100h len0F00h /*定义程序存贮区,起始 0100H,长度 0F00H*/ } SECTIONS{ .textPRAM page 0 /*将.text 段映射到 page0 的 param 区*/ } 由于每个程序都需要一个链接配置文件,可以编写一个满足通常需要的链接配置文件。作为本手册通用的 链接配置文件如下,可以满足本书大部分程序的需要。在未特别指明的情况下使用这个通用的链接配置文 件 /* 5402. */ -e CodeStart /*程序入口,必须在程序中定义相应的标号*/ -m map.map /*生成存储器映射报告文件 */ MEMORY { PAGE 0 VECT org0080h len0080h /*中断向量表*/ PARAM org100h len0F00h /*代码区*/ PAGE 1 DARAM org1000h len1000h /*数据区*/ } SECTIONS { .text PARAM PAGE 0 /*代码段*/ .vectors VECT PAGE 0 /*中断向量表*/ STACK DARAM PAGE 1 /*堆栈*/ .bss DARAM PAGE 1 /*未命名段*/ .data DARAM PAGE 1 /*数据段*/ } 更多参考 1.关于代码书写格式SPRU102 TMS320C54x Assembly Language Tools Users Guide,3.5 Source Statement at 2.关于链接配置文件SPRU102 TMS320C54x Assembly Language Tools Users Guide,7.5 Linker Command Files,7.7 The MEMORY Directive,7.8 The SECTIONS Directive 练习 1、试一下不按规定格式书写代码会产生什么样的编译错误。 2、试一下将链接配置文件中的 MEMORY,SECTIONS 改成小写会出现什么样的编译错误。 3.修改程序 1.2 中 AR1,AR2 的值,观察 LED 闪烁频率 实验二 基本运算 <本节选自为 HK-DSP 实验箱写的实验指导书,有待整理> DSP 指令数量最多的是算术指令、逻辑指令和数据加载与传送指令。数据加载与传送指令由于处处要用, 所以不单独列为实验。算术与逻辑指令也是数量繁多,无法一一举例,这里简单举一个加法和除法的例子, 乘法和乘加指令在 FIR 用得比较多,稍后一并介绍。其它指令有兴趣可以对照指令表的说明,试验一下各 指令运行的结果。 实验 2.1 加减法计算 ************************************************ *计算 zxy-w。 ************************************************ .mmregs .def CodeStart Data_DP ;数据段指针 x .word 10 ;初始化变量 y .word 26 w .word 23 z .word 0 .text CodeStart LD Data_DP,DP ;装载数据指针 DP STM STACK10H,SP SUMB LD x,A ;Ax ADD y,A ;AAy SUB w,A ;AA-w STL A,z ;zA END B END 计算结果数据存储器地址存储内容十进制 x1010H000aH10 y1011H001aH26 w1012H0017H23 z1013H000dH13 技巧提示试验算术指令由于不需要外部资源,可以不需要仿真器和实验箱。同学们可以平时自己用软件 仿真,多多实验。但是复杂的算法最好还是在线仿真,因为程序是流水线执行,软件仿真有时与实际硬件 执行结果有所不同。 实验 2.2 除法计算 DSP 并没有除法指令,回想一下我们用在稿纸上演算除法列的竖式,实际是一种移位减法,DSP 中也是通过 做多次减法的办法来做除法。下面例子是把用除以 10 的办法二进制数转成 BCD 码例子 ********************************* *16 进制转 BCD 码 ********************************* .mmregs .global CodeStart .data x .word 1234 ;待转换的数字 y .word 10 ;除数 z .word 0Fh,0Fh,0Fh,0Fh,0Fh;结果区,每位 BCD 存一个字, ;初始化为 F 因为实验板的数码管不显示 F .text CodeStart LD x,DP ;设置 DP LD x,A ;被除数 STM z,AR1 ;结果区指针 loop RPT 15 ;执行完 16 次减法后,A 的高 16 位是余数 SUBC y,A ;低 16 位是商 STH A,*AR1 ;余数保存到 Z AND 0FFFFH,A ;掩盖掉高 16 位,保留商值 BC loop,ANEQ ;继续做除法直到商为 0 end B end 练习 练习其他算术指令 其它参考 spru172cTMS320C54x DSP Reference Set Volume 2 Mnemonic Instruction Set,2.1 Arithmetic Operations 这个资料对每个指令都有详细说明。也可以在 Help 中的 TMS320C54x DSP Mnemonic Instruction Set 中查询或搜索相关指令。 实验三 中断 中断的概念应该不陌生,指的是当某个事件发生时,暂停当前的操作,转向中断服务程序,执行完后再返 回继续原来的操作。这使得 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 0B11 ;TCR 第 11 位 soft0 K_TCR_FREE .set 0B10 ;TCR 第 10 位 free0 K_TCR_PSC .set 0B6 ;TCR 第 9-6 位,可设 TDDR 一样,也可不设自动加载 K_TCR_TRB .set 1B5 ;TCR 第 5 位 TRB1 此位置 1,PSC 会自动加载的 K_TCR_TSS .set 0B4 ;TCR 第 4 位 TSS0 K_TCR_TDDR .set 1001B0 ;TCR 第 3-0 位 TDDR1001B 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 1B4 ;TSS1 时计数器停止 .data DATA_DP ;数据区指针 XF_Flag .word 1 ;当前 XF 的电平标志,如果 XF_Flag1,则 XF1 ; ;主程序 ; .text CodeStart STM STACK10H,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 *f50MHz,定时 2ms 时 *根据定时器长度计算公式TtT*1TDDR*1PRD *给定 TDDR9,PRD9999,CLKOUT 主频 f40MHz,T25ns *Tt20ns*19*199992000us2ms *f100Mhz,定时最大是10ns*24*21610ms, 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_Flag1 then TC1 else TC0 BC ResetXF,TC ;IF TC1 then XF0 else XF1 setXF SSBX XF ;置 XF 为高电平 ST 1,*AR2 ;相应修改标志 B Next ResetXF RSBX XF ;;置 XF 为高电平 ST 0,*AR2 ;相应修改标志 Next POPM ST0 RETE .end 有时定时的长度不能满足需要,比如 DSP 工作频率 50Mhz 时,定时最大值是20ns*24*21620ms。如果 需要更长的定时,就要在定时器中断子程序中再加一个计数器,直到产生一定次数的定时中断后再执行相 应的操作。程序只需要稍作修改,见附盘的 fangbo2.asm 技巧提示寄存器的不同位通常有不同的含义,初始化时单独设定寄存器的每一位可以增加程序可读性, 容易让其它人看懂具体每一位设置的含义,并且易于修改。如果代码太长可以自己写一个初始化的子程序, 需要时修改一下调定时再调用。当然如果对寄存器各个位的含义相当熟悉,直接整个初始化也行。可以自 行选择这些不同的编程风格。 设计指导 1.中断向量表 中断向量表是 DSP 程序的重要组成部分,当有中断发生并且处于允许状态时,程序指针跳转到中断向量表 中对应的中断地址。由于中断服务程序一般较长,通常中断向量表存放的是一个跳转指令,指向实际的中 断服务程序。下面是 5402 中断向量表的一个范例,可以作为模板,使用时稍作修改就行 ***************************************************************** *5402Vectors.asm *完整的 5402 中断向量表示例 *5402 共有 30 个中断向量,每个向量占 4 个字的空间。 *使用向量一般用一条跳转指令转到相应中断服务子程序,其余空位用 NOP 填充 *未使用的向量直接用 RETE 返回,是为了防止意外进入未用中断。 ***************************************************************** .sect “.vectors“ ;开始命名段.vecotrs .global CodeStart ;引用程序入口的全局符号定义 ;。。。引用其它中断程序入口的全局符号定义 .align 0 x80 ; 中断向量表必须对齐 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 会把代码复制过来,而.incl ude 不会。 注 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 ;定时器状态及控制寄存器 ;F50MHz, T20ns*115*1312420ns*16*3125010ms STM 010,TCR1 ;TSS 置位停止 Timer STM 31249,PRD1 STM 2FH,TCR1 RET .end 简单起见本例只能测一次,可以做一些改进,比如每隔 1-2S 自动重新测量,或者用按键来触发测量。 实验 4.1 数码管及 LED 显示接口实验 实验箱说明部分已经介绍了数码管的控制原理,下面的程序 DigitalLED.asm 简单的演示了对数码管和 LED 控制的指令,可以在显示预设的数字和 LED 状态。复杂的程序可以见附盘的流水灯程序,DigitalLED2.as m ; ;DigitalLED.asm ;实验用 DSP 控制实验板数码管 ;DSP 用 I/O 指令对 CPLD 地址 1000-10005 写数据,分别对应 Digtal0-5 ; .mmregs .def main ;主程序入口 .ref Timer0Init ;Timer0 初始化子程序 ;数据管地址 Digital0 .set 1000H ;数据管 1 Digital1 .set 1001H ;数据管 2 Digital2 .set 1002H ;数据管 3 Digital3 .set 1003H ;数据管 4 Digital4 .set 1004H ;数据管 5 Digital5 .set 1005H ;数据管 6 DotPoint .set 1006H ;小数点 LED .set 1007H ;LED STACK .usect “STACK“,10H ;分配堆栈空间 .data DATA .word 1,2,3,4,5,6 ;测试数据 Dot_DATA .word 010101b; LED_DATA .word 0101010b .text main STM STACK10H,SP ;设堆栈指针 SP STM K_SWWSR,SWWSR SSBX INTM ;关中断 LD DATA,DP ;设数据地址 DP STM DATA,AR1 *写数据 PORTW *AR1,Digital0 PORTW *AR1,Digital1 PORTW *AR1,Digital2 PORTW *AR1,Digital3 PORTW *AR1,Digital4 PORTW *AR1,Digital5 PORTW Dot_DATA,DotPoint PORTW LED_DATA,LED END B END .end 技巧提示数码管、LED 的 IO 地址的定义也可以单独存到一个文件中,在需要它的程序中用.include/.co py 指令。 练习修改预设值重新运行观察结果。 实验 4.2 键盘接口实验 实验板上有四个按键,当有键按下时,会触发 DSP 的 INT1 中断,在 INT1 的中断服务程序中读入键码,判 断哪一个键被按下,然后执行相应的操作。各键对应的二进制和十六进制键码分别为 按键 1 0001B 1H 按键 2 0010B 2H 按键 3 0100B 4H 按键 4 1000B 8H 下面有一个小例子 ****************************************************** *keyboardTest.asm *测试按键的功能,响应按键中断,读取键值, *并对不同键按键次数计数 ****************************************************** .mmregs .global CodeStart .global INT1_ISR .include “../DefineIO.asm“ .data DATA_DP Counter1 .word 0 ;按键 1 计数器 Counter2 .word 0 ;按键 2 计数器 Counter3 .word 0 ;按键 3 计数器 Counter4 .word 0 ;按键 4 计数器 Keyvalue .space 30H*16 ;按键历史缓冲区 .text CodeStart .copy “../SP_DP_IPTR.asm“ ;初始化 SP、DP 和 IPTR 代码段 ;初始化变量 STM Keyvalue,AR2 ST 0,Counter1 ST 0