51起步学习——流水灯
从零学习51单片机——流水灯
相信大家初次接触51单片机,大对数都是从流水灯开始,制作出一个简单的流水灯对于初学者不仅有趣好玩又会使学习者从中获得一点成就感。开始的时候我以为流水灯太简单了,耗不了对少时间就能做出来,是的最基本的流水灯确实很简单。但是我们学习不仅仅是为了完成任务,更重要的是产生新的想法、新的思维,从而知道怎样更好、更有效地完成任务。
在流水灯的学习过程中,我使用了一下方法:
- 位定义法:最笨的也是最直接的一种方法
- 移位操作法
- 库函数法
- 精确延时
再用keil编程之前,先在Proteus中画好电路图:
在Proteus中晶振电路和复位电路电源部分可以省略不画。
一、 位定义法
#include<reg52.h> //包含特殊功能寄存器定义的头文件
sbit led0 = P0^0; //位地址声明,其中sbit必须小写,P必须大写 sbit led1 = P0^1; sbit led2 = P0^2; sbit led3 = P0^3; sbit led4 = P0^4; sbit led5 = P0^5; sbit led6 = P0^6; sbit led7 = P0^7; typedef unsigned int u16; //字符变量重定义 void delay(u16 i) { while(i--); } void main() { while(1) { led0 = 0; //第一盏灯亮 delay(50000); //延时大约500ms led0 = 1;
led1=0; delay(50000); led1=1;
led2=0; delay(50000); led2=1;
led3=0; delay(50000); led3=1;
led4=0; delay(50000); led4=1;
led5=0; delay(50000); led5=1;
led6=0; delay(50000); led6=1;
led7=0; delay(50000); led7=1; } }
二、 移位操作
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define led P0
void delay(u16 i){
while(i--);
}
void main(){
u8 i;
led=0xFE;
delay(50000);
while(1){
for(i=0;i<8;i++)
{
led=~(0x01<<i);
delay(50000);
}
}
}
三、库函数法
#include<reg52.h>
#include<intrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define led P2
void delay(u16 i)
{
while(i--);
}
void main(){
u8 i;
led = 0xFE;
delay(50000);
while(1){
for(i=0;i<7;i++)
{
led=_crol_(led,1);
delay(50000); //约延时450ms
}
}
}
以上的流水灯都是采用不精确延时,学了定时器之后自己慢慢摸索如何在流水灯中进行精确延时。。。。终于,摸索出来了,感觉终于慢慢上手了。氮素,在我兴高采烈地编完之后,在Proteus里却没有丝毫反应。。。WTF??怎么会。。。然后各种怀疑自己。。最终发现是自己把变量的类型定义错了,改完之后一切终于okk啦。
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
u8 water_led[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f} ; //流水灯的循环顺序
#define led P0
void main()
{
u16 cnt=0;
u16 sec=0;
TMOD=0X01;
TH0=0Xfc;
TL0=0X67;
TR0=1;
while(1)
{
if(TF0==1)
{
TF0=0;
TH0=0Xfc; //(fc67)h=64615 由(12/11.0592)*x=1ms 得到x=921.6 65536-921.6=64614.4
TL0=0X67;
cnt++; //毫秒数加一
if(cnt==1000) //记满一秒
{
cnt=0; //毫秒数先清零
led=water_led[sec]; //注意sec是数组下标,所以应该之后再给sec+1
sec++;
if(sec>=8) //一次循环完成
sec=0; //秒数清零,进入下一次循环
}
}
}
}
/**********************实现单向流水灯,相邻两盏间间隔1s*************************/
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define led P0
void main()
{
u16 cnt;
u16 sec;
TMOD=0X01;
TH0=0XFC;
TL0=0X67;
TR0=1;
while(1)
{
if(TF0==1)
{
TF0=0;
TH0=0XFC;
TL0=0X67;
cnt++;
if(cnt==1000)
{
cnt=0;
led=~(0x01<<sec);
sec++;
if(sec==8)
sec=0;
}
}
}
}
/**********************实现双向流水灯,相邻两盏间间隔1s*************************/
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define led P0
void main()
{
u16 cnt;
u16 sec;
TMOD=0X01;
TH0=0XFC;
TL0=0X67;
TR0=1;
while(1)
{
if(TF0==1)
{
TF0=0;
TH0=0XFC;
TL0=0X67;
cnt++;
while(cnt==1000)
{
cnt=0;
if(sec<8)
{
led=~(0x01<<sec);
sec++;
}
else if(sec<14)
{
led=~(0x40>>sec-8);
sec++;
}
else
sec=0;
}
}
}
}
到双向流水灯这里,就有点EMMMM。。。每次从1号——>8号灯——>1号,除过第一次之外其余到1号的时候总是多停留一秒,目前还没想到良好的解决方案,想到了再回来改吧。
如果有知道的朋友麻烦留言提示一下,不胜感激,有问题或者不够完善的地方还请多指出来,多谢!
补充:
重新尝试后发现用数组罗列出来这种方法更容易理解些:
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define led P0
u8 water_led1[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
u8 water_led2[]={0xbf,0xdf,0xef,0xf7,0xfb,0xfd};
void main()
{
u16 cnt;
u16 sec;
TMOD=0X01;
TH0=0XFC;
TL0=0X67;
TR0=1;
while(1)
{
if(TF0==1)
{
TF0=0;
TH0=0XFC;
TL0=0X67;
cnt++;
while(cnt==1000)
{
cnt=0;
if(sec<8)
led=water_led1[sec];
if(sec>=8)
led=water_led2[sec-8];
sec++;
if(sec==14)
sec=0;
}
}
}
}
这样流水灯显示是正常的。
再补充:这样以来就明白了第一个双向流水灯,在第一个灯哪里会多停一秒钟的原因了。其实就相当于把第一个流水灯多走了一遍,而且延时一秒钟比较缓慢就很容易发现有错误。之前看到有人这样写,但是延时时间比较短,不仔细看不调试的话很难看出错误。更改以后如下:
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define led P0
void main()
{
u16 cnt;
u16 sec;
TMOD=0X01;
TH0=0XFC;
TL0=0X67;
TR0=1;
while(1)
{
if(TF0==1)
{
TF0=0;
TH0=0XFC;
TL0=0X67;
cnt++;
while(cnt==1000)
{
cnt=0;
if(sec<8)
led=~(0x01<<sec);
if(sec>=8)
led=~(0x40>>sec-8);
sec++;
if(sec==14)
sec=0;
}
}
}
}
上一篇: 51单片机led灯闪烁实例
下一篇: 绘制动态心形图案::R语言绘制心形图
推荐阅读