使用51单片机实现LED呼吸灯

【说在前面的话】
单片机技术是现代工业自动化、电子电气及物联网等的一门必不可少的主流技术。随着人们生活智能化的提高,单片机技术也几乎融入了我们生活的各个角落,比如智能电饭煲、智能音箱、等等。
由此,《 重学51单片机 》系列文章意在帮助初学者入门单片机技术。我们会从最简单的点亮一个灯开始,一步一步实现按键、lcd1602、ds18b20、ds1302、双机通信等模块,同时,还会讲一些硬件通信协议,比如uart、iic、spi等。并结合c语言的编程技巧,以实际的工程项目来给大家讲解编程思路,让大家灵活运用c语言的指针与结构体,实现编程模块化。
言归正传,接下来我们就开始进入今天的主题,用51单片机控制led实现呼吸灯的效果。
【呼吸灯原理】
我们先看一下呼吸灯的效果下
呼吸灯就是先渐渐变亮再渐渐变暗,如此循环就像呼吸一样。可是单片机的管脚要么输出1(亮)要么输出0(灭),怎么会有渐变的效果呢?
这就和我们的眼睛 观看图像会有滞留时间引起的 。当我们在看东西时,眼睛成像后会滞留0.04s(这个标准是网上找的)。
我们按照0.04s计算,就等于40ms,也就是亮灭都是20ms时,看到的led就是一直在亮。如下图:
那led 20ms亮20ms灭和一直常亮的效果一样吗?
哈哈,肯定是不一样的。交替20ms亮20ms灭我们看到的效果要比一直常亮的效果暗。我们假设一直常亮的亮度为100%,那么交替20ms亮20ms灭的亮度就是50%,基于此, 我们就可以调节led的亮度了 。如下图
到此,我们就可以调节led灯的亮度了(就是40ms内设置高电平的持续时间),这个就是大名鼎鼎的pwm调节亮度的原理了,而设置高电平的持续时间就是调节 占空比 (即高电平的时间除以周期数:20/40=50%)。
这里,我们 最重要的还是这个占空比, 比如周期是20ms,交替10ms亮10ms灭,我们看到的亮度还是50%(即占空比为10/20=50%)
接下来我们就看看程序怎么实现吧。
【程序实现】
点亮一个led
首先,我们先从点亮一个led灯开始,然后再一步一步实现一个呼吸灯的效果。我们使用的硬件如下:
开发板零壹单片机培训开发板
单片机型号 stc89c52
led接口 p4^4脚
由原理图我们知道,led灯接到了单片机的p4^4脚,单片机输出1,led亮,输出0,led灭。由此,点亮一个led的程序就很简单了,如下
sbit led1 = p4^4;void led_on(){//led亮 led1 = 1;}void led_off(){//led灭 led1 = 0;}点亮led灯的程序还是很简单,相信大家都会。
调节led亮度
接下来我们就实现一个可以调节亮度的函数(即调节占空比),如下
//调节led亮度void set_led_luminance(unsigned int pwm_duty_cycle){ static unsigned int s_counter = 0;//计时 //调节占空比 if (pwm_duty_cycle >= s_counter) { led_on(); } else { led_off(); } //计数器开始计时 s_counter++; if(s_counter > 255){ //40ms时间到,清零重新计时 s_counter = 0; } }void main(){ while(1){ set_led_luminance(128); }}我们定义了一个静态变量s_counter作为 软件定时器 ,s_counter加到255后清零(这里相当于是一个周期的时间40ms,当然不是严格的40ms,只要周期小于40ms我们就看不到闪烁)函数的参数就是我们要调节的占空比,比如传入的是128,占空比为128/255=50%现在有了设置led亮度的函数,那怎么让它渐渐变亮再渐渐变暗,实现呼吸灯的效果呢?
实现呼吸灯
这个也很简单,我们只要给set_led_luminance函数传的参数从0慢慢加到255然后再从255慢慢减到0就可以了,如下
void breath_led(void){ static int duty_cycle = 0; static char flag = 1; //设置亮度 set_led_luminance(duty_cycle); if(flag == 1){//占空比增加 duty_cycle++; if(duty_cycle > 255){//大于255开始减少 duty_cycle = 255; flag = 0; } }else{//占空比减少 duty_cycle--; if(duty_cycle 511){ //计时器设置为0,重新计时 s_counter = 0; if(flag == 1){//占空比增加 duty_cycle++; if(duty_cycle > 255){//大于255开始减少 duty_cycle = 255; flag = 0; } }else{//占空比减少 duty_cycle--; if(duty_cycle (0xff)){ set_led_luminance(duty_cycle - (0xff));}else{ set_led_luminance( (0xff) - duty_cycle );}第3行,0x1ff就是十进制的511,当s_breathcounter的值为(0x1ff+1)即512时条件成立,因为512&0x1ff=0,前面还有个感叹号就是取非运算(准确的说就是s_breathcounter的值为512的倍数时条件成立,这样都省去了s_breathcounter清零的操作了,不知道给大家解释清楚了没,大家要多想想),条件成立则占空比开始增加或减少
那占空比的范围不是0~ 255吗,第5行怎么也变成0x1ff(511)了,别急,你再往下看第8行,我们又减去了0xff,所以占空比的范围还是0~255
第7~ 10行代码的意思为:当duty_cycle > (0xff)时,即256~511,减去0xff,就相当于从1增加到255,亮度渐渐变亮
当duty_cycle <= (0xff)时,即duty_cycle 从0增加到255,而设置的亮度为255 - duty_cycle ,相当于从255降到0,亮度 渐渐变暗 ,这样就实现了呼吸灯的效果。
哈哈,你以为我们的简化就结束了吗?
no,no,no
其实第7到10行还可以更简单一些。此时就需要用到取绝对值的函数了。
什么,用绝对值干嘛?
你看第10行0xff- duty_cycle就相当于duty_cycle - 0xff然后再取绝对值,好了,化简后的代码如下
set_led_luminance(abs(duty_cycle - (0xff)));取绝对值的宏函数如下
#define abs(n) ((n) < 0 ? -(n) : (n))最后我把整个化简后的代码也贴到下面
#define abs(n) ((n) = s_counter) { led1 = 1; } else { led1 = 0; } s_counter++; s_counter = s_counter & 0xff; }void breath_led(void){ static unsigned int s_breathcounter = 0; static int duty_cycle = 0; s_breathcounter++; if (!(s_breathcounter & (0x1ff))) { duty_cycle++; duty_cycle &= 0x1ff; } set_led_luminance(abs(duty_cycle - (0xff)));}void main(){ while(1){ breath_led(); }}怎么样,是不是很简洁(* ̄︶ ̄)
好了,到这里今天的呼吸灯就讲完了,想和我一起重学51单片机的同学记得 点赞+关注, 这会加速我对文章更新的速度哦

svg补偿装置闭锁会影响用电吗
微软Win10 21H2大更新将完善任务栏自定义选项,但未激活版本无法设置
智能音箱发展步入成熟期
小米手环3NFC版上手体验 市场上最值得购入的手环之一
头戴显示器突破!群创推无晕眩新品,预计2019年四季度放量
使用51单片机实现LED呼吸灯
该如何增强电路板上的I/O连接性能
在虚幻引擎4中使用多核最大化视觉效果
皮秒激光器与飞秒激光器之间有何特点差异
分析苹果或与特斯拉、蔚来等合作造车
兽药残留快速检测仪的主要参数
车电分离模式可以一定程度上解决充电和续航痛点
Pixelworks TrueCut平台荣获2019娱乐技术卢米埃尔奖
2018年生物特征识别技术大会上,Herta表现优异进入最高分数行列
城市内涝积水实时监测系统方案
MacOS/iOS Mach-O应用程序代码混淆
蓝光播放器中应用的触摸芯片
燃料电池领域新动态的简单解析
齿轮减速机正常温度多少
什么是“密码散列”?如何正确使用PassGan?