用戶(hù)與單片機(jī)之間的信息交互需要依賴(lài)于兩類(lèi)設(shè)備:輸入設(shè)備和輸出設(shè)備。前邊講的LED小燈、數(shù)碼管、點(diǎn)陣都是輸出設(shè)備,本章就來(lái)學(xué)習(xí)一下最常用的輸入設(shè)備——按鍵,同時(shí)還會(huì)學(xué)到一些硬件電路的基礎(chǔ)知識(shí)與C語(yǔ)言函數(shù)的一些進(jìn)階知識(shí)。
8.1單片機(jī)最小系統(tǒng)解析
8.1.1電源
學(xué)習(xí)過(guò)程中,很多指標(biāo)都是直接用的概念指標(biāo),比如說(shuō)+5V代表1,GND代表0等等。但在實(shí)際電路中的電壓值并不是完全精準(zhǔn)的,那這些指標(biāo)允許范圍是什么呢?隨著學(xué)習(xí)的內(nèi)容不斷增多,大家要慢慢培養(yǎng)一種閱讀數(shù)據(jù)手冊(cè)的能力。
比如,使用STC89C52RC單片機(jī)的時(shí)候,找到它的數(shù)據(jù)手冊(cè)第11頁(yè),看第二項(xiàng)——工作電壓:5.5V~3.4V(5V單片機(jī)),此處就說(shuō)明這個(gè)單片機(jī)正常的工作電壓是個(gè)范圍值,只要電源VCC在5.5V~3.4V之間都可以正常工作,電壓超過(guò)5.5V是絕對(duì)不允許的,會(huì)燒壞單片機(jī),電壓如果低于3.4V,單片機(jī)不會(huì)損壞,但是也不能正常工作。而在這個(gè)范圍內(nèi),最典型、最常用的電壓值就是5V,這就是后面括號(hào)里“5V單片機(jī)”這個(gè)名稱(chēng)的由來(lái)。除此之外,還有一種常用的工作電壓范圍是2.7V~3.6V、典型值是3.3V的單片機(jī),也就是所謂的“3.3V單片機(jī)”。日后隨著大家接觸更多的器件,對(duì)這點(diǎn)會(huì)有更深刻的理解。
打開(kāi)74HC138的數(shù)據(jù)手冊(cè),會(huì)發(fā)現(xiàn)74HC138手冊(cè)的第二頁(yè)也有一個(gè)表格,上邊寫(xiě)了74HC138的工作電壓范圍,最小值是4.75V,額定值是5V,最大值是5.25V,可以得知它的工作電壓范圍是4.75V~5.25V。獲取器件工作參數(shù)的一個(gè)最重要、也是最權(quán)威的途徑,就是查閱該器件的數(shù)據(jù)手冊(cè)。
8.1.2晶振
晶振通常分為無(wú)源晶振和有源晶振兩種類(lèi)型,無(wú)源晶振一般稱(chēng)之為crystal(晶體),而有源晶振則叫做oscillator(振蕩器)。
有源晶振是一個(gè)完整的諧振振蕩器,它是利用石英晶體的壓電效應(yīng)來(lái)起振,所以有源晶振需要供電,當(dāng)把有源晶振電路做好后,不需要外接其它器件,只要給它供電,它就可以主動(dòng)產(chǎn)生振蕩頻率,并且可以提供高精度的頻率基準(zhǔn),信號(hào)質(zhì)量也比無(wú)源信號(hào)要好。
無(wú)源晶振自身無(wú)法振蕩起來(lái),它需要芯片內(nèi)部的振蕩電路一起工作才能振蕩,它允許不同的電壓,但是信號(hào)質(zhì)量和精度較有源晶振差一些。相對(duì)價(jià)格來(lái)說(shuō),無(wú)源晶振要比有源晶振價(jià)格便宜很多。無(wú)源晶振兩側(cè)通常都會(huì)有個(gè)電容,一般其容值都選在10pF~40pF之間,如果手冊(cè)中有具體電容大小的要求則要根據(jù)要求來(lái)選電容,如果手冊(cè)沒(méi)有要求,用20pF就是比較好的選擇,這是一個(gè)長(zhǎng)久以來(lái)的經(jīng)驗(yàn)值,具有極其普遍的適用性。
來(lái)認(rèn)識(shí)下比較常用的兩種晶振的樣貌,如圖8-1和圖8-2所示。
圖8-1 ?有源晶振實(shí)物圖
圖8-2 ?無(wú)源晶振實(shí)物圖
有源晶振通常有4個(gè)引腳,VCC,GND,晶振輸出引腳和一個(gè)沒(méi)有用到的懸空引腳(有些晶振也把該引腳作為使能引腳)。無(wú)源晶振有2個(gè)或3個(gè)引腳,如果是3個(gè)引腳的話(huà),中間引腳接是晶振的外殼,使用時(shí)要接到GND,兩側(cè)的引腳就是晶體的2個(gè)引出腳了,這兩個(gè)引腳作用是等同的,就像是電阻的2個(gè)引腳一樣,沒(méi)有正負(fù)之分。對(duì)于無(wú)源晶振,單片機(jī)上的兩個(gè)晶振引腳接上去即可;而有源晶振,只接到單片機(jī)的晶振的輸入引腳上,輸出引腳上不需要接,如圖8-3和圖8-4所示。
圖8-3 ?無(wú)源晶振接法
圖8-4 ?有源晶振接法
8.1.3復(fù)位電路
分析一下Kingst51開(kāi)發(fā)板上的復(fù)位電路,如圖8-5所示。
圖8-5 ?單片機(jī)復(fù)位電路
當(dāng)這個(gè)電路處于穩(wěn)態(tài)時(shí),電容起到隔離直流的作用,隔離了+5V,而左側(cè)的復(fù)位按鍵是彈起狀態(tài),下方電路就沒(méi)有電壓差的產(chǎn)生,所以按鍵和電容C11以下部分的電位都是和GND相等的,即0V。單片機(jī)是高電平復(fù)位,低電平正常工作,正常工作的電壓是0V。
從沒(méi)有電到上電的瞬間,電容C11上方電壓是5V,下方是0V,根據(jù)初中所學(xué)的知識(shí),電容C11要進(jìn)行充電,正離子從上往下充電,負(fù)電子從GND往上充電,這個(gè)時(shí)候電容對(duì)電路來(lái)說(shuō)相當(dāng)于一根導(dǎo)線(xiàn),全部電壓都加在了R31這個(gè)電阻上,那么RST端口位置的電壓就是5V,隨著電容充電越來(lái)越多,即將充滿(mǎn)的時(shí)候,電流會(huì)越來(lái)越小,那RST端口上的電壓值等于電流乘以R31的阻值,也就會(huì)越來(lái)越小,一直到電容完全充滿(mǎn)后,線(xiàn)路上不再有電流,這個(gè)時(shí)候RST和GND的電位就相等了也就是0V了。
8.2函數(shù)的調(diào)用
在一個(gè)程序的編寫(xiě)過(guò)程中,隨著代碼量的增加,如果把所有的語(yǔ)句都寫(xiě)到main函數(shù)中,一方面程序會(huì)顯得的比較亂;另一方面,當(dāng)同一個(gè)功能需要在不同地方執(zhí)行時(shí),就得再重復(fù)寫(xiě)一遍相同的語(yǔ)句。此時(shí),如果把一些零碎的功能單獨(dú)寫(xiě)成一個(gè)函數(shù),在需要它們時(shí)只需進(jìn)行一些簡(jiǎn)單的函數(shù)調(diào)用,這樣既有助于程序結(jié)構(gòu)的清晰條理,又可以避免大塊的代碼重復(fù)。
在實(shí)際工程項(xiàng)目中,一個(gè)程序通常都是由很多個(gè)子程序模塊組成的,一個(gè)模塊實(shí)現(xiàn)一個(gè)特定的功能,在C語(yǔ)言中,這個(gè)模塊就用函數(shù)來(lái)表示。一個(gè)C程序一般由一個(gè)主函數(shù)和若干個(gè)其他函數(shù)構(gòu)成。主函數(shù)可以調(diào)用其它函數(shù),其它函數(shù)也可以相互調(diào)用,但其它函數(shù)不能調(diào)用主函數(shù)。在51單片機(jī)程序中,還有中斷服務(wù)函數(shù),是當(dāng)相應(yīng)的中斷到來(lái)后自動(dòng)調(diào)用的,不需要也不能由其它函數(shù)來(lái)調(diào)用。
函數(shù)調(diào)用的一般形式是:
函數(shù)名 (實(shí)參列表)
函數(shù)名就是需要調(diào)用的函數(shù)名稱(chēng),實(shí)參列表是根據(jù)實(shí)際需求調(diào)用函數(shù)要傳遞給被調(diào)用函數(shù)的參數(shù)列表,不需要傳遞參數(shù)時(shí)只保留括號(hào),傳遞多個(gè)參數(shù)時(shí)參數(shù)之間要用逗號(hào)隔開(kāi)。
那么先舉例看一下函數(shù)調(diào)用使程序結(jié)構(gòu)更加條理清晰方面的作用?;仡櫼幌聢D6-1所示的程序流程圖和為實(shí)現(xiàn)它而編寫(xiě)的程序代碼,相對(duì)來(lái)說(shuō)這個(gè)主函數(shù)的結(jié)構(gòu)就比較復(fù)雜了,很難一眼看清楚它的執(zhí)行流程。那么如果把其中最重要的兩件事——秒計(jì)數(shù)和數(shù)碼管動(dòng)態(tài)掃描功能都用單獨(dú)的函數(shù)來(lái)實(shí)現(xiàn)會(huì)怎樣呢?來(lái)看以下程序。
#include
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
unsigned char code LedChar[] = { //數(shù)碼管顯示字符轉(zhuǎn)換表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = { //數(shù)碼管顯示緩沖區(qū),初值0xFF確保啟動(dòng)時(shí)都不亮
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
void SecondCount();
void LedRefresh();
void main()
{
ENLED = 0; //使能U3,選擇控制數(shù)碼管
ADDR3 = 1; //因?yàn)樾枰獎(jiǎng)討B(tài)改變ADDR0-2的值,所以不需要再初始化了
TMOD = 0x01; //設(shè)置T0為模式1
TH0 = 0xFC; //為T(mén)0賦初值0xFC67,定時(shí)1ms
TL0 = 0x67;
TR0 = 1; //啟動(dòng)T0
while (1)
{
if (TF0 == 1) //判斷T0是否溢出
{
TF0 = 0; //T0溢出后,清零中斷標(biāo)志
TH0 = 0xFC; //并重新賦初值
TL0 = 0x67;
SecondCount(); //調(diào)用秒計(jì)數(shù)函數(shù)
LedRefresh(); //調(diào)用顯示刷新函數(shù)
}
}
}
/* 秒計(jì)數(shù)函數(shù),每秒進(jìn)行一次秒數(shù)+1,并轉(zhuǎn)換為數(shù)碼管顯示字符 */
void SecondCount()
{
static unsigned int cnt = 0; //記錄T0中斷次數(shù)
static unsigned long sec = 0; //記錄經(jīng)過(guò)的秒數(shù)
cnt++; //計(jì)數(shù)值自加1
if (cnt >= 1000) //判斷T0溢出是否達(dá)到1000次
{
cnt = 0; //達(dá)到1000次后計(jì)數(shù)值清零
sec++; //秒計(jì)數(shù)自加1
LedBuff[0] = LedChar[sec%10];
LedBuff[1] = LedChar[sec/10%10];
LedBuff[2] = LedChar[sec/100%10];
LedBuff[3] = LedChar[sec/1000%10];
LedBuff[4] = LedChar[sec/10000%10];
LedBuff[5] = LedChar[sec/100000%10];
}
}
/* 數(shù)碼管動(dòng)態(tài)掃描刷新函數(shù) */
void LedRefresh()
{
static unsigned char i = 0; //動(dòng)態(tài)掃描的索引
switch (i)
{
case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;
case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;
default: break;
}
}
看一下,主函數(shù)的結(jié)構(gòu)是不是清晰的多了——每隔1ms就去干兩件事,至于這兩件事是什么交由各自的函數(shù)去實(shí)現(xiàn)。還請(qǐng)大家注意一點(diǎn):原來(lái)程序中的i、cnt、sec這三個(gè)變量在放到單獨(dú)的函數(shù)中后,都加了static關(guān)鍵字而變成了靜態(tài)變量。
當(dāng)然,這里刻意把程序功能做了這樣的劃分,主要目的還是來(lái)講解函數(shù)的調(diào)用,對(duì)于這個(gè)程序即使不劃分函數(shù)也復(fù)雜不到哪里去,但繼續(xù)學(xué)下去就能領(lǐng)會(huì)到劃分功能函數(shù)的必要了。現(xiàn)在還是把注意力放在學(xué)習(xí)函數(shù)調(diào)用上,有以下幾點(diǎn)需要注意:
1、函數(shù)調(diào)用的時(shí)候,不需要加函數(shù)類(lèi)型。在主函數(shù)內(nèi)調(diào)用SecondCount()和LedRefresh()時(shí)都沒(méi)有加void。
2、調(diào)用函數(shù)與被調(diào)用函數(shù)的位置關(guān)系,C語(yǔ)言規(guī)定:函數(shù)在被調(diào)用之前,必須先被定義或聲明。意思就是說(shuō):在一個(gè)文件中,一個(gè)函數(shù)應(yīng)該先定義,然后才能被調(diào)用,也就是調(diào)用函數(shù)應(yīng)位于被調(diào)用函數(shù)的下方。但是作為一種通常的編程規(guī)范,推薦main函數(shù)寫(xiě)在最前面(因?yàn)樗鸬教峋V挈領(lǐng)的作用),其后再定義各個(gè)功能函數(shù),而中斷函數(shù)則寫(xiě)在最后。那么主函數(shù)要調(diào)用定義在它之后的函數(shù)怎么辦呢?就在文件開(kāi)頭,所有函數(shù)定義之前,開(kāi)辟一塊區(qū)域,叫做函數(shù)聲明區(qū),用來(lái)把被調(diào)用的函數(shù)聲明一下,該函數(shù)就可以被隨意調(diào)用了。如上述例程所示。
3、函數(shù)聲明的時(shí)候必須加函數(shù)類(lèi)型,函數(shù)的形式參數(shù),最后加上一個(gè)分號(hào)表示結(jié)束。函數(shù)聲明行與函數(shù)定義行的唯一區(qū)別就是最后的分號(hào),其它的都必須保持一致。這點(diǎn)請(qǐng)尤其注意,初學(xué)者很容易因粗心大意而搞錯(cuò)分號(hào)或是修改了定義行中的形參卻忘了修改聲明行中的形參,導(dǎo)致程序編譯不過(guò)。
審核編輯 黃宇
-
無(wú)源晶振
+關(guān)注
關(guān)注
1文章
887瀏覽量
17845 -
晶振
+關(guān)注
關(guān)注
35文章
3556瀏覽量
73414
發(fā)布評(píng)論請(qǐng)先 登錄
【「Linux 設(shè)備驅(qū)動(dòng)開(kāi)發(fā)(第 2 版)」閱讀體驗(yàn)】+讀內(nèi)核處理的核心輔助函數(shù)
第7章 變量進(jìn)階與點(diǎn)陣LED(7.3 7.4)
第7章 變量進(jìn)階與點(diǎn)陣LED(7.1 7.2)
如何進(jìn)行按鍵檢測(cè)
【迅為工業(yè)RK3568穩(wěn)定可靠】itop-3568開(kāi)發(fā)板驅(qū)動(dòng)開(kāi)發(fā)第4章驅(qū)動(dòng)模塊傳參實(shí)驗(yàn)
嵌入式從入門(mén)到進(jìn)階,怎么學(xué)?
Key_Scan按鍵掃描函數(shù)詳解
現(xiàn)代集成電路半導(dǎo)體器件
RK3568驅(qū)動(dòng)指南|第十二篇 GPIO子系統(tǒng)-第130章 GPIO的調(diào)試方法
RK3568驅(qū)動(dòng)指南|第十二篇 GPIO子系統(tǒng)-第135章 GPIO子系統(tǒng)與pinctrl子系統(tǒng)相結(jié)合實(shí)驗(yàn)
第六章 GPIO輸入——按鍵檢測(cè)
RK3568驅(qū)動(dòng)指南|驅(qū)動(dòng)基礎(chǔ)進(jìn)階篇-進(jìn)階7 向系統(tǒng)中添加一個(gè)系統(tǒng)調(diào)用
單片機(jī)外圍電路設(shè)計(jì) (第2版)
【北京迅為】iTOP-RK3568開(kāi)發(fā)板OpenHarmony系統(tǒng)南向驅(qū)動(dòng)開(kāi)發(fā)-第4章 UART基礎(chǔ)知識(shí)
【北京迅為】itop-3568 開(kāi)發(fā)板openharmony鴻蒙燒寫(xiě)及測(cè)試-第1章 體驗(yàn)OpenHarmony—燒寫(xiě)鏡像
第8章 函數(shù)進(jìn)階與按鍵(8.1 8.2)
評(píng)論