注:这是我以前发表在《力源电子工程》的文章。贴在这觉得排版上不好看,大家可以搜索"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期发表后,即收到丁洪昌同志来稿,提出订正和补充意见。编辑部及时把意见转告给原作者涂建坤同志;涂建坤同志认为丁洪昌同志的意见是正确的。现将丁洪昌同志的来稿刊登出来,文章题目是编辑部定的。编辑部认为,学术上的讨论是很正常且是有益的。我们感谢丁洪昌同志对本刊的关心和爱护。对于编审工作中的疏漏表示歉意。
文章评论(0条评论)
登录后参与讨论