原创 对“C51语言应用编程的若干问题”

2007-6-12 10:05 2585 3 3 分类: MCU/ 嵌入式

注:这是我以前发表在《力源电子工程》的文章。贴在这觉得排版上不好看,大家可以搜索"c51 丁洪昌"看其他网站的转载。


对“C51语言应用编程的若干问题”


一文的订正


兰州化工部自动化研究所(730060) 丁洪昌


  摘 要   单片机的定时/计数器使用前需赋初始值,对用高级语言C51编程时使用的一种赋初值算法进行了论证,并为其补充;对C51中条件编译语句#if…#endif的语法中易误用的语句之语法进行了分析、验证,并给予订正。


  关键词 C51语言     算法勘误     条件编译





1 对C51中定时/计数器赋初值算法的补充


   《力源电子工程》1999年第3期《C51语言应用编程的若干问题》一文的例3中,用下述方法为T0赋初值,使T0在启动后满1000个机器周期时产生中断申请:




TMOD = 0X01
/*T0工件在定时器方式1,为16位*/
TH0 = -(1000/256)
TL0 = -(1000%256)

1.1 赋初值算法问题的验证


    这种算法(以下称其为新算法)比较新颖而且方便,不同于传统的算法。但经过验算,发现这种算法有一个漏洞,会引起定时或计数的严重错误,仍以本例进行验证如下:




新算法:
TH0 =-(1000/256)
=-(3)
= 0XFD

TL0

=-(1000%256)
=-(232)
= 0X18
传统算法:
2^16 – 1000 = 64536 = 0XFC18
即:TH0 = 0XFC
TL0 = 0X18
两种算法得出的TH0值相差1。
若使T0在启动后1024个机器周期时产生中断申请。
新算法:
TH0=-(1024/256)
=-(4)
= 0XFC
TL0=-(1024%256)
=-(0)
= 0X00
传统算法:
2^16 – 1024 = 64512 = 0XFC00
即:TH0 = 0XFC
TL0 = 0X00

则两种算法得出的值完全相同。


    经观察发现,当TL0 = 0X00 时,新算法得出的结果是正确的,否则,必须将新算法得出的TH0的值减去1,才能得出正确的结果。


    在所需机器周期数大于256时,这种错误将使中断提早到达256个机器周期;


    在所需机器周期数小于256时,TH0应为0XFF,新算法得出TH0=0X00,将会使中断迟到(0XFF-0X00)* 256=65280个机器周期。


    在所需机器周期数等于0时最甚(当然,在实际编程中不会出现这种情况,但为严谨起见,对其进行论述),中断将会迟到65536个机器周期。


证明:


    为方便理解,仍以16位的定时/计数器为例,对13位的定时/计数器仍可以用下述方法进行证明(见1.3适用范围一节)。


    设X表示定时/计数所需的机器周期数


    设A = X/256(本文中X/256均指X除以256舍尾取整);B = X%256。


    1. 当X>256时


    新算法的计算过程中


TH0 =-(X/256),实际上是TH0 = 256-(X/256),因为TH0是8位长度,它的值不超过255。
TL0 =-(X%256)是同样道理。


则新算法的计算过程可表示为:


TH0TL0 =(256-X/256)* 256 +(256-X%256)
      =(256-A)* 256 +(256-B)
      = 65536-256A +(256-B)    (1)


    式(1)中(256 – B)即 TL0 的值。


    传统算法的计算过程可表示为:


TH0 TL0 = 65536-X
        = 65536-[(X/256)* 256 + X%256]
        = 65536-256A-B   (2)


可以看出,新算法确实比传统算法的正确值多了256,也就是说,TH0的值应减去1,只有当式(2)中B=0时,在新算法中则:TL0 =-(B)=0,即式(1)中的(256-B)=0,此时式(2)与式(1)才相等。


    2. 当X<256时(A=0)


    新算法的计算过程可表示为:


TH0TL0 =-(X/256) * 256 + 256-X%256
        = 256-B     (3)


    传统算法的计算过程可表示为:


TH0TL0 = 65536-B      (4)


    新算法的结果比传统算法结果小65280,若将其TH0减去1变为0XFF,则为正确结果。


    3. 特例,当X=0时(A=B=0)


    新算法的计算结果为:


TH0TL0 = 0X0000      (5)


    传统算法的计算结果为:


TH0TL0 = 0X10000(实际上不可能) (6)


    两者相差65536。


1.2 产生问题的原因分析


    (1) 分析产生这个问题的原因,可先从定时/计数器的工作过程入手。


    新算法中


