0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

使用51單片機實現(xiàn)LED呼吸燈

CHANBAEK ? 來源:嵌入式小書蟲 ? 作者:FledgingSu 支離蘇 ? 2023-07-04 11:40 ? 次閱讀

【說在前面的話】

單片機技術(shù)是現(xiàn)代工業(yè)自動化、電子電氣物聯(lián)網(wǎng)等的一門必不可少的主流技術(shù)。隨著人們生活智能化的提高,單片機技術(shù)也幾乎融入了我們生活的各個角落,比如智能電飯煲、智能音箱、等等。

由此,《 重學(xué)51單片機 》系列文章意在幫助初學(xué)者入門單片機技術(shù)。我們會從最簡單的點亮一個燈開始,一步一步實現(xiàn)按鍵、lcd1602、ds18b20、ds1302、雙機通信等模塊,同時,還會講一些硬件通信協(xié)議,比如uart、IIC、SPI等。并結(jié)合C語言編程技巧,以實際的工程項目來給大家講解編程思路,讓大家靈活運用C語言的指針與結(jié)構(gòu)體,實現(xiàn)編程模塊化。

言歸正傳,接下來我們就開始進入今天的主題,用51單片機控制LED實現(xiàn)呼吸燈的效果。

【呼吸燈原理】

我們先看一下呼吸燈的效果下

呼吸燈就是先漸漸變亮再漸漸變暗,如此循環(huán)就像呼吸一樣??墒菃纹瑱C的管腳要么輸出1(亮)要么輸出0(滅),怎么會有漸變的效果呢?

這就和我們的眼睛 觀看圖像會有滯留時間引起的 。當(dāng)我們在看東西時,眼睛成像后會滯留0.04s(這個標(biāo)準(zhǔn)是網(wǎng)上找的)。

我們按照0.04s計算,就等于40ms,也就是亮滅都是20ms時,看到的LED就是一直在亮。如下圖:

圖片

那LED 20ms亮20ms滅和一直常亮的效果一樣嗎?

哈哈,肯定是不一樣的。交替20ms亮20ms滅我們看到的效果要比一直常亮的效果暗。我們假設(shè)一直常亮的亮度為100%,那么交替20ms亮20ms滅的亮度就是50%,基于此, 我們就可以調(diào)節(jié)LED的亮度了 。如下圖

圖片

到此,我們就可以調(diào)節(jié)LED燈的亮度了(就是40ms內(nèi)設(shè)置高電平的持續(xù)時間),這個就是大名鼎鼎的PWM調(diào)節(jié)亮度的原理了,而設(shè)置高電平的持續(xù)時間就是調(diào)節(jié) 占空比 (即高電平的時間除以周期數(shù):20/40=50%)。

這里,我們 最重要的還是這個占空比, 比如周期是20ms,交替10ms亮10ms滅,我們看到的亮度還是50%(即占空比為10/20=50%)

接下來我們就看看程序怎么實現(xiàn)吧。

【程序?qū)崿F(xiàn)】

點亮一個LED

首先,我們先從點亮一個LED燈開始,然后再一步一步實現(xiàn)一個呼吸燈的效果。我們使用的硬件如下:

開發(fā)板 零壹單片機培訓(xùn)開發(fā)板
單片機型號 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燈的程序還是很簡單,相信大家都會。

調(diào)節(jié)LED亮度

接下來我們就實現(xiàn)一個可以調(diào)節(jié)亮度的函數(shù)(即調(diào)節(jié)占空比),如下

//調(diào)節(jié)LED亮度
void set_led_luminance(unsigned int pwm_duty_cycle)
{
    static unsigned int s_Counter = 0;//計時
    //調(diào)節(jié)占空比
    if (pwm_duty_cycle >= s_Counter) {
        LED_ON();
    } else {
        LED_OFF();
    }
    //計數(shù)器開始計時
    s_Counter++;
  if(s_Counter > 255){
    //40ms時間到,清零重新計時
    s_Counter = 0;
  }    
}
void main(){
  while(1){
    set_led_luminance(128);
  }
}
  • 我們定義了一個靜態(tài)變量s_Counter作為 軟件定時器 ,s_Counter加到255后清零(這里相當(dāng)于是一個周期的時間40ms,當(dāng)然不是嚴(yán)格的40ms,只要周期小于40ms我們就看不到閃爍)
  • 函數(shù)的參數(shù)就是我們要調(diào)節(jié)的占空比,比如傳入的是128,占空比為128/255=50%

