單片機(jī)C語言學(xué)習(xí)好資料_第1頁
單片機(jī)C語言學(xué)習(xí)好資料_第2頁
單片機(jī)C語言學(xué)習(xí)好資料_第3頁
單片機(jī)C語言學(xué)習(xí)好資料_第4頁
單片機(jī)C語言學(xué)習(xí)好資料_第5頁
已閱讀5頁,還剩92頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

前言......錯誤!未定義書簽。

基礎(chǔ)知識:單片機(jī)編程基礎(chǔ)....2

第一節(jié):單數(shù)碼管按鍵顯示.?.13

第二節(jié):雙數(shù)碼管可調(diào)秒表???24

第三節(jié):十字路口交通燈……27

第四節(jié):數(shù)碼管驅(qū)動........35

第五節(jié):鍵盤驅(qū)動............40

第六節(jié):低頻頻率計........68

第七節(jié):電子表..............79

第八節(jié):用行口應(yīng)用........86

基礎(chǔ)知識:單片機(jī)編程基礎(chǔ)

單片機(jī)的外部結(jié)構(gòu):

1、DIP40雙列直插;

2、PO,PLP2,P3四個8位準(zhǔn)雙向

I/O引腳;(作為I/O輸入時,要先

輸出高電平)

3、電源VCC(PIN40)和地線GND

(PIN20);

4、高電平復(fù)位RESET(PIN9);(10uF

電容接VCC與RESET,即可實現(xiàn)上電

復(fù)位)

5、內(nèi)置振蕩電路,外部只要接晶體

至XI(PIN18)和XO(PIN19);(頻

率為王頻的12倍)

6、程序配置EA(PIN31)接高電平

VCC;(運行單片機(jī)內(nèi)部ROM中的程

序)

7、P3支持第二功能:RXD、TXD、INTO、

INTI、TO、T1

單片機(jī)內(nèi)部I/O部件:(所為學(xué)習(xí)單

片機(jī),實際上就是編程控制以下I/O

部件,完成指定任務(wù))

1、四個8位通用I/O端口,對應(yīng)引

腳PO、PKP2和P3;

2、兩個16位定時計數(shù)器;(TMOD,

TCON,TLO,THO,TLLTH1)

3、一個串行通信接口;(SCON,SBUF)

4、一個中斷控制器;(IE,IP)

針對AT89C52單片機(jī),頭文件

AT89x52.h給出了SFR特殊功能寄存

器所有端口的定義。教科書的160頁

給出了針對MCS51系列單片機(jī)的C語

言擴(kuò)展變量類型。

C語言編程基礎(chǔ):

1、十六進(jìn)制表示字節(jié)0x5a:二進(jìn)制

為oionoiOB;0x6E為ononio。

2、如果將一個16位二進(jìn)數(shù)賦給一個

8位的字節(jié)變量,則自動截斷為低8

位,而丟掉高8位。

3、++var表示對變量var先增一;

var一表示對變量后減一o

4>xI=OxOf;表示為x=xOxOf;

5、TMOD=(TMOD&OxfO)|0x05;

表示給變量TMOD的低四位賦值

0x5,而不改變TMOD的高四位。

6、While(1);表示無限執(zhí)行該語

句,即死循環(huán)。語句后的分號表示

空循環(huán)體,也就是{;}

在某引腳輸出高電平的編程方法:

(比如Pl.3(PIN4)引腳)

ttinclude<AT89x52.h>//該頭文檔

中有單片機(jī)內(nèi)部資源的符號化定義,

其中包含PL3

voidmain(void)//void表示

沒有輸入?yún)?shù),也沒有函數(shù)返值,這

入單片機(jī)運行的復(fù)位入口

{

Pl_3=1;//給Pl_3賦值L

引腳P1.3就能輸出高電平VCC

While(1);〃死循環(huán),相當(dāng)

LOOP:gotoLOOP;

}

注意:P0的每個引腳要輸出高電平

時,必須外接上拉電阻(如4K7)至

VCC電源。

在某引腳輸出低電平的編程方法:

(比如P2.7引腳)

ttinclude<AT89x52.h>〃該頭文檔

中有單片機(jī)內(nèi)部資源的符號化定義,

其中包含P2.7

voidmain(void)//void表zj\

沒有輸入?yún)?shù),也沒有函數(shù)返值,這

入單片機(jī)運行的復(fù)位入口

{

P2_7=0;//給P2_7賦值0,

引腳P2.7就能輸出低電平GND

While(1);//死循環(huán),相當(dāng)

LOOP:gotoLOOP;

在某引腳輸出方波編程方法:(比如

P3.1引腳)

ttinclude<AT89x52.h>//該頭文檔

中有單片機(jī)內(nèi)部資源的符號化定義,

其中包含P3.1

voidmain(void)//void表示

沒有輸入?yún)?shù),也沒有函數(shù)返值,這

入單片機(jī)運行的復(fù)位入口

{

While(1)//非零表示真,如果

為真則執(zhí)行下面循環(huán)體的語句

{

P3_l=1;//給P3_l賦值L

引腳P3.1就能輸出高電平VCC

P3_l=0;〃給P3_l賦值0,

引腳P3.1就能輸出低電平GND

)〃由于一直為真,所以不斷

輸出高、低、高、低……,從而形成

方波

將某引腳的輸入電平取反后,從另一

個引腳輸出:比如P0.4=

NOT(Pl.1))

ttinclude<AT89x52.h>//該頭文檔

中有單片機(jī)內(nèi)部資源的符號化定義,

