3
收藏
微博
微信
复制链接

单片机流水灯与呼吸灯结合-滴水灯程序及详细教程

提问于
2024-01-09 08:58

TWAS手把手教你做呼吸灯-基于51单片机学习板
内容比较简单,发这个贴主要是针对新手!
什么是呼吸灯?
    顾名思义,灯光在微电脑控制之下完成由亮到暗的逐渐变化,感觉像是在呼吸。用专业的话来说是通过控制PWM的占空比来完成对LED亮度的控制
什么是PWM和占空比?
    脉冲宽度调制(Pulse Width Modulation,简称PWM),是利用微处理器的数字输出来对模拟电路进行控制的一种技术。
    占空比:高电平在一个周期之内所占的时间比率。
呼吸灯原理
    当一颗LED在高速闪烁,闪烁的频率已经超过了人眼的感知的范围,那么我们看到这颗LED就是一直亮的,也就是视觉暂留现象(余晖效应)。如果

我们控制一次闪烁中亮和灭的时间(修改占空比),就可以控制亮度。
    代码实现:
/*******************************************************
*  程序名称:main.c
*  程序功能:实现呼吸灯的主程序文件
*  程序作者:TWAS
*  创建时间:2015-1-22
*  修改时间:
*  程序版本:V0.1
*******************************************************/


/* 包含的头文件 */
#include


/* 寻址变量定义 */
sbit LED_Drive = P3^5;         /* 定义驱动LED的IO口,LED为共阳 */




/******************************************************
* 函数名称:main
* 函数功能:主函数
* 入口参数:NULL
* 出口参数:NULL
*******************************************************/
int main()
{
    unsigned char i;
    /* 初始化 */
        LED_Drive = 1;


        /* 主循环 */
        while(1)
        {
            for(i = 0; i < 200; i++)
                {


                    /* 外边的for循环共循环200次,前面10次点亮LED,后面180次熄灭LED
                         * 通过修改if后面的值,就可以改变占空比
                         */
                        if(i < 10)
                        {
                                LED_Drive = 0;
                        }
                        else
                        {
                                LED_Drive = 1;
                        }
             }   
        }


        return 0;
}


/******************************************
*                      程序结束
*****************************************/

可以明显看出我们所控制的LED比电源灯暗许多,既然我们会控制亮度,想实现呼吸灯也就变的简单了
    代码实现:(为了节约空间和界面简洁,只贴出主要实现的部分)
int main()
{
   
    unsigned char         i;
        unsigned char ucNum = 0;                 /* 新增两个变量,ucNum控制占空比*/
        bit            bAdd = 1;                 /* bAdd选择是增大占空比还是减小占空比 */
    /* 初始化 */
        LED_Drive = 1;


        /* 主循环 */
        while(1)
        {
            
            for(i = 0; i < 200; i++)
                {


                    /* 外边的for循环共循环200次,前面10次点亮LED,后面180次熄灭LED
                         * 通过修改if后面的值,就可以改变占空比
                         */
                        if(i < ucNum)
                        {
                                LED_Drive = 0;
                        }
                        else
                        {
                                LED_Drive = 1;
                        }
             }
                 /* 选择是增大占空比还是减小占空比 */
                 if (1 == bAdd)
                 {
                     ucNum++;
                 }
                 else
                 {
                     ucNum--;
                 }
                 
                 /* 当Num等于200也就是最大值时,bAdd置0,Num开始减小 */
                 if (200 == ucNum)
                 {
                     bAdd = 0;
                 }
                 /* 当Num等于200也就是最大值时,bAdd置1,Num开始增大 */
                 else if (0 == ucNum)
                 {
                     bAdd = 1;
                 }
                    
        }


        return 0;
}
     由于图片看不到效果,这个地方就不贴图了,根据测试,我们的所需要的功能实现了!
但是这时候有的人就有疑问了,这是很普通的LED,那如果是特殊一点的呢?比如我所用的
学习板上面,16颗LED是用595驱动的,那呼吸灯又该如何实现呢?
    其实很简单,我们把驱动LED的函数封装一下,直接替换,其它不变就行了!
    代码实现:(为了节约空间和界面简洁,只贴出主要实现的部分)
for(i = 0; i < 200; i++)
{


    /* 外边的for循环共循环200次,前面10次点亮LED,后面180次熄灭LED
         * 通过修改if后面的值,就可以改变占空比
         */
        if(i < ucNum)
        {
                DriveLED(0x0003);   /* LED驱动,点亮D1和D2 */
        }
        else
        {
                DriveLED(0x0002);   /* LED驱动,熄灭D1点亮D2 */
        }
}
     当我把程序改成这样的时候,出了一点问题,不能呼吸,变成闪烁了!等等,先把LED驱动部分发一下:

/******************************************************
* 函数名称:SendData
* 函数功能:74HC595数据的发送
* 入口参数:unsigned int uiDataOne, unsigned int uiDataTwo
* 出口参数:void
*******************************************************/
void SendData(unsigned char ucDataOne, unsigned char ucDataTwo)
{
         
    unsigned int i = 0;
        
          /* 将片选信号置为低电平 */
          HC595RCK = 0;
        
          /* 输入第一个数据:uiDataOne */
          for (i = 0; i < 8; i++)
          {
        /* 给出脉冲信号,首先将CLK置为0 */
                          HC595CLK = 0;
                          if (0 != (ucDataOne & 0x80))
                                {
            HC595DATA = 1;   
        }
                                else
                                {
            HC595DATA = 0;
        }
                                /* 给出脉冲信号,首先将CLK置为1 */
                                HC595CLK = 1;
                                /* 准备第二个数据 */
                                ucDataOne = ucDataOne << 1;
    }
               
          /* 输入第二个数据:uiDataTwo */
          for (i = 0; i < 8; i++)
          {
        /* 给出脉冲信号,首先将CLK置为0 */
                          HC595CLK = 0;
                          if (0 != (ucDataTwo & 0x80))
                                {
            HC595DATA = 1;   
        }
                                else
                                {
            HC595DATA = 0;
        }
                                /* 给出脉冲信号,首先将CLK置为1 */
                                HC595CLK = 1;
                                /* 准备第二个数据 */
                                ucDataTwo = ucDataTwo << 1;
    }
               
                /* 将片选信号置为高电平 */
          HC595RCK = 1;
}


/********************************************************
*  函数名称:DriveLED
*  函数功能:595驱动程序是分两个数据发的,
*            本函数把它合并成一个数据
*  入口参数:uiData:16颗LED需要显示的数据
*  出口参数:NULL
*******************************************************/
void DriveLED(unsigned int uiData)
{
    SendData(uiData >> 8,uiData);
}

     继续刚刚的问题,呼吸灯变成闪烁了,怎么回事呢?在程序逻辑上是没有问题的,
我们只是更改了LED的驱动部分,看来就是LED驱动的问题了!先来Debug看一下,执行
DriveLED这个函数,时间是接近400微妙,循环200次,就是差不多80毫秒,那么LED的
闪烁频率为12.5HZ(都是大概的值,没有精确计算),还不足以形成视觉暂留现象。
那怎么办呢,减少循环次数为50,频率增大到50HZ左右,像这样:
for(i = 0; i < 50; i++)
{



         /* 通过修改ucNum的值,就可以改变占空比 */
        if(i < ucNum)
        {
                DriveLED(0x0003);   /* LED驱动,点亮D1和D2 */
        }
        else
        {
                DriveLED(0x0002);   /* LED驱动,熄灭D1点亮D2 */
        }
}
经过验证!成功了!
如有错误的地方,希望大家批评指正!

收藏 278 0 3
粉丝
0
关注
0
主题
5
学习的是一种思维的方法,感觉挺好,收藏待练习
0

回答于 · 2024-01-10 15:34

粉丝
0
关注
0
主题
6
刚刚验证了一下,确实可以实现,楼主厉害
0

回答于 · 2024-01-10 21:03

粉丝
0
关注
0
主题
4
楼主我也遇到麻烦为啥用PWM。调光的呼吸灯的程序变成流水灯程序的一个函数调用的形式。结果只有流水没有呼吸。
求救大佬:'(
#include"reg52.h"
#include
typedef unsigned int u16;
typedef unsigned char u8;


sbit led1=P2;
int PWM_Low,Clock=500;
#define led P2
void delay(u16 a)
{while(a--);
}


           void main1(int t)
           {       
                          
                             for(PWM_Low=Clock;PWM_Low>0;PWM_Low--)
                                  {t=1;
                                delay(PWM_Low);
                                {int b;
for (b=0;b                                 t=0;
                                delay(Clock-PWM_Low);
                                        {int b;
for (b=0;b        
                                }        }
                                 
void main()
{u8 i,t,t1,t2;
t1=0xfe;
t2=0x7f;
for(i=0;i<4;i++)
{t=t1&t2;
led=t;
main1(t);
delay(50000);

t1=_crol_(t1,1);
t2=_cror_(t2,1);}  }
0

回答于 · 2024-01-11 17:15