專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

第5課 讓發(fā)光二極管閃爍

作者:huqin   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2013年12月30日   【字體:

         在第4課中我們分別用位操作法和總線操作法點(diǎn)亮了第一個(gè)發(fā)光二極光,也可以說正式邁出了單片機(jī)程序設(shè)計(jì)的第一步,相信通過前面的學(xué)習(xí),大家已經(jīng)可以很輕松的點(diǎn)亮發(fā)光二極管。但光會(huì)點(diǎn)亮發(fā)光二極管肯定不夠的,大家經(jīng)常可以看到外面大街小巷各種各樣漂亮的閃爍的流水或者說廣告燈,如圖1,這是怎么實(shí)現(xiàn)的呢,這節(jié)課我們就來講下如讓發(fā)光二極管閃爍和流水燈程序的設(shè)計(jì)。

圖1 漂亮的廣告燈
1如何實(shí)現(xiàn)發(fā)光二極管閃爍和簡(jiǎn)單延時(shí)
學(xué)過匯編的同學(xué)都清楚,單片機(jī)機(jī)在執(zhí)行指令時(shí),是一條一條指令順序執(zhí)行的,對(duì)于C語(yǔ)言也一樣,也是一條一條語(yǔ)句執(zhí)行,而每執(zhí)行一條指令或一條語(yǔ)句,都要占用一定的時(shí)間,利用單片機(jī)執(zhí)行程序的這個(gè)特點(diǎn)就可能用來實(shí)現(xiàn)發(fā)光二極管閃爍。
具體方法就如圖2所示,是先點(diǎn)亮發(fā)光二極管,然后寫一條延時(shí)語(yǔ)句,在這段延時(shí)時(shí)間內(nèi),單片機(jī)什么事也不做,而此時(shí)發(fā)光二極管還是一直保持亮的狀態(tài),然后延時(shí)時(shí)間完后,再讓發(fā)光二極管熄滅,熄滅后也寫一條延時(shí)程序,而在這段時(shí)間內(nèi),發(fā)光二極管一直保持熄滅,直到延時(shí)時(shí)間結(jié)束再?gòu)?fù)上而的亮的過程,這樣一直不斷的循環(huán)下去就實(shí)現(xiàn)在發(fā)光二極不斷閃爍。
圖2 發(fā)光二極管閃爍 
 
延時(shí)可以由while語(yǔ)句來實(shí)現(xiàn),也可由for語(yǔ)句實(shí)現(xiàn),由于前面我們已經(jīng)介紹過while語(yǔ)句,所以這里先介紹如何利用while語(yǔ)句實(shí)現(xiàn)延時(shí)。
(1)   利用while語(yǔ)句進(jìn)行延時(shí)
由于while語(yǔ)句在前我們已經(jīng)講解過,這里直接給出程序如例1。
例1 利用whiel語(yǔ)句實(shí)現(xiàn)發(fā)光二極管閃爍程序
#include<reg52.h>
 
sbit D1=P1^0;
unsigned int a;
 
