第6章TMS320C54DSP的C程序设计(未完)_第1页
第6章TMS320C54DSP的C程序设计(未完)_第2页
第6章TMS320C54DSP的C程序设计(未完)_第3页
第6章TMS320C54DSP的C程序设计(未完)_第4页
第6章TMS320C54DSP的C程序设计(未完)_第5页
已阅读5页,还剩49页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第6章TMS320C64xDSP的C/C++程序设计6.1C/C++程序设计基础6.2程序设计示例6.3用C语言和汇编语言混合编程6.1C/C++程序设计基础DSP生产厂商及第三方为DSP软件开发提供了C编译器,使得利用高级语言实现DSP程序的开发成为可能。在TI公司的DSP软件开发平台CCS中,又提供了优化的C编译器,可以对C语言程序进行优化编译,提高程序效率,目前在某些应用中,C语言优化编译的结果可以达到手工编写的汇编语言效率的90%以上。DSP生产厂商和相关公司也在不断对C优化编译器进行改进设计,C语言程序优化编译的效果将会进一步改善。返回首页6.1.1面向DSP的程序设计原则面向DSP的C/C++程序设计和通用计算机上的C/C++程序设计从本质和工作原理上来说是一致的,但受硬件资源和处理对象的不同,存在一些区别:DSP的数据存储区有限,所以只能实时处理小批量数据;DSP代码需要绝对定位,计算机的C代码由操作系统定位。DSP的C/C++程序设计原则:

灵活使用嵌入式C语言中的位操作指令;

编译系统不允许有太多的程序嵌套;

需要考虑对DSP硬件的时序要求;

区别不同库函数的使用;

不同存储类型变量的使用;

尽可能模块化设计。类型大小数据格式最小值最大值Signedchar16位ASCII-3276832767char,unsignedchar16位ASCII065535short,signedshort16位二进制补码-3276832767unsignedshort16位二进制065535int,signedint16位二进制补码-3276832767unsignedint16位二进制065535long,signedlong32位二进制补码-21474836482147483647unsignedlong32位二进制04294967295enum16位二进制补码-3276832767float32位IEEE32位1.175494e-383.40282346e+38double32位IEEE32位1.175494e-383.40282346e+38longdouble32位IEEE32位1.175494e-383.40282346e+38pointers16位二进制00x0FFFF表6.1CCS中C语言数据类型6.1.2C/C++语言数据类型6.1.5C/C++的DSP访问规则1.DSP片内寄存器的访问CCS工程中的头文件与其它基于C语言的开发环境中的头文件基本相同,主要是用来定义工程中的常量和数据结构。为了提高开发效率,开发人员往往会将DSP片内的控制寄存器定义写成一个头文件(如reg.h),这样在各个基于相同型号DSP的开发中,可以共享这个定义控制寄存器的头文件。在C语言中对DSP片内寄存器一般采用指针方式来访问,所以常常采用的方法是将DSP寄存器地址的列表定义在头文件,中如下所示:#defineIMR(volatileunsignedint*)0x0000#defineIFR(volatileunsignedint*)0x0001#defineST0(volatileunsignedint*)0x0006#defineST1(volatileunsignedint*)0x0007#defineAL(volatileunsignedint*)0x0008#defineAH(volatileunsignedint*)0x0009#defineAG(volatileunsignedint*)0x000A#defineBL(volatileunsignedint*)0x000B#defineBH(volatileunsignedint*)0x000C#defineBG(volatileunsignedint*)0x000D#defineT(volatileunsignedint*)0x000E#defineTRN(volatileunsignedint*)0x000F#defineAR0(volatileunsignedint*)0x0010#defineAR1(volatileunsignedint*)0x0011#defineAR2(volatileunsignedint*)0x0012#defineSP(volatileunsignedint*)0x0018#defineBK(volatileunsignedint*)0x0019#defineBRC(volatileunsignedint*)0x0001A#defineRSA(volatileunsignedint*)0x001B#defineREA(volatileunsignedint*)0x001C#definePMST(volatileunsignedint*)0x001D#defineXPC(volatileunsignedint*)0x001E在用户程序中,若要读出或者写入一个特定的寄存器,就要对相应的指针进行操作。下例通过指针操作对ST0和AR1进行初始化Intuserfunc(){…*ST0=0x1278;*AR1=0x6013;…}2.DSP内部和外部存储器的访问规则

DSP对存储器的访问也采用指针方式。例:对内部存储器单元0x4000和外部存储器单元0x80FF进行操作。

int*data1=0x4000;int*data2=0x80FF;intuserfunc(){…*data1=7000;*data2=0;…}3.DSPI/O端口的访问规则在C语言中读写DSP的I/O端口空间,用关键字ioport实现。用关键字ioport定义I/O端口的格式如下:

ioporttypeporthex_num其中ioport是关键字,指出这是一个I/O端口变量。type只能是char、short、int或unsigned这几种数据类型。porthex_num是端口号,hex_num是一个十六进制数。所有端口变量都必须在文件级定义,不能在函数中定义。下面的代码定义I/O端口为unsignedport10h,将数据a写入port10h,然后从port10h读入数据存到b中:Ioportunsignedport10;/*端口说明*/intfunc(){…port10=a;/*数据a写入port10h*/…b=port10;/*从port10h读出数据存到b*/}端口变量不仅可在分配端口中使用,也可像其他变量一样使用a=port10+b/*从port10h读入数据并加上数据b,再存到a中*/port10+=a/*从port10h读出数据并加上数据a,将结果从port10h输出*/在函数调用时,I/O端口变量的数值可以传递,但I/O端口变量本身不能当作函数参数。例:call(port10);/*从port10h读出数据,将该数据作为参数并调用相应子函数*/call(&port10);/*无效的参数传递*/6.3C语言和汇编语言混合编程6.3.1独立的C模块和汇编模块接口6.3.2从C程序中访问汇编程序变量6.3.3在C程序中直接嵌入汇编语句

返回首页用C语言和汇编语言混合编程,能充分利用DSP芯片的资源,更好地发挥C语言和汇编语言进行软件开发的各自的优点,可以将两者有机结合起来,兼顾两者的优点,避免其弊端。因此,在很多情况下,采用混合编程方法能更好地达到设计要求,完成设计功能。C语言和汇编语言的混合编程有以下几种方法:

(1)独立编写汇编程序和C程序,分开编译或汇编形成各自的目标代码模块,用链接器将C模块和汇编模块链接起来,这是一种灵活性较大的方法。

(2)在C程序中直接内嵌汇编语句

(3)将C程序编译生成相应的汇编程序,手工修改和优化C编译器生成的汇编代码。

独立编写汇编程序和C程序,分开编译或汇编形成各自的目标代码模块,用链接器将C模块和汇编模块链接起来,这是一种灵活性较大的方法。

采用这种方法,C程序可以调用汇编程序,并且可以访问汇编程序中定义的变量。同样,汇编程序也可以调用C程序或访问C程序中定义的变量。但用户必须自己维护各汇编模块的入口和出口代码,自己计算传递的参数在堆栈中的偏移量,工作量稍大,但能做到对程序的绝对控制。在编写C程序和汇编程序时,必须遵循有关的寄存器规则和函数调用规则。6.3.1独立的C模块和汇编模块接口寄存器规则明确了编译器如何使用寄存器以及在函数调用过程中如何保护寄存器。如果汇编程序不符合寄存器使用规则,则C运行环境将会被破坏。(1)辅助寄存器:AR1、AR6、AR7由被调用函数保护;AR0、AR2、AR3、AR4、AR5由调用函数保护,在被调用函数中可以自由使用且不必恢复。(2)堆栈指针SP在函数调用时必须保护,但它是自动保护的。(3)ARP

在函数进入和返回时,必须为0,即当前辅助寄存器为AR0。函数执行时可以为其他值。1.TMS320C54xDSP中寄存器规则(4)在默认的情况下,编译器总是认为ST1中的OVM为0。因此,若在汇编程序中将OVM置为1,则返回C/C++环境时,必须将其恢复为0。(5)在默认的情况下,编译器总是认为CPL为1。因此,若在汇编程序中将CPL清0,则在返回C/C++环境时,必须将其恢复为1。(6)寄存器变量编译器为两个寄存器关键字声明的变量分配寄存器,第一个变量必须声明为AR1,第二个变量必须声明为AR6,其顺序不能改变。变量必须声明为全局变量,必须在变量列表和函数语句的第一块中声明。其他状态位和寄存器在子程序中可以任意使用,不必将其恢复。寄存器使用和保护规则寄存器用途由被调用函数保护由调用函数保护AR0指针和表达式不是是AR1指针和表达式是不是AR2~AR5指针和表达式不是是AR6指针和表达式是不是AR7指针、表达式和结构指针是不是A表达式,传递第一个参数给函数,返回函数值不是是B表达式不是是SP堆栈指针不是是T乘法和移位表达式不是是ST0,ST1状态寄存器见表2见表2BRC块重复寄存器不是是表2状态寄存器字段描述字段字段名称状态位值修改ARP辅助寄存器指针0可以ASM累加器移位——可以BRAF块循环标志——不可以C进位标志——可以C16双16位标志0不可以CMPT兼容方式选择0不可以CPL编译方式选择1不可以FRCT小数方式选择0不可以OVA累加器A溢出标志——可以OVB累加器B溢出标志——可以OVM溢出方式位选择0只能在内部修改SXM符号扩展方式选择——可以SMUL饱和方式选择0只能在内部修改SST饱和方式存储0不可以TC测试位标准——可以函数调用涉及参数传递、内存使用等问题。C编译器规定了一组严格的函数调用规则。除特殊的运行支持函数外,任何调用C函数或被C函数调用的函数都必须遵循规则,否则会破坏C运行环境而导致程序失败。2.TMS320C54xDSP中函数调用规则(1)参数传递

