延時的概念
前面的程序中我們只是點一下燈,熄一下燈,或者點亮另幾個燈,再熄滅,有這編程的功夫還不如裝個開關(guān)吧。你還想咋地?“寶寶想看那種五顏六色還能一閃一閃亮晶晶的”。如果你的女朋友(或男朋友)看到你學(xué)單片機只會點燈時這么對你說,你該咋辦?毫無疑問,咱無論如何也要滿足她(他)呀,還不止只給她看一閃一閃亮晶晶呢,你應(yīng)該要給她看星星,看月亮,看整個宇宙的。畢竟現(xiàn)在這社會競爭這么大,你不給她,另一個朋友能給她,到時你該怎么辦?都到談戀愛這份上了,為了這點小事難道你要“善罷甘休”,真不想以后交換戒指的是你和她?
想清楚了吧,為了未來的那一大幕,我們現(xiàn)在是不是要先來研究研究LED怎么一閃一閃這一小步呢?
我們要讓單片機控制的LED一閃一閃要怎么做呢,是不是讓它亮一會再滅一會?那要怎么讓它亮了再滅呢?難道寫一個點亮程序,再寫一個熄滅程序不停交替下載這兩程序?要是這樣,你女朋友早跟樓下小王跑了。
前面的內(nèi)容中我們說了數(shù)字芯片大多都是需要時序來控制的吧,單片機是數(shù)字芯片的集合體,它的時鐘源就是我們上節(jié)課說的晶振電路,晶振電路產(chǎn)生的時鐘信號輸入到其內(nèi)部,然后其內(nèi)部電路就根據(jù)這個信號按照一定的規(guī)律進(jìn)行運行,如果給到的程序有什么功能它內(nèi)部就隨著這個時鐘一步一步實現(xiàn)我們的功能,如果我們給了一個"空白"程序(空白不是什么都沒有,用無意義來形容可能更貼切,就是不處理正經(jīng)事單純消耗系統(tǒng)時鐘的命令,實現(xiàn)這一點的原理是因為單片機中任何命令執(zhí)行都會消耗時間,類比人休息但時間也在流失道理一樣),那它內(nèi)部在這一段時鐘信號內(nèi)就保持原樣什么都不變動。就像我們?nèi)艘粯?,?dāng)我們在走路時,大腦控制我們的神經(jīng),帶動肌肉往復(fù)運動,我們休息時就讓肌肉放松下來。那我們要怎么控制芯片讓它暫停一下呢,看完上面內(nèi)容是不是就有答案了?給它一段“空白"命令,不行就2段,或者N段。
那在程序中要怎么實現(xiàn)讓單片機執(zhí)行一段空白命令呢?
前面接收C語言基礎(chǔ)時我們是不是講到了循環(huán)語句?我們在循環(huán)語句中不執(zhí)行任何功能,這樣就可以解決這個問題了吧。一個“暫?!惫δ艿某绦蛟谀X海中應(yīng)該有了吧。這種情況我們一般稱為延時,和暫停是一個意思,但不這不是意味著單片機就真的是停下來的吧,就像我們休息的時候心跳也要照樣跳動一樣,“暫停”都是假象。
下面來看一下我們平時常用的延時代碼:
while(n<100)
{
n--;
}
我們也可以改寫成for循環(huán)的方式吧。
for(n=0;n<100;n++)
{
;
}
當(dāng)然,以上程序還可以進(jìn)行嵌套,如果你需要設(shè)置的延時時間很久的話。另外,熟練編程之后我們也可以把大括號對{}省略不寫,至于為什么?自己思考一下。
這里我們在“暫停”時用一個變量n來控制“空白”命令的次數(shù),如果短了就把n加大,如果長了就減小。但到底多少合適呢,也就是我們要怎樣實現(xiàn)可控的延時時間呢,這里我們需要來了解幾個關(guān)于51單片機的新概念,即時鐘周期,機器周期,指令周期。
時鐘周期就是指晶振振蕩的周期,它是晶振頻率的倒數(shù),我們仿真電路中使用的是12MHz的晶振那它就是(1/12M)秒。
機器周期在51單片機中機器周期就是12個時鐘周期,即(1/1M)秒,也就是1微秒(us)。
指令周期就是單片機執(zhí)行一條指令的時間,它由若干個機器周期組成,在單片機中一些簡單指令是1個機器周期,一部分復(fù)雜指令需要2個或多個機器周期。
當(dāng)然我們項目開發(fā)時這些內(nèi)容需要參考芯片數(shù)據(jù)手冊,因為這不是固定的定義,現(xiàn)在很多增強型51單片機的時鐘都不是這樣的了,其他更高級的芯片就不用說了。
那我們到底要怎么確定我們延時程序中的n,能延時多久呢?我們可以通過程序的調(diào)試功能進(jìn)行仿真確定(畢竟是仿真與實際會有一些出入)。最死板的辦法是我們可以把程序下載到單片機中進(jìn)行測試分析呀,用示波器測量單片機引腳電平變化周期就不出來了,然后大概記住一些典型時間,如0.1s,0.5s,1s等延時n的值,以后直接使用也可以。但我們肯定還有更簡單又準(zhǔn)確的延時辦法,因為設(shè)計到定時器的使用,所以今天就先不講解,后面學(xué)習(xí)定時器是在做具體介紹。
寫了這么多內(nèi)容,終于把基礎(chǔ)寫完了,老司機們是不是早就迫不及待的發(fā)車了?那我們先上車開起來吧
閃燈程序
看了以上原理,現(xiàn)在我們想來實現(xiàn)讓一個燈閃起來吧。我們來看看程序內(nèi)容,跟著注釋看一遍是不是就很明了了。
#include //這是52單片機的頭文件
#include //這也是編譯器的一個頭文件后面會用到
typedef unsigned char u8; //typedef 是別名關(guān)鍵字
typedef unsigned int u16;
void main()
{
u8 i,j; //定義變量
P1 = 0xfe;//點亮第一個燈
for(i=0;i<200;i++) //第一段延時
{
for(j=0;j<200;j++)
{
;
}
}
P1 = 0xff;//熄滅第一個燈
for(i=0;i<200;i++)//第二段延時
{
for(j=0;j<200;j++)
{
;
}
}
}
這個代碼里我新引入一個頭文件#include ,它也是編譯軟件自帶的一個頭文件,里面包含對寄存器的循環(huán)操作接下來的程序中我們會使用到。
typedef這個關(guān)鍵字就是取別名的意思,就是把一長串名稱給一個簡短的名字,編程時可以巧用它來簡化某些程序。
使用上一節(jié)內(nèi)容中對P1端口整體賦值的方式進(jìn)行點燈。
點完燈后延時一會,接下來講燈熄滅,熄滅后再進(jìn)行一個延時,這樣就實現(xiàn)了我們的閃燈效果。
創(chuàng)建函數(shù),做配件
如果我們把延時程序打包成一個函數(shù)以后就可以重復(fù)使用了,這是不是爽歪歪?
再來看看修改好的代碼:
#include //這是52單片機的頭文件
#include //這也是編譯器的一個頭文件
typedef unsigned char u8;
typedef unsigned int u16;
//void delay();//聲明函數(shù)
void delay(u8 ms);
void main()
{
P1 = 0xfe;//將LED1點亮
delay(50);//調(diào)用函數(shù)
P1 = 0xff; //將LED1關(guān)閉
delay(50);
}
void delay(u8 ms) //定義函數(shù)
{
u8 i,j;//定義這個延時函數(shù)中的i,j變量
for(i=0;i
}
還是同樣的意思,這么一對比是不是可以看出程序有層次感就出來了,代碼瞬間就好看了很多吧。當(dāng)然把延時函數(shù)打包成void delay()還是void delay(u8 ms)就看個人了,要想使用更靈活那當(dāng)然是后者咯。
循環(huán)流水燈
只是知道閃燈未免太單調(diào),也打動不了人心吧,接下來我們再整點復(fù)雜的,使用循環(huán)位移操作實現(xiàn)流水燈變化,現(xiàn)在先來看看程序。
#include
#include
typedef unsigned char u8;
typedef unsigned int u16;
void delay(u8 ms);
void main()
{
u8 rol = 0xfe;
u8 i;
for(i=0;i<8;i++)
{
//先將P1端口初始化,先設(shè)定P1.0點亮
P1 = rol;
//延時一會
delay(100);
//將變量移位
rol = _cror_(rol,1);
//移位完成在下一個循環(huán),rol的值將重新賦值給P1寄存器
}
}
void delay(u8 ms) //定義函數(shù)
{
u8 i,j;//定義這個延時函數(shù)中的i,j變量
for(i=0;i
}
這個程序?qū)χ?a href="http://www.wenjunhu.com/v/" target="_blank">視頻仿真效果一起看是不是還好理解呢?
_cror_函數(shù)相當(dāng)于一個環(huán)形位移 11111110 向右移動一位就變成 01111111,其高位是將末位補充過來。
如果換成_iror_結(jié)果就不一樣了。
_iror_是單向位移,11111110 向右移動一位變成01111111,其高位是補0。
當(dāng)然左移就和右移剛好相反了。
_crol_函數(shù)是環(huán)形移動,11111110 移動一位就變成 11111101,其末尾是將最高位補充過來。
_irol_中11111110 向左移動一位變成11111100,其末尾是補0。
這兩種循環(huán)反方式在編程時各有用途,所以要區(qū)分得開來,如果覺得不好掌握就多編程嘗試一下。
評論
查看更多