TH0 =-(X / 256)
TL0 =-(X % 256)


    其主观意图是在TH0中装入0X100H(即256)减去X/256的整数部分得出的差ΔTH0,使TH0控制计数ΔTH0* 256个机器周期T(或输入ΔTH0* 256个计数脉冲),TL0也同理;然而当TL0≠0时,定时/计数器在启动后的(256-TL0)个机器周期T后即向TH0进位。按前面所说的主观意图来理解,则是在TH0中记录下“已计满256个机器周期T的时间或256个输入脉冲”的信息。也就是说:TL0所设定的(256-TL0)个机器周期T,错误地使TH0(应记录整数个256T)“跳数”(加1),将使定时/计数器产生非预期的中断申请。


    所以当TL0≠0时新算法得出的TH0值应减去1,则可以避免此错误的发生。程序应以如下形式:


TH0 =-(X / 256)
TL0 =-(X % 256)
if (TL0<>0) TH0- -;


    相反,当TL0=0时,TL0计数满后向TH0进位正是新算法的本意,所以不会发生定时/计数错误。


    上面分析了当X>256时的情况(A>0),当X<256时,A=0,新算法得出TH0=0,显而易见应为TH0=0XFF。


    (2) 简单地说:新算法是把传统算法的0X10000-hhll(hhll=X)当作(0X100-hh)赋给TH0,(0X100-ll)赋给TL0来计算,当ll=0时,(0X100-ll)=0X100,没用向0X100的最高位“1”即TH0借位;当ll≠0时,要发生借位,传统算法中有这一过程,而新算法则丢失了这个借位,所以会在TH0中多出1来。


    之所以要从不同角度进行分析,是因为在实际编程中,我们可能会从不同角度出发确定算法,经过上述分析,可以避免类似失误的发生。


1.3 适用范围


    当定时/计数器工作在模式0时,将算法中的256替换为32,使用如下语句:


TH0 =-(X/32)
TL0 =(-(X%32))& 0X1F
if(TL0<>0) TH0--;


    则同样适用上述结论。文中以定时/计数器0 的定时器方式为例,但对定时/计数器1以及它们工作在计数器方式时也是适用的。


2 对C51条件编译语句#if…用法的订正


  《力源电子工程》1999年3期《C51语言应用编程的若干问题》一文中有下述宏定义与条件编译例子(例1):




#define flag 1
#ifdef flag==1
   #define fosc 6M
  delay="10";
#elif flag==0

#define fosc 8M

delay=12;
#else
#define fosc 12M
  delay="20";
#endif
main()
  {
  for(I=0;I
  }

    经实际运行,发现“#ifdef flag==1”一句似有不妥,为叙述方便起见,设有如下情形。


    1. 情形1
    #ifdef 标识符
    该条件编译语句当并且仅当 “标识符” 曾被“#define”语句定义过(如果有“# undef 标识符”语句则在其前面)时为TRUE,即使如下形式也是如此:
    #ifdef 标识符 == 常量 亦即:#ifdef 常量表达式。


    2. 情形2
    #if 标识符 == 常量 亦即:#if 常量表达式
    该条件编译语句当并且仅当 “标识符 == 常
    量”为TRUE时,为TRUE。


    所以在上述例子中“#ifdef flag == 1”的值将永远为TRUE,不论“flag == 1”是否为TRUE。


    根据程序目的可知,这一句应为:
“#if flag ==1”。


    另外,在Franklin C51 V3.20编译系统目录OMF51\EXAMPLES\DCLOCK.C 中有如下一段:


#ifdef CPU==8051
#define SECOND (4000/(12/FREQ))
#else
#define SECOND (200/(12/FREQ))
#endif


    在此段前没有出现对“CPU”的定义,实际上, #ifdef CPU==8051


一句是不合理的。其值等于 #ifdef CPU


而非


(#ifdef CPU) && (#if CPU==8051)


    特别指出,在参考文献2第128页的例子中,也出现了


#ifdef CPU==8051,这一语句。


    上述结论作者经过Franklin C51 V3.20编译验证。


参 考 文 献


1 张毅刚, 修林成, 胡振江. MCS-51单片机应用设计. 哈尔滨:哈尔滨工业大学出版社, 1990


2 徐爱钧, 彭秀华. 单片机高级语言C51应用程序设计. 北京:电子工业出版社, 1998


(收稿日期:1999-12-14)


编后 《C51语言应用编程的若干问题》一文在本刊1999年第3期发表后,即收到丁洪昌同志来稿,提出订正和补充意见。编辑部及时把意见转告给原作者涂建坤同志;涂建坤同志认为丁洪昌同志的意见是正确的。现将丁洪昌同志的来稿刊登出来,文章题目是编辑部定的。编辑部认为,学术上的讨论是很正常且是有益的。我们感谢丁洪昌同志对本刊的关心和爱护。对于编审工作中的疏漏表示歉意。

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
3
关闭 站长推荐上一条 /3 下一条