現(xiàn)在有了設(shè)置LED亮度的函數(shù),那怎么讓它漸漸變亮再漸漸變暗,實現(xiàn)呼吸燈的效果呢?

實現(xiàn)呼吸燈

這個也很簡單,我們只要給set_led_luminance函數(shù)傳的參數(shù)從0慢慢加到255然后再從255慢慢減到0就可以了,如下

void breath_led(void){
  static int duty_cycle = 0;  
  static char flag = 1;
  //設(shè)置亮度
  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;
    }
  }
}
void main(){
  while(1){
    breath_led();
  }
}
  • 定義一個靜態(tài)變量duty_cycle來保存占空比,當(dāng)flag為1時,占空比慢慢增加到255,然后把flag設(shè)置成0 ,duty_cycle再從255慢慢減為0,如此循環(huán)就可以了。

哈哈,到這里你是不是覺得呼吸燈已經(jīng)實現(xiàn)了, 然而,并不是 。不相信的同學(xué)可以自己試試上面的代碼哦。

那到底是哪里有問題呢?

這就是我們直接調(diào)用設(shè)置亮度的函數(shù)set_led_luminance()是有問題的,因為此函數(shù)執(zhí)行一個周期需要40ms,也就是在這40ms期間是不可以改變占空比的,否則可就調(diào)不了亮度了。再來看看breath_led函數(shù),每次調(diào)用完set_led_luminance()函數(shù)設(shè)置占空比后就立即改變了duty_cycle的值,并沒有延時40ms。

此時,我們還需要再加一個軟件定時器,定時超過40ms后修改duty_cycle的值,修改后的程序如下

void breath_led(void){
  static unsigned int s_breathCounter = 0;
  static int duty_cycle = 0;  
  static char flag = 1;  
  //設(shè)置亮度
  set_led_luminance(duty_cycle);
  //計時器增加
  s_breathCounter++;
  //超過40ms
  if(s_breathCounter > 511){
    //計時器設(shè)置為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 < 0){//小于0開始增加
        duty_cycle = 0;
        flag = 1;
      }
    }
  }
}
  • 注意: 我們定時器的時間要大于40ms(也就是s_breathCounter的值要大于255)就可以, 但最好是周期的倍數(shù) ,比如我們的周期為255(即0 ~ 255共256個數(shù)),我們設(shè)置成它的兩倍即256*2-1=511(即0~511共512個數(shù))。

到這里,我們的呼吸燈就制作好了,是不是很簡單(* ̄︶ ̄)

接下來就是今天的彩蛋環(huán)節(jié)

雖然實現(xiàn)了呼吸燈的效果,但是代碼還是不夠簡潔和優(yōu)雅,用了一堆if和else,我們想辦法再簡化一下。

首先化簡set_led_luminance()函數(shù)中的這一部分,如下圖所示

圖片

化簡之前,先講一個C語言的小知識,就是 按位與運算 。

1 & 0 = 0;
1 & 1 = 10 & 0 = 0;
0 & 1 = 0;

由此,我們知道:不論是1還是0,和0做與運算都為0,

不論是1還是0,和1做與運算還是它本身 ,

為了方便計算,我們采用16進制數(shù)(即前面加0x),比如0xff對應(yīng)的十進制就是255;那么

一個小于等于0xff的數(shù)和0xff做與運算,結(jié)果還是它本身 ,如下

0x03 & 0xff = 0x03;
0xff & 0xff = 0xff;

那一個大于0xff的數(shù)和0xff做與運算呢?

0x100 & 0xff = 0x00;
0x1ff & 0xff = 0xff;
  • 結(jié)果就是這個數(shù)除以(0xff+1)的余數(shù)(即結(jié)果還是在0~0xff之間)

有了這個與運算,上面的代碼就可以化簡為

s_Counter++;
s_Counter = s_Counter & 0xff;
  • 這樣s_Counter的取值范圍就一直在0x00~0xff了

