51单片机之流水灯

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

首先是硬件电路:

clip_image002

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

clip_image004

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

image

 design->configure power rails菜单中,可以设置电源和地引脚的默认电压值。一般VCCVDD设置为5VGND设置为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<<1temp>>7的值

clip_image010

发现,temp的值为0x80的时候,temp>>7的值,竟然变为0xff。而不是0x01。想了半天,想起,定义的tempchar型,而char型,这里51应该是认为有符号型数据(我认为),有符号移位,当向右移位时,最高位补符号位。所以temp0x80,这时,最高位为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_image011

clip_image012

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

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

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

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

 

此条目发表在ARM嵌入式分类目录,贴了, 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。