函数调用前,将参数以逆序压入运行堆栈,即最右边的参数最先入栈,然后自右向左将参数依次入栈。但是,对于TMS320C54X,在函数调用时,第一个参数放入累加器A中进行传递。若参数是长整型和浮点数时,则低位字先压栈,高位字后压栈。若参数中有结构形式,则调用函数给结构分配空间,其地址通过累加器A传递给被调用函数。函数调用时堆栈的使用如下所示:

函数调用规则包括:返回地址PC函数参数2函数参数3SPSP+1SP+2堆栈函数参数1累加器A高地址低地址(2)局部帧的产生每调用一个函数,就建立一个当前函数帧。C环境利用该帧保护调用者的有关信息、传递参数和生成局部变量块。在C语言中调用汇编语言编写的子程序,主要应弄清楚堆栈的使用情况,特别注意这里使用的堆栈不是DSP的硬件堆栈,而是.cmd文件定义的软堆栈。TMS320C54x中调用函数时堆栈的使用情况(3)函数的返回

如果被调用函数修改了寄存器AR1,AR6和AR7,则必须予以恢复。函数的返回值保存在累加器A中。整数和指针在累加器A的低16位中返回;浮点数和长整型数在累加器A的32位中返回;如果函数返回一个结构体,则被调用函数将结构体的内容拷贝到累加器A所指向的存储器空间;如果函数没有返回值,则A置0。(1)函数如何调用函数(调用者)在调用被调用函数时执行以下任务:①调用者将第一个(最左边)的参数值放进累加器A。调用者将剩下的参数按自右向左的顺序传进参数块,剩下的最左边的参数在最低的地址。②若函数返回一个结构,则调用者为该结构分配空间,然后用累加器A传递返回空间的地址给被调用的函数。③调用者调用函数。(2)被调用函数如何响应被调用函数执行以下任务:注意:如果被调用函数是C/C++函数,则下面步骤都是由汇编器自动完成。如果被调用函数是汇编函数,则:①若被调用函数要修改AR1,AR6或AR7,则首先将他们压入堆栈。②被调用函数通过从SP减去一个常数,为自身局部变量块和局部参数块分配存储器。该常数按以下公式计算,即:

局部变量块的大小+局部参数块的大小+paddingpadding值是为了保证SP对准偶数边界而可能要求补充的一个字,padding为1或0。之所以SP要对准偶数边界,是因为5000系列DSP指令可一次读写存储器的32b,例如DLD,DADD等。这样,编译器必须保证所有32b的目标都驻留在偶数边界。对于混合编程而言可以在汇编函数中,按本步骤的方法在堆栈中分配局部帧,但本方法相对比较麻烦,尤其该汇编函数还要调用其他函数时,所以,一般而言编程者通常用其他方法分配局部帧,比如用.bss伪指令定义局部变量供函数使用。③被调用函数执行函数中的应用代码。④若被调用函数返回一个值,则被调用函数将该值放在累加器A中;若被调用函数返回一个结构,则被调用函数将该结构复制到累加器A指到的存储器块;若调用者不返回函数值,则A被置0。⑤被调用函数给SP上加上第二步计算的常数,释放为局部变量和局部参数分配的存储空间。对混合编程而言,如果编程者没有在堆栈中分配局部帧,则本步骤省略。⑥被调用函数恢复所有保存的寄存器。⑦被调用函数执行返回。汇编语言被调用函数的例子:callee:;函数入口PSHMAR6;存AR6PSHMAR7;存AR7FRAME#-15;分配函数体框架参数

