重庆-风雪 发表于 2015-1-22 22:46:22

TWAS手把手教你做呼吸灯-基于朱兆琪51学习板

本帖最后由 重庆-风雪 于 2015-1-26 22:16 编辑


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

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


/* 包含的头文件 */
#include <reg52.h>


/* 寻址变量定义 */
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 */
      }
}
经过验证!成功了!
如有错误的地方,希望大家批评指正!

jianhong_wu 发表于 2015-1-22 23:06:06

有图有代码,绝对是精品。{:soso_e179:}

重庆-风雪 发表于 2015-1-23 00:46:45

本帖最后由 重庆-风雪 于 2015-1-26 22:26 编辑

流水灯与呼吸灯结合-滴水灯
--基于朱兆琪51学习板
何为滴水灯?
模拟水滴形成并且滴落下
模拟过程
水滴形成:LED亮度从暗到亮
滴落过程:普通流水灯带拖尾效果
何为空间替换时间?
用空间替换时间,某些数据运算的时候会占用CUP和时间,我们可以在之前把这些结果写出来,到时候直接读取就是了,而且还增加了可读性,因为不用去看计算的过程。
滴水灯如何实现
水滴形成的过程跟呼吸灯一样,所以不多做概述。
我们直接看流水灯带拖尾效果,这时候我们需要做的就是同时让几颗
LED显示不同的亮度,也就是我们要同时输出几组不同的占空比。先来
看看这样输出八组不同的占空比行不行:
/************ 主循环部分 *****************/


code unsigned char LightLevel={0,1,2,4,8,16,32,64};//定义8个亮度级别
unsigned char i = 0;
unsigned char j = 0;
unsigned char k = 0;
unsigned char temp = 0xFF;
while(1)
{
      for(i = 0; i < 64; i++)
      {
                /* 里面的for循环更新8颗LED亮灭状态 */
                for(j = 0; j < 8; j++)
                {
                        if(LightLevel <= i)      
                        {
                              /* 达到条件,点亮第j颗LED */
                              temp |= (1 << j);
                        }
                        else
                        {
                              /* 未达条件,熄灭第j颗LED */
                              temp &= ~(1 << j);
                        }
                }
                DriveLED(temp);
      }
}
      先来分析一下程序,里面的一个for循环根据外面一个for循环的变量i和亮度等级表比对,计算出LED亮灭状态,然后通过DriveLED()驱动LED。不管i的值是多少,LightLevel <= i都成立,那么第一颗LED就是一直都亮的,只有当i等于63的时候,LightLevel <= i才成立,所以8颗LED是根据亮度表来调节占空比的,从理论上来说,这个程序是可行的,但是实际上行不行呢?答案是不行!为什么呢?
      就像上一节提到的,595驱动LED需要消耗一定的时间,数据运算也要消耗一定的时间,虽然每一次执行的时间很少,但是累加起来就足以影响效果了,在上一节中,是通过减小总循环的次数来解决的,但是这次不行,循环次数不能减小了。怎么办呢?我记得朱老师在讲课的时候,曾经讲过一个概念,
用空间替换时间,这些数据运算的结果无非就是在不同的时刻显示不同的LED,显示次数多的就亮一点,次数少的就暗一点,我们可以在之前把这些结果
写出来,到时候直接读取就是了,而且还增加了可读性。
代码如下:
#include <reg52.h>
#include "DriveLED.h"


/* 第几个元素表示亮几颗LED */      
code unsigned int uiCntLEDTab=
{
    0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF,
    0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF,
};


/******************************************************
* 函数名称:main
* 函数功能:主函数
* 入口参数:NULL
* 出口参数:NULL
*******************************************************/
int main()
{
      unsigned char i;
      /* 函数的初始化 */
      InitLED();
      /* 主循环 */
      while(1)
      {
                for(i=0; i < 32; i++)
                {


                        DriveLED(uiCntLEDTab);
                }
      }


      return 0;
}
下到板子的结果是完美输出了16组不同的占空比!而且一看就明白了,不像上一段代码,各种for循环嵌套,头都看晕了!会了这一步,滴水灯基本上就出来了!

滴水灯代码:
/******************************************************
*程序名称:main.c
*程序功能:实现滴水灯的主程序文件
*程序作者:TWAS
*创建时间:2015-1-22
*修改时间:2015-1-24
*程序版本:V0.1
******************************************************/


/* 包含的头文件 */
#include <reg52.h>
#include "DriveLED.h"
#include "delay.h"


/* 第一个元素表示亮几颗LED */      
code unsigned int uiCntLEDTab=
{
    0x0001,0x0003,0x0007,0x000F,0x001F
};
/* 亮度等级 */
code unsigned char ucLightLevelTab =
{
    32,16,8,4,1
};
/******************************************************
* 函数名称:main
* 函数功能:主函数
* 入口参数:NULL
* 出口参数:NULL
*******************************************************/
int main()
{
      unsigned char i,j,k,time,ucNum = 0;
      /* 函数的初始化 */
      InitLED();
      /* 主循环 */
      while(1)
      {
            /* 水滴形成 */
                while(1)
                {
                  time = 2;
                        while(time--)                /* 此循环控制每一个状态保持的时间,可以改变水滴形成的速度 */
                        {
                              for(i = 0; i < 50; i++)
                              {
                                     /* 通过修改ucNum的值,就可以改变占空比      */
                                        if(i < ucNum)
                                        {
                                                DriveLED(0x8000);   /* LED驱动点亮D16 */
                                        }
                                        else
                                        {
                                                DriveLED(0x0000);   /* LED驱动,熄灭所有 */
                                        }
                              }
                        }
                        ucNum++;
                        if (ucNum==50)
                        {
                            break;   
                        }
                }
                /* 拖尾效果 */

                /* 最外层for循环控制水滴滴下的过程,k的值表示这5组占空比位于16颗LED的哪一个位置 */
                for (k = 15; k >= 0 && k <= 15; k--)
                {
                  time = 2;
                  while(time--)               /* 此循环控制每一个状态保持的时间,可以改变水滴滴落的速度 */
                        {
                              for (i = 0; i < 5; i++)
                              {
                                    for (j = 0; j < ucLightLevelTab; j++)
                                        {
                                          /* 根据ucLightLevelTab[]选择LED点亮次数 */
                                          DriveLED(uiCntLEDTab << k);
                                        }      
                              }
                        }      
                }
                InitLED();
                while(1);
      }
      return 0;
}


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

重庆-风雪 发表于 2015-1-23 00:51:03

jianhong_wu 发表于 2015-1-22 23:06
有图有代码,绝对是精品。

多谢鸿哥夸奖!

jianghong891011 发表于 2015-1-24 17:02:31

:loveliness:学习了,顶一个

重庆-风雪 发表于 2015-1-24 23:56:45

{:soso_e183:}

重庆-风雪 发表于 2015-1-26 21:14:59

jianghong891011 发表于 2015-1-24 17:02
学习了,顶一个

:handshake

jianghong891011 发表于 2015-1-28 15:59:16

重庆-风雪 发表于 2015-1-26 21:14


:handshake
页: [1]
查看完整版本: TWAS手把手教你做呼吸灯-基于朱兆琪51学习板