51单片机之LED灯
- 🌴前言:
- 🏮点亮LED灯的原理
- 💘点亮你的第一个LED灯
- 💘点亮你的八个LED灯
- 📌让LED灯闪烁的原理
- 🎽 LED灯的闪烁
- 🏓错误示范1
- 🏓正确的LED闪烁代码应该是这样:
- 🏓错误示范2
- 🏓错误示范3
- 📞点亮LED流水灯的原理
- 🤠循环左移函数_crol_()
- 🤠循环右移函数_cror_()
- 🤠 LED流水灯
- ⭕总结
🌴前言:
我们在学习单片机的时候,最好还是以实物的学习为主,因为兴趣是最好的老师,实物的视觉冲突比仿真的效果要更好,每一个系列的开发板由于原理图不同,他们对应的IO口控制的东西就不一样,如果你是使用实物来学习51单片机,在进行软件编程前,一定要学会看原理图,即找到某一个器件是由哪个IO口来控制的。
今天给大家带来点亮LED灯的原理及对应的程序讲解和实物演示效果展示。
🏮点亮LED灯的原理
点亮一个LED灯,我们在初中物理电路中应该就学习过,很简单的电路就可以实现,即一个电阻、一个电源、一个LED灯、一个连通的回路。
这个电路图相信大家都很容易理解,那在用单片机IO口控制LED灯的点亮原理也是类似,我们看我们板子的的LED模块的原理图:
我们板子的LED模块一共有8个LED小灯,由P2口的每一个小的IO口控制,由于P2口可以进行位寻址,所以我们可以操作它的每一位,进而控制每一个LED灯的亮灭。
另外观察电路图,他们是共阳的,意思是有共同的正极,我们只需要让每一个LED电路的另一端IO口输出低电平就可以了,因为有上拉电阻(这个电阻的功能是让IO口处于高电平,下次我们在具体的分析),每个小的IO口的初始状态都是高电平,与VCC的电压相同,所以每一个LED的电路没有电压差就不会导通,我们想点亮其中一个,只需要将那一个接的IO口设置为低电平0就可以点亮。
💘点亮你的第一个LED灯
#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit LED = P2^0;//设置需要点亮的LED灯的IO口
int main()
{
while(1)
{
LED = 0;//将其设置为低电平,使电路导通
}
return 0;
}
实物效果演示:
当我们使用烧录软件把程序烧录进去之后,你会发现此时D1灯亮了,当然你也可以编写一个程序,让他们全亮。
💘点亮你的八个LED灯
点亮一个LED的原理和代码都清楚了,我们来试试点亮8个LED灯吧。
#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
#define LED P2//将P2口宏定义为LED,增强代码可读性和健壮性
int main()
{
while(1)
{
LED = 0;//将整个P2口都设置为低电平
}
return 0;
}
实物演示:
可以看到和我们预期的一样全部点亮了,当然你也可以分别设置P2口的每个位地址为0,但是这样的话代码量会增加很多。
📌让LED灯闪烁的原理
理解了点亮的原理,控制LED闪烁就非常简单了,你只需要知道,当对应IO口输出高电平的时候LED就会亮,输出低电平的时候就会灭。你可能会这样写:
🎽 LED灯的闪烁
🏓错误示范1
#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit LED = P2^0;//设置需要点亮的LED灯的IO口
int main()
{
while(1)
{
LED = 0;//将其设置为低电平,使电路导通
LED = 1;将其设置为高电平,使电路截止
}
return 0;
}
我可以很负责任的告诉你,这段代码的的确确实现了闪烁,但是你会发现,当你去将这段代码通过下载软件下载到板子中时,板子的效果是D1常亮,这是为什么呢?
LED从点亮到熄灭的时间就是程序执行一条语句的实现,计算机的速度是很快的,我们可以使用keil调试来测试一下:
我们设置断点,启动调试,程序执行到LED = 1;
时的时间是
0.00038900
0.00038900
0.00038900秒,我们不需要这个时间,我们只需要执行当前语句的时间,我们点击下图左上角RST将时间归0,下图左边调试窗口sec显示的有程序执行到当前语句的时间,你可以手动归0。
此时我们已经成功归0了,接下来执行这条语句。
所以执行这条语句的时间是 0.00039 0.00039 0.00039秒,人眼正常的极限分辨率是50hz或60hz,从亮到灭这个过程,频率是一个吓人的数字2500hz大概是,人眼是不可能看到这个变化的过程的,因为实在是太快了!
我们想解决这个问题,就需要自己写一个延时函数
void delay(u16 i)
{
while(i--);
}
i为1的时候大概是延时了1us,大家可以自己去调试。
🏓正确的LED闪烁代码应该是这样:
typedef unsigned int u16;
typedef unsigned char u8;
sbit LED = P2^0;//将P2口宏定义为LED,增强代码可读性和健壮性
void delay(u16 i)
{
while(i--);
}
int main()
{
while(1)
{
LED = 0;//将其设置为低电平,使电路导通
delay(50000);//0.5s左右
LED = 1;将其设置为高电平,使电路截止
delay(50000);//0.5s左右
}
return 0;
实物演示:
注意:我们每设置一个状态,都必须延时一段时间,否则从这个状态到另外一个状态太快了,人眼无法观测到前一个状态,就会一直维持那个可以观察到的状态。
🏓错误示范2
看下面代码,试思考其展示结果:
#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit LED = P2^0;//将P2口宏定义为LED,增强代码可读性和健壮性
void delay(u16 i)
{
while(i--);
}
int main()
{
while(1)
{
LED = 0;//将其设置为低电平,使电路导通
delay(50000);//延时0.5s
LED = 1;将其设置为高电平,使电路截止
}
return 0;
}
实物效果展示:
可以看到亮的效果,但是看不到灭的效果,这是因为从亮到灭有延时我们可以观察到亮的过程,但是从灭到亮太快了,我们眼睛还没察觉到灭,这个灯就已经亮了。
🏓错误示范3
同样的我们在来看这段代码,思考它的实物效果:
#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit LED = P2^0;//将P2口宏定义为LED,增强代码可读性和健壮性
void delay(u16 i)
{
while(i--);
}
int main()
{
while(1)
{
LED = 0;//将其设置为低电平,使电路导通
LED = 1;将其设置为高电平,使电路截止
delay(50000);
}
return 0;
}
实物效果展示:
可以看到,眼睛并没有看见灯亮,这是因为在设置完D1 IO口为高电平后延时了一段0.5s,所以我们可以看到灭的效果,但是延时之后执行LED= 0;LED = 1
,由于没有延时,所以我们还没有察觉到灯亮,它就已经熄灭了。
相信现在你应该可以很好的理解LED闪烁的原理了,那如果我们想控制所有的灯点亮闪烁该怎么办呢?你可以这样去写:
#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
#define LED P2//将P2口宏定义为LED,增强代码可读性和健壮性
void delay(u16 i)
{
while(i--);
}
int main()
{
while(1)
{
LED = 0;//将其设置为低电平,使电路导通
delay(50000);
LED = 0xff;将其设置为高电平,使电路截止
delay(50000);
}
return 0;
}
实物效果演示:
可能又有小伙伴有疑惑了,这个0我可以理解,但是你这个0xff
是什么意思呢?P20~P27对应8个小的IO口,一共有8个bit位,把他们都设置为1就是,1111 1111,表示为16进制就是0xff。
如果你不想用十六进制来赋值你还可以使用十进制,但是必须先用计算器把这个值算出来,0xff十进制表示是255。 那样的话代码也可以改成这样:
#include<reg52.h>
typedef unsigned int u16;
typedef unsigned char u8;
#define LED P2//将P2口宏定义为LED,增强代码可读性和健壮性
void delay(u16 i)
{
while(i--);
}
int main()
{
while(1)
{
LED = 0;//将其设置为低电平,使电路导通
delay(50000);
LED = 255;将其设置为高电平,使电路截止
delay(50000);
}
return 0;
}
📞点亮LED流水灯的原理
流水灯,顾名思义,就是这些灯看起来像在流水一样,我们的板子里面有八个灯,我们可以设计一个程序让他们依次点亮,但是记得延时,如果没有延时那就是全亮了,因为流动的太快了,就好像他们都一直亮着一样。
我们先来认识两个函数:
🤠循环左移函数_crol_()
这个函数的功能是把一个变量大小的二进制代码进行循环左移,举个例子:
这个函数有两个参数,第一个参数是需要进行循环左移变量,第二参数是循环左移的次数。
🤠循环右移函数_cror_()
这个函数的功能是把一个变量大小的二进制代码进行循环右移,参数和上面一个函数类似。
这个函数的定义在头文件"intrins.h"
中,我们在调用这两个函数时需要加这个#include"intrins.h"
,双引号可以换为尖括号。
流水灯程序设计思路:
🤠 LED流水灯
#include"reg52.h"
#include"intrins.h"
typedef unsigned int u16;
typedef unsigned int u8;
#define led P2
void delay(u16 i)
{
while(i--);
}
int main()
{
u8 i;
led = 0xfe;//1111 1110
while(1)
{
led = 0xfe;
delay(25000);//0.25s
for(i = 0;i < 7;++i)
{
led = _crol_(led,1); //0xfe 0x1111 1110 -> 0x1111 1011 0x1110 1111 0x1011 1111
delay(25000);//0.25s
}
for(i = 0;i < 6;++i)
{
led = _cror_(led,1); //0xfe 0x1111 1110 -> 0x1111 1011 0x1110 1111 0x1011 1111
delay(25000);//0.25s
}
}
return 0;
}
实物演示效果:
可能有小伙伴有一些疑惑,为什么第二个循环右移只循环了6次呢?也就是最后一次执行完是0x11111101,这是因为最后一次和初始化重复了,如果执行7次这次D1亮的时间就变成了了0.5秒,和预期不符。当然你也可以通过if条件语句来控制,让其执行7次,效果却不受影响。
⭕总结
这篇博客主要谈到了使用51控制LED的一些状态,如果你有兴趣的话,可以利用proteus仿真,做一个爱心的流水灯送给你的女神哦(开玩笑的)。另外,若本篇博客有任何问题欢迎指出。下面是本篇博客的思维导图: