weiqi7777

51单片机之流水灯

0
阅读(2792)

今天重温了下51单片机,先从流水灯做起。用的是proteus8.0来仿真。

首先是硬件电路:

clip_image002

左边的部分,是51的最小系统电路。包括时钟电路和复位电路。芯片的电源引脚和地引脚隐藏了,默认电源接VCC,地接GND。

clip_image004

在器件的属性,隐藏管脚中可以看到。

clip_image008 clip_image007

在design->configure power rails菜单中,可以设置电源和地引脚的默认电压值。一般VCC,VDD设置为5V,GND设置为0V。

在proteus中仿真,复位引脚和时钟引脚是没有用的。对于51,当RST复位引脚为高的时候,系统复位,程序重新从开头执行。时钟引脚接的晶振是决定51单片机运行时的时钟。但是在protues中,这两个引脚是没有用的。输入什么都不影响,即使时钟不接晶振,电路也正常工作。复位信号给高,电路也正常工作。所以这就是用软件模拟硬件仿真和实际的硬件仿真区别。不过对于学习51单片机的程序设计,就不要在乎这些细节。等要自己设计51单片机,要注意这些东西。

下面开始写程序:实现流水灯循环下移。


#include"reg52.h"
sbit key = P1^7;
void delay_ms(int t)
{
char i;
while(t--)
{
for(i=0; i<100; i++);
}
}
int main()
{
char temp=0x01;
P0 = ~temp ;
while(1)
{
delay_ms(500);
temp = (temp<<1)|((temp>>7));
P0=~temp;
}
}


使用一个temp,变量来进行充当中间变量进行移位。

temp = (temp<<1)|((temp>>7)); 将temp8位数据向左移一位,然后在将temp 8位数据向右移7位,这样最高位就到最低位了,在用位或|操作,将两个数据拼接,这样就实现了循环左移功能。

感觉是没错,载入到proteus中仿真。

发现,最开始移位是正确的,流水灯循环向下移。但是当移到最后一个流水灯的时候,在移一次的时候,灯就全亮了。然后就没有移位了。这和设计的功能不一样啊。

然后找原因,使用keil的软件仿真,每次循环看temp的值。当移位到最后一位的时候,即temp=0x80的时候。查看temp<<1和temp>>7的值

clip_image012

发现,temp的值为0x80的时候,temp>>7的值,竟然变为0xff。而不是0x01。想了半天,想起,定义的temp为char型,而char型,这里51应该是认为有符号型数据(我认为),有符号移位,当向右移位时,最高位补符号位。所以temp为0x80,这时,最高位为1,则向右移位7位的话,补符号位,那么移位结果就是0xff了。

将char temp=0x01 改为unsigned char temp=0x01

然后在protues中仿真,发现结果对了。

发现这程序,多用了一个temp变量,何不把这变量去掉,直接就用P0移位操作了。

改改代码:

P0 = ~0x01;
while(1)
{
delay_ms(500);
P0 = (P0<<1)|((P0>>7));
}

似乎也没有什么问题。将生成的hex文件在入到proteus中,运行。发现,就只有最开始亮了一次灯,下次的时候,登就全灭了。

这不科学啊,这又是什么问题了。在来软件仿真,看结果:

clip_image014

clip_image016

第一次,结果是正确的,但是到第二次的时候,P0就全部变为全零了。这又是为什么了。查找下资料,原来P0是开漏输出的,外不接上拉电阻的话,输出都是低电平,软件仿真的时候,是认为P0是没有接上拉电阻,所以P0的输出都是0.所以在下一次移位,输出就全是0了。

将P0口改为P2口,输出就正确了。因为P2口是推免输出。可以输出高电平。

P2 = ~0x01;
while(1)
{
delay_ms(500);
P2 = (P2<<1)|((P2>>7));
}

就一个简单的流水灯,也发现了这么多的问题。看来基础还是比较重要啊。不过,能发现问题是好事啊,最重要的是,能将这些问题都给解决。