在51单片机上写出最优代码

来源:互联网 发布:日本书法 知乎 编辑:程序博客网 时间:2024/06/03 19:44

列举一些如何在8051单片机上写出高性能代码的方法,高性能指的是编译出的代码具有更小的size和更快的执行速度。以下的方法在大多数情况下都是能够起作用的。

  1. 存储模式(Memory Model)
    存储模式最能够影响最终产生代码的大小和执行速度。编译时采用SMALL模式可以产生最小、最快的代码。在SMALL模式下所有的变量,除非特别说明,都会存放在8051内部存储区。单片机访问内部存储区的速度非常看(通常是1~2个时钟周期),产生的代码尺寸也比使用COMPACT或LARGE模式产生的代码小很多。比如以下代码
void do_nothing(void);void main(){    unsigned char i;     for (i=0; i<100; i++)    {        do_nothing();    }}void do_nothing(void){    ;}

若使用SMALL模式进行编译,生成的汇编代码如下:

             ; FUNCTION main (BEGIN)                                           ; SOURCE LINE # 3                                           ; SOURCE LINE # 4                                           ; SOURCE LINE # 60000 E4                CLR     A0001 F500        R     MOV     i,A0003         ?C0001:                                           ; SOURCE LINE # 7                                           ; SOURCE LINE # 80003 120000      R     LCALL   do_nothing                                           ; SOURCE LINE # 90006 0500        R     INC     i0008 E500        R     MOV     A,i000A C3                CLR     C000B 9464              SUBB    A,#064H000D 40F4              JC      ?C0001                                           ; SOURCE LINE # 10000F         ?C0004:000F 22                RET                  ; FUNCTION main (END)             ; FUNCTION do_nothing (BEGIN)                                           ; SOURCE LINE # 12                                           ; SOURCE LINE # 13                                           ; SOURCE LINE # 150000 22                RET                  ; FUNCTION do_nothing (END)
在SMALL模式中,变量i保存在内部存储区。访问i的指令MOV A, i 和 INC i 只需要两个字节的空间。另外,执行这些指令只需要一个时钟周期。生成的代码size总共为17bytes。若使用LARGE模式,则生成下面的汇编指令
             ; FUNCTION main (BEGIN)                                           ; SOURCE LINE # 3                                           ; SOURCE LINE # 4                                           ; SOURCE LINE # 60000 E4                CLR     A0001 900000      R     MOV     DPTR,#i0004 F0                MOVX    @DPTR,A0005         ?C0001:                                           ; SOURCE LINE # 7                                           ; SOURCE LINE # 80005 120000      R     LCALL   do_nothing                                           ; SOURCE LINE # 90008 900000      R     MOV     DPTR,#i000B E0                MOVX    A,@DPTR000C 04                INC     A000D F0                MOVX    @DPTR,A000E E0                MOVX    A,@DPTR000F C3                CLR     C0010 9464              SUBB    A,#064H0012 40F1              JC      ?C0001                                           ; SOURCE LINE # 100014         ?C0004:0014 22                RET                  ; FUNCTION main (END)             ; FUNCTION do_nothing (BEGIN)                                           ; SOURCE LINE # 12                                           ; SOURCE LINE # 13                                           ; SOURCE LINE # 150000 22                RET                  ; FUNCTION do_nothing (END)

在LARGE模式中,变量i存放在外部存储区。为了访问变量i,编译器需要先加载该变量的地址,然后执行外部数据访问操作(offset为0001h~0004h), 执行这两条执行需要花费4个时钟周期。0008h~000dh是i++的指令。这些指令占用了6bytes内存空间,执行时间是7个时钟周期。在LARGE模式下,生成的代码大小总共为22bytes。
2. 变量存储方式
由于CPU访问内部存储区比外部存储区效率更高,所以需要频繁擦写的变量需要存放在内部存储区更好一些。内部存储区包括register, bit,stack以及存储类型为data的变量。
由于8051的内部存储区有限(128字节或256字节),所以程序中使用的变量不一定都能放到内部存储区中。此时你需要把一些变量分配到其他存储区。这里介绍两种方法。
2.1 在keil中设置memory model,让编译器去做这个工作。这个方法最简单,但生成的代码的不是最优的。
2.2 使用xdata存储类型声明将一些不经常使用的变量放置到外部存储区,而频繁擦写的变量放置到内部存储区。这样就能够合理的使用单片机的资源,生成最优的代码。
3. 定义变量的类型
8051家族都是8位单片机。使用8位的数据类型(char/unsigned char)会比使用 int/long类型的数据参与运算更有效率。基于此,我们在代码中尽量使用8位的数据类型。
4. 无符号类型
如果使用有符号类型变量,编译器会产生跟多的代码,所以尽量使用无符号类型变量。
5. 局部变量
在做代码优化时,编译器会尝试将局部变量放置到寄存器中,寄存器访问是最快的内存访问方式,所以我们尽可能使用局部变量。
如将变量i改为signed char类型

   1          void do_nothing(void);   2             3          void main()   4          {   5   1        signed char i;    6   1        for (i=0; i<100; i++)   7   1        {   8   2          do_nothing();   9   2        }  10   1      }  11            12          void do_nothing(void)  13          {  14   1        ;  15   1      }

生成的汇编代码为

             ; FUNCTION main (BEGIN)                                           ; SOURCE LINE # 3                                           ; SOURCE LINE # 4                                           ; SOURCE LINE # 60000 E4                CLR     A0001 F500        R     MOV     i,A0003         ?C0001:                                           ; SOURCE LINE # 7                                           ; SOURCE LINE # 80003 120000      R     LCALL   do_nothing                                           ; SOURCE LINE # 90006 0500        R     INC     i0008 C3                CLR     C0009 E500        R     MOV     A,i000B 6480              XRL     A,#080H000D 94E4              SUBB    A,#0E4H000F 40F2              JC      ?C0001                                           ; SOURCE LINE # 100011         ?C0004:0011 22                RET                  ; FUNCTION main (END)             ; FUNCTION do_nothing (BEGIN)                                           ; SOURCE LINE # 12                                           ; SOURCE LINE # 13                                           ; SOURCE LINE # 150000 22                RET                  ; FUNCTION do_nothing (END)
通过比较,可以看出,i改为signed char类型后,汇编中多了一条指令
000B 6480              XRL     A,#080H
致使最终生成的代码size为19bytes,大于i为unsigned char的情况。

6. 算法的影响
有些时候,更好的算法也会提高代码的速度,减小代码的存储空间。

以上参考自keil编译手册。

0 0
原创粉丝点击