其中包含P0.4和PL1

voidmain(void)//void表示

沒有輸入?yún)?shù),也沒有函數(shù)返值,這

入單片機(jī)運行的復(fù)位入口

{

Pl_l=1;//初始化。Pl.1作

為輸入,必須輸出高電平

While(1)〃非零表示真,如果

為真則執(zhí)行下面循環(huán)體的語句

{

if(Pl_l==1)〃讀取

Pl.h就是認(rèn)為Pl.1為輸入,如果

P1.1輸入高電平VCC

{P0_4=0;}//給P0_4賦值

0,引腳P0.4就能輸出低電平GND

else//否則Pl.1輸入為

低電平GND

//{P0_4=0;}//給P0_4賦值

0,引腳P0.4就能輸出低電平GND

{P0_4=1;}//給P0_4賦值

L引腳P0.4就能輸出高電平VCC

}//由于一直為真,所以不斷

根據(jù)PL1的輸入情況,改變P0.4的

輸出電平

將某端口8個引腳輸入電平,低四位

取反后,從另一個端口8個引腳輸出:

(比如P2=NOT(P3))

ttinclude<AT89x52.h>〃該頭文檔

中有單片機(jī)內(nèi)部資源的符號化定義,

其中包含P2和P3

voidmain(void)//void表示

沒有輸入?yún)?shù),也沒有函數(shù)返值,這

入單片機(jī)運行的復(fù)位入口

P3=Oxff;//初始化。P3作為

輸入,必須輸出高電平,同時給P3

口的8個引腳輸出高電平

While(1)//非零表示真,如果

為真則執(zhí)行下面循環(huán)體的語句

{〃取反的方法是異或1,而

不取反的方法則是異或0

P2=P3^0x0f//讀取P3,就是認(rèn)

為P3為輸入,低四位異或者1,即

取反,然后輸出

)〃由于一直為真,所以不斷

將P3取反輸出到P2

)

注意:一個字節(jié)的8位D7、D6至DO,

分別輸出到P3.7、P3.6至P3.0,比

如P3=0x0f,則P3.7、P3.6、P3.5、

P3.4四個引腳都輸出低電平,而

P3.3、P3.2、P3.1、P3.0四個引腳都

輸出高電平。同樣,輸入一個端口P2,

即是將P2.7、P2.6至P2.0,讀入到

一個字節(jié)的8位D7、D6至DO。

第一節(jié):單數(shù)碼管按鍵顯示

單片機(jī)最小系統(tǒng)的硬件原理接線圖:

1、接電源:VCC(PIN40)、GND

(PIN20)o加接退耦電容O.luF

2、接晶體:X1(PIN18)、X2(PIN19)。

注意標(biāo)出晶體頻率(選用12MHz),

還有輔助電容30pF

3、接復(fù)位:RES(PIN9)O接上電復(fù)

位電路,以及手動復(fù)位電路,分析

復(fù)位工作原理

4、接配置:EA(PIN31)o說明原因。

發(fā)光二極的控控制:單片機(jī)I/O輸出

將一發(fā)光二極管LED的正極(陽極)

接PL1,LED的負(fù)極(陰極)接地GNDo

只要PL1輸出高電平VCC,LED就正

向?qū)ǎ▽?dǎo)通時LED上的壓降大于

IV),有電流流過LED,至發(fā)LED發(fā)亮。

實際上由于Pl.1高電平輸出電阻為

10K,起到輸出限流的作用,所以流

過LED的電流小于(5VTV)/10K二

0.4mAo只要Pl.1輸出低電平GND,

實際小于0.3V,LED就不能導(dǎo)通,結(jié)

果LED不亮。

開關(guān)雙鍵的輸入:輸入先輸出高

一個按鍵KEY_ON接在PL6與GND之

間,另一個按鍵KEY_OFF接PL7與

GND之間,按KEY_ON后LED亮,按

KEY_OFF后LED滅。同時按下LED半

亮,LED保持后松開鍵的狀態(tài),即ON

亮OFF滅。

ttinclude<at89x52.h>

ttdefineLEDPl」//用符號

LED代替Pl_l

ttdefineKEY_ONPr6//用符號

KEYON代替Pl6

ttdefineKEY_OFFP「7//用符

號KEY_OFF代替Pl_7

voidmain(void)〃單片機(jī)

復(fù)位后的執(zhí)行入口,void表示空,無

輸入?yún)?shù),無返回值

(

KEY_0N=1;//作為輸入,首先輸

出高,接下KEY_0N,Pl.6則接地為0,

否則輸入為1

KEY_0FF=1;//作為輸入,首先

輸出高,接下KEY_0FF,PL7則接地

為0,否則輸入為1

While(1)//永遠(yuǎn)為真,所以永

遠(yuǎn)循環(huán)執(zhí)行如下括號內(nèi)所有語句

if(KEY_ON==O)LED=1;//是

KEY_ON接下,所示PL1輸出高,LED

if(KEY_OFF==O)LED=O;//是

KEY_OFF接下,所示Pl.1輸出低,LED

}//松開鍵后,都不給LED賦值,

所以LED保持最后按鍵狀態(tài)。

//同時按下時,LED不斷亮滅,各

占一半時間,交替頻率很快,由于

人眼慣性,看上去為半亮態(tài)

數(shù)碼管的接法和驅(qū)動原理

一支七段數(shù)碼管實際由8個發(fā)光

二極管構(gòu)成,其中7個組形構(gòu)成數(shù)字

8的七段筆畫,所以稱為七段數(shù)碼管,

