最近写了几个程序,一个是用51单片机读取模数传感器adc0832的电压值,一个是读取ds1302的时刻值,成果都呈现了读数一向为0的状况。我调试了近一个星期,修正了一个我以为不可能会错的语句,程序运转成功了,这才发现了一个极端荫蔽的过错。(我用的是xp体系,用keil4软件编译)
先上代码:第一个为过错代码,第二个为正确代码。这是用来向ds1302芯片写入指令或数据的函数。完成把8位的数据dat一位一位地写入ds1302的io口。其间ACC0为ACC的第0位。
仔细比照这两个代码,可能会觉得没差异,并且这两个代码都能够通过编译(加上reg52.h和一些宏界说)。我也是一向以为for()这里边没有过错,成果。。。试着修正时钟信号,添加延时之类的,调了良久仍是错,严峻冲击我的自信心。这两个代码的差异就只有for(i=0;i<8;i++)和for(i=8;i>0;i–)了。学过c言语的人都知道,这两个语句都是完成一个8次的循环,功用如出一辙。怎么会由于这个语句的差异就导致单片机操控的过错呢?奇特!
接着我试着把过错程序中的ACC改为51芯片的寄存器B,烧录进单片机,程序运转成功,跟“for(i=8;i>0;i–),ACC版”相同,lcd在很放肆地显现着正确的时刻( for(i=0;i<8;i++),ACC版lcd的时刻显现为0)。附:
这样就知道原因了,运用for(i=0;i<8;i++)的运算中可能有累加器ACC参加了,导致修正了ACC的值,使写入的指令呈现过错。但为什么for(i=8;i>0;i–)就没有ACC的参加呢?一个大大的问号。根据我调试了一个星期的程序,皆由于这一个奇特的过错,我真实不甘心,决议研讨究竟。所以,别离查看了这三个程序代码用 keil4 编译后得到的 汇编代码。(学过汇编便是爽啊,哈)
比照后,能够发现,犯错的原因是for(i=0;i<8;i++)ACC版中,用ACC接纳了实参(存储的为要写入的指令),然后在 for 循环前要给变量 “ i " 赋值时,要用到ACC清零,再把ACC中的零赋给 R7 ("i"的值存储在R7)。这样的话,本来存储在ACC中的写入指令就被清零,自然会导致操控呈现过错,终究无法读取ds1302芯片的时刻,故显现为零。
而在for(i=8;i>0;i–)ACC版中,也用ACC接纳了实参的值,但在 for 循环前,给变量“ i ” 赋值时,赋值为8,不需要用到ACC,所以ACC一向是存储着实参中的指令,没有被清零,所以能够顺畅地向ds1302发送指令,然后能够读取到时刻。
总结:
由于用for(i=0;i0;i–)类的指令多了 CLR A 和 INC R7 两条指令,CJNE 指令又比较DJNZ指令多了一个字节的程序代码存储空间,在频率为12M的51单片机上体现为履行相同功用的程序,要多用2us,代码空间花多一字节。所以前者是毫无优势的,今后应养成用
for(i=n;i>0;i–)的习气。
请不要辩驳我用了这么长的时刻去研讨,只能使单片机履行快2us,而说我钻牛角尖,仅仅由于,这个过错导致我整个程序无法正常运转,这不是一件小事。
至于为什么要用到累加器ACC来接纳实参,是由于后边的程序要把一个8位的实参一位一位地输出到一个io口,自界说一个变量的话,按位寻址如同比较费事,要通过一系列 位运算 ,或许用bit界说8个位(有好的办法请告诉我,哈),并且我写不出来。而用ACC的话,能够很轻易地操作ACC的恣意一位,如ACC0,ACC7。在网上查了一下,如同还有一种办法是界说 一种叫 位域 的东东,我看的c言语的书都没介绍,所以还不是很了解。
/************************************************************/
刚刚想了一下,不必ACC 的办法,作一个位运算dat &0x01,修正如下:
想到了这个办法后,觉得自己好痴人,今后都不必ACC了。