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灯

#includetypedef 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灯吧。

#includetypedef 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

#includetypedef 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.000389000.000389000.00038900秒,我们不需要这个时间,我们只需要执行当前语句的时间,我们点击下图左上角RST将时间归0,下图左边调试窗口sec显示的有程序执行到当前语句的时间,你可以手动归0

此时我们已经成功归0了,接下来执行这条语句。

所以执行这条语句的时间是0.000390.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

看下面代码,试思考其展示结果:

#includetypedef 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

同样的我们在来看这段代码,思考它的实物效果:

#includetypedef 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闪烁的原理了,那如果我们想控制所有的灯点亮闪烁该怎么办呢?你可以这样去写:

#includetypedef unsigned int u16;typedef unsigned char u8;#define LEDP2//将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。 那样的话代码也可以改成这样:

#includetypedef unsigned int u16;typedef unsigned char u8;#define LEDP2//将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 P2void 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 1111delay(25000);//0.25s } for(i = 0;i < 6;++i) {led = _cror_(led,1); //0xfe 0x1111 1110 -> 0x1111 1011 0x1110 1111 0x1011 1111delay(25000);//0.25s } } return 0;}

实物演示效果:

可能有小伙伴有一些疑惑,为什么第二个循环右移只循环了6次呢?也就是最后一次执行完是0x11111101,这是因为最后一次和初始化重复了,如果执行7次这次D1亮的时间就变成了了0.5秒,和预期不符。当然你也可以通过if条件语句来控制,让其执行7次,效果却不受影响。

⭕总结

这篇博客主要谈到了使用51控制LED的一些状态,如果你有兴趣的话,可以利用proteus仿真,做一个爱心的流水灯送给你的女神哦(开玩笑的)。另外,若本篇博客有任何问题欢迎指出。下面是本篇博客的思维导图: