收藏 分享(赏)

51单片机程序优化.doc

上传人:cjc2202537 文档编号:173860 上传时间:2018-03-23 格式:DOC 页数:12 大小:99KB
下载 相关 举报
51单片机程序优化.doc_第1页
第1页 / 共12页
51单片机程序优化.doc_第2页
第2页 / 共12页
51单片机程序优化.doc_第3页
第3页 / 共12页
51单片机程序优化.doc_第4页
第4页 / 共12页
51单片机程序优化.doc_第5页
第5页 / 共12页
点击查看更多>>
资源描述

1、 程序优化 由于单片机的性能同电脑的性能是天渊之别的,无论从空间资源上、内存资源、工作频率,都是无法 与之比较的。PC 机编程基本上不用考虑空间的占用、内存的占用的问题,最终目的就是实现功能就可以了。 对于单片机来说就截然不同了,一般的单片机的 Flash 和 Ram 的资源是以 KB 来衡量的,可想而知,单片 机的资源是少得可怜,为此我们必须想法设法榨尽其所有资源,将它的性能发挥到最佳,程序设计时必须 遵循以下几点进行优化: 1. 使用尽量小的数据类型 能够使用字符型(char) 定义的变量,就不要使用整型 (int)变量来定义;能够使用整型变量定义的变 量就不要用长整型(long int)

2、,能不使用浮点型(float)变量就不要使用浮点型变量。当然,在定义变 量后不要超过变量的作用范围,如果超过变量的范围赋值,C 编译器并不报错,但程序运行结果却错了, 而且这样的错误很难发现。 2. 使用自加、自减指令 通常使用自加、自减指令和复合赋值表达式(如 a-=1 及 a+=1 等)都能够生成高质量的 程序代码,编译器通常都能够生成 inc 和 dec 之类的指令,而使用 a=a+1 或 a=a-1 之类 的指令,有很多 C 编译器都会生成二到三个字节的指令。 3. 减少运算的强度 可以使用运算量小但功能相同的表达式替换原来复杂的的表达式。 (1) 求余运算 N= N %8 可以改为

3、N = N i-) for(j=1000;i=0;j-) 说明:两个函数的延时效果相似,但几乎所有的 C 编译对后一种函数生成的代码均比前一种代码少 13 个字节,因为几乎所有的 MCU 均有为 0 转移的指令,采用后一种方式能够生成这类指令。 4. while 与 do.while 的区别 void DelayNus(UINT16 t) while(t-) NOP(); 可以改为 void DelayNus(UINT16 t) do NOP(); while(-t) 说明:使用 dowhile 循环编译后生成的代码的长度短于 while 循环。 5. register 关键字 void UA

4、RTPrintfString(INT8 *str) while(*str while(*pstr UINT16 CRC16CheckFromTbl(UINT8 *buf,UINT8 len) UINT16 i; UINT16 uncrcReg = 0, uncrcConst = 0xffff; for(i = 0;i 8) *buf+) uncrcConst B?A:B) 可以改为 #define MAX(A,B) (A)(B)?(A):(B) 说明:函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函 数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查

5、选项,一般在函数的头会嵌入一些汇编语 句对当前栈进行检查;同时,cpu 也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些 cpu 时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。 9. 适当地使用算法 假如有一道算术题,求 1100 的和。 作为程序员的我们会毫不犹豫地点击键盘写出以下的计算方法: UINT16 Sum(void) UINT8 i,s; for(i=1;i1; return s; 结果很明显,同样的结果不同的计算方法,运行效率会有大大不

6、同,所以我们需要最大限度地通过数 学的方法提高程序的执行效率。 10. 用指针代替数组 在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相 比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。 UINT8 szArrayA64; UINT8 szArrayB64; UINT8 i; UINT8 *p=szArray; for(i=0;i64;i+)szArrayBi=szArrayAi; for(i=0;i64;i+)szArrayBi=*p+; 指针方法的优点是,szArrayA 的地址装入指针

7、p 后,在每次循环中只需对 p 增量操作。在数组索引 方法中,每次循环中都必须进行基于 i 值求数组下标的复杂运算。 11. 强制转换 C 语言精髓第一精髓就是指针的使用,第二精髓就是强制转换的使用,恰当地利用指针和强制转换不但 可以提供程序效率,而且使程序更加之简洁,由于强制转换在 C 语言编程中占有重要的地位,下面将已五 个比较典型的例子作为讲解。 例子 1:将带符号字节整型转换为无符号字节整型 UINT8 a=0; INT8 b=-3; a=(UINT8)b; 例子 2:在大端模式下(8051 系列单片机是大端模式 ),将数组 a2转化为无符号 16 位整型值。 方法 1:采用位移方法。

8、 UINT8 a2=0x12,0x34; UINT16 b=0; b=(a08)|a1; 结果:b=0x1234 方法 2:强制类型转换。 UINT8 a2=0x12,0x34; UINT16 b=0; b= *(UINT16 *)a; /强制转换 结果:b=0x1234 例子 3:保存结构体数据内容。 方法 1:逐个保存。 typedef struct _ST UINT8 a; UINT8 b; UINT8 c; UINT8 d; UINT8 e; ST; ST s; UINT8 a5=0; s.a=1; s.b=2; s.c=3; s.d=4; s.e=5; a0=s.a; a1=s.b;

9、 a2=s.c; a3=s.d; a4=s.e; 结果:数组 a 存储的内容是 1、2、3、4、5。 方法 2:强制类型转换。 typedef struct _ST UINT8 a; UINT8 b; UINT8 c; UINT8 d; UINT8 e; ST; ST s; UINT8 a5=0; UINT8 *p=(UINT8 *)/强制转换 UINT8 i=0; s.a=1; s.b=2; s.c=3; s.d=4; s.e=5; for(i=0;isizeof(s);i+) ai=*p+; 结果:数组 a 存储的内容是 1、2、3、4、5。 例子 4:在大端模式下(8051 系列单片机是

10、大端模式 )将含有位域的结构体赋给无符号字节整型值 方法 1:逐位赋值。 typedef struct _BYTE2BITS UINT8 _bit7:1; UINT8 _bit6:1; UINT8 _bit5:1; UINT8 _bit4:1; UINT8 _bit3:1; UINT8 _bit2:1; UINT8 _bit1:1; UINT8 _bit0:1; BYTE2BITS; BYTE2BITS Byte2Bits; Byte2Bits._bit7=0; Byte2Bits._bit6=0; Byte2Bits._bit5=1; Byte2Bits._bit4=1; Byte2Bits

11、._bit3=1; Byte2Bits._bit2=1; Byte2Bits._bit1=0; Byte2Bits._bit0=0; UINT8 a=0; a|= Byte2Bits._bit77; a|= Byte2Bits._bit66; a|= Byte2Bits._bit55; a|= Byte2Bits._bit44; a|= Byte2Bits._bit33; a|= Byte2Bits._bit22; a|= Byte2Bits._bit11; a|= Byte2Bits._bit00; 结果:a=0x3C 方法 2:强制转换。 typedef struct _BYTE2BITS

12、 UINT8 _bit7:1; UINT8 _bit6:1; UINT8 _bit5:1; UINT8 _bit4:1; UINT8 _bit3:1; UINT8 _bit2:1; UINT8 _bit1:1; UINT8 _bit0:1; BYTE2BITS; BYTE2BITS Byte2Bits; Byte2Bits._bit7=0; Byte2Bits._bit6=0; Byte2Bits._bit5=1; Byte2Bits._bit4=1; Byte2Bits._bit3=1; Byte2Bits._bit2=1; Byte2Bits._bit1=0; Byte2Bits._bit

13、0=0; UINT8 a=0; a = *(UINT8 *) UINT8 _bit6:1; UINT8 _bit5:1; UINT8 _bit4:1; UINT8 _bit3:1; UINT8 _bit2:1; UINT8 _bit1:1; UINT8 _bit0:1; BYTE2BITS; BYTE2BITS Byte2Bits; UINT8 a=0x3C; Byte2Bits._bit7=a Byte2Bits._bit6=a Byte2Bits._bit5=a Byte2Bits._bit4=a Byte2Bits._bit3=a Byte2Bits._bit2=a Byte2Bits.

14、_bit1=a Byte2Bits._bit0=a 方法 2:强制转换。 typedef struct _BYTE2BITS UINT8 _bit7:1; UINT8 _bit6:1; UINT8 _bit5:1; UINT8 _bit4:1; UINT8 _bit3:1; UINT8 _bit2:1; UINT8 _bit1:1; UINT8 _bit0:1; BYTE2BITS; BYTE2BITS Byte2Bits; UINT8 a=0x3C; Byte2Bits= *(BYTE2BITS *) 12. 减少函数调用参数 使用全局变量比函数传递参数更加有效率。这样做去除了函数调用参数入

15、栈和函数完成后参数出栈所 需要的时间。然而决定使用全局变量会影响程序的模块化和重入,故要慎重使用。 13. switch 语句中根据发生频率来进行 case 排序 switch 语句是一个普通的编程技术,编译器会产生 if-else-if 的嵌套代码,并按照顺序进行比较, 发现匹配时,就跳转到满足条件的语句执行。使用时需要注意。每一个由机器语言实现的测试和跳转仅仅 是为了决定下一步要做什么,就把宝贵的处理器时间耗尽。为了提高速度,没法把具体的情况按照它们发 生的相对频率排序。换句话说,把最可能发生的情况放在第一位,最不可能的情况放在最后。 14. 将大的 switch 语句转为嵌套 switc

16、h 语句 当 switch 语句中的 case 标号很多时,为了减少比较的次数,明智的做法是把大 switch 语句转为嵌 套 switch 语句。把发生频率高的 case 标号放在一个 switch 语句中,并且是嵌套 switch 语句的最外 层,发生相对频率相对低的 case 标号放在另一个 switch 语句中。比如,下面的程序段把相对发生频率 低的情况放在缺省的 case 标号内。 UINT8 ucCurTask=1; void Task1(void); void Task2(void); void Task3(void); void Task4(void); void Task16

17、(void); switch(ucCurTask) case 1: Task1();break; case 2: Task2();break; case 3: Task3();break; case 4: Task4();break; case 16: Task16();break; default:break; 可以改为 UINT8 ucCurTask=1; void Task1(void); void Task2(void); void Task3(void); void Task4(void); void Task16(void); switch(ucCurTask) case 1: T

18、ask1();break; case 2: Task2();break; default: switch(ucCurTask) case 3: Task3();break; case 4: Task4();break; case 16: Task16();break; default:break; Break; 由于 switch 语句等同于 if-else-if 的嵌套代码,如果大的 if 语句同样要转换为嵌套的 if 语句。 UINT8 ucCurTask=1; void Task1(void); void Task2(void); void Task3(void); void Task4(void); void Task16(void); if (ucCurTask=1) Task1(); else if(ucCurTask=2) Task2(); else

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 教育学

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报