void main()
{
       while(1)
       {
              a=50000;
              D1=0;
              while(a--);
              a=50000;
              D1=1;
              while(a--);
       }
}
程序分析:上面程序中出現(xiàn)了新的語(yǔ)句,unsigned int a;由于程序的需要,這里我們先定義了一個(gè)變量a,所謂變量其實(shí)就是代表單片機(jī)內(nèi)存中具有特定屬性的一個(gè)存儲(chǔ)單元,它用來存放數(shù)據(jù),也就是變量的值,如下面的a=50000,在程序中這些值是可以改變的。其中a表示變量的名字,unsigned int是無符號(hào)整形的意思,它的取值范圍為0-65535,也就是定義成此類型后,在對(duì)程序編譯時(shí),編譯系統(tǒng)會(huì)給這個(gè)變量分配一個(gè)存儲(chǔ)空間用來存放數(shù)據(jù),且無符號(hào)整形的空間存數(shù)的范圍為0-65535。 后面的程序?qū)?/span>a賦值時(shí),其范圍只能是0-65535,如程序后面的a=50000,如果此時(shí)我們對(duì)a賦值a=70000,此時(shí)編譯時(shí)就是報(bào)錯(cuò)。我們也可以把變量a定義為其它類型的,如unsigned char a表示為把變量a定義為無符號(hào)字符型,其賦值范圍為0-255
當(dāng)然我們也可以定義其它的類型 short int、float等,但作為單片機(jī)初學(xué)者,一般只用到無符號(hào)字符型unsigned char(賦值范圍為0-255)和無符號(hào)整形unsigned int(賦值范圍為0-65535)兩種,其它類型可以暫時(shí)不管,具體請(qǐng)參考C語(yǔ)言相關(guān)書籍。
unsigned int a明白后,下面就是一個(gè)大循環(huán)while(1),在大循環(huán)里,我們先給a賦值50000,然后燈亮D1=0,再進(jìn)行延時(shí)while(a--)(a--,表示自減1的意思,while(a--),表示a從50000開始自減50000次,此時(shí)每自減1有一定的時(shí)間,所以自減50000次相當(dāng)于延時(shí)的意思),再后的程序就是延時(shí)后再燈滅,再延時(shí),再重復(fù)前面的過程,最終就可以實(shí)現(xiàn)燈一亮一滅閃爍的現(xiàn)象。下載到實(shí)驗(yàn)室的現(xiàn)象如圖3所示。
圖3 發(fā)光管閃爍
通過上面程序我們實(shí)現(xiàn) 了發(fā)光管閃爍的功能,但這里閃爍的間隔時(shí)間我們并不清楚具體是多少,我們只能根據(jù)具體的多次實(shí)驗(yàn)確定我們所需的時(shí)間間隔,如果實(shí)在要知道的話,可以通過51單片機(jī)的軟件仿真得出,這里我們不作講解,以后等大家單片機(jī)學(xué)得差不多再去學(xué)習(xí)。
下面我們?cè)俳o出另一種程序延時(shí)的for語(yǔ)句的寫法,這種寫法在具體的項(xiàng)目用得更多。
(2)   for語(yǔ)句及其延時(shí) 
for語(yǔ)句是C語(yǔ)言中重要的語(yǔ)句,其格式如下:
格式:

利用for語(yǔ)句同樣可以寫出延時(shí)程序,例如:
 unsigned int i;
for(i=1;i<=3;i++) ;
這里首先定義了一個(gè)無符號(hào)變量i,在for語(yǔ)句中,i=1為表達(dá)式1;i<=100為表達(dá)式2;i++為表達(dá)式3,for語(yǔ)句內(nèi)程序執(zhí)行如下
1、給變量i賦值為1;
2、判斷i是否小于等于3;此時(shí)值為真,即執(zhí)行for中的語(yǔ)句,此處for后面的語(yǔ)句為空語(yǔ)句,省略不寫,
相當(dāng)于什么都不執(zhí)行,然后跳到語(yǔ)句3;
3、執(zhí)行i++,i++為自加1的意思,此時(shí)i的值變?yōu)?,
4、跳到第2 步判斷i是否小于等于3,此時(shí)仍然小于等于3,又執(zhí)行空語(yǔ)句;
5、執(zhí)行i++,此時(shí)i的值變?yōu)?;
6、跳到第2 步判斷i是否小于等于3,此時(shí)等于3,直接跳出;
通過上面6步,for語(yǔ)句執(zhí)行完畢,注意,單片機(jī)在執(zhí)行每一條語(yǔ)句的時(shí)候都需要一定的時(shí)間,此時(shí)我們只需要改變表達(dá)式2的值就
可以得到我們想要的延時(shí)時(shí)間了。
此處需要注意的時(shí),我們把i定義為無符號(hào)整形后,i的值最大值只能為65535,也就是利用上面的格式,我們可以寫出最大的延時(shí)程序如下:
 unsigned int i;
