【51单片机实验笔记】1. LED的初级控制
目录
- 前言
- 硬件介绍
- 单色LED
- 封装形式
- 正负极判断
- 双色LED
- 三色LED
- 七彩自动闪烁LED(内置IC)
- 原理图分析
- 软件实现
- 点亮一颗LED
- 方法一
- 方法二
- LED闪烁
- 流水灯
- 跑马灯
- 方法一
- 方法二
- 方法三
- LED常用函数封装
- 总结
前言
本节内容我们学习如何控制一颗LED灯,并简单控制它的行为:亮、灭、闪烁。并对多个LED实现流水灯、跑马灯效果。除此之外,了解一些多色LED的内部构造及驱动方式。
硬件介绍
单色LED
LED全称Light Emitting Diode
,即发光二极管。其特点是功耗低、高亮度、抗振动、低光衰,属于冷光源。
- 响应时间短。LED 灯的响应时间为纳秒级,而白炽灯的响应时间要达到毫秒级。
- 发光色谱宽。例如:砷化镓二极管发红光,磷化镓二极管发绿光,碳化硅二极管发黄光,氮化镓二极管发蓝光。
- 驱动电压低。通常工作电压为1.6-2.2V,电流在5-20mA之间,亮度随电流变化。
封装形式
一般LED有两种封装:直插式和贴片式。
正负极判断
如何判断LED的正负极?仔细观察。
- 直插式LED
- 长脚为正极,短脚为负极
- 大电极片为负极,小电极片为正极
- 贴片式LED
- 三角形顶点所对的为负极,底边所对的为正极
双色LED
顾名思义,可以发出2种颜色的灯光,一般为红、绿两种颜色。本质是将两个LED封装在一起,每次单独点亮其中一个,达到双色显示的目的。
图1 透明双色LED | 图2 雾状双色LED | 图3 原理图 |
直插式的双色LED由三个引脚组成,中间的引脚为公共端口,有共阴极和共阳极两种。可以观察中间引脚的电极片,如果是大电极,则为负极,即为共阴极。
三色LED
与双色LED类似,三色LED有4个引脚,分别为GND
、R
、G
、B
,控制相应引脚即可发出红、绿、蓝三种颜色的灯光。
七彩自动闪烁LED(内置IC)
共2个引脚,从表面看和单色LED一致,但实际上电后可以自动转换7种颜色。每一个小小的灯里都内置IC,成本贵于普通LED,但控制方便,稳定。
原理图分析
LED的电路符号如下:
最大的特点即具有单向导通性。由于单颗LED的驱动电流比较小,过大的电流会烧毁LED,故一般在电路中串联阻值100Ω左右的电阻,称为限流电阻。
由于开发板中已经帮我们焊好了外设和电路,所以我们不需要自己搭建电路,但需要去查看电路原理图(各个开发板不一样,但这没有关系),找到它所对应的控制引脚,这有利于我们的后续编程。
在我的开发板中,可以看到他设计的是所有 LED 共阳极(即 LED 的阳极接在一起),故当对应 IO 引脚置低电平时 LED 发光,高电平时 LED 熄灭。
软件实现
点亮一颗LED
方法一
#include <REGX52.H>
#define LED_PORT P2void main(){//法一:直接定义整个P2口的电平LED_PORT = 0xfe; //1111 1110 D1灯点亮while(1); //使程序在这里死循环。
}
首先引入头文件 “REGX52.H”
,这是C51的常用头文件,里面定义了各种寄存器,方便我们操作硬件。这里对P2口使用宏定义是需要注意的编程习惯,因为单片机的P0~P3口在不同的硬件电路设计中连接的外设并不相同,并且,通用的引脚名并不能让阅读者立马知晓它的作用,利用宏定义可以增强代码的可读性和可维护性。以后的代码中,我都会采用这样的代码风格。
其次,由原理图可知,8颗LED对应P2口的8位引脚,故若要使D1灯点亮,则需要将P2.0置低电平,其余全部置高电平,即1111 1110
,常用16进制表示,即0xfe
。
最后,我们希望灯点亮之后保持这样的状态,而不是反复被点亮,所以应使程序一直停滞在main函数中,故采用一个死循环实现这样的目的。当然,如果没有写这样的死循环,你会发现实验现象并没有改变。但事实情况是,当执行完赋值语句后,程序会退出main函数,随后会不断的反复执行main函数,P2.0也会反复的置低电平,这当然不会有问题,但当任务复杂时,反复执行main函数会产生意想不到的错误。
方法二
#include <REGX52.H>
#define LED_PORT P2
//法二:定义P2口需要控制的位,并赋值电平
sbit LED_1 = LED_PORT^0; //D1灯void main(){ //赋值低电平LED_1 = 0;while(1); //使程序在这里死循环。
}
在方法一中,我们定义了8个引脚的电平情况,但事实上这是比较费事的。因为我们P2口内部有上拉电阻,即引脚默认输出高电平,我们实际只需要将一个引脚置低电平即可。故方法二中,我们先定义了需要控制的引脚位,再进行赋值。
注意:
sbit
是C51扩展的标识符,而非数据类型,sbit
声明的部分是编译器预处理的部分,是在函数没有编译之前必须完成的,所以必须写在main函数外。且不能写进数组或是结构体中循环调用。- 在位定义时,
^
代表的是位的位置,而不是异或运算符
LED闪烁
#include <REGX52.H>
#define LED_PORT P2
//typedef可以将一些复杂的关键字重命名
typedef unsigned char u8; //0-255,1字节
typedef unsigned int u16; //0-65535,2字节void main(){//申明延时函数void delay(u16 msec);while(1){LED_PORT = 0xfe; // 1111 1110delay(50000); //大约450msLED_PORT = 0xff; // 1111 1111delay(50000); }
}//延时函数 大致10微秒
void delay(u16 msec){while(msec--);
}
所有视频、动画、游戏的理论基础都是人眼的暂留效应。一般暂留时间大约为50ms,即一秒钟20帧以上的画面就会产生动态的视觉效果。单片机的晶振频率为12MHz,计算其机器周期大约为1微秒,也就是说,两个连续的语句执行时间间隔相当短暂,以致于人眼根本无法分别LED灯的实际状态。
为了能够看到闪烁的效果,必须在点亮
和熄灭
的语句之间加入延时函数。延时函数的本质即让运行中的程序暂停一段时间(CPU空转一段时间)。经过Keil的软件仿真,50000次自减循环大致消耗450ms,这个时间间隔足以让人眼观察到灯的熄灭和点亮了。
当然你也可以自己去计算精确的延时时间,但这在本节中意义不大,我们仅需观察到闪烁现象即可。精确的延时需要配合定时器实现。
流水灯
我理解的流水灯是这种累加的效果
具体代码实现
#include <REGX52.H>
#define LED_PORT P2typedef unsigned char u8;
typedef unsigned int u16;void delay(u16 t){while(t--);
}void main(){while(1){u8 i;LED_PORT = 0xfe; //1111 1110delay(50000);for(i=0;i<8;i++){LED_PORT <<= 1; //左移一位delay(50000); //延时450ms}}
}
实现的关键是左移运算符<<
,每往左移1位,最低位补0,即表现为LED逐一点亮的效果。
跑马灯
跑马灯应该是单个灯循环跑动的效果
方法一
#include <REGX52.H>
#define LED_PORT P2
#define DELAY_TIME 20000 //设置跑马灯时间间隔typedef unsigned char u8;
typedef unsigned int u16;void delay(u16 sec){while(sec--);
}//法一:列举法,流水灯一共8种状态。
void ledTest_1(){LED_PORT = 0xfe; // 1111 1110delay(DELAY_TIME);LED_PORT = 0xfd; // 1111 1101delay(DELAY_TIME);LED_PORT = 0xfb; // 1111 1011delay(DELAY_TIME);LED_PORT = 0xf7; // 1111 0111delay(DELAY_TIME);LED_PORT = 0xef; // 1110 1111delay(DELAY_TIME);LED_PORT = 0xdf; // 1101 1111delay(DELAY_TIME);LED_PORT = 0xbf; // 1011 1111delay(DELAY_TIME);LED_PORT = 0x7f; // 0111 1111delay(DELAY_TIME);
}void main(){while(1){ledTest_1();}
}
方法一的思路就是把所有情况对应的十六进制都写出来,总共也8种状态,写呗。但缺陷也很明显,当LED数量多了之后,这种代码写起来就很痛苦了。
方法二
#include <REGX52.H>
#define LED_PORT P2
#define DELAY_TIME 20000 //设置跑马灯时间间隔typedef unsigned char u8;
typedef unsigned int u16;void delay(u16 sec){while(sec--);
}//法二:使用左移和取反运算,配合循环实现
void ledTest_2(){int i;for(i=0;i<8;i++){LED_PORT = ~(0x01<<i); //将1左移i位后补0,取反,即第i+1位灯亮delay(50000); //延迟450ms}
}void main(){while(1){ledTest_2();}
}
方法二的思路是,用一个循环实现每次将 0 移位。但直接应用左移运算符<<
会存在一个问题:移位后低位自动补 0 ,这样低位的灯也会被点亮了。那怎么办?正面算法行不通,就尝试从反面去实现,即用一个循环实现每次将 1 移位,再将它按位取反(~
)即可。
注:当我们只想改变某一位的状态而不改变其他位的状态时,这是一个非常常用的位操作技巧。
// 指定位, 置1
port |= 0x01<<i
// 指定位, 置0
port &= ~(0x01<<i)
方法三
#include <REGX52.H>
#include <INTRINS.H> // 定义了移位函数
#define LED_PORT P2
#define DELAY_TIME 20000 //设置跑马灯时间间隔typedef unsigned char u8;
typedef unsigned int u16;void delay(u16 sec){while(sec--);
}//法三:使用左移函数_crol_()
void ledTest_3(){LED_PORT = _crol_(LED_PORT, 1); //左移1位(跟左移运算符不同,高位循环补至低位)delay(50000); //延迟450ms
}void main(){//法三:先初始化P2LED_PORT = 0xfe;delay(50000); //延迟450mswhile(1){ledTest_3();}
}
方法三的思路是调用C51中定义好的函数实现。首先引入头文件INTRINS.H
,然后就可以使用左移函数_crol_()
。当然也有相应的右移 函数_cror_()
LED常用函数封装
在复杂场景中,LED仅仅作为一个小模块来配合整个项目(比如指示灯)。每次对硬件编程是耗时耗力的。基于模块化和硬件抽象的思想,有必要将LED的功能封装,像系统库函数或是应用软件的 API 一样,对外提供接口,直接调用,这样方便以后快速构建项目。
delay.h
#ifndef _DELAY_H_
#define _DELAY_H_#include <regx52.h>typedef unsigned char u8;
typedef unsigned int u16;void delay_10us(u16);
void delay_ms(u16);#endif
delay.c
#include "delay.h"/*** @brief 延时函数(10us)* @param t:0~65535,循环一次约10us* @retval 无*/
void delay_10us(u16 t){while(t--);
}/*** @brief 延时函数(ms)* @param t:0~65535,单位ms* @retval 无*/
void delay_ms(u16 t){while(t--){delay_10us(100);}
}
led.h
#ifndef _LED_H_
#define _LED_H_#include "delay.h"void led_on(u8);
void led_stream(u16);
void led_run(u16);#endif
led.c
#include "led.h"// 定义led引脚
#define LED_PORT P2/*** @brief 指定某个LED亮* @param pos: 位置* @retval 无*/
void led_on(u8 pos){LED_PORT &= ~(0x01<<(pos-1));
}/*** @brief 指定某个LED灭* @param pos: 位置* @retval 无*/
void led_off(u8 pos){LED_PORT |= 0x01<<(pos-1);
}/*** @brief LED流水灯* @param time 延时时间* @retval 无*/
void led_stream(u16 time){u8 i;for(i=0;i<8;i++){led_on(i+1);delay_10us(time);}// 全部熄灭for(i=0;i<8;i++){led_off(i+1);}
}/*** @brief LED跑马灯* @param time 延时时间* @retval 无*/
void led_run(u16 time){u8 i;for(i=0;i<8;i++){led_on(i+1);delay_10us(time);led_off(i+1);}
}
main.c
#include "led.h"
/*** @brief 仅提供一个demo* @author Qiu* @date 2023.4.13*/void main(){// 点亮一颗LED//led_on(2); while(1){// 流水灯//led_stream(50000); // 跑马灯led_run(50000); }
}
多文件调用为C语言内容,本文不再赘述。我只写了几种LED显示函数,各位童鞋可以自行扩充,改写。
总结
单片机是软件和硬件的桥梁,这跟C语言在编程语言中的地位类似。一般是先了解硬件的原理和硬件电路图之后,才开始软件的算法编程,实现相应的效果。路还很长,共勉。
【51单片机实验笔记】1. LED的初级控制相关推荐
- 【51单片机实验笔记】3. LED点阵的基本控制
目录 前言 硬件介绍 驱动芯片 74HC595芯片 MAX7219芯片 原理分析 软件实现 爱心图片 旋转大风车 滚动日期 螺旋线动画 LED点阵功能函数封装 总结 前言 本章接触的硬件依然与LED息 ...
- AutoLeaders控制组—51单片机学习笔记(LED控制、独立按键、数码管)
本篇内容是观看B站江科大自化协UP主的教学视频所做的笔记,对其中内容有所引用,并结合自己的单片机板块进行了更改调整. 以下笔记内容以一个视频为一个片段(内容较多,可能不适合速食,望见谅) 根据测试,目 ...
- [51单片机学习笔记ONE]-----LED灯的多种使用方法
一.交替闪烁8个LED灯,时间间隔为1s 1 /****************************************************** 2 实验名称: 交替闪烁8个LED灯,时间 ...
- 【51单片机实验笔记】2. 数码管的基本控制
目录 前言 硬件介绍 原理图分析 段选和位选 驱动芯片 74HC138芯片 74HC245芯片 软件实现 点亮一只数码管 倒计时效果 动态显示字符 总结 前言 本节内容我们学习如何控制数码管,先尝试点 ...
- 【51单片机实验笔记】4. 蜂鸣器与扬声器的基本控制
目录 前言 硬件介绍 PWM基础 蜂鸣器简介 扬声器简介 原理图分析 蜂鸣器驱动电路 扬声器驱动电路 软件实现 蜂鸣器短鸣 蜂鸣器功能封装 总结 前言 蜂鸣器和扬声器在生活中的应用实则相当广泛.通过本 ...
- 51单片机实验——LED小灯的操作(一)
目录 引言 led模块原理图解析 led小灯的操作 1.点亮led小灯 位操作法 总线操作法 2.闪烁led小灯 位操作法 总线操作法 3.led流水灯 位操作法 总线操作法 总线操作-数组循环遍历法 ...
- 10.4.4 51单片机控制系统8个LED“跑马灯”实验
10.4.4 51单片机控制系统8个LED"跑马灯"实验 仿真+代码 方法一 利用数组 #include"reg52.h"#define u8 unsigned ...
- 51单片机实验 LED半秒闪烁 定时器0方式2 中断实现
51单片机实验 LED半秒闪烁 定时器0方式2 中断实现 实验目的: P1.0接一个发光管,实现亮半秒灭半秒, 要求使用定时器0工作方式2,中断实现. C语言代码: #include "at ...
- 基于51单片机的汇编/C程序:拨码开关控制八位LED实现流水灯
基于51单片机的汇编程序:拨码开关控制八位LED实现流水灯 2022-03-08,51单片机学习笔记 功能 打开K1循环左移,打开K2循环右移,打开K3两侧到中心来回点亮,打开K4单双数位置交替点亮. ...
最新文章
- IIS 7 应用程序池自动回收关闭的解决方案
- std string与线程安全_详解linux系统中断线程的那些事
- 门户网站建设与运营需要付出更多成本
- leetcode 293.Flip Game(lintcode 914) 、294.Flip Game II(lintcode 913)
- linux sed给空文件首行插入_linux下批量修改文件后缀名以及合并多行
- Android开发之--Preferences的使用
- Intel Realsense C/C++ 转 python (9)rs-multicam 多摄像头可视化窗体显示
- LINQ系列:LINQ to SQL Join连接
- 用python排序算法_Python - 八大排序算法
- 在Java里面使用instanceof的性能影响
- 前端学习(3246):react的生命周期getSnap
- ES6_类_note
- vscode remote免密登录
- impacket安装 python_安装impacket
- linux怎么开启httpd服务公钥,在Apache httpd服务器上部署SSL证书
- 口布杯花的60种叠法_10种餐巾折花杯花的步骤用文字解说怎么折
- iOS开发实例 | Demo:数独小游戏
- Google网络硬盘GDrive在几个月内即将成真?!
- nuc972 linux 升级,NUC972移植工作记录
- uniapp中使用网页录音并上传声音文件(发语音)——js-audio-recorder的使用【伸手党福利】
热门文章
- 使用Grabit自定义SQL收集metadata
- 【YOLOv7】结合GradCAM热力图可视化
- 关于RFID电感耦合方式的射频前端工作原理,你了解吗?
- 关于三子棋游戏的简易实现与N子棋胜利判断方法
- 陈庆平获评2021年湖南省“最美科技工作者”
- ch376inc.h
- pc端启用微信小程序
- html怎么移动按钮位置,CSS Nav按钮向左下方移动(CSS Nav buttons move bottom left)
- linux模拟树莓派,使用QEMU模拟树莓派Raspberry Pi
- Redis集群Hash槽分配异常 CLUSTERDOWN Hash slot not served的解决方式