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