而余下的1個發(fā)光二極管作為小數(shù)

點。作為習(xí)慣,分別給8個發(fā)光二極

管標(biāo)上記號:a,b,c,d,e,f,g,ho對

應(yīng)8的頂上一畫,按順時針方向排,

中間一畫為g,小數(shù)點為h。

我們通常又將各二極與一個字

節(jié)的8位對應(yīng),

a(DO),b(DI),c(D2),d(D3),e(D4),f

(D5),g(D6),h(D7),相應(yīng)8個發(fā)光二

極管正好與單片機(jī)一個端口Pn的8

個引腳連接,這樣單片機(jī)就可以通過

引腳輸出高低電平控制8個發(fā)光二極

的亮與滅,從而顯示各種數(shù)字和符

號;對應(yīng)字節(jié),引腳接法為:a(Pn.0),

b(Pn.1),c(Pn.2),d(Pn.3),

e(Pn.4),f(Pn.5),g(Pn.6),

h(Pn.7)o

如果顯示字符共陰極段選碼其陽極段選碼顯示字符共陰極段選碼共陽極段選碼

03FHC0HC39HC6H

將8個106HF9HD5EHA1H

25BHA4HE79H86H

34FHB0HF71H84H

466H99HP73H82H

發(fā)光二56DH92HU3EHC1H

67DH82Hr31HCEH

707HF8Hy6EH91H

極管的87FH80H8FFH00H

96FH90H“滅”00HFFH

A77H88H

負(fù)極B7CH83H

(陰極)內(nèi)接在一起,作為數(shù)碼管的

一個引腳,這種數(shù)碼管則被稱為共陰

數(shù)碼管,共同的引腳則稱為共陰極,

8個正極則為段極。否則,如果是將

正極(陽極)內(nèi)接在一起引出的,則

稱為共陽數(shù)碼管,共同的引腳則稱為

共陽極,8個負(fù)極則為段極。

以單支共陰數(shù)碼管為例,可將段極

接到某端口Pn,共陰極接GND,則可

編寫出對應(yīng)十六進(jìn)制碼的七段碼表

字節(jié)數(shù)據(jù)如右圖:

16鍵碼顯示的程序

我們在P1端口接一支共陰數(shù)碼管

SLED,在P2、P3端口接16個按鍵,

分別編號為KEY_O、KEY_1到KEY_F,

操作時只能按一個鍵,按鍵后SLED

顯示對應(yīng)鍵編號。

ttinclude<at89x52.h>

ttdefineSLEDPl

ttdefineKEY0P2P

ttdefineKEY_1P2」

ttdefineKEY_2P2~2

ttdefineKEY_3P2~3

ttdefineKEY_4P2N

ttdefineKEY_5P2"

ttdefineKEY_6P2飛

ttdefineKEY_7P2M

ttdefineKEY_8P3飛

ttdefineKEY_9P3」

ttdefineKEY_AP3^2

ttdefineKEY_BP3~3

ttdefineKEY_CP3N

ttdefineKEY_DP3~5

ttdefineKEY_EP3飛

ttdefineKEYFP3/

CodeunsignedcharSeg7code[16]二

//用十六進(jìn)數(shù)作為數(shù)組下標(biāo),可直

接取得對應(yīng)的七段編碼字節(jié)

//012345

6789Ab

CdEF

{0x3f,0x06,0x5b,0x4f,0x66,

0x6d,0x7d,0x07,0x7f,0x6f,0x77,

0x7c,0x39,0x5e,0x79,0x71};

voidmain(void)

