DSP程序优化方法(1)
1、选择合适的算法和数据结构
选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。
在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。
数组索引指针运算
For(;;){ p=array
A=array[t++]; for(;;){
a=*(p++);
。。。。。。。。。。。。。。。
} }
指针方法的优点是,array 的地址每次装入地址p 后,在每次循环中只需对p 增量操作。在数组索引方法中,每次循环中都必须根据t 值求数组下标的复杂运算。
2、使用尽量小的数据类型
能够使用字符型(char) 定义的变量,就不要使用整型(int) 变量来定义;能够使用整型变量定义的变量就不要用长整型(long int) ,能不使用浮点型(float) 变量就不要使用浮点型变量。当然,在定义变量后不要超过变量的作用范围,如果超过变量的范围赋值,C 编译器并不报错,但程序运行结果却错了,而且这样的错误很难发现。
在ICCA VR 中,可以在Options 中设定使用printf 参数,尽量使用基本型参数(%c 、%d 、%x 、%X 、%u 和%s 格式说明符) ,少用长整型参数(%ld 、%lu 、%lx 和%lX 格式说明符) ,至于浮点型的参数(%f) 则尽量不要使用,其它C 编译器也一样。在其它条件不变的情况下,使用%f 参数,会使生成的代码的数量增加很多,执行速度降低。
3、减少运算的强度
(1)、查表(游戏程序员必修课)
一个聪明的游戏大虾,基本上不会在自己的主循环里搞什么运算工作,绝对是先计算好了,再到循环里查表。看下面的例子:
旧代码:
long factorial(int i)
{
if (i == 0)
return 1;
else
return i * factorial(i - 1);
}
新代码:
static long factorial_table[] =
{1 ,1 ,2 ,6 ,24 ,120 ,720 /* etc */ };
long factorial(int i)
{
return factorial_table;
}
如果表很大,不好写,就写一个init 函数,在循环外临时生成表格。
(2)、求余运算
a=a%8;
可以改为:
a=a&7;
说明:位操作只需一个指令周期即可完成,而大部分的C 编译器的“% ”运算均是调用子程序来完成,代码长、执行速度慢。通常,只要求是求2n 方的余数,均可使用位操作的方法来代替。
(3)、平方运算
a=pow(a, 2.0);
可以改为:
a=a*a;
说明:在有内置硬件乘法器的单片机中( 如51 系列) ,乘法运算比求平方运算快得多,因为浮点数的求平方是通过调用子程序来实现的,在自带硬件乘法器的A VR 单片机中,如ATMega163 中,乘法运算只需2 个时钟周期就可以完成。既使是在没有内置硬件乘法器的A VR 单片机中,乘法运算的子程序比平方运算的子程序代码短,执行速度快。
如果是求3 次方,如:
a=pow(a ,3 。0);
更改为:
a=a*a*a ;
则效率的改善更明显。
(4)、用移位实现乘除法运算
a=a*4;
b=b/4;
可以改为:
a=a<<2;
b=b>>2;
通常如果需要乘以或除以2n ,都可以用移位的方法代替。在ICCA VR 中,如果乘以2n ,都可以生成左移的代码,而乘以其它的整数或除以任何数,均调用乘除法子程序。用移位的方法得到代码比调用乘除法子程序生成的代码效率高。实际上,只要是乘以或除以一个整数,均可以用移位的方法得到结果,如:
a=a*9
可以改为:
a=(a<<3)+a
采用运算量更小的表达式替换原来的表达式,下面是一个经典例子:
旧代码:
x = w % 8;
y = pow(x ,2.0);
z = y * 33;
for (i = 0;i < MAX;i++)
{
h = 14 * i;
printf("%d" ,h);
}
新代码:
x = w & 7; /* 位操作比求余运算快*/
y = x * x; /* 乘法比平方运算快*/
z = (y << 5) + y; /* 位移乘法比乘法快*/
for (i = h = 0; i < MAX; i++)
{
h += 14; /* 加法比乘法快*/
printf("%d" ,h);
}
(5)、避免不必要的整数除法
整数除法是整数运算中最慢的,所以应该尽可能避免。一种可能减少整数除法的地方是连除,这里除法可以由乘法代替。这个替换的副作用是有可能在算乘积时会溢出,所以只能在一定范围的除法中使用。
不好的代码:
int i ,j ,k ,m ;
m = i / j / k ;
推荐的代码:
int i ,j ,k ,m ;
m = i / (j * k) ;
(6)、使用增量和减量操作符
在使用到加一和减一操作时尽量使用增量和减量操作符,因为增量符语句比赋值语句更快,原因在于对大多数CPU 来说,对内存字的增、减量操作不必明显地使用取内存和写内存的指令,比如下面这条语句:
x=x+1;
模仿大多数微机汇编语言为例,产生的代码类似于:
move A ,x ; 把x 从内存取出存入累加器A
add A ,1 ; 累加器A 加1
store x ; 把新值存回x
如果使用增量操作符,生成的代码如下:
incr x ;x 加1
显然,不用取指令和存指令,增、减量操作执行的速度加快,同时长度也缩短了。(7)、使用复合赋值表达式
复合赋值表达式( 如a-=1 及a+=1 等) 都能够生成高质量的程序代码。
(8)、提取公共的子表达式
在某些情况下,C++ 编译器不能从浮点表达式中提出公共的子表达式,因为这意味着相当于对表达式重新排序。需要特别指出的是,编译器在提取公共子表达式前不能按照代数的等价关系重新安排表达式。这时,程序员要手动地提出公共的子表达式(在https://www.doczj.com/doc/9d7309583.html, 里有一项“全局优化”选项可以完成此工作,但效果就不得而知了)。
不好的代码:
float a ,b ,c ,d ,e ,f ;
。。。
e = b * c / d ;
f = b / d * a ;
推荐的代码:
float a ,b ,c ,d ,e ,f ;
。。。
const float t(b / d) ;
e = c * t ;
f = a * t ;
不好的代码:
float a ,b ,c ,e ,f ;
。。。
e = a / c ;
f = b / c ;
推荐的代码:
float a ,b ,c ,e ,f ;
。。。
const float t(1.0f / c) ;
e = a * t ;
f = b * t ;
4、结构体成员的布局
很多编译器有“使结构体字,双字或四字对齐”的选项。但是,还是需要改善结构体成员的对齐,有些编译器可能分配给结构体成员空间的顺序与他们声明的不同。但是,有些编译器并不提供这些功能,或者效果不好。所以,要在付出最少代价的情况下实现最好的结构体和结构体成员对齐,建议采取下列方法:
(1)按数据类型的长度排序
把结构体的成员按照它们的类型长度排序,声明成员时把长的类型放在短的前面。编译器要求把长型数据类型存放在偶数地址边界。在申明一个复杂的数据类型( 既有多字节数据又有单字节数据) 时,应该首先存放多字节数据,然后再存放单字节数据,这样可以避免内存的空洞。编译器自动地把结构的实例对齐在内存的偶数边界。
(2)把结构体填充成最长类型长度的整倍数
把结构体填充成最长类型长度的整倍数。照这样,如果结构体的第一个成员对齐了,所有整个结构体自然也就对齐了。下面的例子演示了如何对结构体成员进行重新排序:
不好的代码,普通顺序:
struct
{
char a[5] ;
long k ;
double x ;
} baz ;
推荐的代码,新的顺序并手动填充了几个字节:
struct
{
double x ;
long k ;
char a[5] ;
char pad[7] ;
} baz ;
这个规则同样适用于类的成员的布局。
(3)按数据类型的长度排序本地变量
当编译器分配给本地变量空间时,它们的顺序和它们在源代码中声明的顺序一样,和上一条规则一样,应该把长的变量放在短的变量前面。如果第一个变量对齐了,其它变量就会连续的存放,而且不用填充字节自然就会对齐。有些编译器在分配变量时不会自动改变变量顺序,有些编译器不能产生4 字节对齐的栈,所以4 字节可能不对齐。下面这个例子演示了本地变量声明的重新排序:
不好的代码,普通顺序
short ga ,gu ,gi ;
long foo ,bar ;
double x ,y ,z[3] ;
char a ,b ;
float baz ;
推荐的代码,改进的顺序
double z[3] ;
double x ,y ;
long foo ,bar ;
float baz ;
short ga ,gu ,gi ;
(4)把频繁使用的指针型参数拷贝到本地变量
避免在函数中频繁使用指针型参数指向的值。因为编译器不知道指针之间是否存在冲突,所以指针型参数往往不能被编译器优化。这样数据不能被存放在寄存器中,而且明显地占用了内存带宽。注意,很多编译器有“假设不冲突”优化开关(在VC 里必须手动添加编译器命令行/Oa 或/Ow ),这允许编译器假设两个不同的指针总是有不同的内容,这样就不用把指针型参数保存到本地变量。否则,请在函数一开始把指针指向的数据保存到本地变量。如果需要的话,在函数结束前拷贝回去。
不好的代码:
// 假设q != r
void isqrt(unsigned long a ,unsigned long* q ,unsigned long* r)
{
*q = a ;
if (a > 0)
{
while (*q > (*r = a / *q))
{
*q = (*q + *r) >> 1 ;
}
}
*r = a - *q * *q ;
}
推荐的代码:
// 假设q != r
void isqrt(unsigned long a ,unsigned long* q ,unsigned long* r)
{
unsigned long qq ,rr ;
qq = a ;
if (a > 0)
{
while (qq > (rr = a / qq))
{
qq = (qq + rr) >> 1 ;
}
}
rr = a - qq * qq ;
*q = qq ;
*r = rr ;
}
本文来自: DSP交流网(https://www.doczj.com/doc/9d7309583.html,) 详细出处参考:https://www.doczj.com/doc/9d7309583.html,/bbs/viewthread.php?tid=3955&extra=page%3D1
DSP程序优化方法(2)
5、循环优化
(1)、充分分解小的循环
要充分利用CPU的指令缓存,就要充分分解小的循环。特别是当循环体本身很小的时候,分解循环可以提高性能。注意:很多编译器并不能自动分解循环。不好的代码:
// 3D 转化:把矢量V 和4x4 矩阵M 相乘
for (i = 0 ;i < 4 ;i ++)
{
r = 0 ;
for (j = 0 ;j < 4 ;j ++)
{
r += M[j]*V[j] ;
}
}
推荐的代码:
r[0] = M[0][0]*V[0] + M[1][0]*V[1] + M[2][0]*V[2] + M[3][0]*V[3] ;
r[1] = M[0][1]*V[0] + M[1][1]*V[1] + M[2][1]*V[2] + M[3][1]*V[3] ;
r[2] = M[0][2]*V[0] + M[1][2]*V[1] + M[2][2]*V[2] + M[3][2]*V[3] ;
r[3] = M[0][3]*V[0] + M[1][3]*V[1] + M[2][3]*V[2] + M[3][3]*v[3] ;
(2)、提取公共部分
对于一些不需要循环变量参加运算的任务可以把它们放到循环外面,这里的任务包括表达式、函数的调用、指针运算、数组访问等,应该将没有必要执行多次的操作全部集合在一起,放到一个init 的初始化程序中进行。
(3)、延时函数
通常使用的延时函数均采用自加的形式:
void delay (void)
{
unsigned int i;
for (i=0;i<1000;i++) ;
}
将其改为自减延时函数:
void delay (void)
{
unsigned int i;
for (i=1000;i>0;i--) ;
}
两个函数的延时效果相似,但几乎所有的C 编译对后一种函数生成的代码均比前一种代码少1~3 个字节,因为几乎所有的MCU 均有为0 转移的指令,采用后一种方式能够生成这类指令。在使用while 循环时也一样,使用自减指令控制循环会比使用自加指令控制循环生成的代码更少1~3 个字母。但是在循环中有通过循环变量“i ”读写数组的指令时,使用预减循环有可能使数组超界,要引起注意。
(4)、while循环和do…while循环
用while 循环时有以下两种循环形式:
unsigned int i;
i=0;
while (i<1000)
{
i++;
// 用户程序
}
或:
unsigned int i;
i=1000;
do
{
i--;
// 用户程序
}
while (i>0);
在这两种循环中,使用do …while 循环编译后生成的代码的长度短于while 循环。(6)、循环展开
这是经典的速度优化,但许多编译程序( 如gcc -funroll-loops) 能自动完成这个事,所以现在你自己来优化这个显得效果不明显。
旧代码:
for (i = 0; i < 100; i++)
{
do_stuff(i);
}
新代码:
for (i = 0; i < 10; )
{
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
do_stuff(i); i++;
}
可以看出,新代码里比较指令由100 次降低为10 次,循环时间节约了90% 。不过注意: 对于中间变量或结果被更改的循环,编译程序往往拒绝展开,( 怕担责任呗) ,这时候就需要你自己来做展开工作了。
还有一点请注意,在有内部指令cache 的CPU 上( 如MMX 芯片) ,因为循环展开的代码很大,往往cache 溢出,这时展开的代码会频繁地在CPU 的cache 和内存之间调来调去,又因为cache 速度很高,所以此时循环展开反而会变慢。还有就是循环展开会影响矢量运算优化。
(6)、循环嵌套
把相关循环放到一个循环里,也会加快速度。
旧代码:
for (i = 0; i < MAX; i++) /* initialize 2d array to 0's */
for (j = 0; j < MAX; j++)
a[j] = 0.0;
for (i = 0; i < MAX; i++) /* put 1's along the diagonal */
a = 1.0;
新代码:
for (i = 0; i < MAX; i++) /* initialize 2d array to 0's */
{
for (j = 0; j < MAX; j++)
a[j] = 0.0;
a = 1.0; /* put 1's along the diagonal */
}
(7)、Switch语句中根据发生频率来进行case排序
Switch 可能转化成多种不同算法的代码。其中最常见的是跳转表和比较链/ 树。当switch 用比较链的方式转化时,编译器会产生if-else-if 的嵌套代码,并按照顺序进行比较,匹配时就跳转到满足条件的语句执行。所以可以对case 的值依照发生的可能性进行排序,把最有可能的放在第一位,这样可以提高性能。此外,在case 中推荐使用小的连续的整数,因为在这种情况下,所有的编译器都可以把switch 转化成跳转表。
不好的代码:
int days_in_month ,short_months ,normal_months ,long_months ;
。。。。。。
switch (days_in_month)
{
case 28:
case 29:
short_months ++ ;
break ;
case 30:
normal_months ++ ;
break ;
case 31:
long_months ++ ;
break ;
default:
cout << "month has fewer than 28 or more than 31 days" << endl ;
break ;
}
推荐的代码:
int days_in_month ,short_months ,normal_months ,long_months ;
。。。。。。
switch (days_in_month)
{
case 31:
long_months ++ ;
break ;
case 30:
normal_months ++ ;
break ;
case 28:
case 29:
short_months ++ ;
break ;
default:
cout << "month has fewer than 28 or more than 31 days" << endl ;
break ;
}
(8)、将大的switch语句转为嵌套switch语句
当switch 语句中的case 标号很多时,为了减少比较的次数,明智的做法是把大switch 语句转为嵌套switch 语句。把发生频率高的case 标号放在一个switch 语句中,并且是嵌套switch 语句的最外层,发生相对频率相对低的case 标号放在另一个switch 语句中。比如,下面的程序段把相对发生频率低的情况放在缺省的case 标号内。
pMsg=ReceiveMessage();
switch (pMsg->type)
{
case FREQUENT_MSG1:
handleFrequentMsg();
break;
case FREQUENT_MSG2:
handleFrequentMsg2();
break;
。。。。。。
case FREQUENT_MSGn:
handleFrequentMsgn();
break;
default: // 嵌套部分用来处理不经常发生的消息
switch (pMsg->type)
{
case INFREQUENT_MSG1:
handleInfrequentMsg1();
break;
case INFREQUENT_MSG2:
handleInfrequentMsg2();
break;
。。。。。。
case INFREQUENT_MSGm:
handleInfrequentMsgm();
break;
}
}
如果switch 中每一种情况下都有很多的工作要做,那么把整个switch 语句用一个指向函数指针的表来替换会更加有效,比如下面的switch 语句,有三种情况:enum MsgType{Msg1 ,Msg2 ,Msg3}
switch (ReceiveMessage()
{
case Msg1;
。。。。。。
case Msg2;
。。。。。
case Msg3;
。。。。。
}
为了提高执行速度,用下面这段代码来替换这个上面的switch 语句。
/* 准备工作*/
int handleMsg1(void);
int handleMsg2(void);
int handleMsg3(void);
/* 创建一个函数指针数组*/
int (*MsgFunction [])()={handleMsg1 ,handleMsg2 ,handleMsg3};
/* 用下面这行更有效的代码来替换switch 语句*/
status=MsgFunction[ReceiveMessage()]();
(9)、循环转置
有些机器对JNZ( 为0 转移) 有特别的指令处理,速度非常快,如果你的循环对方向不敏感,可以由大向小循环。
旧代码:
for (i = 1; i <= MAX; i++)
{
。。。
}
新代码:
i = MAX+1;
while (--i)
{
。。。
}
不过千万注意,如果指针操作使用了i 值,这种方法可能引起指针越界的严重错误(i = MAX+1;) 。当然你可以通过对i 做加减运算来纠正,但是这样就起不到加速的作用,除非类似于以下情况:
旧代码:
char a[MAX+5];
for (i = 1; i <= MAX; i++)
{
*(a+i+4)=0;
}
新代码:
i = MAX+1;
while (--i)
{
*(a+i+4)=0;
}
(10)、公用代码块
一些公用处理模块,为了满足各种不同的调用需要,往往在内部采用了大量的if-then-else 结构,这样很不好,判断语句如果太复杂,会消耗大量的时间的,应该尽量减少公用代码块的使用。( 任何情况下,空间优化和时间优化都是对立的-- 东楼) 。当然,如果仅仅是一个(3==x) 之类的简单判断,适当使用一下,也还是允许的。记住,优化永远是追求一种平衡,而不是走极端。
(11)提升循环的性能
要提升循环的性能,减少多余的常量计算非常有用(比如,不随循环变化的计算)。
不好的代码( 在for() 中包含不变的if()) :
for( i 。。。)
if( CONSTANT0 )
{
DoWork0( i ) ;// 假设这里不改变CONSTANT0 的值
}
else
{
DoWork1( i ) ;// 假设这里不改变CONSTANT0 的值
}
}
推荐的代码:
if( CONSTANT0 )
{
for( i 。。。)
{
DoWork0( i ) ;
}
}
else
{
for( i 。。。)
{
DoWork1( i ) ;
}
}
如果已经知道if() 的值,这样可以避免重复计算。虽然不好的代码中的分支可以简单地预测,但是由于推荐的代码在进入循环前分支已经确定,就可以减少对分支预测的依赖。(12)、选择好的无限循环
在编程中,我们常常需要用到无限循环,常用的两种方法是while (1) 和for ( ;;) 。这两种方法效果完全一样,但那一种更好呢?然我们看看它们编译后的代码:
编译前:
while (1) ;
编译后:
mov eax ,1
test eax ,eax
je foo+23h
jmp foo+18h
编译前:
for ( ;;) ;
编译后:
jmp foo+23h
显然,for ( ;;) 指令少,不占用寄存器,而且没有判断、跳转,比while (1) 好。
本文来自: DSP交流网(https://www.doczj.com/doc/9d7309583.html,) 详细出处参考:https://www.doczj.com/doc/9d7309583.html,/bbs/viewthread.php?tid=3956&extra=page%3D1
DSP程序优化方法(3)
6、提高CPU的并行性
(1)使用并行代码
尽可能把长的有依赖的代码链分解成几个可以在流水线执行单元中并行执行的没有依赖的代码链。很多高级语言,包括C++ ,并不对产生的浮点表达式重新排序,因为那是一个相当复杂的过程。需要注意的是,重排序的代码和原来的代码在代码上一致并不等价于计算结果一致,因为浮点操作缺乏精确度。在一些情况下,这些优化可能导致意料之外的结果。幸运的是,在大部分情况下,最后结果可能只有最不重要的位(即最低位)是错误的。
不好的代码:
double a[100] ,sum ;
int i ;
sum = 0.0f ;
for (i=0 ;i<100 ;i++)
sum += a ;
推荐的代码:
double a[100] ,sum1 ,sum2 ,sum3 ,sum4 ,sum ;
int i ;
sum1 = sum2 = sum3 = sum4 = 0.0 ;
for (i = 0 ;i < 100 ;i += 4)
{
sum1 += a ;
sum2 += a[i+1] ;
sum3 += a[i+2] ;
sum4 += a[i+3] ;
}
sum = (sum4+sum3)+(sum1+sum2) ;
要注意的是:使用 4 路分解是因为这样使用了4 段流水线浮点加法,浮点加法的每一个段占用一个时钟周期,保证了最大的资源利用率。
(2)避免没有必要的读写依赖
当数据保存到内存时存在读写依赖,即数据必须在正确写入后才能再次读取。虽然AMD Athlon 等CPU 有加速读写依赖延迟的硬件,允许在要保存的数据被写入内存前读取出来,但是,如果避免了读写依赖并把数据保存在内部寄存器中,速度会更快。在一段很长的又互相依赖的代码链中,避免读写依赖显得尤其重要。如果读写依赖发生在操作数组时,许多编译器不能自动优化代码以避免读写依赖。所以推荐程序员手动去消除读写依赖,举例来说,引进一个可以保存在寄存器中的临时变量。这样可以有很大的性能提升。下面一段代码是一个例子:
不好的代码:
float x[VECLEN] ,y[VECLEN] ,z[VECLEN] ;
。。。。。。
for ( unsigned int k = 1 ;k < VECLEN ;k ++)
{
x[k] = x[k-1] + y[k] ;
for (k = 1 ;k { x[k] = z[k] * (y[k] - x[k-1]) ; } 推荐的代码: float x[VECLEN] ,y[VECLEN] ,z[VECLEN] ; 。。。。。。 float t(x[0]) ; for ( unsigned int k = 1 ;k < VECLEN ;k ++) { t = t + y[k] ; x[k] = t ; } t = x[0] ; for (k = 1 ;k < ;VECLEN ;k ++) { t = z[k] * (y[k] - t) ; x[k] = t ; } 7、循环不变计算 对于一些不需要循环变量参加运算的计算任务可以把它们放到循环外面,现在许多编译器还是能自己干这件事,不过对于中间使用了变量的算式它们就不敢动了,所以很多情况下你还得自己干。对于那些在循环中调用的函数,凡是没必要执行多次的操作通通提出来,放到一个init 函数里,循环前调用。另外尽量减少喂食次数,没必要的话尽量不给它传参,需要循环变量的话让它自己建立一个静态循环变量自己累加,速度会快一点。 还有就是结构体访问,东楼的经验,凡是在循环里对一个结构体的两个以上的元素执行了访问,就有必要建立中间变量了( 结构这样,那C++ 的对象呢? 想想看) ,看下面的例子: 旧代码: total = a->b->c[4]->aardvark + a->b->c[4]->baboon + a->b->c[4]->cheetah + a->b->c[4]->dog; 新代码: struct animals * temp = a->b->c[4]; total = temp->aardvark + temp->baboon + temp->cheetah + temp->dog; 一些老的C 语言编译器不做聚合优化,而符合ANSI 规范的新的编译器可以自动完成这个优化,看例子: float a ,b ,c ,d ,f ,g; 。。。 a = b / c * d; f = b * g / c; 这种写法当然要得,但是没有优化 float a ,b ,c ,d ,f ,g; 。。。 a = b / c * d; f = b / c * g; 如果这么写的话,一个符合ANSI 规范的新的编译器可以只计算b/c 一次,然后将结果代入第二个式子,节约了一次除法运算。 8、函数优化 (1)Inline函数 在C++ 中,关键字Inline 可以被加入到任何函数的声明中。这个关键字请求编译器用函数内部的代码替换所有对于指出的函数的调用。这样做在两个方面快于函数调用:第一,省去了调用指令需要的执行时间;第二,省去了传递变元和传递过程需要的时间。但是使用这种方法在优化程序速度的同时,程序长度变大了,因此需要更多的ROM 。使用这种优化在Inline 函数频繁调用并且只包含几行代码的时候是最有效的。 (2)不定义不使用的返回值 函数定义并不知道函数返回值是否被使用,假如返回值从来不会被用到,应该使用void 来明确声明函数不返回任何值。 (3)减少函数调用参数 使用全局变量比函数传递参数更加有效率。这样做去除了函数调用参数入栈和函数完成后参数出栈所需要的时间。然而决定使用全局变量会影响程序的模块化和重入,故要慎重使用。 (4)所有函数都应该有原型定义 一般来说,所有函数都应该有原型定义。原型定义可以传达给编译器更多的可能用于优化的信息。 (5)尽可能使用常量(const) 尽可能使用常量(const) 。C++ 标准规定,如果一个const 声明的对象的地址不被获取,允许编译器不对它分配储存空间。这样可以使代码更有效率,而且可以生成更好的代码。(6)把本地函数声明为静态的(static) 如果一个函数只在实现它的文件中被使用,把它声明为静态的(static)以强制使用内部连接。否则,默认的情况下会把函数定义为外部连接。这样可能会影响某些编译器的优化——比如,自动内联。 本文来自: DSP交流网(https://www.doczj.com/doc/9d7309583.html,) 详细出处参考:https://www.doczj.com/doc/9d7309583.html,/bbs/viewthread.php?tid=3957&extra=page%3D1 DSP程序优化方法(4) 9、采用递归 与LISP之类的语言不同,C语言一开始就病态地喜欢用重复代码循环,许多C程序员都是除非算法要求,坚决不用递归。事实上,C编译器们对优化递归调用一点都不反感,相反,它们还很喜欢干这件事。只有在递归函数需要传递大量参数,可能造成瓶颈的时候,才应该使用循环代码,其他时候,还是用递归好些。 10、变量 (1)register变量 在声明局部变量的时候可以使用register关键字。这就使得编译器把变量放入一个多用途的寄存器中,而不是在堆栈中,合理使用这种方法可以提高执行速度。函数调用越是频繁,越是可能提高代码的速度。 在最内层循环避免使用全局变量和静态变量,除非你能确定它在循环周期中不会动态变化,大多数编译器优化变量都只有一个办法,就是将他们置成寄存器变量,而对于动态变量,它们干脆放弃对整个表达式的优化。尽量避免把一个变量地址传递给另一个函数,虽然这个还很常用。C语言的编译器们总是先假定每一个函数的变量都是内部变量,这是由它的机制决定的,在这种情况下,它们的优化完成得最好。但是,一旦一个变量有可能被别的函数改变,这帮兄弟就再也不敢把变量放到寄存器里了,严重影响速度。看例子: a = b(); c(&d); 因为d的地址被c函数使用,有可能被改变,编译器不敢把它长时间的放在寄存器里,一旦运行到c(&d),编译器就把它放回内存,如果在循环里,会造成N次频繁的在内存和寄存器之间读写d的动作,众所周知,CPU在系统总线上的读写速度慢得很。比如你的赛杨300,CPU主频300,总线速度最多66M,为了一个总线读,CPU可能要等4-5个周期,得。。得。。得。。想起来都打颤。 (2)、同时声明多个变量优于单独声明变量 (3)、短变量名优于长变量名,应尽量使变量名短一点 (4)、在循环开始前声明变量 11、使用嵌套的if结构 在if结构中如果要判断的并列条件较多,最好将它们拆分成多个if结构,然后嵌套在一起,这样可以避免无谓的判断。 本文来自: DSP交流网(https://www.doczj.com/doc/9d7309583.html,) 详细出处参考:https://www.doczj.com/doc/9d7309583.html,/bbs/viewthread.php?tid=3958&extra=page%3D1 C6XX优化经验总结 一、c6x的编译的常用选项 (一)c6x的编译程序为“”使用的方法 Cl6x [options] [filenames] Cl6x:编译程序 Options:编译选项 Filenames:C或汇编源文件 说明: 编译选项是一个字母或者两个字母,对大小写不敏感。 编译选项的前面需要有一个“-”符号。 一个字母的选项可以合并在一起。比如“-sgq”与“-s -g -q”相同。 两个字母的选项如果第一个字母相同也可以合并在一起。比如“-mgt”与“-mg -mt”相同。 (二)有关优化的选项 -mt:表示在程序中没有使用alaising技术,这使得编译器可以进行比较好的优化。 -o3:对文件级别进行最强的优化,一般在编译时应该使用这个选项。但是在个别情况下使用这个选项优化程序可能会出现 错误(-o2有相同现象,-o0和-o1不会出现错误)。可能是在优化循环,组织流水线的时候发生错误。如果有这种现象出现可以同时 使用-g选项,程序优化就不会出现错误,但是优化效果会下降。另外可以调整程序的表达方式,可能会避免编译器发生错误。 -pm:在程序级别进行优化。可以将所以文件联合在一起进行优化,主要有去掉没有被调用的函数、总是常数的变量以及没有使用的 函数返回值。建议由程序员自己进行这种优化工作。使用这个选项在win98下编译可能会出现找不到编译程序的情况。 -ms0:不使用冗余循环进行优化,减小程序的大小。一般情况下这个选项对程序大小的优化作用不明显。 -mh[n]:去掉流水线的epilog,减小程序的大小。这个选项的作用比较明显。但是有可能出现读取地址超出有效范围的问题, 所以要在数据段的开始和结尾处增加一些pading,或者在分配内存时保证数组的前面和后面一段范围内都是有效的地址。 可选的参数n给出这种pading的长度字节数。 (三)保留编译和优化信息的选项 -k:保留优化后生成汇编语言文件。 实验0 实验设备安装才CCS调试环境 实验目的: 按照实验讲义操作步骤,打开CCS软件,熟悉软件工作环境,了解整个工作环境内容,有助于提高以后实验的操作性和正确性。 实验步骤: 以演示实验一为例: 1.使用配送的并口电缆线连接好计算机并口与实验箱并口,打开实验箱电源; 2.启动CCS,点击主菜单“Project->Open”在目录“C5000QuickStart\sinewave\”下打开工程文件sinewave.pjt,然后点击主菜单“Project->Build”编译,然后点击主菜单“File->Load Program”装载debug目录下的程序sinewave.out; 3.打开源文件exer3.asm,在注释行“set breakpoint in CCS !!!”语句的NOP处单击右键弹出菜单,选择“Toggle breakpoint”加入红色的断点,如下图所示; 4.点击主菜单“View->Graph->Time/Frequency…”,屏幕会出现图形窗口设置对话框 5.双击Start Address,将其改为y0;双击Acquisition Buffer Size,将其改为1; DSP Data Type设置成16-bit signed integer,如下图所示; 6.点击主菜单“Windows->Tile Horizontally”,排列好窗口,便于观察 7.点击主菜单“Debug->Animate”或按F12键动画运行程序,即可观察到实验结果: 心得体会: 通过对演示实验的练习,让自己更进一步对CCS软件的运行环境、编译过程、装载过程、属性设置、动画演示、实验结果的观察有一个醒目的了解和熟悉的操作方法。熟悉了DSP实验箱基本模块。让我对DSP课程产生了浓厚的学习兴趣,课程学习和实验操作结合为一体的学习体系,使我更好的领悟到DSP课程的实用性和趣味性。 买视频资料、买书、买开发板(F28335)、各种版本开发环境安装,导入工程、创建工程、了解controlSUITE,终于创建了一个工程,且是按照自己的意愿将代码在开发板上跑起来,现总结如下: 实验目的: 将开发板上可控的两个LED灯LD3、LD4实现交替闪烁,并在RAM中仿真实验; 实验步骤: 1、创建工程、且包含main.c文件 按照上面的五步操作,然后点击【finish】。 遇到的问题:在第四步如果设置新的工作空间文件夹,在点击【finish】后,应实际的去工作空间文件夹下去看一下,是否确实创建好了。 建议:在点击完【finish】后,项目框架基本创建完成,然后关闭CCS,再次进去看看是否直接进入刚刚创建好的工程。如果不能最好分析一下原因,重建一次。我在这里反复了3次,也就是重建3次才成功。如果可以进入,进入后面的步骤。 2、复制controlSUITE下C:\ti\controlSUITE\device_support\f2833x下面的DSP2833x_common和 DSP2833x_headers两个文件夹到所创建的工程目录; 复制完成后最好仔细浏览一下每个目录下都有哪些文件。 3、在项目的属性设置对话框中设置include路径如下: 4、在main.c添加如下头文件: #include "DSP2833x_Device.h" // DSP2833x Headerfile Include File #include "DSP2833x_Examples.h" // DSP2833x Examples Include File 5、这是可以编译一下,肯定会出错,需要删除一些文件。如下 以上红圈内的文件全部删除 6、然后再编译一下,看看有什么问题就自己处理吧,呵呵。。。。。 7、现在已经有了基本的函数库、头文件,剩下就是实现LED灯的控制了。 代码如下: #include"DSP2833x_Device.h"// DSP2833x Headerfile Include File iPone 的一句大家都耳熟能详的广告词:“一直被模仿,从未被超越” 。笔者认为主要是因为他们掌握着核心的算法和机器的优化策略。因为一般的硬件我们都买的回来,但是能否将该硬件发挥到极致,就会公司之间的差别,因为同样的硬件,如果软件执行的速度不同,那结果就会有很大的差别,所以说:真正的技术是买不来的。 所以,我们进行嵌入式开发的时候,一旦选定了DSP6000系列的芯片,就不能把它当成单片机来用,必须发挥dsp 与众不同,独一无二的性能。也就是说如何调整c 语言才能够适应这么强悍的硬件就是我们考虑的重点内容,即我们应该按照哪种既定的原则去编写C 代码才能够让dsp 真正作为dsp 在工作,发挥到dsp 的优势。 dsp 的优势在于:速度! 所以,dsp 的优化成为一门专业。 所以,我们一定要使自己在dsp 上编写的c 代码高效运行。因为制约运行速度的因素是硬件和软件。因为dsp 一旦选定,硬件也就确定了。所以,我们首先要注意如何提升软件的效率。对于软件来说,一般情况下有3 个优化等级。第一:算法上优化。第二:程序结构上的优化。第三:汇编级的优化。 我们需要的是研究前两个等级的优化。所以,在这篇文章中,我们需要研究的重点有两个:dsp 的硬件结构和在dsp 上如何优化c 代码。 dsp 的硬件结构 关于dsp 硬件结构的特色有几个:哈佛结构,流水线结构,带宽和运算方式的高效等。1.1.1 哈佛结构 哈佛结构的本质属性是数据存储器(RAM存储数据的存储器)和程序存储器(存储指令)分开。Cpu可以一边取指令,一边取数据。这样会极大的提高处理的速度,因为以前是冯诺依曼结构,总线是分时复用的,这样会降低处理的速度。 而且,dsp6000系列是基于VLIW结构的,具体来讲就说CPU可以提取通过程序从程序存储器中一次提取256bit的指令,即CPU可以一次取8条指令放在处理中。加上和8级流水线的配合,相当于8个传统的CPU一起工作。 1.1.2 流水线结构 流水线结构涉及的CPU 单元包括取指令单元(fetch ),分配指令单元(dispatch )和执行指令单元(executive )。 dsp学习心得体会 篇一:DSP学习总结 DSP学习总结 摘要:本总结介绍了数字信号技术(DSP)的基本结构,特点,发展及应用现状。通过分析与观察,寄予了DSP 美好发展前景的希望。 关键字:数字信号处理器,DSP,特点,应用 1 DSP介绍 数字信号处理简称DSP,是进行数字信号处理的专用芯片,是伴随着微电子学、数字信号处理技术、计算机技术的发展而产生的新器件,是对信号和图像实现实时处理的一类高性能的CPU。所谓“实时实现”,是指一个实际的系统能在人们听觉、视觉或按要求所允许的时间范围内对输入信号进行处理,并输出处理结果。 数字信号是利用计算机或专用的处理设备,以数值计算的方式对信号进行采集、变换、综合、估计与识别等加工处理,从而达到提取信息和方便应用的目的。数字信号处理的实现是以数字信号处理理论和计算技术为基础的。 2 结构 32位的C28xDSP整合了DSP和微控制器的最佳特性,能够在一个周期内完成32*32位的乘法累加运算。 所有的C28x芯片都含一个CPU、仿真逻辑以及内存和片内外设备的接口信号(具体结构图见有关书籍)。CPU的主要组成部分有: 程序和数据控制逻辑。该逻辑用来从程序存储器取回的一串指令。实时和可视性的仿真逻辑。 地址寄存器算数单元(ARAU)。ARAU为从数据存储器取回的数据分配地址。算术逻辑单元(ALU)。32位的ALU执行二进制的补码布尔运算。 预取对列和指令译码。 为程序和数据而设的地址发生器。 定点MPY/ALU。乘法器执行32位*32位的二进制补码乘法,并产生64位的计算结果。中断处理。 3 特点 采用哈佛结构。传统的冯·诺曼结构的数据总线和指令总线是公用的,因此在高运算时在传输通道上会出拥堵现象。而采用哈佛结构的DSP 芯片片内至少有4 套总线:程序的地址总线与数据总线,数据的地址总线与数据总线。由于这 实验三 IIR 滤波器设计 一、实验目的: 1.认真复习滤波器幅度平方函数的特性,模拟低通滤波器的巴特沃思逼近、切比雪夫型逼近方法;复习从模拟低通到模拟高通、带通、带阻的频率变换法;从模拟滤波器到数字滤波器的脉冲响应不变法、双线性变换法的基本概念、基本理论和基本方法。 2掌握巴特沃思、切比雪夫模拟低通滤波器的设计方法;利用模拟域频率变换设计模拟高通、带通、带阻滤波器的方法.。 3.掌握利用脉冲响应不变法、双线性变换法设计数字滤波器的基本方法;能熟练设计巴特沃思、切比雪夫低通、带通、高通、带阻数字滤波器。 4.熟悉利用MATLAB 直接进行各类数字滤波器的设计方法。 二、实验内容 a. 设计模拟低通滤波器,通带截止频率为10KHz,阻带截止频率为16KHz,通带最大衰减1dB,阻带最小衰减20dB。 (1) 分别用巴特沃思、切比雪夫I、切比雪夫II 型、椭圆型滤波器分别进行设计,并绘制所设计滤波器的幅频和相频特性图。 (2) 在通带截止频率不变的情况下,分别用n=3,4,5,6 阶贝塞尔滤波器设计所需的低通滤波器,并绘制其相应的幅频响应和相频响应图。 %%%%%%%%%----巴特沃思-----%%%%%%% clc;clear all; omegap=10000*2*pi;omegas=16*10^3*2*pi; Rp=1;As=20; [N,omegac]=buttord(omegap,omegas,Rp,As,'s');%低通的节次 [b,a]=butter(N,omegac,'s'); [H,w]=freqs(b,a); %设计滤波器的幅频和相频特性图 subplot(211) plot(w/2*pi/1000,20*log10(abs(H))) DSP应用技术实习报告 课程课题:基于DSP的键盘控制数字图像处理 方式及LCD显示 专业班级:电子信息科学与技术 学生姓名: 指导教师:邹修国李林徐友杨红兵 完成时间: 一、课程内容: ?掌握直方图统计的原理和程序设计 ?了解边缘检测的算法和用途 ?了解锐化的算法和用途 ?了解取反的算法和用途 ?掌握直方图均衡化增强的原理和程序设计 ?了解液晶显示器的显示控制原理及编程方法 ?了解键盘的使用原理及编程方法 二、设计功能及工作原理 能够实现功能: ?数字图像直方图统计 ?数字图像边缘检测(Sobel算子) ?数字图像的锐化(LAPLACE算子) ?数字图像的取反 ?数字图像直方图均衡化增强 通过键盘的输入可以控制图像处理的方式,并且LCD显示何种处理方式。 工作原理: ◆灰度直方图是灰度值的函数,描述的是图像中具有该灰度值的像素的个数,其横坐标表 示像素的灰度级别,纵坐标表示的是该灰度出现的频率。 ◆边缘检测算子检查每个像素的邻域并对灰度变化率进行量化,也包括方向的确定。大多 数使用基于方向导数掩模求卷积的方法。 ◆图像的锐化处理就是使模糊的图像变得更加清晰起来,拉普拉斯锐化法属于常用的一种 微分锐化方法。 ◆求反处理的图像与原来的图像黑白颠倒,取得类似照片底片的效果。 ◆直方图增强的方法就是压缩直方图中比例少的像素所占用的灰度范围,多出来的灰度空 间按照统计比例分配给直方图中比例高的像素使用。 三、设计过程 1.各种功能工程的分别建立: ?数字图像直方图统计 ?数字图像边缘检测(Sobel算子) ?数字图像的锐化(LAPLACE算子) ?数字图像的取反 ?数字图像直方图均衡化增强 ?键盘输入 ?液晶显示器控制显示 2.将各个功能模块进行组合: 1)打开键盘输入的工程文件,浏览key.c文件内容; 2)打开数字图像直方图统计的工程文件,将Histo.c主函数内的处理部分的代码复制到 key.c文件的case1语句后; 3)打开数字图像边缘检测的工程文件,将Image.c主函数内的处理不放入内的代码复制 到key.c文件的case2语句后; dsp心得体会 篇一:dsp实验报告心得体会 TMS320F2812x DSP原理及应用技术实验心得体会 1. 设置环境时分为软件设置和硬件设置,根据实验的需要设置,这次实验只是 软件仿真,可以不设置硬件,但是要为日后的实验做准备,还是要学习和熟悉硬件设置的过程。 2. 在设置硬件时,不是按实验书上的型号选择,而是应该按照实验设备上的型 号去添加。 3. 不管是硬件还是软件的设置,都应该将之前设置好的删去,重新添加。设置好的配置中 只能有一项。 4. CCS可以工作在纯软件仿真环境中,就是由软件在PC机内存中构造一个虚拟的 DSP环境,可以调试、运行程序。但是一般无法构造DSP 中的外设,所以软件仿真通常用于调试纯软件算法和进行效率分析等。 5. 这次实验采用软件仿真,不需要打开电源箱的电源。 6. 在软件仿真工作时,无需连接板卡和仿真器等硬件。 7. 执行write_buffer一行时。如果按F10执行程序,则程序在mian主函数中运行, 如果按F11,则程序进入write_buffe函数内部的程序运行。 8. 把str变量加到观察窗口中,点击变量左边的“+”,观察窗口可以展开结构变 量,就可以看到结构体变量中的每个元素了。 9. 在实验时,显示图形出现问题,不能显示,后来在Graph Title 把Input的大写 改为input,在对volume进行编译执行后,就可以看到显示的正弦波图形了。 10. 在修改了实验2-1的程序后,要重新编译、连接执行程序,并且必须对.OUT 文件进行重新加载,因为此时.OUT文件已经改变了。如果不重新加载,那么修改执行程序后,其结果将不会改变。 11. 再观察结果时,可将data和data1的窗口同时打开,这样可以便于比较,观察 结果。 12. 通过这次实验,对TMS320F2812x DSP软件仿真及调试有了初步的了解与认识,因为做 实验的时候都是按照实验指导书按部就班的,与真正的理解和掌握还是有些距离的。但是这也为我们日后运用这些知识打下了基础,我觉得实验中遇到的问题,不要急于问老师或者同学,先自己想办法分析原因,想办法解决,这样对 一、实验原理: 1、无限冲击响数字滤波器的基础理论; 2、模拟滤波器原理(巴特沃斯滤波器、切比雪夫滤波器、贝塞尔滤波器); 3、双线性变换的设计原理。 二、实验内容: 1、复习有关巴特沃斯滤波器设计和用双线性变换法设计IIR数字滤波器的知识; 2、阅读本实验所提供的样例子程序; 3、运行CCS软件,对样例程序进行跟踪,分析结果; 4、填写实验报告。 5、样例程序实验操作说明 1)正确完成计算机、DSP仿真器和实验箱连接后,开关K9拨到右边,即仿真器选择连接右边的CPU:CPU2; 2)“A/D转换单元”的拨码开关设置: JP3 3)检查:计算机、DSP仿真器、实验箱是否正确连接,系统上电; 4)置拨码开关S23的1、2拨到OFF,用示波器分别观测模拟信号源单元的2号孔“信号源1”和“信号源2”输出的模拟信号,分别调节信号波形选择、信号频率、信号输出幅值等旋钮,直至满意,置拨码开关S23的1到ON,两信号混频输出; 三、程序分析: cpu_init(); //CPU初始化 fs = 25000; //设置采样频率为2500HZ nlpass = 0.18; //设置通带上限频率归一化参数为0.18 nlstop = 0.29; //设置阻带下限截止频率归一化参数为0.29 biir2lpdes(fs,nlpass,nlstop,a,b); 根据双线性变换法求滤波器的系数a和b set_int(); //调用低通滤波器子程序对信号进行滤波 中断程序注释: interrupt void int1() { in_x[m] = port8002; //读取port8002端口的数值 in_x[m] &= 0x00FF; //取后八位送入X[m] m++; //每取一个数字m加1 intnum = m; if (intnum == Len) //当取到128个字节时,重新读取port8002端口的数值 { intnum = 0; xmean = 0.0; for (i=0; i DSP课程设计总结(2013-2014学年第2学期) 题目: 专业班级:电子1103 学生姓名:万蒙 学号:11052304 指导教师: 设计成绩: 2014 年6 月 目录 一设计目的----------------------------------------------------------------------3 二系统分析----------------------------------------------------------------------3 三硬件设计 3.1 硬件总体结构-----------------------------------------------------------3 3.2 DSP模块设计-----------------------------------------------------------4 3.3 电源模块设计----------------------------------------------------------4 3.4 时钟模块设计----------------------------------------------------------5 3.5 存储器模块设计--------------------------------------------------------6 3.6 复位模块设计----------------------------------------------------------6 3.7 JTAG模块设计--------------------------------------------------------7 四软件设计 4.1 软件总体流程-----------------------------------------------------7 4.2 核心模块及实现代码---------------------------------------8 五课程设计总结-----------------------------------------------------14 学校代码学号分类号密级 DSP实验报告 院系名称 专业名称 年级 学生姓名 指导老师 年月日 实验一数据存储实验 一、实验目的 1. 掌握 TMS320C54X 程序空间的分配; 2. 掌握 TMS320C54X 数据空间的分配; 3. 能够熟练运用TMS320C54X 数据空间的指令。 二、实验设备 计算机,CCS 3.1版软件,DSP仿真器,E300实验箱,DSP-54XP CPU板。 三、实验系统相关资源介绍 1. 本实验指导书是以TMS320VC5416为例,介绍其相关的内部和外部存储器资源。对于其他类型的CPU请参考查阅相关的数据手册。) 下面给出TMS320VC5416的存储器分配表: 对于数据存储空间而言,映射表相对固定。值得注意的是内部寄存器都映射到数据存储器空间内。因此在编程时这些特定的空间不能作其他用途。 对于程序空间而言,其映射表和CPU 的工作模式有关。当MP/MC 引脚为高电平时,CPU 工作在微处理器模式;当MP/MC引脚为低电平时,CPU工作在微计算机模式。具体的MP和MC模式下的程序和数据映射关系如上图所示。 2. 样例程序实验操作简单说明: 本实验程序将对0x1000 开始的8 个地址空间,填写入0xAAAA 的数据,然后读出,并存储到以0x1008开始的8个地址空间,在CCS中可以观察DATA存储器空间地址0x1000~0x100F 值的变化。 四、实验步骤与内容 1. 在进行 DSP实验之前,需先连接好仿真器、实验箱及计算机,连接方法如下所示: 2. E300 底板的开关SW4 的第1位置ON,其余位置OFF,SW5全部置ON,其余开关不做设置要求。 3. 上电复位 在硬件安装完成后,确认安装正确、各实验部件及电源连接无误后,启动计算机,接通仿真器电源,此时,仿真器上的“红色指示灯”应点亮,否则DSP开发系统与计算机连接存 本文主要写给Kinetis的初学者,特别是首次使用Cortex-M内核产品的工程师,文章主要针对用户在下载程序或调试过程中经常遇到的问题进行分析,其内容主要包括: 1.原理图说明 2.硬件电路板自检 3.Kinetis调试工具说明 4.程序无法下载分析 5.Check List 一.原理图自检 这里仅介绍与调试相关的原理图 图1 Kinetis MCU接口连接示意图 二.硬件电路板自检 在贴片硬件电路板之前,最好先对电路板做一下简单的短路测试,特别有些PCB尺寸非常小,加工精度要求比较高的板子,如果生产厂家没有做相应的测试,很有可能PCB本身是存在短路的。MCU相关电路需要特别注意以下几点: 1.各级VCC/VDD和GND之前是否存在短路。 2.外部晶体电路是否存在对地短路。 在PCB焊接之后,同样需要做相应的检测: 1.检测JTAG/SWD插针到MCU管脚是否存在虚焊或连焊。 2.检测NMI管脚上拉是否有效。 3.上电检测各级VCC是否正常工作。 4.检测MCU的IO口是否有因虚焊导致的过压/过流。 5. 三.Kinetis调试工具说明 Kinetis常见的评估板包括:FRDM Board, TWR Board, MAPS Board, 目前常见的这些开发板都板载了mbed仿真器,用户无需额外购买仿真器来进行调试。板载的mbed仿真器可以通过U盘拖拽的方式下载编译好的二进制代码,也可以通过在IDE调试环境中通过CMSIS-DAP来进行代码的仿真。同时,用户可以通过按住Reset按钮上电的方式,使mbed仿真器进入Bootloader模式,用户可以通过此模式更换仿真器的固件,使mbed仿真器模拟成J-Link仿真器。点击这里查看详细步骤。 用户自己设计的板子,通常需要使用外部的仿真器进行调试或者烧写,常见的仿真器有J- Link和U-Multilink. 图2 J-Link、U-Multilink仿真器图 J-Link只有一组接口10*2(2.54间距),支持JTAG和SWD两种模式: 数字信号处理课程实验报告 题目:P30-2-6和P63-3-22-d 信道编码 专业:xxx 学号:xxx 姓名:xx 一、书上习题运算 一、实验内容 2.6一个特定的线性和时不变系统,描述它的差分方程如下:y(n)+0.1y(n-1)-0.06y(n-2) = x(n)-2x(n-1)求系统脉冲响应的前10个样本。 如果此系统输入为x(n)=[5+3cos(0.2πn)+4sin(0.6πn)]μ(n),在0≤n≤20求出y(n)的响应。 3.22计算下列序列的N点循环卷积z(n)。 D x1(n)=nR N(n);x2=(N-n)R N(n);N=10 二、实验程序代码 2.6程序: function[x,n]=impseq(np,ns,nf) if ns>np|ns>nf|np>nf error('输入位置参数不满足ns<=np<=nf') else n=[ns:nf]; x=[(n-np)==0]; end a=[1,0.1,0.06];b=[1-2]; x=impseq(0,0,20); h=filter(b,a,x); n=0:20; x=5+3*cos(0.2*pi*n)+4*sin(0.6*pi*n); y=conv(h,x) stem(y) 3.22程序: function y=circonvt(x1,x2,N) x1=[x1,zeros(1,N-length(x1))]; x2=[x2,zeros(1,N-length(x2))]; m=[0:N-1]; x2m=x2(mod(-m,N)+1); H=toeplitz(x2m,[0,x2(2:N)]); y=x1*H; n=0:9; x1=n; x2=10-n; y=circonvt(x1,x2,10) stem(y) 三、实验结果 2.6 M A T L A B上机实习报告 姓名: 班级: 指导老师: 2009.12.13 一、实验课程名称:数字信号处理 二、实验目的 掌握应用MATLAB 解决数字信号处理问题的方法,即熟悉应用MATLAB 处理数字信号处理问题相关的函数。 三、实验设备 安装有MATLAB 软件的计算机 四、实验内容 编写MATLAB 程序,实现下列题目: 实验一: 设线性时不变(LTI )系统的冲激响应为h(n),输入序列为x(n) 1、h(n)=(0.8)n ,0≤n ≤4; x(n)=u(n)-u(n-4) 2、h(n)=(0.8)n u(n), x(n)=u(n)-u(n-4) 3、h(n)=(0.8)n u(n), x(n)=u(n) 求以上三种情况下系统的输出y(n)。 实验二: 1、设有离散序列 x(n)=cos(0.48πn)+cos(0.52πn) 分析下列三种情况下的幅频特性。 (1) 采集数据长度N=16,分析16点的频谱,并画出幅频特性。采集数据长度N=16,并 补零到64点,分析其频谱,并画出幅频特性。 (2) 采集数据长度N=64,分析64点的频谱,并画出幅频特性。 观察三幅不同的幅频特性图,分析和比较它们的特点及形成原因。 2、实现序列的内插和抽取所对应的离散傅里叶变换。(选做) ()()n R n n n x 128]365.1cos 36[cos ?? ? ??+??? ??=ππ 求()()n x n x 41=和()?? ? ??=42n x n x 对应的128点傅里叶变换,比较三个结果所得到的幅频特性,分析其差别产生的原因。 实验三: 1、设计一个Butterworth 数字低通滤波器,设计指标如下: 通带截止频率:0.2π,幅度衰减不大于1分贝 阻带截止频率:0.3π,幅度衰减大于15分贝 2、让不同频率的正弦波通过滤波器,验证滤波器性能。 3、分析不同滤波器的特点和结果。 4、编程设计实现IIR 滤波器。 3.5寸,红外转发网络烧写使用说明 一、安装软件 1. 双击“LMFlashProgrammer”进入安装。 2. 点击Next进入下一步 3. 选中“lAgree”,单击“Next”进入下一步 4. 在Folder一栏单击“Browse…”选择程序安装目录后,单击“Next”进入下一步 5. 单击“Next”进入下一步 6. 程序安装中 7. 安装程序完成,单击“Close”退出 二、烧写程序 1. 双击桌面“”图标打开软件 2. 选择“configuration”界面。 2.1. 在“Quick set”一栏复选框里选择“Manual configuration-see below”。 2.2. 在“Interface”一栏复选框里选择“Ethernet”。 2.3. 在“Client IP Address:”后面的框里填上你要烧写的设备的IP地址,如 192.168.0.50。 2.4. “Client MAC Address:”后面的框里填上你要烧写的设备的MAC地址,如 001205071319。 2.5. 3.5寸、红外转发的IP地址与MAC地址都可以通过 获取到。 3. 换到“program”界面 3.1 在“Select.bin file”一栏里,点“browse”选择程序路径。 3.2 单击“Program”烧写 烧写完成后设备会重起。 三、注意事项 1. 设备要与电脑在同一个网段里。 2. 网络烧写时,要用有线连接,不可使用无线连接烧写。 3. 烧写前,3.5寸最好重起一直,重起时不可出现异常现像。 数字信号处理—DSP课程学习的认识 今年学习了DSP这门课程后,有了一些自己的认识和见解,并且体会到了它强 大的功能和作用,它不但在高端的技术领域有很重要的地位,如通信、雷达、声纳、语音合成和识别、图像处理、高速控制等;而且在生活中有它不小的作用,如,影视、仪器仪表、医疗设备、家用电器等众多领域。 但是,由于DSP技术发展很快,生产DSP芯片的厂家又多,这既是它的优点同时又是缺点,因为这样会导致产品更新换代的周期越来越短,还有,每一种芯片,都有其独特的硬件结构和一套专门的指令系统与开发工具,这更加的带来了学习DSP技术的困难。在我自己的学习体系里,有自己的一点经验可以用来辅助的学习这门课程。古时候的一个习语说的是“窥一斑而知全豹”这个同时可以用来指导学习DSP,尽管它的更新换代很快,但也只是为了完善它而更好用和借助其它的技术为它添加一些别的功能而使其更强大,其实质的理论还没有变话,所以,我们可以就其某一个芯片的知识来指导其它的芯片的学习和利用。这里,以TMS320系列DSP芯片为例来进行知识的梳理。 x(t)→抗混叠滤波器→A/D→数字信号处理器→D/A→低通滤波器→y(t) 数字信号处理系统简化框图 这个作为基本的理论模型,然后开始主要内容:1.1,TMS320系列DSP芯片的概述﹑分类及应用、平台;1.2,主要特性有①CPU,②存储器,③指令系统,④在片外围电路,⑤电源,⑥在片仿真接口,⑦速度、组成框图;1.3,总线结构; 1.4,存储器空间分配、存储器(程序存储器、数据存储器);1.5,中央处理单元中,算术逻辑运算单元①ALU的输入,②ALU的输出,③溢出处理,④进位位,⑤双16 位算术运算,累加器A和B(保存累加器的内容、累加器移位和循环移位、专用指令),桶形移位器乘法器/加法器单元,比较、选择和存储单元,指数编码器,CPU状态和控制寄存器;1.6,数据寻址方式①立即寻址,②绝对寻址,③累加器寻址,④直接寻 DSP实验学习心得 论DSP发展前景 DSP 即为数字信号处理器(Digital Signal Processing),是在模拟信号变换成数字信号以后进行高速实时处理的专用处理器。它的工作原理是将现实世界的模拟信号转换成数字信号,再用数学方法处理此信号,得到相应的结果。自从数字信号处理器(Digital Signal Processor)问世以来,由于它具有高速、灵活、可编程、低功耗和便于接口等特点,已在图形、图像处理,语音、语言处理,通用信号处理,测量分析,通信等领域发挥越来越重要的作用。随着成本的降低,控制界已对此产生浓厚兴趣,已在不少场合得到成功应用。DSP 数字信号处理器DSP 芯片采用了数据总线和程序总线分离的 哈佛结构及改进的哈佛结构,较传统处理器的冯?诺依曼结构具有更高的指令执行速度。其处理速度比最快的CPU 快10-50 倍。在当今数字化时代背景下,DSP 已成为通信、计算机、消费类电子产品等领域的基础器件,被誉为信息社会革命的“旗手”。 最初的DSP 器件只是被设计成用以完成复杂数字信号处理的算法。DSP 器件紧随着数字信号理论的发展而不断发展。DSP发展最快,现在的DSP 属于第五代产品,它与第四代相比,系统集成度更高,将DSP 芯核及外围组件综合集成在单一芯片上。这种集成度极高的DSP 芯片不仅在通信、计算机领域大显身手,而且逐渐渗透到人们日常消费领域,前景十分可观。近年来,随着通信技术的飞速发展,DSP已经成为信号与信息处理领域里一门十分重要的新兴学科,它代表着当今无线系统的主流发展方向。现在,通信领域中许多产品 都与DSP 密切联系,例如,Modem、数据加密、扩频通信、可视电话等。而寻找DSP 芯片来实现算法最开始的目标是在可以接受的时间内对算法做仿真,随后是将波形存储起来,然后再加以处理。 在短短的十多年时间,DSP芯片已经在信号处理、通信、雷达等许多领域得到广泛的应用。目前, DSP 芯片的价格也越来越低,性能价格比日益提高,具有巨大的应用潜力。DSP 芯片的应用主要有:(1)信号处理--如,数字滤波、自适应滤波、快速傅里叶变换、相关运算、频谱分析、卷积等。(2)通信--如,调制解调器、自适应均衡、数据加密、数据压缩、回坡抵消、多路复用、传真、扩频通信、纠错编码、波形产生等。(3)语音--如语音编码、语音合成、语音识别、语音增强、说话人辨认、说话人确认、语音邮件、语音储存等。(4)图像/图形--如二维和三维图形处理、图像压缩与传输、图像增强、动画、机器人视觉等。(5)军事--如保密通信、雷达处理、声纳处理、导航等。(6)仪器仪表--如频谱分析、函数发生、锁相环、地震处理等。(7)自动控制--如引擎控制、深空、自动驾驶、机器人控制、磁盘控制。(8)医疗--如助听、超声设备、诊断工具、病人监护等。(9)家用电器--如高保真音响、音乐合成、音调控制、玩具与游戏、数字电话/电视等DSP 的发展前景DSP 的功能越来越强,应用越来越广,达到甚至超过了微控制器的功能,比微控制器做得更好而且价格更便宜,许多家电用第二代DSP 来控制大功率电机就是一个很好的例子。汽车、个人通信装置、家用电器以及数以百万计的工厂使用DSP 系统。数码相机、IP 电话和手持电子设备的热销带来了对DSP 芯片的巨大需求。而手机、 程序烧写说明 OK300C根据用户选配单片机类型不同烧写程序的方式也有所不同,主要分三类:STC单片机用户烧写方法,并口ISP下载器烧写方法,USB接口ISP下载器烧写方法。下面将详细介绍烧写程序的方法。 一、 STC单片机用户烧写方法 启动下载软件,首次设置时只需注意芯片的选择,在左上角下拉框中选择STC89C52RC,一般的台式机大多只有一个串口,所以COM栏就选择COM1,如果使用别的串口那就选择相应的串口号,其它全部使用默认,不明白时最好不要乱改,不然可能会把芯片锁死,以后就用不了了。总体设置如下图: 点击软件界面上的Open File 打开对话框,将*.hex或者*.bin文件选择,选择好后点击Open 。 要先把实验板上的电源关掉,因为STC的单片机内有引导码,在上电的时候会与计 算机自动通讯,检测是否要执行下载命令,所以要等点完下载命令后再给单片机上电。然后点击如图中的Download/下载钮, 接着按下实验板上电源给单片机上电 若出现上述图片,则说明已经给单片机成功下载了程序,并且已经加密。 二、 并口ISP下载器烧写方法 将ISP下载器插在主板的ISP口处。确认板上JP1三个跳线都断开,连接好电源线,打开电源。打开下载软件MuCodeISP,以下载AT89S52为例,选择89S5X,选择89s52,如下图 然后加载要烧录的程序 点击File菜单下的openflash,出现如下对话框 选择要烧录的文件,这里是*.hex文件 烧录,点击下图中的Program按钮就可以把程序下载到单片机 A VR单片机的程序烧录过程也是一样的。不过烧录51和A VR单片机切换时,要注意不要忘记进行51/avr单片机选择键的操作。 三、 USB接口ISP下载器烧写方法 选配USB接口isp下载器的用户,使用的烧写软件是A VRSTUDIO。首先要安装好该软件,安装方法和安装普通软件一样。 可以用AVR Studio的4.13或更高版本控制STK500/AVRISP,选择STK500 or AVRISP和Auto 或者具体的COM?端口进行联机,点击avr studio主窗口中的图标前面标有Con的那个图标,然后按下图选择即可进行STK500或者JTAG的联机,由于avr studio会记忆用户使用的设备是STK500ISP还是JTAG并且同时会记忆用户使用的COM号,如果下次和上次使用的是相同的设备并且没有更换COM口,那么下次使用的时候直接点击右边标有AVR的那个图标就可以快速进入联机状态;如果下次和上次使用了不同的设备或不同的COM口请使用Con图标进行联机。 连接STK500ISP: dsp心得体会范文 dsp心得体会篇一:DSP原理及应用的学习体会 这个学期通过《对DSP芯片的原理与开发应用》课程的学习,对DSP芯片的概念、基本结构、开发工具、常用芯片的运用有了一定的了解和认识,下面分别谈谈自己的体会。 一,DSP芯片的概念 数字信号处理(DigitalSignalProcessing)是利用计算机或专 用处理设备,以数字形式对信号进行采集、变换、增强、滤波、估值、压缩、识别等处理,以得到符合人们需要的信号形式。20世纪60年代以来,随着计算机和信息技术的飞速发展,数字信号处理技术应运而生并得到迅速的发展。在通信、等诸多领域得到极为广泛的应用。 DSP(DigitalSignalProcess)芯片,即数字信号处理器,是一种 特别适合于进行数字信号处理运算的微处理器,其应用主要是实时快速的实现各种数字信号处理算法。该芯片一般具有以下主要特点:(1)在一个指令周期内可完成一次乘法和一次加法; (2)程序与数据空间分开,可以同时访问指令和数据; (3)片内具有快速RAM,通常可通过独立的数据总线在两块中同时访问; (4)具有低开销或无开销循环及跳转的硬件支持; (5)快速的中断处理和硬件支持; (6)具有在单周期内操作的多个硬件地址产生器; (7)可以并行执行多个操作; (8)支持流水线操作,使取值、译码和执行等操作可以同时进行。 世界上第一个单片DSP芯片应当是1978年AMI公司发布的 S2811,1979年美国INTEL 公司发布的商用可编程器件2920是DSP芯片的一个主要里程碑。这两种芯片内部都没有现代DSP芯片所必须有的单周期乘法器。1980年,日本NEC公司推出的uPD7720是第一个具有乘法器的商用DSP芯片。 当前,美国德州公司(TI),Motorola公司,模拟器件公司(AD),NEC公司,AT&T公司是DSP芯片主要生产商。 选择合适的DSP芯片,是设计DSP应用系统的一个非常重要的 环节。一般来说,要综合考虑如下因素:(1),DSP芯片的运算速度;(2),DSP芯片的价格; (3),DSP芯片的硬件资源;4),DSP芯片的运算精度;(5),DSP芯片的开发工具;(6),DSP芯片的功耗等等。 二,DSP芯片的基本结构。 TI公司的TMS320系列芯片的基本结构包括: (1)哈佛结构。哈佛结构是一种并行体系结构,主要特点是将程序和数 据存储在 不同的存储空间中,独立编址,独立访问。由于设立了程序总 线和数据总线两条总线,从而使数据的吞吐量提高了一倍。为了进一 dsp实习心得体会 DSP大家注意和传统的概念区分一下,传统我们经常说的DSP(Digital Signal Processing(数字信号处理))的缩写也就是说是一些功能算法,这里的DSP是指(Digital Signal Process(数字信号处理器))的缩写,也就是说他是一个集成一些外设的一个芯片,类似我们的单片机。我们通过程序实现一些特定的功能。 和传统单片机比较的区别? DSP功能比普通单片机高出很多,当然价格也比较高。所以直接用DSP和单片机比较是不合适的。我们这里比较不是从他的应用领域来比较,我们是从开发的角度来比较,为了是使那些熟练使用单片机的朋友可以很快上手。当然我的主要目的的大家可以比较学习,达到 熟悉一种CPU其他就可以很快上手。下面从几个方面比较一下 1,硬件上比较 从硬件上比较DSP和传统的单片机主要有几个方面不一样,很多DSP电源系统比传统的复杂,但是这个并不影响我们因为如TI的DSP都提供相关的测试电路。开始的时候大家可以完全按照他来设计。调试方式上有很大不同,DSP一 般通过JTAG来进行仿真和烧写的,而单片机是通过直接仿真器来仿真的(这里讲的单片机是比较早的,现在的单片机也有很多采用JTAG调试方式)。其他设计比如重要的时序设计所以CPU系统是一样的只要满足时序就可以达到目标。 2,软件上比较 相比硬件软件应该是DSP差别比较大的DSP的软件需要CMD文件,一般的单片机编译器编译以后就可以了不需要。并且CMD也是DSP学习过程中比较困难的一个方面。后面我们简单说明一下。 dsp实习心得体会(二) 小系统的是任何DSP系统开发前必须要完成的,你可以从一下几个方面获得小系统。一、购买一个市场上比较成熟的小系统产品;二、自己动手设计一个小系统。我们这里主要告诉大家怎么自己设计一个最小系统。 首先我给最小系统一个定义,我按照我个人的习惯把最小系统分成2个方面 1,狭义的最小系统 所谓狭义最小系统是指就是能够完成一个独立功能,并且方便观察的一个系统。比如我们常见的通过DSP控制一个LCD灯让它闪起来。完成这个功能我们可以认为狭义的最小系统完成。 独立完成功能,我们很容易想到要一个系统能够独立完dsp优化心得
DSP实验报告
我的首个DSPTMSFLED灯闪烁项目实验总结
dsp优化心得
dsp学习心得体会
DSP实验二
DSP实习报告模板
dsp心得体会
dsp实验报告5
dsp课程设计实验报告总结
DSP实验报告
Kinetis程序烧写检查单
DSP实验报告
DSP实习实验报告
程序烧写方法
DSP学习心得
DSP实验学习心得
程序烧写说明
dsp心得体会范文
dsp实习心得体会