您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > DSP编程技巧(共27讲)
DSP编程技巧之1:聊一聊编译流程在我们有关DSP的论坛、博客和一些互动活动中,网友经常会问一些有关“编程技巧”的问题。关于编程技巧的定义,也没有一个统一的标准,例如实现一个很复杂的多层嵌套的指针设计算是一种编程熟练的体现吧;又或者用C/C++的高级特性做了很复杂的功能,但是编译器不能完全支持,例如某种内存的动态重分配方法,这时对编译器特性的熟悉就显得很重要了。接下来我们就聊一聊DSP编程技巧里面有关编译技巧的一些问题,具体的分类包括编译器的选项、代码的优化、库文件的使用、代码实现标准、实时运行环境等等;了解了这些具体的问题,对于解决在编译和链接程序的时候经常遇到的让人摸不着头脑的警告和错误也是很有帮助的;因为程序的语法错误很容易被编译器发现并定位到具体的代码中,而链接器输出的那些莫名其妙的警告是很难定位的,如果理解了编译流程中各个环节的作用所在,就有了对症下药的入口。编译器主要以TI的CCS编译器为例,目标器件为28系列,相比6000系列的,28系列的要稍微简单一些。首先要了解一下基于CCS开发的一个完整流程,以免“只见树木,不见森林”:因为很少有文档把它们的具体汉语含义讲清楚,所以在此我们要用通俗的语言描述一下各个部分:1.在这个流程中,与我们编程效率直接相关的就是C/C++编译器了(如果没有使用汇编直接编写的话),它的直接用途是将C/C++代码编译为针对DSP汇编指令集的汇编代码。目前的C和C++语言标准有好几个版本,CCS的编译器目前支持的版本包括:C语言的C89和C99版本的ISO标准(C99部分支持,主要是与C89一样的特性):通俗的讲就是我们学过的C语言里常用的功能都是支持的,除了一些多字节字符和一些极少数的特性等。不清楚的可以参考Kernighan和Ritchie编写的C语言书籍《TheCProgrammingLanguage》第二版。C++语言的2003版本的的ISO标准:可以参考Ellis和Stroustrup编写的经典书籍《TheAnnotatedC++ReferenceManual》。同时也支持一部分的嵌入式C++特性。因为C++的特性众多,而许多并不适用于这样一种嵌入式的环境,所以不支持的特性相比C要多一些。2.汇编器的作用是将汇编语言代码转换为机器语言(目标文件),这里的汇编代码包括前面由C/C++生成的汇编代码和我们直接编写的汇编代码。3.链接器是作用是把所有的库文件、目标文件等链接成为一个可执行的目标文件,其中包含程序的机器代码和数据,以及其他用来链接和加载该程序所需的信息(在TIDSP上是COFF格式,通俗地讲就是.out二进制文件),同时根据内存地址的分配对各目标文件进行重定位,并解析外部参考,例如在一个源程序里引用另一个源程序中定义的变量就可以理解为外部参考,假如一个目标文件引用了一个未定义的符号symbol,则链接器搜索其他目标文件中定义的全局符号,找到匹配的符号修补指令。否则报告一个错误;所以有时候编译所有程序完成在链接的时候会提示xxxsymbol为定义,说明对应的文件没有加到工程里面。4.归档器archiver:也可以叫压缩器,看一下我们常用的压缩软件winrar的全称winrararchiver就不难理解了。5.实时支持库:包括标准C和C++的运行支持函数、编译器公用程序函数、浮点运算函数和C编译器支持的I/O函数。6.十六进制转换程序:把编译、链接等步骤生成的可执行文件,转换为十六进制文件,例如.HEX格式,然后可以烧写到EEPROM、FLASH等外部存储器之中。7.绝对列表器:读取目标文件并输出.abs文件,通过汇编.abs文件可产生含有绝对地址的列表文件,从而使得我们不用手工费时费力地去创建列表文件。这本来不就是软件该做的事情吗?8.交叉引用列表:与3中外部参考解析相关的,它用目标文件产生参照列表文件,可显示符号及其定义,以及符号所在的源文件。9.C/C++命名还原工具:C/C++编译器会将程序中的变量名、函数名转换成内部名称,这个过程被称作NameMangling,反过程被称作NameDemangling,即命名还原工具。内部名称包含了变量或函数的更多信息,例如编译器看到?g_var@@3HA,就知道这是:intg_var。具体的还原规则一般是不开放给我们用户的,只要编译器知道就行了。10.调试工具:例如我们电脑上装的CCS软件,让我们可以用断点、图形窗口等进行软件的调试。此外,并没有明确列在前面的流程中,但是隐含在流程中,或者我们也有可能会用到的工具或者流程包括:1.优化工具:在编译时对代码进行优化的工具,可以根据我们期望的优化级别,进行从不优化到直至CPU寄存器级别的优化。2.反编译器:可以对目标文件进行解码,显示对应的汇编语言。在CCS的调试模式下,我们可以打开disassembler窗口,然后单步运行,就能看到一条条的汇编指令是如何执行的了。3.加载器:把可执行的二进制文件复制到DSP的内存中,并运行启动程序,使得程序从程序入口处开始运行,这个入口地址可能是地址0,也可能是带有一个偏移量的地址,这个具体的值我们可以以后再具体讨论。4.其它:补充一下名词的定义,包括:符号:在整个编译、链接的过程中都会使用到符号symbol的概念,简单地理解,符号就是指一些变量、函数名字等。库文件:多个目标文件的压缩包,包含了所有目标文件定义的全局符号的索引。在源程序中如果找不到某些符号的定义,链接器从尝试从库里面提取出对应的目标文件,然后链接到可执行文件里。DSP编程技巧之2:揭开编译器神秘面纱之处理器选项也许你已经熟练使用了CCS好多年,可是当某一天出现一个与cl2000有关的错误的时候,突然间也摸不着头脑了;例如使用老版本的还不支持C2000FPU的CCS来编译28335的程序,cl2000就会提示你各种不支持然后报错不运行了。Cl2000.exe是神马?Cl2000.exe就是和我们的程序编译密切相关的编译器了,使用的方法是:cl2000[编译器选项][需要编译的文件][--运行链接器[链接器选项]目标文件]]使用方法看起来很复杂,还好CCS已经帮我们调用它了(或者说CCS就是一个框架,它完成的编译、调试、链接等功能几乎都需要调用一些别的exe来执行,所以你可能体会过升级了一些库文件、编辑器版本等,界面文件等却不需要进行任何的更改),这些句子会显示在工程的属性里面。当然如果你想亲自体验一把,也可以在ccs安装目录下面的tools\compiler\c2000_6.1.5\bin下面找到它,即cl2000.exe,然后用命令行的方式运行起来)。举个简单例子:cl2000-v28symtab.cfile.cseek.asm--run_linker--library=lnk.cmd--output_file=myprogram.out在上面的例子中,如果需要编译的文件,例如几个.c或者.asm找不到,CCS就会提示xxx.c或者xxx.asm找不到或者未定义了;或者你改了目标文件的名字,例如改成了a.out,但是加载程序到DSP中的时候却仍然使用更改前的b.out,自然有可能出现预料之外的结果了:在以前帮助网友解决问题的时候,确实出现过这样的状况。明白了编译器的调用方法之后,我们就更进一步,揭开编译器中形形色色选项的神秘面纱,从此看到编译器提示的形形色色的警告和错误不再用发怵。具体说来,编译器的选项有多大20个大类,超过一百个具体的选项。当然这些选项是有轻重之分的,有的是必须用到的,例如支持一下FPU等功能;有的则是不常接触的,例如MISRA这样的汽车工业软件可靠性检查,只有在对软件进行标准化时才会用到。所以我们首先看一下最常用的选项,例如处理器的选项,它们的意义在于定义了在编译程序时CPU的模式。补充一点是,cl2000的帮助里看到的选项都是很长的名字,在CCS里面为了书写方便(因为选项框就那么点面积啊),一般用别名来代替;没有别名的则直接使用选项名字。处理器选项别名含义--silicon_version=28-v28为C28x架构的DSP产生目标文件;不选择的话模式为C27x模式,也可以选择为C2xLP兼容模式(例如让C28的CPU支持C24的汇编语句,存在较多的兼容性问题,因为寻找模式、CPU架构等都发生了一定的变化,有的指令不再适用于新器件)。后两种模式大部分网友都几乎不会用到,所以我们的编译器选项里面一般都会选择-v28。更详细的信息可以参考DSP的CPU介绍和汇编编程指南。--large_memory_model-ml产生“大内存模式”下的代码。开启这个选项的话,会强迫编译器把整个地址空间当作一块完整的22位宽的空间(实际是分为16位宽的低地址和超过16位宽度之后的高地址空间的),从而使得寻址时使用的指针也是22位的(这个指针是针对CPU寻址来说的,不是我们C程序里用的指针),这样寻找空间就不必局限于2的16次方,即64K了。这种模式适合在C++编程的时候使用,使得编译生成的代码可以访问超过16位宽度的地址空间的存储单元,这样就没有64K字的空间限制了。那么为什么在在C++编程时使用呢?是因为目前编译器不支持C++的关键字far;如果你了解C++关键字的话,那用同样的思路来理解这个模式就容易了。此外,在开启FPU的情况下,大内存模式是必须开启的,否则编译器会报错。在新建C2000的工程,需要添加相关的库文件的时候,如果你再看到有的库是rts2800.lib,有的是rts2800_ml.lib,这次应该明白改用哪个了吧。小建议是为了省事和保持兼容性,没有别的顾虑的话就把这个选项打开吧。--unified_memory-mt在“统一的内存模式”下产生代码。顾名思义,就是把所有的存储空间定义为一个整体,这样编译器在编译时就可以使用RPT与PREAD指令来处理大部分的内存复制memcpy调用和结构体的分配(它也不用“担心”存储空间突然出个断层,没法连续寻找了)。例如像下面的汇编指令就可以得到更加高效的执行:MOVLXAR7,#Array1;XAR7指向数组1MOVLXAR2,#Array2;XAR2指向数组2RPT#(N-1);重复执行下一条指令N次||PREAD*XAR2++,*XAR7;Array2[i]=Array1[i],i++这样的一段汇编代码我们可以直接手工编写;如果你有个for循环的C代码的话,看一下编译生成的汇编代码,是不是几乎一模一样的?--cla_support[=cla0]无--cla_support是C2833x系列之后的Piccolo系列才有的特性,叫控制规律加速器,意思是把一些与控制系统性能息息相关的代码放到CLA中独立运行,不占用CPU时间,这样整个控制软件的运行速度都得到极大提高,从而保证实时性。--float_support={fpu32|softlib|fpu64}无在启用了-v28和-ml的前提下才能使用;含义是启用软件处理(比如调用一些优化好的库函数)、32位或者64位的FPU协处理器进行浮点运算,从而支持相关的汇编指令。这个子选项是不能同时使用的,即使用方法为:--float_support=fpu32或者--float_support=fpu64需要补充的是,这里的64位浮点运算指的数据类型是longdouble,而实际上28335这样的DSP中FPU目前只硬件支持32位的FPU运算,64位的浮点运行要经过CPU折算再送给FPU处理的,所以不是必须的话尽量不要使用FPU64这样的运算。--vcu_support[=vcu0]无VCU是F2837xD这样的高端芯片上具备的功能,指的是Viterbiandcomplexunit(VCUII)accelerators,即通过采用viterbi复杂单元(VCUII)加速器执行振动分析来更好地预测电机故障,振动信号的来源是加速度传感器或者振动传感器等,可以使用流行的MEMS,可以贴在电机的外壳、编码器等部位。如果需要使用这个功能的话,就需要在编译器选项里面打开它。DSP编程技巧
本文标题:DSP编程技巧(共27讲)
链接地址:https://www.777doc.com/doc-4359249 .html