51单片机之LED灯

2023-12-30 18:45:52

在这里插入图片描述

🌴前言:

我们在学习单片机的时候,最好还是以实物的学习为主,因为兴趣是最好的老师,实物的视觉冲突比仿真的效果要更好,每一个系列的开发板由于原理图不同,他们对应的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仿真,做一个爱心的流水灯送给你的女神哦(开玩笑的)。另外,若本篇博客有任何问题欢迎指出。下面是本篇博客的思维导图:

在这里插入图片描述

文章来源:https://blog.csdn.net/wszhj_/article/details/135275683
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。