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