×
嵌入式 > 技术百科 > 详情

一个从定时计数器赋初值引起的故事

发布时间:2020-10-09 发布时间:
|
最近单片机课讲到了定时计数器,在C语言中定时计数器的初值可以采用这种方式(假设计数10000次)TH0=(65536-10000)/256;TL0=(65536-10000)%256;这是通用的方法,65536-10000=55536=0xD8F0;赋值后TH0=0xD8,TL0=0xF0。我联想到补码的规则,65536-10000的数值在计算机中和-10000数据存储是一样的,于是我就简单赋值为TH0=(-10000)/256.;TL0=(-10000)%256;可以少写一个数据,减少敲字的工作。我就这样给学生讲了。这两种方法都可以。

在一天李老师看到我的学生作业都是写TH0=(-10000)/256.;TL0=(-10000)%256;她说-10000可能使用不对。当天晚上的时候在QQ上发消息过来说,经验证,在Keil中,TH0=(65536-10000)/256;TL0=(65536-10000)%256;的赋值方式TH0=0xD8,TL0=0xF0。但是TH0=(-10000)/256.;TL0=(-10000)%256;的赋值方式TH0=0xD9,TL0=0xF0。TH0的数值总是要大1,而且取不同的数值验证均是这个结果,两种方式TH0总是相差1,而TL0数值是一样的。我打开Keil,输入程序,然后调试查看汇编指令,得到如下结果:

     8: TH0=(65536-10000)/256; 
C:0x009B    758CD8   MOV      TH0(0x8C),#0xD8
     9:   TH1=(-10000)/256; 
C:0x009E    758DD9   MOV      TH1(0x8D),#0xD9

发现汇编指令直接对于TH0和TH1进行赋值,没有经过任何的运算,但是就是相差1,这是为什么呢?我无法理解,后来在百度知道上提问,得到的回答是:这个和默认数据类型有关,TH0=(65536-10000)/256,默认unsigned char,即TH0=0xD8;TH0=(-10000)/256,默认signed char,二进制最高位为符号位,负数为1,所以TH0=0xD9 。

原来是Keil编译器计算数据的时候默认的数据类型不一样,65536-10000=55536是unsigned类型,55536/256=216=0xD8,而-10000是signed类型,(-10000)/256=-39=0xD9。原来如此,Keil的编译器预先处理的时候根据不同类型的数据进行了不同的运算,然后直接赋值。我又验证了一下,TH0=(unsigned int)(-10000)/256;发现先把-10000强制转换为unsigned类型后,得到的结果就是正确的了TH0=0xD8。得到答案后脸红了,不过多亏是在放假期间,没有学生看到。开学后立即在课堂上更正了。╮(╯▽╰)╭,这次糗大了。

    我重新写了一个程序,#include
void main()

unsigned int  i;
 unsigned char j;
 i= - 10000;
 j=i/256;

while(1)
 ;
}

中间加一个变量,看Keil会怎么处理,结果发现

     2: void main() 
     3: {  
     4: unsigned int  i; 
     5:  unsigned char j; 
     6:  i=-10000; 
C:0x000F    7ED8     MOV      R6,#0xD8
     7:  j=i/256; 
     8:  
C:0x0011    8E08     MOV      0x08,R6
     9: while(1) 
C:0x0013    80FE     SJMP     C:0013

还是直接赋值,编译器太聪明了,知道80C51对于数据运算非常非常的不擅长,于是直接处理完数据,然后用赋值的方式来写汇编的指令。而且还知道,i的低字节没有用到,在指令里根本没有出现,这也太聪明了吧。Keil软件是最流行,最好用的编译器,不是浪得虚名的。

我再修改:

include
void main()

unsigned int  i;
 unsigned char j;
 i=-10000;
 i++;
 j=i/256;

while(1)
 ;
}

结果发现代码只增加了一点。

         2: void main() 
     3: {  
     4: unsigned int  i; 
     5:  unsigned char j; 
     6:  i=-10000; 
C:0x0003    7FF0     MOV      R7,#B(0xF0)
C:0x0005    7ED8     MOV      R6,#0xD8
     7:  i++; 
C:0x0007    0F       INC      R7
C:0x0008    BF0001   CJNE     R7,#0x00,C:000C
C:0x000B    0E       INC      R6
     8:  j=i/256; 
     9:  
C:0x000C    8E08     MOV      0x08,R6
    10: while(1) 
C:0x000E    80FE     SJMP     C:000E

但对于j的运算还是用赋值的方式。我再改,把i类型变成signed类型,结果大吃一惊:

include
void main()

    int  i;
 unsigned char j;
 i=-10000;
 
 j=i/256;

while(1)
 ;
}[page]

程序变得非常庞大,代码从28B猛增到169B:

C:0x0000    02009D   LJMP     C:009D
                 C?SIDIV:
C:0x0003    C2D5     CLR      F0(0xD0.5)
C:0x0005    EC       MOV      A,R4
C:0x0006    30E709   JNB      0xE0.7,C:0012
C:0x0009    B2D5     CPL      F0(0xD0.5)
C:0x000B    E4       CLR      A
C:0x000C    C3       CLR      C
C:0x000D    9D       SUBB     A,R5
C:0x000E    FD       MOV      R5,A
C:0x000F    E4       CLR      A
C:0x0010    9C       SUBB     A,R4
C:0x0011    FC       MOV      R4,A
C:0x0012    EE       MOV      A,R6
C:0x0013    30E715   JNB      0xE0.7,C:002B
C:0x0016    B2D5     CPL      F0(0xD0.5)
C:0x0018    E4       CLR      A
C:0x0019    C3       CLR      C
C:0x001A    9F       SUBB     A,R7
C:0x001B    FF       MOV      R7,A
C:0x001C    E4       CLR      A
C:0x001D    9E       SUBB     A,R6
C:0x001E    FE       MOV      R6,A
C:0x001F    120039   LCALL    C?UIDIV(C:0039)
C:0x0022    C3       CLR      C
C:0x0023    E4       CLR      A
C:0x0024    9D       SUBB     A,R5
C:0x0025    FD       MOV      R5,A
C:0x0026    E4       CLR      A
C:0x0027    9C       SUBB     A,R4
C:0x0028    FC       MOV      R4,A
C:0x0029    8003     SJMP     C:002E
C:0x002B    120039   LCALL    C?UIDIV(C:0039)
C:0x002E    30D507   JNB      F0(0xD0.5),C:0038
C:0x0031    C3       CLR      C
C:0x0032    E4       CLR      A
C:0x0033    9F       SUBB     A,R7
C:0x0034    FF       MOV      R7,A
C:0x0035    E4       CLR      A
C:0x0036    9E       SUBB     A,R6
C:0x0037    FE       MOV      R6,A
C:0x0038    22       RET      
                 C?UIDIV:
C:0x0039    BC000B   CJNE     R4,#0x00,C:0047
C:0x003C    BE0029   CJNE     R6,#0x00,C:0068
C:0x003F    EF       MOV      A,R7
C:0x0040    8DF0     MOV      B(0xF0),R5
C:0x0042    84       DIV      AB
C:0x0043    FF       MOV      R7,A
C:0x0044    ADF0     MOV      R5,B(0xF0)
C:0x0046    22       RET      
C:0x0047    E4       CLR      A
C:0x0048    CC       XCH      A,R4
C:0x0049    F8       MOV      R0,A
C:0x004A    75F008   MOV      B(0xF0),#0x08
C:0x004D    EF       MOV      A,R7
C:0x004E    2F       ADD      A,R7
C:0x004F    FF       MOV      R7,A
C:0x0050    EE       MOV      A,R6
C:0x0051    33       RLC      A
C:0x0052    FE       MOV      R6,A
C:0x0053    EC       MOV      A,R4
C:0x0054    33       RLC      A
C:0x0055    FC       MOV      R4,A
C:0x0056    EE       MOV      A,R6
C:0x0057    9D       SUBB     A,R5
C:0x0058    EC       MOV      A,R4
C:0x0059    98       SUBB     A,R0
C:0x005A    4005     JC       C:0061
C:0x005C    FC       MOV      R4,A
C:0x005D    EE       MOV      A,R6
C:0x005E    9D       SUBB     A,R5
C:0x005F    FE       MOV      R6,A
C:0x0060    0F       INC      R7
C:0x0061    D5F0E9   DJNZ     B(0xF0),C:004D
C:0x0064    E4       CLR      A
C:0x0065    CE       XCH      A,R6
C:0x0066    FD       MOV      R5,A
C:0x0067    22       RET      
C:0x0068    ED       MOV      A,R5
C:0x0069    F8       MOV      R0,A
C:0x006A    F5F0     MOV      B(0xF0),A
C:0x006C    EE       MOV      A,R6
C:0x006D    84       DIV      AB
C:0x006E    20D21C   JB       OV(0xD0.2),C:008D
C:0x0071    FE       MOV      R6,A
C:0x0072    ADF0     MOV      R5,B(0xF0)
C:0x0074    75F008   MOV      B(0xF0),#0x08
C:0x0077    EF       MOV      A,R7
C:0x0078    2F       ADD      A,R7
C:0x0079    FF       MOV      R7,A
C:0x007A    ED       MOV      A,R5
C:0x007B    33       RLC      A
C:0x007C    FD       MOV      R5,A
C:0x007D    4007     JC       C:0086
C:0x007F    98       SUBB     A,R0
C:0x0080    5006     JNC      C:0088
C:0x0082    D5F0F2   DJNZ     B(0xF0),C:0077
C:0x0085    22       RET      
C:0x0086    C3       CLR      C
C:0x0087    98       SUBB     A,R0
C:0x0088    FD       MOV      R5,A
C:0x0089    0F       INC      R7
C:0x008A    D5F0EA   DJNZ     B(0xF0),C:0077
C:0x008D    22       RET      

     2: void main() 
     3: {  
     4:     int  i; 
     5:  unsigned char j; 
     6:  i=-10000; 
     7:   
C:0x008E    7FF0     MOV      R7,#B(0xF0)
C:0x0090    7ED8     MOV      R6,#0xD8
     8:  j=i/256; 
     9:  
C:0x0092    7C01     MOV      R4,#0x01
C:0x0094    7D00     MOV      R5,#0x00
C:0x0096    120003   LCALL    C?SIDIV(C:0003)
C:0x0099    8F08     MOV      0x08,R7
    10: while(1) 
C:0x009B    80FE     SJMP     C:009B

就是一个signed和unsigned的区别,用的着差别这么大吗?

通过以上的实验,可以得出结论:Keil编译器非常智能,会生成最短的代码,能够智能判断每个变量的使用,生成最短的代码。同时,学习单片机的各位同仁,除非万不得已,千万不要用signed类型。




『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
iPhone将是质的飞跃:苹果A14处理器+高通X55基带