同樣的,上面breath_led函數(shù)中的軟件定時器也可以化簡一下,如下

//計時器增加
s_breathCounter++;
if (!(s_breathCounter & (0x1ff))) {
    duty_cycle++;
    duty_cycle &= 0x1ff;
}
if(duty_cycle > (0xff)){
  set_led_luminance(duty_cycle - (0xff));
}else{
  set_led_luminance( (0xff) - duty_cycle );
}
  • 第3行,0x1ff就是十進制的511,當(dāng)s_breathCounter的值為(0x1ff+1)即512時條件成立,因為512&0x1ff=0,前面還有個感嘆號就是取非運算(準(zhǔn)確的說就是s_breathCounter的值為512的倍數(shù)時條件成立,這樣都省去了s_breathCounter清零的操作了,不知道給大家解釋清楚了沒,大家要多想想),條件成立則占空比開始增加或減少

  • 那占空比的范圍不是0~ 255嗎,第5行怎么也變成0x1ff(511)了,別急,你再往下看第8行,我們又減去了0xff,所以占空比的范圍還是0~255

  • 第7~ 10行代碼的意思為:當(dāng)duty_cycle > (0xff)時,即256~511,減去0xff,就相當(dāng)于從1增加到255,亮度漸漸變亮

    當(dāng)duty_cycle <= (0xff)時,即duty_cycle 從0增加到255,而設(shè)置的亮度為255 - duty_cycle ,相當(dāng)于從255降到0,亮度 漸漸變暗 ,這樣就實現(xiàn)了呼吸燈的效果。

哈哈,你以為我們的簡化就結(jié)束了嗎?

no,no,no

其實第7到10行還可以更簡單一些。此時就需要用到取絕對值的函數(shù)了。

什么,用絕對值干嘛?

你看第10行0xff- duty_cycle就相當(dāng)于duty_cycle - 0xff然后再取絕對值,好了,化簡后的代碼如下

set_led_luminance(ABS(duty_cycle - (0xff)));

取絕對值的宏函數(shù)如下

#define ABS(N) ((N) < 0 ? -(N) : (N))

最后我把整個化簡后的代碼也貼到下面

#define ABS(N)    ((N) < 0 ? -(N) : (N))
static void set_led_luminance(unsigned int pwm_duty_cycle)
{
    static unsigned int s_Counter = 0;
    if (pwm_duty_cycle >= 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();    
  }
}

怎么樣,是不是很簡潔(* ̄︶ ̄)

好了,到這里今天的呼吸燈就講完了,想和我一起重學(xué)51單片機的同學(xué)記得 點贊+關(guān)注, 這會加速我對文章更新的速度哦

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • led
    led
    +關(guān)注

    關(guān)注

    240

    文章

    23065

    瀏覽量

    657074
  • 單片機
    +關(guān)注

    關(guān)注

    6030

    文章

    44490

    瀏覽量

    632005
  • 51單片機
    +關(guān)注

    關(guān)注

    273

    文章

    5697

    瀏覽量

    122997
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7595

    瀏覽量

    135871
  • 呼吸燈
    +關(guān)注

    關(guān)注

    10

    文章

    109

    瀏覽量

    42688
收藏 人收藏

    評論

    相關(guān)推薦

    51單片機呼吸程序設(shè)計

    51單片機呼吸程序設(shè)計是基于單片機的原理與接口設(shè)計,采用單片機I/O口,加以C語言編程
    發(fā)表于 07-14 07:54

    基于51單片機呼吸怎樣設(shè)計

    課程設(shè)計報告書題目:基于51單片機呼吸設(shè)計信息工程學(xué)院課程設(shè)計任務(wù)書學(xué) 號1學(xué)生姓名專業(yè)(班級)設(shè)計題目基于51
    發(fā)表于 07-15 08:01

    怎么樣去設(shè)計基于單片機與PWM的呼吸

    51單片機(呼吸)C語言版基于單片機與PWM的呼吸
    發(fā)表于 07-15 06:02

    如何利用51單片機實現(xiàn)呼吸的設(shè)計?

    如何利用51單片機實現(xiàn)呼吸的設(shè)計?
    發(fā)表于 10-19 06:24

    基于51單片機實現(xiàn)呼吸設(shè)計

    實現(xiàn)效果如圖:注:該51單片機led引腳為P2^ 0 - P2^7,實際代碼按照引腳圖實現(xiàn)。代
    發(fā)表于 11-18 06:48

    51單片機呼吸實現(xiàn)源程序

    51單片機呼吸實現(xiàn),源程序,hex文件都有,用到的led
    發(fā)表于 12-10 16:35 ?169次下載

    使用51單片機設(shè)計LED點陣呼吸效果程序免費下載

    本文檔的主要內(nèi)容詳細介紹的是使用51單片機設(shè)計LED點陣呼吸效果程序免費下載。
    發(fā)表于 07-04 17:41 ?20次下載
    使用<b class='flag-5'>51</b><b class='flag-5'>單片機</b>設(shè)計<b class='flag-5'>LED</b>點陣<b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>效果程序免費下載

    使用51單片機實現(xiàn)呼吸工程文件免費下載

    本文檔的主要內(nèi)容詳細介紹的是使用51單片機實現(xiàn)呼吸工程文件免費下載。
    發(fā)表于 09-03 08:00 ?13次下載
    使用<b class='flag-5'>51</b><b class='flag-5'>單片機</b><b class='flag-5'>實現(xiàn)</b>的<b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>工程文件免費下載

    使用單片機實現(xiàn)呼吸的設(shè)計程序免費下載

    本文檔的主要內(nèi)容詳細介紹的是使用單片機實現(xiàn)呼吸的設(shè)計程序免費下載。
    發(fā)表于 03-24 08:00 ?2次下載
    使用<b class='flag-5'>單片機</b><b class='flag-5'>實現(xiàn)</b><b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>的設(shè)計程序免費下載

    基于單片機與PWM實現(xiàn)呼吸的設(shè)計

    本設(shè)計是基于單片機的原理與接口設(shè)計,采用單片機I/O口,加以C語言編程實現(xiàn)LED漸亮再漸暗類似人的呼吸一樣的效果。
    發(fā)表于 05-19 08:00 ?22次下載
    基于<b class='flag-5'>單片機</b>與PWM<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>的設(shè)計

    使用單片機實現(xiàn)呼吸的程序免費下載

    本文檔的主要內(nèi)容詳細介紹的是使用單片機實現(xiàn)呼吸的程序免費下載。
    發(fā)表于 06-05 17:35 ?45次下載
    使用<b class='flag-5'>單片機</b><b class='flag-5'>實現(xiàn)</b><b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>的程序免費下載

    51單片機基于定時器的精準(zhǔn)呼吸

    51單片機基于定時器的精準(zhǔn)呼吸設(shè)計目的:實現(xiàn)LED
    發(fā)表于 11-21 09:51 ?50次下載
    <b class='flag-5'>51</b><b class='flag-5'>單片機</b>基于定時器的精準(zhǔn)<b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>

    51單片機心形實現(xiàn)旋轉(zhuǎn)呼吸流水編程

    51單片機心形實現(xiàn)旋轉(zhuǎn)呼吸流水編程
    發(fā)表于 11-23 16:20 ?23次下載
    <b class='flag-5'>51</b><b class='flag-5'>單片機</b>心形<b class='flag-5'>燈</b><b class='flag-5'>實現(xiàn)</b>旋轉(zhuǎn)<b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>流水編程

    基于51單片機呼吸程序編寫

    利用51單片機編寫的呼吸小程序,實驗程序內(nèi)容截圖分享~
    發(fā)表于 11-23 16:36 ?33次下載
    基于<b class='flag-5'>51</b><b class='flag-5'>單片機</b>的<b class='flag-5'>呼吸</b><b class='flag-5'>燈</b>程序編寫

    51單片機呼吸頻率怎么調(diào)節(jié)

    51單片機呼吸頻率的調(diào)節(jié)方法可以通過改變PWM(脈沖寬度調(diào)制)的工作周期和占空比來實現(xiàn)。在下面的文章中,我將詳細介紹如何使用
    的頭像 發(fā)表于 12-19 10:39 ?2228次閱讀