class="markdown_views prism-dracula">
51单片机入门教程(2)——实现流水灯
一、搭建流水灯电路
在Proteus中搭建流水灯电路如图
二、流水灯程序
我们可以把流水灯看作依次点亮若干个灯。
程序如下:
#include <reg52.h>
sbit led1 = P2^0;
sbit led2 = P2^1;
sbit led3 = P2^2;
sbit led4 = P2^3;
sbit led5 = P2^4;
sbit led6 = P2^5;
sbit led7 = P2^6;
sbit led8 = P2^7;
void main()
{
led1 = 1;
led2 = 0;
led3 = 0;
led4 = 0;
led5 = 0;
led6 = 0;
led7 = 0;
led8 = 0;
led1 = 0;
led2 = 1;
led3 = 0;
led4 = 0;
led5 = 0;
led6 = 0;
led7 = 0;
led8 = 0;
while(1);
}
编译并下载程序到仿真中,观察现象发现只有第二个灯是亮的???
什么鬼???
2.1 延时程序
单片机的执行指令速度非常快,一个晶振是12MHz的单片机执行一条指令的速度是微秒级的,所以点亮第一个灯的时间太短了,以至于我们根本没有察觉。
因此我们需要一个延时的语句。
实现延时的方法就是循环执行很多次空指令。程序如下:
int i,j;
for(i = 0;i < 110; ++i)
{
for(j = 0; j < 1000; ++j)
{
;
}
}
然后我们就可以把流水灯的程序改成这样的:
#include <reg52.h>
sbit led1 = P2^0;
sbit led2 = P2^1;
sbit led3 = P2^2;
sbit led4 = P2^3;
sbit led5 = P2^4;
sbit led6 = P2^5;
sbit led7 = P2^6;
sbit led8 = P2^7;
void main()
{
int i,j;
led1 = 1;
led2 = 0;
led3 = 0;
led4 = 0;
led5 = 0;
led6 = 0;
led7 = 0;
led8 = 0;
for(i = 0;i < 110; ++i)
{
for(j = 0; j < 1000; ++j)
{
;
}
}
led1 = 0;
led2 = 1;
led3 = 0;
led4 = 0;
led5 = 0;
led6 = 0;
led7 = 0;
led8 = 0;
while(1);
}
编译并下载程序到仿真中,观察现象发现首先第一个灯亮,过了一会儿第二个灯亮。
2.2 延时函数
我们剩下的任务就是依次点亮每个灯,但是每次点亮一个灯就需要写一段延时程序,很麻烦!
为了程序的可读性(toulan),可以把延时程序写成一个子函数,随时供我们使用。
C语言中子函数的定义方式如下
返回值类型 函数名 (参数1,参数2,……)
{
函数体;
}
这样我们就可以把延时函数写成这样:
void delay1s()
{
int i,j;
for(i = 0; i<110;++i)
{
for(j = 0; j<1000;++j)
{
}
}
}
几点说明:
void
:因为该延时函数不需要返回值,所以写为void
delay1s
:该函数的函数名,命名需要符合C语言的标识符命名规则。
()
: 不需要传入参数,所以括号中为空
至此我们可以把流水灯程序写为以下形式:
#include <reg52.h>
sbit led1 = P2^0;
sbit led2 = P2^1;
sbit led3 = P2^2;
sbit led4 = P2^3;
sbit led5 = P2^4;
sbit led6 = P2^5;
sbit led7 = P2^6;
sbit led8 = P2^7;
void delay1s()
{
int i ,j;
for(i = 0;i<110; ++i){
for(j = 0;j<1000;++j){
;
}
}
}
void main()
{
led1 = 1;
led2 = 0;
led3 = 0;
led4 = 0;
led5 = 0;
led6 = 0;
led7 = 0;
led8 = 0;
delay1s();
led1 = 0;
led2 = 1;
led3 = 0;
led4 = 0;
led5 = 0;
led6 = 0;
led7 = 0;
led8 = 0;
while(1);
}
2.3 按字节寻址
我们可以看到,上面的代码十分冗长,每次点亮一个灯需要8条语句,那么如何简化?
比如把
led1 = 1;led2 = 0; led3 = 0; led4 = 0; led5 = 0; led6 = 0; led7 = 0; led8 = 0;
这8条语句替代为
P2 = 0000 0001
???
答案是可以的。代码如下
unsigned char a = 0x01;
P2 = a;
至此,我们可以把流水的代码优化为如下形式:
#include <reg52.h>
void delay1s()
{
int i ,j;
for(i = 0;i<110; ++i){
for(j = 0;j<1000;++j){
;
}
}
}
void main()
{
unsigned char a1 = 0x01 ;
unsigned char a2 = 0x02;
P2 = a1;
delay1s();
P2 = a2;
while(1);
}
2.4 逻辑移位
依次点亮8个灯,每点亮一个灯都需要一句赋值语句还是很麻烦 。
所以可以使用逻辑移位语句,每次赋值后,将数值左移一位。
C语言逻辑左移代码如下:
unsigned char a = 0x01;
unsigned char b = a<<1;
usingned char c = a<<3;
至此,我们可以把流水灯的代码优化如下:
#include <reg52.h>
void delay1s()
{
int i ,j;
for(i = 0;i<110; ++i){
for(j = 0;j<1000;++j){
;
}
}
}
void main()
{
unsigned char a = 0x01;
while(1)
{
P2 = a;
a = a<<1;
delay1s();
}
}
编译并下载程序到仿真中,观察现象发现8个灯依次亮过之后不再亮了。
2.5 条件判断
因为在移位操作中,当变量
a
的值为
1000 0000
时,再次执行左移操作,
a
中的1就溢出了,因此a的值变为
0000 0000
,此时我们需要加一个判断,使a再次恢复为
0000 0001
C语言中,if条件判断使用方式如下
if(判断条件)
{
}
当判断条件为真时,执行
{ }
中的语句。
至此,流水灯代码可改成如下形式:
#include <reg52.h>
void delay1s()
{
int i ,j;
for(i = 0;i<110; ++i){
for(j = 0;j<1000;++j){
;
}
}
}
void main()
{
unsigned char a = 0x01;
while(1)
{
if(a == 0x00)
{
a = 0x01;
}
P2 = a;
a = a<<1;
delay1s();
}
}