【说在前面的话】
单片机技术是现代工业自动化、电子电气及物联网等的一门必不可少的主流技术。随着人们生活智能化的提高,单片机技术也几乎融入了我们生活的各个角落,比如智能电饭煲、智能音箱、等等。
由此,《重学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;voidLED_ON(){//LED亮LED1 =1; }voidLED_OFF(){//LED灭LED1 =0; }
点亮LED灯的程序还是很简单,相信大家都会。
调节LED亮度
接下来我们就实现一个可以调节亮度的函数(即调节占空比),如下
//调节LED亮度voidset_led_luminance(unsignedintpwm_duty_cycle){staticunsignedints_Counter =0;//计时//调节占空比if(pwm_duty_cycle >= s_Counter) {LED_ON(); }else{LED_OFF(); }//计数器开始计时s_Counter++;if(s_Counter >255){//40ms时间到,清零重新计时s_Counter =0; } }voidmain(){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就可以了,如下
voidbreath_led(void){staticint duty_cycle =0;staticchar 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 <0){//小于0开始增加duty_cycle =0; flag =1; } } }voidmain(){while(1){ breath_led(); } }
- 定义一个静态变量duty_cycle来保存占空比,当flag为1时,占空比慢慢增加到255,然后把flag设置成0 ,duty_cycle再从255慢慢减为0,如此循环就可以了。
哈哈,到这里你是不是觉得呼吸灯已经实现了,然而,并不是。不相信的同学可以自己试试上面的代码哦。
那到底是哪里有问题呢?
这就是我们直接调用设置亮度的函数set_led_luminance()是有问题的,因为此函数执行一个周期需要40ms,也就是在这40ms期间是不可以改变占空比的,否则可就调不了亮度了。再来看看breath_led函数,每次调用完set_led_luminance()函数设置占空比后就立即改变了duty_cycle的值,并没有延时40ms。
此时,我们还需要再加一个软件定时器,定时超过40ms后修改duty_cycle的值,修改后的程序如下
voidbreath_led(void){staticunsignedints_breathCounter =0;staticintduty_cycle =0;static