;函数体FRAME#15;分配函数体框架参数POPMAR7;恢复AR7POPMAR6;恢复AR6RET;返回在编写独立的汇编程序时,必须注意以下几点:(1)不论是用C语言编写的函数还是用汇编语言编写的函数,都必须遵循寄存器使用规则。(2)必须保护函数要用到的几个特定寄存器。

(3)中断程序必须保护所有用到的寄存器。(4)从汇编程序调用C函数时,第一个参数(最左边)必须放入累加器A中,剩下的参数按自右向左的顺序压入堆栈。总结(5)调用C函数时,注意C函数只保护了几个特定的寄存器,而其他是可以自由使用的。(6)长整型和浮点数在存储器中存放的顺序是低位字在高地址,高位字在低地址。(7)如果函数有返回值,返回值存放在累加器A中。(8)汇编语言模块不能改变由C模块产生的.cinit段,如果改变其内容将会引起不可预测的后果。(9)编译器在所有标识符(函数名、变量名等)前加下划线“_”。

(10)任何在汇编程序中定义的对象或函数,如果需要在C程序中访问或调用,则必须用汇编指令.global定义。

(11)编辑模式CPL指示采用何种指针寻址,如果CPL=1,则采用堆栈指针SP寻址;如果CPL=0,则选择页指针DP进行寻址。

[例1]在C语言中调用汇编语言函数C程序:

Extern

int

asmfunc

(

);

/*声明外部的汇编子程序*/

/*注意函数名前不要加下划线*/

int

gvar;

/*定义全局变量*/

main(

)

{

int

i=3;

gvar=asmfunc(i);

/*进行函数调用*/

}汇编程序:

_asmfunc:

;函数名前一定要有下划线

ADD

*(_gvar),A

;加法结果存在A中

STL

A,*(_gvar)

;将A中的结果存到gvar

RETD

;子程序返回返回本节[例2]用C/C++编写的主程序调用汇编语言编写的32位乘法运算子程序。虽然用C/C++语言表达32b乘法运算较为方便和明了,但由于C/C++语言无法很好利用DSP汇编语言为实现各种乘法运算而提供的指令,而使得C/C++程序效率低下。所以这里用汇编语言完成32b乘法运算,再用C/C++程序调用它。算法简介由于16b定点DSP中没有32b乘法指令,所以一定要用几种16b乘法指令结合一定算法来进行32b乘法运算。一个32b数在存储器中是分开存储的。高16位存放在低地址,它在进行乘法运算是可以看作一个16b有符号数;低16位存放在相邻的低地址,他进行乘法运算时可以看作一个16b无符号数。于是算式如下:x1

x0

y1

y0

×x0×y0

x1×y0

y1×x0

y1×x1

w0

w1

w2

w3

U×U

S×U

S×U

S×S

U

U

U

S

其中:S代表符号数;U代表无符号数。由上算式可见,在32b乘法运算中,实际上包含了3种乘法运算:U*U,S*U和S*S。一般的乘法运算指令都是两个带符号数相乘,即S*S。所以在编程时,还要用到以下两条乘法指令:#include<stdio.h>#include<stdlib.h>intmain(){externint*MPY32(longX32,longY32);声明外部的32b乘法运算汇编子程序,该函数返回指针值intw[4]int*pintip=MPY32(0x11112222,0x33334444);进行函数调用;该指针指向数据存储器中,存放相乘结果单元的首地址for(i=0;i<4;i++){w[i]=*p++;将乘法结果存到数组中printf(“%x\n”,W[i]);以十六进制方式输出该数组值}}举例:C语言主程序在主程序中进行MPY32函数调用时,函数传递情形如图2所示。函数MPY32的第一参数存放在A累加器中,第二个参数在堆栈中,高16位在堆栈中的低地址,低16位在堆栈中的高地址。由于MPY32是汇编语言函数,所以编译器不为其分配局部帧,局部帧的分配在汇编程序中进行。汇编语言函数:.mmregs.bssx1,1;为局部帧分配内存空间.bssx0,1.bssy1,1.bssy0,1.bssw3,1.bssw2,1.bssw1,1.bssw0,1.text.global_MPY32;声明函数名为全局变量STM#X1,AR2;令AR2指向第一个参数存放单元STM#Y1,AR3;令AR3指向第二个参数存放单元STM#W0,AR4;令AR4指向乘法结果存放单元STH,A,*AR2+;传递第一个参数STL,A,*AR2LD*SP(1),A;传递第二个参数STHA,*AR3+LD*SP(2),ASTL,A,*AR3LD#00,ALD*AR2,T;T=x0_MPY32:MPYU*AR3-,A;A=ux0*uy0STLA,*AR4-;w0=ux0*uy0LDA,-16,AMACSU*AR2-,*AR3+,A;A+=y1*ux0MACSU*AR3-,*AR2,A;A+=x1*uy0STLA,*AR4-;w1=ALDA,-16,AMAC*AR2,*AR3,A;A+=x1*y1STLA,*AR4-;为W2赋值STHA,*AR4;为W3赋值LDW3,A;将W3单元地址作为返回值,传递到A累加器RET汇编程序可以看出,在汇编程序中至少要为局部帧分配8个单元,其中4个单元用来存放参数值,4个单元用来存放运算结果从C程序中访问在汇编程序中定义的变量或常数,根据变量所在的位置和属性,可以分为以下3种情况:(1)访问在.bss块中定义的变量

