《中學(xué)生C51單片機易學(xué)實戰(zhàn)入門教程》第二課 讓LED閃起來實驗準(zhǔn)備:使用第一課已裝配好的實驗板 課程內(nèi)容: 一、分析LED接線原理圖:著重左上角的 LED電路部分。 LED正極接電源5V,現(xiàn)程序控制 LED負(fù)極(原理圖中管腳P1^0)為0V,LED兩端正向有電壓差,于是LED導(dǎo)通,發(fā)光。 那如何控制讓 LED燈 不發(fā)光呢? 只要LED兩端沒有電壓差就可以了,現(xiàn)在LED正極通過電阻已接上5V電壓,只要LED負(fù)極也是5V電壓,LED就不會導(dǎo)通,不發(fā)光了。 上一課中,控制LED負(fù)極電壓的代碼是: P10 = 0; 表示控制管腳 P1^0的電壓為 0V,現(xiàn)在要改成控制管腳 P1^0的電壓為 5V,這塊單片機芯片只認(rèn)識兩個數(shù)字0與1,0代表0V,1代表5V。于是讓LED燈不發(fā)光,只須改成以下程序代碼: P10 = 1; 相當(dāng)簡單。 動手改程序,編譯,下載到實驗板中觀察運行結(jié)果。 實驗板沒反應(yīng)?不會壞了吧。 當(dāng)然不是壞,分析下電路原理吧。 在沒有加電的時候,LED燈兩端電壓為0,沒電的狀態(tài)下肯定不會發(fā)光。 當(dāng)下載程序后,實驗板加上電了,LED正極接上了5V電壓,LED負(fù)極讓程序控制了,也是5V電壓,兩端沒有產(chǎn)生能讓LED導(dǎo)通的電壓差,于是LED不發(fā)光。 修改控制LED的代碼改為 P10 = 0; 下載實驗板,LED燈又發(fā)光了。 再修改控制LED的代碼改為 P10 = 1; 下載實驗板,LED燈又不發(fā)光了。 再修改控制LED的代碼改為 P10 = 0; 下載實驗板,LED燈又發(fā)光了。 … … 我想讓LED 像星星一樣閃啊閃,而這樣的只能是不發(fā)光或只發(fā)光的狀態(tài)不合要求。 二、如何才能實現(xiàn)閃的效果?那就是 先控制LED 發(fā)光一段時間,然后再控制LED不發(fā)光一段時間,如此重復(fù)下去。 單片機的工作狀態(tài)是:只要給它加電,它就永不停息的工作,不斷執(zhí)行設(shè)定的程序,直到斷電或燒壞。 所以,單片機是不會自己暫停一小段時間來讓LED轉(zhuǎn)狀態(tài)的,必須由程序來控制。那單片機既然是永不停息的工作,又如何讓編程序控制單片機暫停一小段時間呢?方法很簡單,就是讓它空跑,做無用的工作,達(dá)到消耗時間的目的,也就是浪費一下它的時間,實現(xiàn)延時效果。 開發(fā)單片機的前輩已設(shè)計好這個消耗時間的程序,稱為延時函數(shù),如下: void mDelay(uint Delay) { uint i; for(;Delay>0;Delay--) { for(i=0;i<> { ; } } } 先不管這代碼是如何設(shè)計,先把這段代碼輸入到上一課的程序中,并控制LED燈的狀態(tài)切換。 #include 'reg52.h' #define uint unsigned int #define uchar unsigned char sbit P10= P1^0; void mDelay(uint Delay) { uint i; for(;Delay>0;Delay--) { for(i=0;i<> { ; } } } void main() { P10=0; mDelay(1000); P10=1; mDelay(1000); } 要注意,輸入延時函數(shù)時,兩行#define和整個延時函數(shù)代碼要放在主程序段 void main()之前。因為要前面先有代碼功能,后面才可能拿來使用,也就是調(diào)用延時功能。 代碼量增加了,要注意按花括號分層級縮進代碼,方便以后分析程序。 代碼量增加了,這時可能編譯時又會報錯,必須要花時間去排除錯誤,這就叫查BUG和修復(fù)BUG。編代碼,查BUG,修復(fù)BUG,測試,這幾步重重復(fù)復(fù),就是編程序的工作過程。 編譯正確,下載到實驗板上,可以看到達(dá)到了讓LED閃的效果,可是閃得很慢,一秒才閃一下。 想改快點,也很簡單,代碼中的 mDelay(1000); 括號中的數(shù)據(jù)稱為參數(shù),給不同的數(shù)據(jù),延時函數(shù)就有不同的工作效果??梢苑謩e把1000改成500、100、20等做實驗檢驗下效果,實踐是檢驗真理的唯一方法,必須動手做實驗。 三、程序分析雖然實驗有了效果,但是我們卻仍然不知道為何能有這正確的結(jié)果。 我們來分塊分析。 首先,主程序 main()里面的代碼,容易理解,只要知道代碼 mDelay(1000); 能延時一秒就行了。 然后再看mDelay 延時函數(shù)內(nèi)部代碼及相關(guān)代碼,這回多了兩行代碼,這兩行稱為預(yù)編譯定義: #define uint unsigned int //用 uint 代表unsigned int #define uchar unsigned char //用uchar 代碼unsigned char 以后的程序中所有的uint 和uchar 在代碼編譯時會自動換成unsigned int和unsigned char,也就是就輸入代碼量減少了,但結(jié)效果不變,省時省力。 這兩行基本代碼以后的程序中都會出現(xiàn)。 void mDelay(uint Delay) //功能模塊:延時函數(shù),帶控制參數(shù) { uint i; //后面要用到的臨時變量,使用前要先定義 for(;Delay>0;Delay--) //不斷重復(fù)運行括號的代碼,直到Delay減到0 { for(i=0;i<124;i++)>124;i++)> //直到i加到124才停止 { ; // 僅分號,沒命令,效果是運行一次就空轉(zhuǎn)一次 } } } 要點:其中 for()這行括號里面,第一位置放的是初始值,留空的話表示不用管初始值,中間位置放置控制結(jié)束的條件,條件符合就繼續(xù)工作,條件不符合時就退出,第三位置放置的是每完成一次工作后要做的小小改變,達(dá)到控制程序的目的。 這個for()命令,稱為循環(huán)結(jié)構(gòu),用于重復(fù)做一段相同的工作。 四、程序優(yōu)化優(yōu)化程序,能讓編程素質(zhì)提高。 細(xì)心的同學(xué)可能會發(fā)現(xiàn),在main()程序段中 P10=0; mDelay(1000); P10=1; mDelay(1000); 先控制了LED發(fā)光,然后延時1秒,再控制LED不發(fā)光,然后延時1秒,然后 … … 沒有然后了,程序結(jié)束了。但是為何程序卻能按原意重復(fù)一閃一閃? 沒錯,程序的確是結(jié)束了。 于是LED不發(fā)光了。 但是,由于單片機加電之后是永不停息的工作,于是它重新回到初始加電狀態(tài),于是又從頭再次執(zhí)行了 main()代碼,于是LED又閃了。 也就是說,這個LED不停閃動。并不是我們的程序控制,而是單片機需要不停工作。 這可不行,這方式中有部分時間不是由程序控制,也就是不可控。這是編程大忌,絕不能讓程序編著編著變成不由我控制了。 那個不停的工作也必須由代碼控制。 剛好,剛學(xué)會使用的 for()循環(huán)結(jié)構(gòu)派上用場了。 給LED燈加上永遠(yuǎn)循環(huán)的控制,main()主程序變成: void main() { for(;;) { P10=0; mDelay(1000); P10=1; mDelay(1000); } } 也就是不用管初始條件,不用管是否結(jié)束,只要做下去就是了。 繼續(xù)優(yōu)化。 功能增加了,代碼也增加了,單片機做的工作也多了,這樣工作時間就花多了,于是工作效率就下降了。 為提高效率,程序可以修改成如下: #include 'reg52.h' #define uint unsigned int #define uchar unsigned char sbit P10= P1^0; void mDelay(uint Delay) { uint i; for(;Delay>0;Delay--) for(i=0;i<> } void main() { P10=!P10; mDelay(1000); } 可以看到,程序長度一下子少了一半,這是因為 C語言中,如果控制的命令只有一行,那么可以省去配對的花括號。 主程序main()中,命令由原來的4行變成2行,只因一個嘆號! 符號!是C語言的一個運算符,就像數(shù)學(xué)課中的 +(加) –(減)一樣,它的作用是取反,單片機只認(rèn)識0與1兩個數(shù),0取反就是1,1取反就是0 。 P10=!P10; 這代碼就實現(xiàn)了每次循環(huán)讓P10在0與1之間改變,達(dá)到控制閃的目的。 五、增補C語言注釋功能為增加程序可讀性,可以在代碼行后面使用//符號,并加入文字說明,幫助理解這段代碼的功能,還可以使用 /* 說明文字 */ 方式注釋,其中說明文字部分可以分多行。 附記: |
|