for(i=1;i<=65536;i++)
但我們此時(shí)如還需要更長(zhǎng)的時(shí)間時(shí),如果再讓i值變大的話,編譯時(shí)就會(huì)出錯(cuò),此時(shí)我們可以寫成如下的寫法:
unsigned int i;
unsigned int j;
for(i=1;i<=65536;i++)
{
      for(j=1;j<=100;j++);
}
上面的語(yǔ)句稱為c語(yǔ)句的嵌套,是指一個(gè)for語(yǔ)句中又包含一個(gè)完整的for語(yǔ)句,內(nèi)嵌的語(yǔ)句還可以嵌套for語(yǔ)句,這稱為多層嵌套,我們這里只有兩層。
注意第一個(gè)for語(yǔ)句后面沒有“;”,這里第一個(gè)for語(yǔ)句執(zhí)行了65535次,第二個(gè)for語(yǔ)句執(zhí)行了100次,相當(dāng)于總共執(zhí)行了655535x100次,這樣的寫法就可以寫出較長(zhǎng)的延時(shí)時(shí)間。
這里的for語(yǔ)句和上面的while語(yǔ)句都可以用來進(jìn)行延時(shí),這種方法稱為軟件延時(shí),和上面for語(yǔ)句一樣,其準(zhǔn)確的延時(shí)時(shí)間在C語(yǔ)言程序中不容易計(jì)算出來。如果想要得到精確的延時(shí)時(shí)間,我們則可以用到單片機(jī)內(nèi)部的硬件資源定時(shí)器,它可以精確的微秒級(jí),這個(gè)后面的課程中會(huì)講到。雖然得不到精確的延時(shí)時(shí)間,但大概的時(shí)間我們剛可以通過軟件仿真的方法得出,關(guān)于仿真具體的調(diào)試方法我們這里不作講解,這此我們給出一個(gè)51單片機(jī)經(jīng)常用到延時(shí)時(shí)間:
for語(yǔ)句中兩個(gè)變量的類型都為unsigned int 時(shí),內(nèi)層的for語(yǔ)句變量的值恒為110時(shí),外層for語(yǔ)句變量的值為多少,這個(gè)for嵌套語(yǔ)句延時(shí)時(shí)間就約為多少毫秒。如下面的程序:
unsigned int i;
unsigned int j;
for(i=1;i<=1000;i++)
{
      for(j=1;j<=110;j++);
}
這個(gè)程序中外層for語(yǔ)句中變的值為1000,其延時(shí)的時(shí)間就約為1000毫秒,即1秒,若要想得到其它的延時(shí)時(shí)間,只需要改變i變量的值就可以了。
下面我們利用 for語(yǔ)句寫一個(gè)程序,其功能為讓L1燈以間隔1秒的時(shí)間閃爍。程序如例2所示。
例2 L2燈以1秒的時(shí)間時(shí)隔閃爍
#include<reg52.h>
#define unsigned int;
 sbit D1=P1^0;
uint i,j;
void main()
{
       while(1)
       {
              D1=0;
              for(i=1;i<=1000;i++)
             {
                  j=1;j<=110;j++;
              }
              D1=1;
              for(i=1;i<=1000;i++)
             {
                  j=1;j<=110;j++;
              }
       }
}
下載到實(shí)驗(yàn)板上現(xiàn)象如圖4所示:
圖4 L1以1秒的間隔閃爍。
例2程序中,在寫延時(shí)程序時(shí),分別用到了兩個(gè)延時(shí)程序,并且這兩個(gè)程序內(nèi)容格式完全相同,此時(shí)從簡(jiǎn)化程序的角度出發(fā),我們可以采取另外一種寫法-子函數(shù)調(diào)用法.下面對(duì)其進(jìn)行介紹.
3、不帶參數(shù)的子函數(shù)調(diào)用
我們把上式中的延時(shí)函數(shù)單獨(dú)提取出來,然后寫成一個(gè)函數(shù)—此函數(shù)稱為一個(gè)程序中的子函數(shù),然后在主函數(shù)中直接調(diào)用即可。上式中延時(shí)函數(shù)的子函數(shù)寫法如下。
void delay1s()
{ 
 for(i=1;i<=1000;i++)
             {
                  j=1;j<=110;j++;
              }
 }
上式中,void意為無返回值,也就是此函數(shù)執(zhí)行完畢后不返回任何值。delay1s表示函數(shù)名,這里用戶可以隨便取,只需要滿意C語(yǔ)言的命名規(guī)則即,因?yàn)檫@個(gè)子程序在上面已經(jīng)寫過,是延時(shí)1鈔的函數(shù),所以我們此時(shí)命名延時(shí)1秒(delay1ms)樣可以便于大家閱讀和記憶。函數(shù)名delay1s后面緊跟一小括號(hào),括號(hào)里面什么內(nèi)容也沒有,這表示此子函數(shù)不帶任何參數(shù)(后面會(huì)介紹到帶參數(shù)的子函數(shù))。
需要注意的是,程序中中的子函數(shù),如果是寫在主函數(shù)之前,此不需要聲明,但如果是寫在主函數(shù)之后,則需要聲明,聲明的方法如下:將返回值特性、函數(shù)名及后面的小括號(hào)完全復(fù)制,若是無參函數(shù),則小括號(hào)內(nèi)為空,若是有參函數(shù),則需要在小括號(hào)內(nèi)依次寫上參數(shù)類型,參數(shù)類型之間用逗號(hào)隔開。最后在小括號(hào)后面加上分號(hào)即可。
下面對(duì)例2的程序改為不帶參數(shù)子函數(shù)的寫法,程序如例3所示。
例2 L2燈以1秒的時(shí)間時(shí)隔閃爍(不帶參數(shù)子函數(shù)的寫法)
#include<reg52.h>
#define unsigned int;
 sbit D1=P1^0;
 