实现方法如下:①用.bss或.usect伪指令定义变量②用.global伪指令定义外部变量③汇编语言中在变量名前加下划线④在C中将变量定义成外部的,然后用普通的方式访问。采用上述方法后,在C程序中就可以访问这个变量6.3.2从C程序中访问汇编程序变量例:汇编语言程序://注意变量名前都有下划线

.bss_var,1;定义变量

.global_var;说明为外部变量C语言程序:externintvar;/*外部变量*/

var=1;/*访问变量*/(2)访问不在.bss块中定义的变量

不希望变量定义在.bss段,例如一个在汇编语言中定义的数据表,不希望将它存放在RAM中。在这种情况下,必须定义一个指针指向对象,然后在C程序中间接访问它。所需的操作如下:定义对象,最好将其放入一个独立的初始化段中。声明一个全局标号,指向对象的起始地址。这样对象可以被连接到存储空间的任意位置。在C程序中,将标号声明为外部的,不要在标号前加下划线。在C程序中,定义一个指针变量指向该对象。这样做之后就可以正常地访问该对象了以C程序访问汇编程序中数据表为例。汇编语言:.global_sine;定义指向该表起始的全局符号

.sect“sine_tab”;定义一个独立的段_sine:;表起始地址.float0.0

.float0.015987

.float0.022145C语言程序:externfloatsine[];/*声明外部数组*/

float*sine_p=sine;/*定义一个指针变量指向该数据表*/

f=sine_p[4];/*访问sine_p*/(3)C程序中访问汇编程序中的常量一般情况下,汇编中的常量在C程序中也定义为同样的常量,从而避免在C程序中访问汇编程序中的常量。对于在汇编程序中用.set和.global伪指令定义的全局常数,也可以使用特殊的操作从C程序中访问它们。在汇编中定义的常数,符号表包含的是常数的值,C中定义的变量,符号表实际上包括的是变量值的地址,而非变量值本身。但编译器不会区分符号表中的数值与地址。如果按常量名访问,得到的将是地址为常量的存储单元的数值。为了避免这种情况的出现,必须用&运算符来得到常量值。所以在C中访问汇编中的常数_x,应在常数名前加一个地址操作符为“&x”例:汇编语言程序:_table_size.set10000;定义常量

.global_table_size;定义为全局变量C语言程序:externinttable_size/*声明外部变量*/#defineTABLE_SIZE((int)(&table_size))/*用地址符号指明变量*/

for(i=0;i<TABLE_SIZE;i++)/*正常使用符号*/6.3.3在C程序中直接嵌入汇编语句在C程序中嵌入汇编语句是一种直接的C模块和汇编模块接口方法。采用这种方法一方面可以在C程序中实现用C语言难以实现的一些硬件控制功能。另一方面,也可以用这种方法在C程序中的关键部分用汇编语句代替C语句以优化程序。采用这种方法的一个缺点是它比较容易破坏C环境,因为C编译器在编译嵌入了汇编语句的C程序时并不检查或分析所嵌入的汇编语句。嵌入汇编语句的方法比较简单,只需在汇编语句的两边加上括号和双引号,并且在括号前加上asm标识符

即可,即asm(“汇编语句”)。但是,采用此种方法必须注意以下几点:

①括号中的汇编语句必须以标号、空格、tab、分号开头,这和通常的汇编编程的语法一样。

②不要破坏C环境,因为C编译器并不检查和分析嵌入的汇编语句。

③插入跳转语句和标号会产生不可预测的结果。

④汇编语句不要改变C程序中变量的值。

⑤不要在汇编语句中加入汇编器选项而改变汇编环境。

例如:在程序中可用下列汇编语句实现一些硬件

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论