顧名思義,燈光在微電腦控制之下完成由亮到暗的逐漸變化,感覺像是在呼吸。用專業的話來說是通過控制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 */
}
}
經過驗證!成功了!
如有錯誤的地方,希望大家批評指正!