void delay1s()
void main()
{
       while(1)
       {
              D1=0;    
      delay1s();   
              D1=1;
            delay1s();
       }
}
void delay1s()
{
 uint i,j;
 for(i=1;i<=1000;i++)
             {
                  j=1;j<=110;j++;
              }
 }
在例3中,我們注意到“uint i,j”語(yǔ)句,i和j兩個(gè)變量的定義放到了子函數(shù)里,而沒有寫在主函數(shù)的最外面。在主函數(shù)外面定義的變量叫做全局變量,像這種定義在了函數(shù)內(nèi)部的稱為局部變量,這里i和j就是局部變量;局部變量只在當(dāng)前函數(shù)中起作用,程序一但執(zhí)行完當(dāng)前子函數(shù),它內(nèi)部的所有變量將自動(dòng)銷毀,當(dāng)下次再調(diào)用此子函數(shù)時(shí),編譯器重新為其分配內(nèi)存空間。我們要知道,在一個(gè)程序中,每個(gè)變量都占據(jù)著單片機(jī)的固定的RAM, 局部變量是用時(shí)隨時(shí)分配,不用時(shí)立即銷毀。一個(gè)單片機(jī)的RAM是很有限的,像STC89C52內(nèi)部有256字節(jié)的RAM,所以用時(shí)我們必須節(jié)約,顯然,放在子程序中更能節(jié)省RAM空間。將來程序下載到實(shí)驗(yàn)板,我們此時(shí)依然可以看到如例2中的現(xiàn)象,L1燈以1秒的時(shí)間間隔閃爍。
4、帶參數(shù)子函數(shù)的寫法
上面我們講到過,C語(yǔ)言中子函數(shù)分為兩種,一種是不帶參數(shù)子函數(shù),另一種是帶參數(shù)的子函數(shù)。下面我們講一下帶參數(shù)子函數(shù)。在例3中,我們延時(shí) 1000ms,i=1000.如果我們要延時(shí)500ms,剛i=500.如果要延時(shí)300ms,i=300,這樣程序改起來就很麻煩,特別是在一些大型的程序中,這里我們?nèi)绻麑懗蓭?shù)的子函數(shù)就方便很多。程序?qū)懛ㄈ缦拢?/div>
void delayxms(unsigned int z)
{ 
 for(i=z;i>0;i--)
             {
                  j=1;j<=110;j++;
              }
 }

 

上面程序中小括號(hào)內(nèi)多了“unsigned int z”,其中“z”這個(gè)就是這個(gè)子函數(shù)帶的參數(shù),unsigned int 是定義參數(shù)z的類型為無符號(hào)整型。子函數(shù)中z這個(gè)參數(shù)我們稱為形參,在調(diào)用子函數(shù)時(shí)我們用一個(gè)真實(shí)的數(shù)據(jù)代替此形參,這個(gè)真實(shí)的數(shù)據(jù)我們稱為實(shí)參。在調(diào)用子函數(shù)時(shí),只需要的延時(shí)時(shí)間改變小括號(hào)內(nèi)的數(shù)據(jù)就可以了,如要延時(shí)1000s,則delayxms(1000),800ms時(shí),delayxms(800)。
下面我們寫一個(gè)程序讓8個(gè)發(fā)光二極管以間隔800ms的時(shí)間閃爍。
例5 8個(gè)發(fā)光二極管以間隔500ms的時(shí)間間隔閃爍。
#include<reg52.h>
#define unsigned int;
 sbit D1=P1^0;
 
void delayxms(unsigned int z)
{ 
 for(i=z;i>0;i--)
             {
                  j=1;j<=110;j++;
              }
 }
void main()
{
       while(1)
       {
              D1=0;    
      delay1s();   
              D1=1;
            delay1s();
       }
}

 

下載到實(shí)驗(yàn)板上的現(xiàn)象如下:

相關(guān)文章