51单片机之LED灯

51单片机之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灯

c 复制代码
#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灯吧。

c 复制代码
#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

c 复制代码
#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大概是,人眼是不可能看到这个变化的过程的,因为实在是太快了!

我们想解决这个问题,就需要自己写一个延时函数

c 复制代码
void delay(u16 i)
{
  while(i--);
}

i为1的时候大概是延时了1us,大家可以自己去调试。

🏓正确的LED闪烁代码应该是这样:

c 复制代码
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

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

c 复制代码
#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

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

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

c 复制代码
#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。 那样的话代码也可以改成这样:

c 复制代码
#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流水灯

c 复制代码
#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仿真,做一个爱心的流水灯送给你的女神哦(开玩笑的)。另外,若本篇博客有任何问题欢迎指出。下面是本篇博客的思维导图:

相关推荐
Navigator_Z5 小时前
LeetCode //C - 1288. Remove Covered Intervals
c语言·算法·leetcode
创界工坊工作室6 小时前
22-基于STM32的汽车防盗系统设计
stm32·单片机·嵌入式硬件·proteus
书生执笔画浮沉6 小时前
分享一个开源小工具CAdapt
linux·c·gnu·glibc·kernel·升级·灵感
铅笔小新z7 小时前
【C语言】深入理解指针
c语言·开发语言
雨田言炎7 小时前
STM32之RCC系统时钟控制(芯片的心脏控制器)
stm32·单片机·嵌入式硬件
李永奉8 小时前
日常使用的锂电池上,新、旧锂电池的差异分析
单片机·嵌入式硬件·物联网·智能手机·电脑
额额额对了8 小时前
IMX6ULL 杂项设备 LED 驱动
stm32·单片机·嵌入式硬件
长春的KAKA9 小时前
Proxmark3 Iceman分支CLI命令总表
嵌入式硬件
半杯咖啡半行码10 小时前
STM32开发之IIC通信详解:从协议基础到FM24CL04实战
stm32·单片机·嵌入式硬件
硅基手札10 小时前
【I2C 技术系列 05】I2C 多主仲裁与时钟同步
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设