(

unsignedchari=0;//作為數(shù)組

下標(biāo)

P2=Oxff;//P2作為輸入,初始

化輸出高

P3=Oxff;//P3作為輸入,初始

化輸出高

While(1)

if(KEY_O==0)i=0;

if(KEY_1==0)i=l;

if(KEY_2==0)i=2;

if(KEY_3==0)i=3;

if(KEY_4==0)i=4;

if(KEY_5==0)i=5;

if(KEY_6==0)i=6;

if(KEY_7==0)i=7;

if(KEY_8==0)i=8;

if(KEY_9==0)i=9;

if(KEYA==0)i=0xA;

if(KEY_B==0)i=OxB;

if(KEY_C==0)i=OxC;

if(KEY_D==0)i=OxD;

if(KEYE==0)i=OxE;

if(KEYF==0)i=OxF;

SLED=Seg7Code[i];〃開始

時顯示3根據(jù)i取應(yīng)七段編碼

第二節(jié):雙數(shù)碼管可調(diào)秒表

解:只要滿足題目要求,方法越簡單

越好。由于單片機(jī)I/。資源足夠,所

以雙數(shù)碼管可接成靜態(tài)顯示方式,兩

個共陰數(shù)碼管分別接在P1(秒十位)

和P2(秒個位)口,它們的共陰極都

接地,安排兩個按鍵接在P3.2(十位

數(shù)調(diào)整)和P3.3(個位數(shù)調(diào)整)上,

為了方便計時,選用12MHz的晶體。

為了達(dá)到精確計時,選用定時器方式

2,每計數(shù)250重載一次,即250us,

定義一整數(shù)變量計數(shù)重載次數(shù),這樣

計數(shù)4000次即為一秒。定義兩個字

節(jié)變量S10和S1分別計算秒十位和

秒個位。編得如下程序:

ttinclude<at89x52.h>

CodeunsignedcharSeg7code[16]二

//用十六進(jìn)數(shù)作為數(shù)組下標(biāo),可直

接取得對應(yīng)的七段編碼字節(jié)

//012345

6789Ab

CdEF

{0x3f,0x06,0x5b,0x4f,0x66,

0x6d,0x7d,0x07,0x7f,0x6f,0x77,

0x7c,0x39,0x5e,0x79,0x71};

voidmain(void)

{

unsignedintus250=0;

unsignedcharslO=0;

unsignedcharsi=0;

unsignedcharkeylO=0;//記憶

按鍵狀態(tài),為1按下

unsignedcharkeyl=0;//記

憶按鍵狀態(tài),為1按下

//初始化定時器TimerO

TMOD=(TMOD&OxFO)0x02;

TH1=-250;//對于8位二進(jìn)數(shù)來

說,-250二6,也就是加250次1時為

256,即為0

TRI=1;

while⑴{

//---------循環(huán)1

Pl=Seg7Code[slO];〃顯示

秒十位

P2=Seg7Code[si];〃顯示

秒個位

while(1){

//---------循環(huán)2

〃計時處理

if(TFO==1){

TFO=0;

if(++us250>=4000){

us250=0;

if(++sl>=10){

si=0;

if(++slO>=6)slO=0;

)

break;〃結(jié)束“循環(huán)2”,

修改顯示

)

)

〃按十位鍵處理

P3.2=1;//P3.2作為輸入,

先要輸出高電平

if(keylO=1){〃等松鍵

if(P3.2==1)key10=0;

else{〃未按鍵

if(P3.2==0){

keylO=1;

if(++slO>=6)slO=0;

break;〃結(jié)束“循環(huán)2”,

修改顯示

〃按個位鍵處理

P3.3=1;〃P3.3作為輸入,

先要輸出高電平

if(keyl==1)//等松鍵

{if(P3.3==1)key1=0;}

else{〃未按鍵

if(P3.3==0){keyl=1;

if(++sl>=10)si=0;

break;〃結(jié)束“循環(huán)2”,

修改顯示

)

)

}〃循環(huán)2'end

}//循環(huán)1'end

}//main'end

第三節(jié):十字路口交通燈

如果一個單位時間為1秒,這里設(shè)定

的十字路口交通燈按如下方式四個

步驟循環(huán)工作:

?60個單位時間,南北紅,東西

綠;

?10個單位時間,南北紅,東西

黃;

?60個單位時間,南北綠,東西

紅;

?10個單位時間,南北黃,東西

紅;

解:用P1端口的6個引腳控制交通

燈,高電平燈亮,低電平燈滅。

ttinclude<at89x52.h>

//sbit用來定義一個符號位地址,方

便編程,提高可讀性,和可移植性

sbitSNRed=P「0;//南北方向紅

sbitSNYellow=Pl[;//南北方

向黃燈

sbitSNGreen=P12;//南北方向

綠燈

sbitEWRed=P「3;//東西方向紅

sbitEWYellow=P14;//東西方

向黃燈

sbitEWGreen=P15;//東西方向

綠燈

/*用軟件產(chǎn)生延時一個單位時間

voidDelaylUnit(void)

unsignedinti,j;

for(i=0;i<1000;i++)

for(j<0;j<1000;j++);//通

過實測,調(diào)整j循環(huán)次數(shù),產(chǎn)生1ms

延時

//還可以通過生成匯編程序來計算

指令周期數(shù),結(jié)合晶體頻率來調(diào)整j

循環(huán)次數(shù),接近1ms

)

/*延時n個單位時間*/

voidDelay(unsignedintn){for(;

n!=0;n--)DelaylUnit();}

voidmain(void)

while(1)

SNRed=O;SNYellow=0;

SNGreen=l;EWRed=l;EWYellow=0;

EWGreen=O;Delay(60);

SNRed=0;SNYellow=l;

SNGreen=0;EWRed=l;EWYellow=0;

EWGreen=0;Delay(10);

SNRed=l;SNYellow=0;

SNGreen=0;EWRed=0;EWYellow=0;

EWGreen=l;Delay(60);

SNRed=1;SNYellow=0;

SNGreen=0;EWRed=0;EWYellow=l;

EWGreen=0;Delay(10);

第四節(jié):數(shù)碼管驅(qū)動

顯示“12345678”

P1端口接8聯(lián)共陰數(shù)碼管SLED8的段

極:PL7接段h,…,P1.0接段a

P2端口接8聯(lián)共陰數(shù)碼管SLED8的段

極:P2.7接左邊的共陰極,…,P2.0

接右邊的共陰極

方案說明:晶振頻率fosc二12MHz,數(shù)

碼管采用動態(tài)刷新方式顯示,在1ms

定時斷服務(wù)程序中實現(xiàn)

ttinclude<at89x92.h>

unsignedcharDisBuf[8];//全局

顯示緩沖區(qū),DisBuf[0]對應(yīng)右SLED,

DisBuf[7]對應(yīng)左SLED,

voidDisplayBrush(void)

{codeunsignedchar

cathode[8]={Oxfe,Oxfd,Oxfb,0xf7

,Oxef,Oxdf,Oxbf,0x7f};〃陰極控

制碼

Codeunsignedchar

Seg7Code[16]=//用十六進(jìn)數(shù)作為

數(shù)組下標(biāo),可直接取得對應(yīng)的七段編

碼字節(jié)

{0x3f,0x06,0x5b,0x4f,0x66,0x6d

,0x7d,0x07,0x7f,0x6f,0x77,0x7c,

0x39,0x5e,0x79,0x71};

staticunsignedchari=0;//

(0<i<7)循環(huán)刷新顯示,由于是

靜態(tài)變量,此賦值只做一次。

P2=Oxff;//顯示消隱,以免下

一段碼值顯示在前一支SLED

Pl=Seg7Code[DisBuf[i]];//

從顯示緩沖區(qū)取出原始數(shù)據(jù),查表變

為七段碼后送出顯示

P2=cathode[i];〃將對

應(yīng)陰極置低,顯示

if(++i>=8)i=0;//指向下

一個數(shù)碼管和相應(yīng)數(shù)據(jù)

)

voidTimerOIntRoute(void)

interrupt1

TLO=-1000;〃由于TLO只有

8bits,所以將(-1000)低8位賦給

TL0

TH0=(-1000)?8;〃取(-1000)

的高8位賦給TH0,重新定時1ms

DisplayBrush();

)

voidTimerOInit(void)

{TMOD=(TMOD&OxfO)|0x01;//

初始化,定時器TO,工作方式1

TLO=-1000;//定時1ms

THO=(-1000)?8;

TRO=1;//允許TO開始計數(shù)

ETO=1;//允許TO計數(shù)溢出時

產(chǎn)生中斷請求

voidDisplay(unsignedcharindex,

unsignedchar

dataValue){DisBuf[index_=

dataValue;}

voidmain(void)

(

unsignedchari;

for(i=0;i<8;i++){Display(i,

8-i);}//DisBuf[0]為右,DisBuf[7]

為左

TimerOInit();

EA=1;//允許CPU響應(yīng)中斷

請求

While(1);

第五節(jié):鍵盤驅(qū)動

指提供一些函數(shù)給任務(wù)調(diào)用,獲取按

鍵信息,或讀取按鍵值。

定義一個頭文檔<KEY.H>,描述可用

函數(shù),如下:

#ifndef_KEY_H_//防止重復(fù)引用

該文檔,如果沒有定義過符號

_KEY_H_,則編譯下面語句

#define_KEY_H_//只要引用過一

次,即ttinclude<key.h>,則定義

符號_KEY_H_

unsignedcharkeyHit(void);//

如果按鍵,則返回非0,否則返回0

unsignedcharkeyGet(void);//

讀取按鍵值,如果沒有按鍵則等待到

按鍵為止

voidkeyPut(unsignedchar

ucKeyVal);//保存按鍵值ucKeyVal

到按鍵緩沖隊列末

voidkeyBack(unsignedchar

ucKeyVal);//退回鍵值ucKeyVal到

按鍵緩沖隊列首

#endif

定義函數(shù)體文檔KEY.C,如下:

#include“key.h"

#defineKeyBufSize16〃定義按

鍵緩沖隊列字節(jié)數(shù)

unsignedchar

KeyBuf[KeyBufSize];//定義一

個無符號字符數(shù)組作為按鍵緩沖隊

列。該隊列為先進(jìn)

//先出,循環(huán)存取,下

標(biāo)從0到KeyBufSize-1

unsignedcharKeyBufWp=O;//作為

數(shù)組下標(biāo)變量,記錄存入位置

unsignedcharKeyBufRp=O;〃作為

數(shù)組下標(biāo)變量,記錄讀出位置

//如果存入位置與讀出位置相同,則

表明隊列中無按鍵數(shù)據(jù)

unsignedcharkeyHit(void)

{if(KeyBufWp==KeyBufRp)

return(0);elsereturn(1);}

unsignedcharkeyGet(void)

{unsignedcharretVal;//暫存

讀出鍵值

while(keyHit()=0);〃等待

按鍵,因為函數(shù)keyHit()的返回值為

0表示無按鍵

retVal二KeyBuf[KeyBufRp];

//從數(shù)組中讀出鍵值

if(++KeyBufRp>=KeyBufSize)

KeyBufRp=0;//讀位置加1,超出隊

列則循環(huán)回初始位置

return(retVal);

voidkeyPut(unsignedchar

ucKeyVal)

{KeyBuf[KeyBufWp]=ucKeyVal;

//鍵值存入數(shù)組

if(++KeyBufWp>=KeyBufSize)

KeyBufWp=O;//存入位置加1,超出

隊列則循環(huán)回初始位置

)

vlxsixslzvlxsixsixsixslzsixxlxslzsixsixsix*1*sixsix

?T*XTX^T**TX?TXXTXXTX*TX?TXXTX

%lzsl*sixsixvizsix%lzsl*sixsix%lzsixslzxlxsixvizsix%lzsixvizsix

?p**T**7XXIXXTXXT%XTS*TXXjXXTXZT%*TXXjXX7XXjXXT^*TX^TX?TXXTX*TX^TX*T%*TX

vL*vt*%tzxt*%tzv!<sixsizxlz%tzsizxl<vl<>lxxlx%lx

XTS*TS^TS?TX*TXZTX^TS^TXZT**TX^TX?TXZT**TX^TX?TX^TX^T%^T%^TXXTXXTX^TS*TS

由于某種原因,讀出的按鍵,沒有用,

但其它任務(wù)要用該按鍵,但傳送又不

方便。此時可以退回按鍵隊列。就如

取錯了信件,有必要退回一樣

xlxsix>1^xlxxlxxlx^lx*1^v!x>lx*1<^lxxlx>lx*1<^lxxlxvt*

?TXXIX^TX?TXXTXXTXXIXXTXXT*ZTX^T*?TXXTXXIXXTXXT%^TX*TXXIXXTXXT*^T%ZT*XTXXTXXIXXT%^TXXTXXTXXIX

vtxviz%!<xlxsixslzsixsixsix%!<xlxsixslzx!<xt*xlx^lzsixsixsix^lzsixslzx!<

*TXXT*XTXXTXXT*XT**TXXjXXTXXT^^TXXT*XT*XT^XTXX7XXT^^TXXT*XTXXT^^TXXTXXT*XTX

six>lxvlx*lx>lxxlx>lx*1**1*sixvlx*lx>lxxlxxlzsix%lzsixsl<slzsixxixvtxsixsix/

?TX?TX^TX^TX^TXXTX*TSZTXXTS*7XXTS*7X^TXXjXXTXXT*XT^*TSZTX/

voidkeyBack(unsignedchar

ucKeyVal)

(

/*

如果KeyBufRp=0;減1后則為FFH,

大于KeyBufSize,即從數(shù)組頭退回

到數(shù)組尾。或者由于干擾使得

KeyBufRp超出隊列位置,也要調(diào)整

回到正常位置,

*/

if(一一KeyBufRp>=KeyBufSize)

KeyBufRp=KeyBufSize-l;

KeyBuf[KeyBufRp]=ucKeyVal;

//回存鍵值

下面漸進(jìn)講解鍵盤物理層的驅(qū)動。

電路共同點:P2端口接一共陰數(shù)碼

管,共陰極接GND,P2.0接a段、P2.1

接b段、…、P2.7接h段。

軟件共同點:codeunsignedchar

Seg7Code[10]是七段數(shù)碼管共陰編

碼表。

CodeunsignedcharSeg7code[16]二

//012345

6789Ab

CdEF

{0x3f,0x06,0x5b,0x4f,0x66,

0x6d,0x7d,0x07,0x7f,0x6f,0x77,

0x7c,0x39,0x5e,0x79,0x71};

例一:Pl.0接一按鍵到GND,鍵編號

為‘6"顯示按鍵。

ttinclude<at89x52.h>

ttinclude“KEY.H"

voidmain(void)

{Pl_0=1;//作為輸入引腳,必須

先輸出高電平

while(1)//永遠(yuǎn)為真,即死循環(huán)

{if(Pl_0==0)〃如果按鍵,

則為低電平

{keyPut(6);//保存按鍵編號

值為按鍵隊列

while(P10二二0);//如果

一直按著鍵,則不停地執(zhí)行該循

環(huán),實際是等待松鍵

)

if(keyHit()!=0)//如果隊

列中有按鍵

P2=Seg7Code[keyGet()];//

從隊列中取出按鍵值,并顯示在

數(shù)碼管上

例二:在例一中考慮按鍵20ms抖動

問題。

ttinclude<at89x52.h>

ttinclude“KEY.H”

voidmain(void)

{Pl_0=1;//作為輸入引腳,必須

先輸出高電平

while(1)〃永遠(yuǎn)為真,即死循環(huán)

{if(Pl_0==0)〃如果按鍵,

則為低電平

{delay20nls();//延時20ms,跳

過接下抖動

keyPut(6);//保存按鍵編號

值為按鍵隊列

while(P10二二0);//如果

一直按著鍵,則不停地執(zhí)行該循環(huán),

實際是等待松鍵

delay201ns();//延時20ms,跳

過松開抖動

if(keyHit()!=0)〃如果隊

列中有按鍵

P2=Seg7Code[keyGet()];//

從隊列中取出按鍵值,并顯示在

數(shù)碼管上

例三:在例二中考慮干擾問題。即小

于20ms的負(fù)脈沖干擾。

ttinclude<at89x52.h>

ttinclude“KEY.H"

voidmain(void)

{Pl0=1;〃作為輸入引腳,必須

先輸出高電平

while(1)〃永遠(yuǎn)為真,即死循環(huán)

{if(P1_O==0)〃如果按鍵,

則為低電平

{delay20nls();//延時20ms,跳

過接下抖動

if(Pl0=1)continue;

//假按鍵

keyPut(6);//保存按鍵編號

值為按鍵隊列

while(P10二二0);//如果

一直按著鍵,則不停地執(zhí)行該循環(huán),

實際是等待松鍵

delay20nls();//延時20ms,跳

過松開抖動

if(keyHit()!=0)/7如果隊

列中有按鍵

P2=Seg7Code[keyGet()];//

從隊列中取出按鍵值,并顯示在

數(shù)碼管上

例四:狀態(tài)圖編程法。通過20ms周

期中斷,掃描按鍵。

/*J^?<J^?<j^?<J^><j^><j^?<j^?<j^?<j^?<j^?<j^><J^aej^?<j^?<J^><j^?<j^?

/

^T^

^3^

采用晶體為12KHz時,指令周期為1ms

(即主頻為IKHz),這樣T0工作在定

時器方式2,8位自動重載。計數(shù)值

為20,即可產(chǎn)生20nls的周期性中斷,

在中斷服務(wù)程序中實現(xiàn)按鍵掃描

*T^^TS#T^^Ts#Ts#T^#T^^T%#T^#T^^Tx^T%^Ts

/

^S#T^#T*^T%^T%#T^/

ttinclude<at89x52.h>

ttinclude“KEY.H"

voidmain(void)

(

TMOD=(TMOD&OxfO)|0x02;

//不改變T1的工作方式,TO為定

時器方式2

THO=-20;//計數(shù)周期為

20個主頻脈,即20nls

TLO=THO;〃先軟加載一次

計數(shù)值

TRO=1;//允許TO開始計

數(shù)

ETO=1;//允許TO計數(shù)溢

出時產(chǎn)生中斷請求

EA=1;//允許CPU響應(yīng)中

斷請求

while(1)//永遠(yuǎn)為真,即死循環(huán)

{

if(keyHit()!=0)//如果隊

列中有按鍵

P2=Seg7Code[keyGet()];//

從隊列中取出按鍵值,并顯示在

數(shù)碼管上

voidtimerOint(void)interrupt

1//20ms;TO的中斷號為1

{staticunsignedcharsts=0;

Pl_0=1;//作為輸入引腳,必須

先輸出高電平

switch(sts)

{

case0:if(Pl_0==0)sts=l;

break;//按鍵則轉(zhuǎn)入狀態(tài)1

case1:

if(Pl_0==l)sts=0;//假

按錯,或干擾,回狀態(tài)0

else{sts=2;keyPut(6);}

//確實按鍵,鍵值入隊列,并轉(zhuǎn)

狀態(tài)2

break;

case2:if(P1_O==1)sts=3;

break;//如果松鍵,則轉(zhuǎn)狀態(tài)3

case3:

if(P1_O==O)sts=2;//假

松鍵,回狀態(tài)2

elsests=O;//真松鍵,

回狀態(tài)0,等待下一次按鍵過程

例五:狀態(tài)圖編程法。

/

/

?J^>

如果采用晶體為12MHz時,指令周期

為lus(即主頻為1MHz),要產(chǎn)生20nls

左右的計時,則計數(shù)值達(dá)到20000,

T0工作必須為定時器方式1,16位非

自動重載,即可產(chǎn)生20ms的周期性

中斷,在中斷服務(wù)程序中實現(xiàn)按鍵掃

<J^?

^TS^Ts

*JL**jL*^JL^*JL***^^JL***JL**^X**%JL**JL*^JL>*jL**JL**JL**X****X;***;!**JL***jL**>1^^JL**jL***X****^//

ttinclude<at89x52.h>

ttinclude“KEY.H"

voidmain(void)

TMOD二(TMOD&OxfO)OxOl;

//不改變T1的工作方式,TO為定

時器方式1

TLO二-20000;//計數(shù)周

期為20000個主頻脈,自動取低8位

THO=(-20000)?8;//右移

8位,實際上是取高8位

TRO=1;//允許T0開始計

數(shù)

ETO=1;//允許T0計數(shù)溢

出時產(chǎn)生中斷請求

EA=1;//允許CPU響應(yīng)中

斷請求

while(1)〃永遠(yuǎn)為真,即死循環(huán)

if(keyHit()!=0)〃如果隊

列中有按鍵

P2=Seg7Code[keyGet()];//

從隊列中取出按鍵值,并顯示在

數(shù)碼管上

}

)

voidtimerOint(void)interrupt

1//20ms;TO的中斷號為1

{staticunsignedcharsts=0;

TLO=-20000;〃方式1

為軟件重載

THO=(-20000)?8;〃右移

8位,實際上是取高8位

P10=1;〃作為輸入引腳,必須

先輸出高電平

switch(sts)

{

case0:if(P1_O==O)sts=l;

break;//按鍵則轉(zhuǎn)入狀態(tài)1

case1:

if(P1_O==1)sts=O;//假

按錯,或干擾,回狀態(tài)0

else{sts=2;keyPut(6);}

//確實按鍵,鍵值入隊列,并轉(zhuǎn)

狀態(tài)2

break;

case2:if(P1_O==1)sts=3;

break;//如果松鍵,則轉(zhuǎn)狀態(tài)3

case3:

if(P1_O==O)sts=2;//假

松鍵,回狀態(tài)2

elsests=0;//真松鍵,

回狀態(tài)3等待下一次按鍵過程

例六:4X4按鍵。

/

/^T%#T^^Ts#T^

<j^?ej^?<j^?<j^?<J^?<J^>

由Pl端口的高4位和低4位構(gòu)成4X4

的矩陣鍵盤,本程序只認(rèn)為單鍵操作

為合法,同時按多鍵時無效。

這樣下面的X,Y的合法值為0x7,Oxb,

Oxd,Oxe,Oxf,通過表keyCode影

射變換可得按鍵值

>J^aj^>?^4?

^T%?*T%<*T^^T%^T%

<J^<J^?<j^?<j^?<J^><j^?<J^?<j^a<j^?<J^>

/

#T^#T^^T%^T^#T^#T*^Ts^Ts/

ttinclude<at89x52.h>

ttinclude“KEY.H"

unsignedcharkeyScan(void)//

返回0表示無按鍵,或無效按鍵,其

它值為按鍵編碼值

{codeunsignedcharkeyCode[16]=

/OxO,0x1,0x2,0x3,0x4,0x5,

0x6,0x7,0x8,0x9,OxA,OxB,OxC,

OxD,OxE,OxF

{0,0,0,0,0,0,

0,1,0,0,0,2,0,

3,4,0):

unsignedcharx,y,retVal;

Pl=0x0f;//低四位輸入,高四

位輸出0

x=Pl&0x0f;//Pl輸入后,清高

四位,作為X值

Pl=0xf0;//高四位輸入,低四

位輸出0

y=(Pl?4)&OxOf;//Pl輸入后

移位到低四位,并清高四位,作為¥

retVal=keyCode[x]*4+

keyCode[y];//根據(jù)本公式倒算按鍵

編碼

if(retVal==0)return(0);else

return(retVal-4);

)

〃比如按鍵」',得X=0x7,Y=0x7,

算得retVal=5,所以返回函數(shù)值1。

〃雙如按鍵7,得XRxb,Y=0xd,

算得retVal二11,所以返回函數(shù)值7。

voidmain(void)

TMOD=(TMOD&OxfO)0x01;

//不改變T1的工作方式,TO為定

時器方式1

TLO=-20000;〃計數(shù)周

期為20000個主頻脈,自動取低8位

THO=(-20000)?8;//右移

8位,實際上是取高8位

TRO=1;//允許T0開始計

數(shù)

ETO=1;//允許T0計數(shù)溢

出時產(chǎn)生中斷請求

EA=1;//允許CPU響應(yīng)中

斷請求

while(1)//永遠(yuǎn)為真,即死循環(huán)

if(keyHit()!=0)〃如果隊

列中有按鍵

P2=Seg7Code[keyGet()];//

從隊列中取出按鍵值,并顯示在

數(shù)碼管上

)

)

voidtimerOint(void)interrupt

1//20ms;TO的中斷號為1

{staticunsignedcharsts=0;

TLO=-20000;//方式1

為軟件重載

THO=(-20000)?8;//右移

8位,實際上是取高8位

P1.0=1;//作為輸入引腳,必須

先輸出高電平

switch(sts)

case0:if(keyScan()!=0)

sts=l;break;//按鍵則轉(zhuǎn)入狀態(tài)1

case1:

if(keyScan()==0)sts=0;

//假按錯,或干擾,回狀態(tài)0

else{sts=2;

keyPut(keyScan());}//確實

按鍵,鍵值入隊列,并轉(zhuǎn)狀態(tài)2

break;

case2:if(keyScan()==0)

sts=3;break;//如果松鍵,則轉(zhuǎn)狀

態(tài)3

case3:

if(keyScan()!=0)sts=2;

//假松鍵,回狀態(tài)2

elsests=0;//真松鍵,

回狀態(tài)3等待下一次按鍵過程

第六節(jié):低頻頻率計

實例目的:學(xué)時定時器、計數(shù)器、中

斷應(yīng)用

說明:選用24MHz的晶體,主頻可達(dá)

2MHzo用T1產(chǎn)生100us的時標(biāo),T0

作信號脈沖計數(shù)器。假設(shè)晶體頻率沒

有誤差,而且穩(wěn)定不變(實際上可達(dá)

萬分之一);被測信號是周期性矩形

波(正負(fù)脈沖寬度都不能小于

0.5us),頻率小于1MHz,大于IHzo

要求測量時標(biāo)1S,測量精度為0.1%O

解:從測量精度要求來看,當(dāng)頻率超

過IKHz時,可采用1S時標(biāo)內(nèi)計數(shù)信

號脈沖個數(shù)來測量信號頻,而信號頻

率低于IKHz時,可以通過測量信號

的周期來求出信號頻率。兩種方法自

動轉(zhuǎn)換。

對于低于IKHz的信號,信號周

期最小為1ms,也就是說超過lOOOus,

而我們用的定時器計時脈沖周期為

0.5us,如果定時多計或少計一個脈

沖,誤差為1US,所以相對誤差為

lus/1000us=0.l%o信號周期越大,

即信號頻率越低,相對誤差就越小。

從上面描述來看,當(dāng)信號頻率超

過IKHz后,信號周期就少于lOOOus,

顯然采用上面的測量方法,不能達(dá)到

測量精度要求,這時我們采用1S單

位時間計數(shù)信號的脈沖個數(shù),最少能

計到1000個脈沖,由于信號頻率不

超過1MHz,而我們定時脈沖為2MHz,

最差多計或少計一個信號脈沖,這樣

相對誤差為1/1000,可見信號頻率越

高,相對誤差越小。

信號除輸入到T1(P3.5)夕卜,還輸入

到INTI(P3.3)o

unsignedintuslOO;//對lOOus

時間間隔單位計數(shù),即有多少個

IOOUSO

unsignedcharSecond;

unsignedintK64;//對64K

單位計數(shù),即有多少個64K

unsignedcharoldTO;

unsignedintoldus,oldK64,

oldTl;

unsignedlongfey;//存放

頻率值,單位為Hz

bitHighLow=l;//l:表示信號

超過IKHz;0:表示信號低于IKHzo

voidInitialHigh(void)

IE=0;IP=0;HighLow=l;

TMOD=(TMOD&OxfO)|0x02;

TH0=-200;TLO=THO;PXO=1;TO=1;

TMOD二(TMOD&OxOf)0x50;

TH1=O;TL1=O;Tl=l;ET1=1;

Usl00=0;Second=0;K64=0;

oldK64=0;oldTl=0;

TCON|=0x50;〃同時置TRO=1;

TR1=1;

EA=1;

)

voidInitialLow(void)

(

IE=0;IP=0;HighLow=0;

TMOD=(TMOD&OxfO)|0x02;

TH0=-200;TLO=THO;ETO=1;TRO=1;

INTI=1;IT1=1;EX1=1;

UslOO=O;Second=0;K64=0;

oldK64=0;oldTl=0;

EA=1;

)

voidTOintr(void)interrupt1

{if(HighLow==0)++usl00;

else

if(++usl00>=10000)

{unsignedinttmpl,tmp2;

TR1=O;tmpl=(THl?8)+(TL1);

tmp2=K64;TR1=1;

fey二((tmp2-oldK64)<<16)+

(tmpl-oldTl);

oldK64=tmpl;oldTl=tmp2;

Second++;

uslOO=O;

voidTlintr(void)interrupt3

{++K64;}

voidXlintr(void)interrupt2

{staticunsignedcharsts=0;

switch(sts)

{

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論