《單片機(jī)應(yīng)用實(shí)訓(xùn)教程》課件第3章_第1頁(yè)
《單片機(jī)應(yīng)用實(shí)訓(xùn)教程》課件第3章_第2頁(yè)
《單片機(jī)應(yīng)用實(shí)訓(xùn)教程》課件第3章_第3頁(yè)
《單片機(jī)應(yīng)用實(shí)訓(xùn)教程》課件第3章_第4頁(yè)
《單片機(jī)應(yīng)用實(shí)訓(xùn)教程》課件第3章_第5頁(yè)
已閱讀5頁(yè),還剩206頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

第3章C51應(yīng)用實(shí)訓(xùn)實(shí)訓(xùn)3.1C51程序開(kāi)發(fā)環(huán)境實(shí)訓(xùn)3.2定時(shí)器/計(jì)數(shù)器C51程序設(shè)計(jì)實(shí)訓(xùn)3.3數(shù)碼顯示和矩陣式鍵盤C51程序設(shè)計(jì)實(shí)訓(xùn)3.4字符型LCD顯示模塊C51程序設(shè)計(jì)實(shí)訓(xùn)3.5單片機(jī)串行通信C51程序設(shè)計(jì)

實(shí)訓(xùn)3.6數(shù)字鐘C51程序的實(shí)現(xiàn)實(shí)訓(xùn)3.1C51程序開(kāi)發(fā)環(huán)境1.實(shí)訓(xùn)目的

(1)了解利用C51控制單片機(jī)系統(tǒng)的軟硬件環(huán)境及C51程序的開(kāi)發(fā)過(guò)程。

(2)了解C51程序的結(jié)構(gòu)。

(3)了解8051特殊功能寄存器SFR和位名稱在C51中的定義方法。

2.實(shí)訓(xùn)設(shè)備與器件實(shí)訓(xùn)設(shè)備:?jiǎn)纹瑱C(jī)集成開(kāi)發(fā)環(huán)境、綜合實(shí)訓(xùn)板。3.項(xiàng)目設(shè)計(jì)要求編制C51程序使目標(biāo)板上連接在P1口的8個(gè)LED閃動(dòng)。4.實(shí)訓(xùn)內(nèi)容目標(biāo)板上連接在P1口的8個(gè)LED閃動(dòng)的C51程序源代碼如下://ex1.c#include"REG51.H" //頭文件為REG51.H,定義了51單片機(jī)的SFR和位名稱/*-----------------延時(shí)函數(shù)-----------------*/delay(intt) {inti,j;for(i=0;i<t;i++)for(j=0;j<10;j++);}/*-----------------main函數(shù)-----------------*/main() {inti;while(1){P1=0xff; //熄滅8個(gè)LED,0xff為十六進(jìn)制數(shù)0ffH,在C語(yǔ)言中用0x前綴表示十六進(jìn)制數(shù)delay(1000);P1=0x00; //點(diǎn)亮8個(gè)LEDdelay(1000); }}

5.調(diào)試方法與步驟本實(shí)訓(xùn)教材采用FranklinC51編譯器和MedWin中文版集成單片機(jī)開(kāi)發(fā)環(huán)境,并假定FranklinC51編譯器已經(jīng)安裝在計(jì)算機(jī)的D:\FC子目錄下。

(1)將開(kāi)發(fā)系統(tǒng)和目標(biāo)板連接好,并接上電源。

(2)啟動(dòng)MedWin中文版,進(jìn)入MedWin集成開(kāi)發(fā)環(huán)境。

(3)設(shè)置編譯環(huán)境。第一次在MedWin中使用C51編譯環(huán)境需進(jìn)行“編譯、匯編、連接配置”(以后使用時(shí)不需再配置)。單擊“設(shè)置”菜單項(xiàng),如圖3.1.1所示。選擇“設(shè)置向?qū)А?,彈出如圖3.1.2所示的“編譯/匯編/連接配置”窗口1。圖3.1.1設(shè)置菜單項(xiàng)圖3.1.2“編譯/匯編/連接配置”窗口1圖3.1.3“編譯/匯編/連接配置”窗口2

圖3.1.4“Newfiles”窗口(4)編輯源程序。打開(kāi)“文件”菜單,選擇“新建”選項(xiàng),在“Newfiles”窗口中輸入新建的文件名,例如“ex1.c”(注意一定要輸入文件擴(kuò)展名),如圖3.1.4所示。單擊“打開(kāi)”,進(jìn)入源程序編輯窗口。

(5)輸入源程序,并存盤。

(6)編譯源程序。編譯后在編輯窗口下面將出現(xiàn)狀態(tài)窗口,如圖3.1.5所示。

(7)連接程序并下載到仿真器中。

(8)在調(diào)試窗口中可以對(duì)程序進(jìn)行單步運(yùn)行、斷點(diǎn)運(yùn)行、全速運(yùn)行等測(cè)試,如圖3.1.6所示為程序全速運(yùn)行時(shí)的畫(huà)面。圖3.1.5編譯后出現(xiàn)的狀態(tài)窗口區(qū)域圖3.1.6全速運(yùn)行程序

6.實(shí)訓(xùn)分析與總結(jié)

1)FranklinC51編譯器簡(jiǎn)介

FranklinC51編譯器的安裝非常簡(jiǎn)單,直接點(diǎn)擊setup,按照提示選擇安裝目錄即可。安裝完成后,在安裝目錄下有以下四個(gè)子目錄:

LIB:庫(kù)文件目錄,包含了所有標(biāo)準(zhǔn)函數(shù);

INC:頭文件目錄(#include);

BIN:可執(zhí)行文件目錄;

EXAMPLES:樣例程序子目錄。

這里對(duì)主要的可執(zhí)行文件簡(jiǎn)介如下:

A51:MCS-51匯編程序,它可將按照A51匯編格式編制的匯編源程序編譯成以Intel目標(biāo)模塊格式產(chǎn)生的可再定位目標(biāo)代碼。

C51:可將C51高級(jí)語(yǔ)言編制的源程序編譯成以Intel目標(biāo)模塊格式產(chǎn)生的可再定位目標(biāo)代碼。

L51:連接定位器,可將幾個(gè)不同程序模塊復(fù)合為一個(gè)模塊,并自動(dòng)從庫(kù)文件中挑選模塊嵌入目標(biāo)文件,同時(shí)定義絕對(duì)地址,并計(jì)算再定位段的地址。

LIB51:C51函數(shù)庫(kù)管理器,可以進(jìn)行函數(shù)庫(kù)的生成與處理。2)C51程序結(jié)構(gòu)圖3.1.7C程序基本結(jié)構(gòu)C51中的函數(shù)與匯編語(yǔ)言中的子程序概念是一樣的。本例中包含delay和main兩個(gè)函數(shù),main函數(shù)是必不可少的主函數(shù),也是程序開(kāi)始執(zhí)行的函數(shù),delay函數(shù)的功能是延時(shí),用于控制燈的閃動(dòng)速度。LED的閃動(dòng)過(guò)程是:點(diǎn)亮→延時(shí)→熄滅→延時(shí)

延時(shí)函數(shù)在很多程序設(shè)計(jì)中都會(huì)用到,本程序中使用了雙重循環(huán),外循環(huán)的循環(huán)次數(shù)由形式參數(shù)t提供,總的循環(huán)次數(shù)是10×t,循環(huán)體是空操作。for語(yǔ)句的使用方法與ANSIC語(yǔ)言相同。

C51程序設(shè)計(jì)中,函數(shù)的定義和調(diào)用方法同ANSIC語(yǔ)言相同。3)8051單片機(jī)的SFR寄存器和位名稱及其C51定義在8051單片機(jī)中,除了程序計(jì)數(shù)器PC和四組通用寄存器組之外,其他所有的寄存器均稱為特殊功能寄存器(SFR)。

MCS-51系列單片機(jī)內(nèi)部定義了21個(gè)特殊功能寄存器,它們分散在片內(nèi)RAM區(qū)的高128字節(jié)中,地址為80H~0FFH,SFR只能用直接尋址方式。

SFR中有11個(gè)寄存器具有位尋址能力(這些寄存器的字節(jié)地址都能被8整除,即字節(jié)地址是以8或0作為尾數(shù)的),且每一位均定義了位名稱。在匯編語(yǔ)言程序設(shè)計(jì)中可以直接使用這些寄存器名稱和位地址名稱,例如: MOV A,#0ffh CLR P1.1

那么,在C51程序設(shè)計(jì)中,如何使用這些寄存器和位名稱呢?在頭文件“REG51.H”已經(jīng)預(yù)定義了所有SFR和位名稱。在REG51.H頭文件中使用了“sfr”和“sbit”兩個(gè)關(guān)鍵字。

(1)關(guān)鍵字“sfr”用于定義特殊功能寄存器的地址,其格式為:

sfr特殊功能寄存器名=特殊功能寄存器地址;注意后面的地址必須為常數(shù),范圍是0x80到0xff,例如:sfrPSW=0xD0;sfrACC=0xE0;sfrB=0xF0;(2)關(guān)鍵字“sbit”與“sfr”類似,用于定義一些特殊的位,其格式為:

sbit位名稱=位地址例如:

sbitCY=0xD7; sbitAC=0xD6; sbitF0=0xD5;也可以寫(xiě)成:

sbitCY=0xD0^7; sbitAC=0xD0^6; sbitF0=0xD0^5;

如果在前面已定義了特殊功能寄存器PSW,那么上面的定義也可以寫(xiě)成:sbitCY=PSW^7;sbitAC=PSW^6;sbitF0=PSW^5;

在C51程序設(shè)計(jì)中,編程員可以直接在自己的程序中利用關(guān)鍵字sfr和sbit來(lái)定義這些特殊功能寄存器和特殊位名稱,也可以把“REG51.H頭文件包含在自己的程序中,直接使用SFR名稱和位名稱。頭文件REG51.H的內(nèi)容如下:7.思考題(1)修改源程序,使8個(gè)LED模擬霓虹燈的各種顯示方式。(2)利用對(duì)P1口某一位的操作設(shè)計(jì)霓虹燈的顯示方式。(3)以單步、斷點(diǎn)等各種方式運(yùn)行程序。(4)在程序運(yùn)行過(guò)程中如何查看單片機(jī)各種資源的狀態(tài)。實(shí)訓(xùn)3.2定時(shí)器/計(jì)數(shù)器C51程序設(shè)計(jì)1.實(shí)訓(xùn)目的(1)復(fù)習(xí)MCS-51單片機(jī)定時(shí)器/計(jì)數(shù)器和中斷的知識(shí)。(2)掌握利用C51進(jìn)行單片機(jī)內(nèi)部資源控制的方法。(3)掌握利用C51進(jìn)行中斷編程的方法。(4)了解C51的基本語(yǔ)法。2.實(shí)訓(xùn)設(shè)備與器件實(shí)訓(xùn)設(shè)備:?jiǎn)纹瑱C(jī)集成開(kāi)發(fā)環(huán)境,綜合實(shí)訓(xùn)板。

3.項(xiàng)目設(shè)計(jì)要求

(1)利用定時(shí)器查詢方式,編制C51程序使目標(biāo)板上連接在P1口的8個(gè)LED循環(huán)顯示,時(shí)間間隔為1s。

(2)利用定時(shí)器中斷方式,編制C51程序使目標(biāo)板上連接在P1口的8個(gè)LED循環(huán)顯示,時(shí)間間隔為1s。

4.實(shí)訓(xùn)內(nèi)容

1)定時(shí)器查詢方式C51程序設(shè)計(jì)

(1)程序設(shè)計(jì)方法:用定時(shí)器1的方式1編制1s的延時(shí)程序,假定系統(tǒng)采用12MHz晶振,定時(shí)器1、方式1定時(shí)50ms,再循環(huán)20次即可定時(shí)到1s。(2)源程序如下://ex2.c#include"REG51.H"/*-----------------延時(shí)函數(shù)-----------------*/voiddelay(){intj;for(j=0;j<0x14;j++) //設(shè)置20次循環(huán)次數(shù)

{TH1=0x3c; //置定時(shí)器初值

TL1=0xb0;TR1=1; //啟動(dòng)定時(shí)器1while(!TF1); //查詢計(jì)數(shù)是否溢出,即定時(shí)50ms時(shí)間到,TF=1TF1=0; //50ms時(shí)間到,將定時(shí)器溢出標(biāo)志位TF清零

}}/*-----------------main函數(shù)-----------------*/main(){inti,w;TMOD=0x10; //置定時(shí)器1為方式1while(1){w=0x01; //燈的位置初值為01hfor(i=0;i<8;i++){P1=~w; //循環(huán)點(diǎn)亮燈

w<<=1; //點(diǎn)亮燈的位置移動(dòng)

delay(); //調(diào)用1ms延時(shí)

}}}2)定時(shí)器中斷方式C51程序設(shè)計(jì)

(1)程序設(shè)計(jì)方法:用定時(shí)器1的方式1編制1s的延時(shí)程序,假定系統(tǒng)采用12MHz晶振,定時(shí)器1、方式1定時(shí)50ms,采用中斷方式編程,需中斷20次,外部變量count作為計(jì)數(shù)次數(shù),位變量flag為1s定時(shí)到標(biāo)志位。

(2)源程序如下://ex3.c#include"REG51.H"intcount; //定義外部變量bitflag; //1s時(shí)間到標(biāo)志/*-----------------中斷函數(shù)-----------------*/voiddelay()interrupt3//interrupt3表示該函數(shù)為中斷號(hào)3的中斷函數(shù){TH1=0x3c; //重新置定時(shí)器1初值

TL1=0xb0;TR1=1; //開(kāi)定時(shí)器1中斷

count--; //中斷次數(shù)減1if(count==0)flag=1; //若20次中斷已完成,則置1s延時(shí)時(shí)間到標(biāo)志

}/*-----------------main函數(shù)-----------------*/main(){intj,w;TMOD=0x10; //初始化定時(shí)器1TH1=0x3c;TL1=0xb0;EA=1; //開(kāi)總中斷ET1=1; //定時(shí)器1開(kāi)中斷

TR1=1; //啟動(dòng)定時(shí)器1while(1){w=0x01;for(j=0;j<8;j++){flag=0; //初始標(biāo)志

count=0x14; //設(shè)置中斷次數(shù)

P1=~w; //循環(huán)點(diǎn)亮燈

while(flag==0); //等待1s定時(shí)時(shí)間到

w<<=1; //點(diǎn)亮燈的位置移動(dòng)

}}}

5.實(shí)訓(xùn)分析與總結(jié)

1)C51程序基本結(jié)構(gòu)

C51的程序結(jié)構(gòu)同ANSIC語(yǔ)言相同。C語(yǔ)言是一種結(jié)構(gòu)化編程語(yǔ)言。結(jié)構(gòu)化程序由若干模塊組成,每個(gè)模塊中包含著若干個(gè)基本結(jié)構(gòu),而每個(gè)基本結(jié)構(gòu)中有若干條語(yǔ)句??偟膩?lái)說(shuō),C語(yǔ)言有3種基本結(jié)構(gòu):順序結(jié)構(gòu)、選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)。順序結(jié)構(gòu)是一種最基本、最簡(jiǎn)單的程序結(jié)構(gòu),程序由低地址到高地址順序執(zhí)行程序代碼。選擇結(jié)構(gòu)也稱為分支結(jié)構(gòu),根據(jù)條件測(cè)試結(jié)果選擇不同的程序執(zhí)行方向,常用的選擇語(yǔ)句有:if,elseif,switch/case語(yǔ)句。在程序ex3.c的delay的斷函數(shù)中有下面的選擇語(yǔ)句:

if(count==0)flag=1;//若20次中斷已完成,則置1s延時(shí)時(shí)間到標(biāo)志

循環(huán)結(jié)構(gòu)是指重復(fù)執(zhí)行某一程序段的程序結(jié)構(gòu),是選擇結(jié)構(gòu)的一種特殊情形,程序設(shè)計(jì)中使用非常廣泛。C語(yǔ)言中用于循環(huán)的語(yǔ)句有:while,dowhile,for語(yǔ)句。前面的例程中使用了很多循環(huán)語(yǔ)句,舉例如下:

(1)單片機(jī)控制程序的主程序,即main函數(shù)中都有一個(gè)后臺(tái)無(wú)限循環(huán)語(yǔ)句,結(jié)構(gòu)如下:main(){...... //初始化部分

while(1){ //無(wú)限循環(huán)

}}(2)在例程ex1.c中,延時(shí)函數(shù)delay采用了雙重循環(huán)結(jié)構(gòu),其代碼如下:delay(intt) //延時(shí)函數(shù){inti,j;for(i=0;i<t;i++)for(j=0;j<10;j++);}

在上面的雙重循環(huán)結(jié)構(gòu)中,循環(huán)體是空的,表示什么都不做,僅用于延時(shí)的功能。(3)在例程ex2.c中,延時(shí)函數(shù)采用了查詢方式編程,查詢語(yǔ)句如下:

while(!TF1);//查詢計(jì)數(shù)是否溢出,即定時(shí)50ms時(shí)間到,TF=1

當(dāng)定時(shí)時(shí)間未到時(shí),TF=0,此時(shí)測(cè)試條件!TF的結(jié)果為1,即條件為真,繼續(xù)重復(fù)執(zhí)行循環(huán)體(此處循環(huán)體為空);當(dāng)定時(shí)時(shí)間到時(shí),TF=1,條件!TF的結(jié)果為0,即條件為假,退出while循環(huán),繼續(xù)執(zhí)行下面的語(yǔ)句。在單片機(jī)程序設(shè)計(jì)中,查詢方式編程經(jīng)常采用上面的結(jié)構(gòu),例如:

while((P1&0x01)==0);該語(yǔ)句用于測(cè)試P1口的P1.0的電平狀態(tài)。當(dāng)P1.0=0時(shí),條件成立,繼續(xù)等待;若P1口的P1.0由0變?yōu)?,則循環(huán)終止,繼續(xù)執(zhí)行下面的語(yǔ)句。2)C51的數(shù)據(jù)與運(yùn)算法則簡(jiǎn)介(1)C51的數(shù)據(jù)類型。表3.2.1FranklinC51編譯器支持的數(shù)據(jù)類型

當(dāng)計(jì)算結(jié)果隱含著另外一種數(shù)據(jù)類型時(shí),數(shù)據(jù)類型可以自動(dòng)轉(zhuǎn)換。例如將一個(gè)位變量賦給一個(gè)整型變量時(shí),位值自動(dòng)轉(zhuǎn)換為整型值,有符號(hào)變量的符號(hào)也能自動(dòng)進(jìn)行處理。

FranklinC51編譯器也支持符號(hào)常量和變量,符號(hào)常量的定義方法如下:

#defineCOUNT20

符號(hào)常量在整個(gè)程序中,其值不可變,通常是具有一定含義的英文單詞或符號(hào)。因此,采用符號(hào)常量可以增加程序的可讀性和可修改性。

下面結(jié)合本實(shí)訓(xùn)中的兩個(gè)例程,對(duì)變量定義應(yīng)注意的幾個(gè)問(wèn)題加以討論。①變量數(shù)據(jù)類型的選擇。在C51程序設(shè)計(jì)中,變量數(shù)據(jù)類型的定義極其重要,因?yàn)樵谒袛?shù)據(jù)類型中只有bit和unsignedchar兩種數(shù)據(jù)類型可以直接支持機(jī)器指令,對(duì)于其他的數(shù)據(jù)類型C51編譯器都要進(jìn)行一系列復(fù)雜的變量數(shù)據(jù)和變量類型的處理,而這種處理將會(huì)對(duì)應(yīng)很長(zhǎng)一段機(jī)器指令,最終會(huì)使程序變得復(fù)雜、龐大,運(yùn)行速度降低。由此可見(jiàn),在C51程序設(shè)計(jì)過(guò)程中,在滿足數(shù)據(jù)要求的情況下,應(yīng)盡可能使用unsignedchar變量和bit變量。signedchar變量雖然也只占用一個(gè)字節(jié),但需要進(jìn)行額外的操作來(lái)測(cè)試代碼的符號(hào)位,因此會(huì)降低代碼執(zhí)行效率。

在例程ex2.c的main函數(shù)中定義了整型變量i和w,仔細(xì)分析發(fā)現(xiàn),i的取值范圍是0~8,而表示亮燈位置的變量w的取值也在00H~0ffH之間,所以,這兩個(gè)變量應(yīng)該定義成unsignedchar,而不是int。同樣,在該程序的delay函數(shù)中,也定義了整型變量j,分析一下,最好是將j定義成什么數(shù)據(jù)類型呢?同樣,在例程ex3.c的main函數(shù)中的變量i和w也應(yīng)該定義成unsignedchar,外部變量count也應(yīng)該定義成unsigedchar。②全局變量和局部變量。這里應(yīng)該注意的是:在例程ex3.c中,整型變量count和位變量flag是定義在所有函數(shù)外部的變量,稱為外部變量,即全局變量;而變量i和w是定義在main函數(shù)內(nèi)部的,稱為內(nèi)部變量,即局部變量。全局變量與局部變量的區(qū)別是:前者可以在本文件的所有函數(shù)中使用,其有效范圍是從定義變量的位置開(kāi)始到本源文件結(jié)束為止。例如count和flag,不僅在delay函數(shù)中使用,也在main函數(shù)中使用,后者只在定義本函數(shù)的范圍內(nèi)有效,即只能在本函數(shù)內(nèi)使用。③變量的定義。

bit數(shù)據(jù)類型是ANSIC語(yǔ)言所沒(méi)有的數(shù)據(jù)類型,在此做如下說(shuō)明。位變量的定義格式為:

bit變量名;在程序ex3.c中,定義了位變量flag作為定時(shí)時(shí)間到的標(biāo)志,若定時(shí)時(shí)間未到,則flag=0,若定時(shí)時(shí)間到,則flag=1。在單片機(jī)C語(yǔ)言程序設(shè)計(jì)中,為了使程序更加優(yōu)化,當(dāng)需要設(shè)定只有兩種狀態(tài)的標(biāo)志變量時(shí),應(yīng)該將其定義成位變量,而不要定義成字符或整型變量。④用于訪問(wèn)sfr的數(shù)據(jù)類型。

FranklinC51編譯器的數(shù)據(jù)類型除了包括ANSIC語(yǔ)言所具有的位尋址、字符、整數(shù)、長(zhǎng)整、浮點(diǎn)數(shù)、指針等之外,還有3種用于訪問(wèn)SFR的數(shù)據(jù)類型,如表3.2.2所示。表3.2.2訪問(wèn)SFR的數(shù)據(jù)類型(2)C51的運(yùn)算符。

C51編譯器所支持的運(yùn)算符與ANSIC語(yǔ)言相同,分為算術(shù)運(yùn)算符、關(guān)系運(yùn)算符、邏輯運(yùn)算符、位操作符、自增減運(yùn)算符和復(fù)合運(yùn)算符等。算術(shù)運(yùn)算符:+、-、*、/、%。關(guān)系運(yùn)算符:<、>、<=、>=、==、!=。邏輯運(yùn)算符:&&、||、!。位操作符:&、|、^、~、<<、>>。自增減運(yùn)算符:++、--。復(fù)合運(yùn)算符:凡是二目運(yùn)算符,都可以與賦值運(yùn)算符“=”一起組成復(fù)合賦值運(yùn)算符,包括+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=。

尤其值得注意的是:由于單片機(jī)C語(yǔ)言程序和單片機(jī)硬件緊密相關(guān),因此會(huì)用到大量的位運(yùn)算或邏輯運(yùn)算。在例程ex2.c和ex3.c中,為了使亮燈的順序移動(dòng),采用了取反和左移操作,具體操作如下:初值:w=0x01;w:00000001(初值)P1=~w;11111110(“1”使相應(yīng)燈熄滅,“0”使相應(yīng)燈點(diǎn)亮)W<<=1;00000010(左移一位)思考:如果將該程序中初值直接寫(xiě)成:“11111110”,應(yīng)該如何修改程序?3)中斷函數(shù)的編寫(xiě)方法

FranklinC51編譯器支持在C源程序中直接以函數(shù)形式編寫(xiě)中斷過(guò)程。常用的中斷函數(shù)定義語(yǔ)法如下:

void函數(shù)名()interruptnC51編譯器允許0~31個(gè)中斷,下列中斷及其相關(guān)地址為8051控制器所提供的外部中斷:

0:EXTERNAL0 地址:0003H 1:TIMER/COUNTER0 地址:000BH 2:EXTERNAL1 地址:0013H 3:TIMER/COUNTER1 地址:001BH 4:SERIALPORT 地址:0023H

在例程ex3.c中,使用了TIMER/COUNT1中斷,中斷號(hào)為3,因此該中斷函數(shù)的結(jié)構(gòu)如下:voiddelay()interrupt3//interrupt3表示該函數(shù)為中斷號(hào)3的中斷函數(shù){

::}

編寫(xiě)中斷函數(shù)時(shí)應(yīng)遵循下列規(guī)則:

(1)不能進(jìn)行參數(shù)傳遞,如果中斷過(guò)程包括任何參數(shù)聲明,則編譯器將產(chǎn)生一個(gè)錯(cuò)誤信息。

(2)無(wú)返回值,如果想定義一個(gè)返回值將產(chǎn)生錯(cuò)誤,但是,如果返回整型值編譯器將不產(chǎn)生錯(cuò)誤信息,因?yàn)檎椭凳悄J(rèn)值,編譯器不能清楚識(shí)別。

(3)在任何情況下不能直接調(diào)用中斷函數(shù),否則編譯器會(huì)產(chǎn)生錯(cuò)誤。由于退出中斷過(guò)程是由指令RETI完成的,該指令影響MCS-51單片機(jī)的硬件中斷系統(tǒng),直接調(diào)用中斷函數(shù)時(shí)硬件上沒(méi)有中斷請(qǐng)求存在,因而這個(gè)指令的結(jié)果是不定的并且通常是致命的。(4)編譯器從絕對(duì)地址8n+3處產(chǎn)生一個(gè)中斷向量,其中n為中斷號(hào),該向量包括一個(gè)中斷過(guò)程的跳轉(zhuǎn),向量的產(chǎn)生可由編譯器控制指令NOINTVECTOR壓縮,因而程序員可以從獨(dú)立的匯編模塊中提供中斷向量。

(5)可以在中斷函數(shù)定義中使用using指定當(dāng)前使用的寄存器組,格式如下:

void函數(shù)名([形式參數(shù)])interruptn[usingm]MCS-51單片機(jī)有四組寄存器R0~R7,程序具體使用哪一組寄存器由程序狀態(tài)字PSW中的兩位RS1和RS0來(lái)確定。在中斷函數(shù)定義時(shí),可以用using指定該函數(shù)具體使用哪一組寄存器,m在0,1,2,3這4個(gè)數(shù)中取值,對(duì)應(yīng)四組寄存器組。例如:

voiddelay()interrupt3using2(6)在中斷函數(shù)中調(diào)用的函數(shù)所使用的寄存器組必須與中斷函數(shù)相同,當(dāng)沒(méi)有使用using指令時(shí),編譯器會(huì)選擇一個(gè)寄存器組作絕對(duì)寄存器訪問(wèn)。程序員必須保證按要求使用相應(yīng)寄存器組,C編譯器不會(huì)對(duì)此檢查。

(7)如果在中斷函數(shù)中執(zhí)行浮點(diǎn)運(yùn)算,必須保存浮點(diǎn)寄存器狀態(tài),當(dāng)沒(méi)有其他程序執(zhí)行浮點(diǎn)運(yùn)算時(shí),可以不保存。4)C51的數(shù)據(jù)存儲(chǔ)類型與8051存儲(chǔ)器結(jié)構(gòu)由于MCS-51單片機(jī)的存儲(chǔ)器結(jié)構(gòu)特點(diǎn)是:程序存儲(chǔ)器(ROM)和數(shù)據(jù)存儲(chǔ)器(RAM)分開(kāi),并各自有其獨(dú)立的尋址方式。具體來(lái)說(shuō),MCS-51單片機(jī)的存儲(chǔ)空間分為四部分:片內(nèi)數(shù)據(jù)存儲(chǔ)器空間(256字節(jié))、片外數(shù)據(jù)存儲(chǔ)器空間(64K字節(jié))、片內(nèi)程序存儲(chǔ)器空間和片外程序存儲(chǔ)器空間,那么程序中定義的變量在哪個(gè)存儲(chǔ)區(qū)域呢?FranklinC51編譯器通過(guò)將變量、常量定義成不同的存儲(chǔ)類型的方法,將它們定位在不同的存儲(chǔ)區(qū)中。C51編譯器支持的存儲(chǔ)器類型如表3.2.3所示。表3.2.3C51編譯器支持的存儲(chǔ)器類型存儲(chǔ)器類型可以和數(shù)據(jù)類型一起使用,例如:intdatai;//整數(shù)i為內(nèi)部數(shù)據(jù)存儲(chǔ)器中的變量intxdataj;//整數(shù)j定義在外部數(shù)據(jù)存儲(chǔ)器(64K字節(jié))內(nèi)一般在定義變量時(shí)經(jīng)常省略存儲(chǔ)器類型的定義,采用默認(rèn)存儲(chǔ)器類型,而默認(rèn)存儲(chǔ)器類型和存儲(chǔ)器模式有關(guān)。5)C51編譯器支持的存儲(chǔ)器模式表3.2.4C51編譯器支持的存儲(chǔ)器模式SMALL模式:所有缺省變量參數(shù)均裝入內(nèi)部RAM(與使用顯式的data關(guān)鍵字來(lái)定義結(jié)果相同)。使用該模式的優(yōu)點(diǎn)是訪問(wèn)速度快,缺點(diǎn)是空間有限,而且分配給堆棧的空間比較少,遇到函數(shù)嵌套調(diào)用和函數(shù)遞歸調(diào)用時(shí)必須小心,該模式適用于較小的程序。

COMPACT模式:所有缺省變量均位于外部RAM區(qū)的一頁(yè)(與使用顯式的pdata關(guān)鍵字來(lái)定義結(jié)果相同),最多能夠定義256字節(jié)變量。使用該模式的優(yōu)點(diǎn)是變量定義空間比SMALL模式大,但運(yùn)行速度比SMALL模式慢。使用本模式時(shí),程序通過(guò)@R0和@R1來(lái)訪問(wèn)變量。LARGE模式:所有缺省變量可放在多達(dá)64K字節(jié)的外部RAM區(qū)(與使用顯式的xdata關(guān)鍵字來(lái)定義結(jié)果相同),均使用數(shù)據(jù)指針DPTR來(lái)尋址。該模式的優(yōu)點(diǎn)是空間大,可定義變量多,缺點(diǎn)是速度較慢,一般用于較大的程序,或擴(kuò)展了大容量外部RAM的系統(tǒng)中。存儲(chǔ)模式?jīng)Q定了變量的默認(rèn)存儲(chǔ)類型、參數(shù)傳遞區(qū)和無(wú)明確存儲(chǔ)類型的說(shuō)明。例如若定義chars,則在SMALL存儲(chǔ)模式下,s被定位在DATA存儲(chǔ)區(qū);在COMPACT存儲(chǔ)模式下,s被定位在IDATA存儲(chǔ)區(qū);在LARGE存儲(chǔ)模式下,s被定位在XDATA存儲(chǔ)區(qū)。

存儲(chǔ)模式定義關(guān)鍵字SMALL、COMPACT和LARGE屬于C51編譯器控制指令,可以在命令行輸入,也可以在源文件的開(kāi)始直接使用下面的預(yù)處理語(yǔ)句:

#pragmaSMALL//定義為SMALL模式除非特殊說(shuō)明,本書(shū)中的C51程序均運(yùn)行在SMALL模式下。

6.思考題

(1)例程ex3.c進(jìn)行優(yōu)化修改后的程序如下,試指出哪些地方做了修改?修改后有什么好處?//ex3_1.c#include"REG51.H"#defineTIME20unsignedcharcount; //定義外部變量bitflag; //1s時(shí)間到標(biāo)志/*-----------------中斷函數(shù)-----------------*/voiddelay()interrupt3 //interrupt3表示該函數(shù)為中斷號(hào)3 的中斷函數(shù){TH1=0x3c; //重新置定時(shí)器1初值

TL1=0xb0;TR1=1; //開(kāi)定時(shí)器1中斷

count--; //中斷次數(shù)減1if(count==0)flag=1; //若20次中斷已完成,則置1s延時(shí)時(shí)間到標(biāo)志}/*-----------------main函數(shù)-----------------*/main(){unsignedcharj,w;TMOD=0x10; //初始化定時(shí)器1TH1=0x3c;TL1=0xb0;EA=1; //開(kāi)總中斷

ET1=1; //定時(shí)器1開(kāi)中斷

TR1=1; //啟動(dòng)定時(shí)器1while(1){w=0x01;for(j=0;j<8;j++){flag=0; //初始標(biāo)志

count=TIME; //設(shè)置中斷次數(shù)

P1=~w; //循環(huán)點(diǎn)亮燈

while(flag==0); //等待1s定時(shí)時(shí)間到

w<<=1; //點(diǎn)亮燈的位置移動(dòng)

}}}(2)修改實(shí)訓(xùn)中的兩個(gè)例程,分別采用定時(shí)器/計(jì)數(shù)器0的方式0和方式2實(shí)現(xiàn)定時(shí)功能。實(shí)訓(xùn)3.3數(shù)碼顯示和矩陣式鍵盤C51程序設(shè)計(jì)

1.實(shí)訓(xùn)目的

(1)復(fù)習(xí)MCS-51單片機(jī)擴(kuò)展可編程接口芯片8155的方法。

(2)復(fù)習(xí)8155編程方法及矩陣式鍵盤的硬件接口及掃描方法,復(fù)習(xí)LED的動(dòng)態(tài)接口方法。

(3)掌握利用C51進(jìn)行鍵盤和LED顯示的編程思路。

(4)掌握利用C51進(jìn)行絕對(duì)地址訪問(wèn)的方法。

2.實(shí)訓(xùn)設(shè)備與器件實(shí)訓(xùn)設(shè)備:?jiǎn)纹瑱C(jī)集成開(kāi)發(fā)環(huán)境、綜合實(shí)訓(xùn)板。

3.項(xiàng)目設(shè)計(jì)要求

(1)編制C51程序使實(shí)驗(yàn)板上的8個(gè)數(shù)碼管移動(dòng)顯示“0~F”。

(2)編制C51程序使實(shí)驗(yàn)板上的8個(gè)數(shù)碼管移動(dòng)顯示“0~F”,移動(dòng)速度是1s移動(dòng)一個(gè)字符。

(3)編制C51矩陣式鍵盤掃描程序,當(dāng)按下實(shí)驗(yàn)板上任意鍵時(shí),在某個(gè)數(shù)碼管上能夠顯示其鍵值,當(dāng)按鍵釋放時(shí),顯示關(guān)閉。

4.實(shí)訓(xùn)內(nèi)容

1)LED顯示程序設(shè)計(jì)初步

(1)硬件電路分析。當(dāng)單片機(jī)提供的并行I/O口不夠用戶使用時(shí),常常需要擴(kuò)展I/O口,8155和8255是擴(kuò)展I/O口時(shí)使用較多的I/O芯片。在實(shí)訓(xùn)電路中,用擴(kuò)展的8155連接了8個(gè)LED數(shù)碼管和16個(gè)按鍵。通過(guò)擴(kuò)展8155可以提供3個(gè)并行I/O口A、B、C口,3個(gè)I/O口的工作方式、輸入/輸出方向是由用戶的編程來(lái)確定的。8位LED顯示的位選碼由8155的A口控制,段選碼由8155的B口控制,LED為共陰極數(shù)碼管,接口方式為動(dòng)態(tài)顯示接口方式。8155的地址如下:控制口:4400HA口:4401HB口:4402HC口:4403H(2)軟件設(shè)計(jì)思路。

LED動(dòng)態(tài)顯示的硬件連接特點(diǎn)是各位數(shù)碼管的段選線相應(yīng)并聯(lián)在一起,由同一個(gè)I/O口控制,各位的位選線(公共陰極或陽(yáng)極)由另外的I/O口線控制。當(dāng)程序向數(shù)碼管傳送一個(gè)顯示碼時(shí),該顯示碼會(huì)同時(shí)到達(dá)每個(gè)數(shù)碼管,到底哪個(gè)數(shù)碼管會(huì)顯示,由位選線確定。當(dāng)以動(dòng)態(tài)方式顯示時(shí),各數(shù)碼管分時(shí)輪流選通,要使其穩(wěn)定顯示,必須采用掃描方式,利用人眼睛的視覺(jué)暫留效應(yīng),每一時(shí)刻顯示一個(gè)字符,一位一位輪流顯示,只要每位顯示間隔時(shí)間足夠短,就可以給人以同時(shí)顯示的感覺(jué)。圖3.3.1LED顯示示意圖

在上述移動(dòng)顯示方式中,要分別顯示23屏不同的數(shù)據(jù),就可以達(dá)到移動(dòng)顯示的效果。每屏顯示數(shù)據(jù)之間有一定的邏輯關(guān)系,把這些顯示字符順序?qū)懗上旅娴母袷剑?/p>

×××××××0123456789AbCdEF×××××××(×表示不顯示)

led

可以看到,第1屏顯示數(shù)據(jù)為“×××××××0”,第二屏顯示數(shù)據(jù)為“××××××01”,依此類推,第23屏顯示數(shù)據(jù)為“F×××××××”。

如果把以上顯示數(shù)據(jù)的顯示碼按順序存放到內(nèi)部存儲(chǔ)器中,首地址為led,那么第1屏顯示碼的首地址為led,第2屏顯示碼的首地址為led+1,第3屏顯示碼的首地址為led+2,依此類推,可以得到第i屏顯示碼的首地址為led+i-1。如果顯示屏從第0屏開(kāi)始數(shù)起,那么第i屏顯示碼的首地址即為led+i。(3)C51源程序代碼如下://ex4.c#include"REG51.H" //定義頭文件#include"absacc.h"#defineCTRL8155XBYTE[0x4400] //采用絕對(duì)地址訪問(wèn)方式定義8155口 地址#definePORTA8155XBYTE[0x4401]#definePORTB8155XBYTE[0x4402]#definePORTC8155XBYTE[0x4403]voidscanled(unsignedcharn[]); //函數(shù)聲明,LED掃描函數(shù),該函數(shù) 將8個(gè)LED //輪流掃描一遍,入口參數(shù)為8個(gè)顯示碼存放的首地址voiddelay(unsignedchart);//延時(shí)函數(shù),入口參數(shù)t確定延時(shí)時(shí)間,用于控制

//每位顯示間隔時(shí)間/*-----------------main函數(shù)-----------------*/main(){unsignedchari,j;unsignedcharled[]={0,0,0,0,0,0,0,0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,0,0,0,0,0,0}; //定義顯示碼數(shù)組,其中顯示碼0沒(méi)有任何顯示CTRL8155=0x03; //寫(xiě)入8155控制字,C口輸入,A,B口輸出for(;;){for(i=0;i<23;i++) //共顯示23屏

for(j=0;j<100;j++) //每屏掃描100遍,用于控制每屏顯示時(shí)間

//同時(shí)也控制了移動(dòng)顯示的速度

scanled(led+i); //調(diào)用LED掃描函數(shù)

}}/*-----------------LED掃描函數(shù)-----------------*/voidscanled(unsignedcharn[]){unsignedchari,temp=0x01;for(i=0;i<8;i++) //控制8個(gè)LED{PORTA8155=0xff; //位碼送0xff,關(guān)閉所有顯示

PORTB8155=n[i]; //B口送顯示碼,根據(jù)不同的位依次送顯示 碼n[0],n[1],…n[7]PORTA8155=~temp; //A口選位

temp<<=1; //位碼左移一位,選擇下一位LEDdelay(20); //每位顯示時(shí)間

}}/*-----------------延時(shí)函數(shù)-----------------*/voiddelay(unsignedchart){unsignedchari;for(i=0;i<t;i++);}2)LED定時(shí)顯示程序設(shè)計(jì)

(1)軟件設(shè)計(jì)思路。1s定時(shí)采用實(shí)訓(xùn)3.2中的設(shè)計(jì)思路,用定時(shí)器1的方式1編制1s的延時(shí)程序。假定系統(tǒng)采用12MHz晶振,定時(shí)器1、方式1定時(shí)50ms,再循環(huán)20次即可定時(shí)到1s,定時(shí)器編程采用查詢方式實(shí)現(xiàn)。本程序設(shè)計(jì)的重點(diǎn)是8位LED動(dòng)態(tài)掃描函數(shù)如何同1s定時(shí)函數(shù)合理結(jié)合起來(lái)。動(dòng)態(tài)掃描顯示的基本原理要求調(diào)用掃描函數(shù)的時(shí)間間隔不能太長(zhǎng),否則顯示就會(huì)出現(xiàn)閃爍的現(xiàn)象。那么,把掃描函數(shù)放到什么地方調(diào)用才能保證每次調(diào)用的時(shí)間間隔不能太長(zhǎng)呢?現(xiàn)在試著把它放到主函數(shù)main中調(diào)用,調(diào)用程序段如下:voidscanled(unsignedcharn[]); //8位LED掃描函數(shù),參見(jiàn)ex4.cvoiddelay(); //定時(shí)器1延時(shí)1s函數(shù),參見(jiàn)ex2.cunsignedchardata*p; //指針定義main(){unsignedchardatai,j;unsignedchardataled[]={0,0,0,0,0,0,0,0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,0,0,0,0,0,0}; TMOD=0x10; for(;;){ p=led; for(i=0;i<23;i++,p++){ scanled(p); //調(diào)用8位LED掃描函數(shù)

delay(); //調(diào)用1s延時(shí)函數(shù)

} }}

把上面程序補(bǔ)充完整,運(yùn)行該程序會(huì)出現(xiàn)什么現(xiàn)象呢?LED無(wú)法正常顯示。因?yàn)樵谥骱瘮?shù)main中每隔1s調(diào)用一次掃描函數(shù),掃描間隔時(shí)間太長(zhǎng)(按照視覺(jué)暫留原理,一般要保證1s內(nèi)掃描次數(shù)在24次以上才可以保證顯示不閃爍),所以把掃描函數(shù)直接放到main函數(shù)中調(diào)用是無(wú)法完成顯示功能的。分析delay函數(shù)發(fā)現(xiàn),執(zhí)行它的絕大部分時(shí)間是在查詢等待定時(shí)時(shí)間到,查詢語(yǔ)句如下:

while(!TF1);

把掃描函數(shù)放到查詢語(yǔ)句中調(diào)用,是否可以保證能正確穩(wěn)定顯示結(jié)果呢?調(diào)用語(yǔ)句如下:

while(!TF1){scanled(p);}(2)程序源代碼如下://ex5.c#include"REG51.H"#include"absacc.h"#defineCTRL8155XBYTE[0x4400]#definePORTA8155XBYTE[0x4401]#definePORTB8155XBYTE[0x4402]#definePORTC8155XBYTE[0x4403]voidscanled(unsignedcharn[]);voiddelay1(unsignedchart);voiddelay();unsignedchardata*p;/*-----------------main函數(shù)-----------------*/main(){unsignedchardatai,j;unsignedchardataled[]={0,0,0,0,0,0,0,0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,0,0,0,0,0,0};TMOD=0x10;for(;;){p=led;for(i=0;i<23;i++,p++)delay();}}/*-----------------延時(shí)1s函數(shù)-----------------*/voiddelay(){unsignedchardataj;for(j=0;j<0x14;j++){ //設(shè)置20次循環(huán)次數(shù)

TH1=0x3c; //置定時(shí)器初值

TL1=0xb0;TR1=1; //啟動(dòng)定時(shí)器1while(!TF1){scanled(p);};//查詢計(jì)數(shù)是否溢出,即定時(shí)50ms時(shí)間到,TF=1TF1=0; //50ms時(shí)間到,將定時(shí)器溢出標(biāo)志位TF清0TH1=0x3c; //重新置計(jì)數(shù)器初值

TL1=0xb0;}}/*-----------------LED掃描函數(shù)-----------------*/voidscanled(unsignedchar*n){unsignedchardatai,temp=0x01;CTRL8155=0x03; //C口輸入,A,B口輸出

for(i=0;i<8;i++) {PORTA8155=0xff; PORTB8155=n[i]; //B口送段碼

PORTA8155=~temp; //A口選位

temp<<=1;delay1(20);}}/*-----------------延時(shí)函數(shù)-----------------*/voiddelay1(unsignedchart){unsignedchardatai;for(i=0;i<t;i++);}3)矩陣式鍵盤程序設(shè)計(jì)

(1)硬件電路連接。矩陣式鍵盤由行線和列線組成,按鍵位于行列的交叉點(diǎn)上。實(shí)驗(yàn)板中4×4矩陣式鍵盤由可編程并行接口芯片8155控制,行線與8155的PC0~PC3相連,列線與8155的PA0~PA3相連,值得注意的是PA口同時(shí)又是8位動(dòng)態(tài)LED的位選線。

(2)軟件設(shè)計(jì)思路。矩陣式鍵盤掃描一般采用逐列掃描法,即將鍵盤的列線逐一清0,然后讀取行線的值。如果行線的值都為1,則表示該列沒(méi)有鍵按下;否則表示該列有鍵按下,但該列有4個(gè)按鍵,到底是哪個(gè)按鍵按下了,由行值中為0的位確定。

針對(duì)實(shí)驗(yàn)板上的電路,將列值逐一清0,也就是分別往8155的A口依次寫(xiě)入下列值:0xfe、0xfd、0xfb、0xf7,將其定義成如下數(shù)組:unsignedcharlie[]={0xfe,0xfd,0xfb,0xf7};0xfe:將PA0連接的列清0,列號(hào)為0;0xfd:將PA1連接的列清0,列號(hào)為1;0xfb:將PA2連接的列清0,列號(hào)為2;0xf7:將PA3連接的列清0,列號(hào)為3。定義變量unsignedchara存放列號(hào),a的取值為0~3。

如果某一列有鍵按下,那么從C口讀取的行值可能為下面的值:0x0e,0x0d,0x0b,0x07,將其定義成如下數(shù)組:unsignedcharsum[]={0x0e,0x0d,0x0b,0x07};0x0e:與PC0連接的行有鍵按下,行號(hào)為0;0x0d:與PC1連接的行有鍵按下,行號(hào)為1;0x0b:與PC2連接的行有鍵按下,行號(hào)為2;0x07:與PC3連接的行有鍵按下,行號(hào)為3。定義變量unsignedcharb存放行號(hào),b的取值為0~3。

把16個(gè)按鍵對(duì)應(yīng)的鍵值定義成如下的二維數(shù)組:unsignedcharkey[4][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}};

該數(shù)組的第一個(gè)下標(biāo)為行號(hào),第二個(gè)下標(biāo)為列號(hào),即key[0][0]為第0行第0列的鍵值,k[1][3]為第1行第3列的鍵值。鍵盤掃描程序一般包括以下3步:第一步:判斷是否有鍵按下。將列值(全0)寫(xiě)入8155的A口,從C口讀取行值,若行值不全為1,則表示有鍵按下,軟件延時(shí)去抖動(dòng),再次用同樣的方法判斷是否有鍵按下。若第二次判斷也有鍵按下,則可以確定有鍵按下;若第二次判斷無(wú)鍵按下,則可能第一次為誤判斷,確定為無(wú)鍵按下,繼續(xù)下一次寫(xiě)入列值。

值得注意的是,軟件延時(shí)去抖動(dòng)這一步不要省略,否則對(duì)按鍵的判斷可能會(huì)產(chǎn)生誤判斷。在使用動(dòng)態(tài)掃描顯示的程序中,一般采用動(dòng)態(tài)掃描顯示程序作為去抖動(dòng)的延時(shí)。第二步:求按鍵位置并得到鍵值。將四列依次置為0,送到8155的A口,讀取行值,若行值不全為1,則確定該列有鍵按下,假設(shè)此列號(hào)為a;將讀取的行值采用逐一查詢的方法,從行號(hào)b=0開(kāi)始,判斷行值與預(yù)先定義的行值數(shù)組中的元素是否相等,若相等即可得到行號(hào)b,同列號(hào)a一起確定按鍵位置,從而將鍵值數(shù)組key中相應(yīng)的鍵值取出來(lái),該程序段如下:for(b=0;b<4;b++){ //查詢按下的行號(hào)

if(c==sum[b]){ //c中為讀取的行值

save=key[b][a]; //得到鍵值保存到變量save中

break;}

由于保存鍵值的變量save的值在main函數(shù)中修改,又在顯示函數(shù)display中使用,因此將其定義成外部變量。

第三步:判斷閉合的按鍵是否釋放。判斷按鍵是否釋放的算法同判斷是否有鍵按下的算法正好相反,判斷有鍵按下的標(biāo)志為:讀取的行值不全為1,而判斷按鍵是否釋放的標(biāo)志是:讀取的行值全為1。鍵盤掃描源程序代碼。//ex6.c#include"reg51.h"#include"absacc.h"#defineCTRLXBYTE[0x4400]#definePAXBYTE[0x4401]#definePBXBYTE[0x4402]#definePCXBYTE[0x4403]voiddisplay();voiddelay1(unsignedchart);unsignedcharsave; //保存鍵值,初值為0unsignedcharledd[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//定義LED顯示碼/*-----------------main函數(shù)-----------------*/main(){unsignedchara,b,c;unsignedcharlie[]={0xfe,0xfd,0xfb,0xf7}; //逐次選中鍵盤的列的碼

unsignedcharsum[]={0x0e,0x0d,0x0b,0x07}; //相應(yīng)行值

unsignedcharkey[4][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}};//鍵值

CTRL=0x03; //寫(xiě)控制字,C口輸入,A、B輸出

PB=0x00; //屏蔽B口

save=0x00;while(1){PB=0x00; //關(guān)LED顯示

PA=0x00; //A口輸出全0c=PC; //讀C口

c=c&0x0f; //屏蔽掉C口高4位

if(c==0x0f)continue; //若列線為全1,則表示沒(méi)有鍵按下,重新判斷

else{ //否則,延時(shí)去抖,第二次判斷是否有鍵按下

delay(20);PA=0x00;c=PC;c=c&0x0f;if(c==0x0f)continue;//第二次判斷無(wú)鍵按下,重新判斷

else{ //兩次判斷均有鍵按下,表示的確有鍵按下

for(a=0;a<4;a++){ //逐次選中鍵盤的列,確定按鍵位置

PA=lie[a];c=PC; //讀C口行值c=c&0x0f; //屏蔽高四位

if(c!=0x0f)break;}for(b=0;b<4;b++){ //查詢按下的行號(hào)

if(c==sum[b]){save=key[b][a];break; //得到鍵值保存到變量save中

}}do{display(); //調(diào)用顯示函數(shù),并等待按鍵是否釋放

PB=0x00; //關(guān)顯示

PA=0x00; //判斷按鍵是否釋放

c=PC; //讀C口行值

c=c&0x0f; }while(c!=0x0f);//屏蔽高四位

}}}}/*-----------------顯示函數(shù)-----------------*/voiddisplay(){unsignedchardatai;for(i=0;i<10;i++){PA=0x7f; //無(wú)鍵按下,送LED顯示位碼

PB=ledd[save];} //將鍵值對(duì)應(yīng)的顯示碼送LED}/*-----------------延時(shí)函數(shù)-----------------*/voiddelay1(unsignedchart){unsignedchardatai;for(i=0;i<t;i++);}

5.實(shí)訓(xùn)分析與總結(jié)

1)外部RAM與擴(kuò)展I/O地址的C51定義

MCS-51單片機(jī)擴(kuò)展外部I/O口采用與片外RAM相同的尋址方法,所有擴(kuò)展的I/O口以及通過(guò)擴(kuò)展I/O口連接的外設(shè)都與片外RAM統(tǒng)一編址,在匯編語(yǔ)言程序設(shè)計(jì)中,使用以下指令訪問(wèn)外部I/O口地址:MOVX@DPTR,A ;尋址外部64K字節(jié)地址范圍0000H~FFFFHMOVX A,@DPTRMOVX @Ri,A ;尋址低256字節(jié)地址范圍00H~FFHMOVX A,@Ri

而在C51程序設(shè)計(jì)中,如何定義外部RAM和擴(kuò)展I/O口的地址呢?首先在程序中必須包含“absacc.h”絕對(duì)地址訪問(wèn)頭文件,然后用關(guān)鍵字XBYTE來(lái)定義I/O口地址或外部RAM地址。#include“absacc.h"#defineCTRL8155XBYTE[0x4400] //定義8155控制口地址#definePORTA8155XBYTE[0x4401] //定義8155的A口地址#definePORTB8155XBYTE[0x4402] //定義8155的B口地址#definePORTC8155XBYTE[0x4403] //定義8155的C口地址有了以上定義后,就可以直接在程序中對(duì)已定義的I/O口名稱進(jìn)行讀寫(xiě)了,例如:

CTRL8155=0x43;

在絕對(duì)地址訪問(wèn)頭文件absacc.h中,定義了MCS-51單片機(jī)所有存儲(chǔ)區(qū)域的絕對(duì)地址訪問(wèn)關(guān)鍵字CBYTE、DBYTE、PBYTE和XBYTE,可以對(duì)相應(yīng)的存儲(chǔ)區(qū)域的絕對(duì)地址進(jìn)行字節(jié)尋址。其中包括,CBYTE尋址CODE區(qū),DBYTE尋址DATA區(qū),PBYTE尋址分頁(yè)XDATA區(qū)(低256字節(jié)),XBYTE尋址XDATA區(qū)。如果要訪問(wèn)外部數(shù)據(jù)存儲(chǔ)區(qū)域0x2000處的內(nèi)容,可以使用如下語(yǔ)句:unsignedcharval;val=XBYTE[0x2000];

也可以像例程ex4.c中一樣,將絕對(duì)地址先預(yù)定義成一個(gè)易于識(shí)別的符號(hào),如CTRL8155、PORTA8155等。2)C51中數(shù)組定義

FranklinC51編譯器支持ANSIC語(yǔ)言中的構(gòu)造數(shù)據(jù)類型,包括數(shù)組、結(jié)構(gòu)、共用體、枚舉等。在C51程序中定義和使用數(shù)組的方法與ANSIC語(yǔ)言中相同。例程ex4.c中將顯示碼定義成了一個(gè)一維數(shù)組led:Unsignedcharled[]={0,0,0,0,0,0,0,0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f, 0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,0,0,0,0,0,0};

該數(shù)組在存儲(chǔ)器中的存放方式:占據(jù)了以符號(hào)地址led為首地址的一串連續(xù)的字節(jié)位置。在例程中,由于LED掃描函數(shù)voidscanled(unsignedcharn[])的入口參數(shù)是8個(gè)顯示碼的首地址,因此在main函數(shù)中調(diào)用LED掃描函數(shù)scanled的格式如下: scanled(led+i);其中,led+i(i是0~22之間的整數(shù))表示該數(shù)組元素的地址,當(dāng)i=0時(shí),led+i就是該數(shù)組的首地址;當(dāng)i=1時(shí),led+i就是該數(shù)組的第一個(gè)元素的地址,依此類推。利用指針訪問(wèn)數(shù)組也是一個(gè)比較好的方法。3)C51中指針定義修改例程ex4.c的源代碼如下://ex4_1.c,本程序中的斜體部分為修改內(nèi)容#pragmaSMALL#include"REG51.H" //定義頭文件#include"absacc.h"#defineCTRL8155XBYTE[0x4400] //采用絕對(duì)地址訪問(wèn)方式定義8155口地址#definePORTA8155XBYTE[0x4401]#definePORTB8155XBYTE[0x4402]#definePORTC8155XBYTE[0x4403]voidscanled(unsignedchar*n); //函數(shù)聲明,LED掃描函 數(shù),該函數(shù)將8個(gè) //LED輪流掃描一遍,入口參數(shù)為8個(gè)顯示碼存放的首地址voiddelay(unsignedchart); //延時(shí)函數(shù),入口參數(shù)t確定延時(shí)時(shí)間,用 于控制每位顯示間隔時(shí)間/*-----------------main函數(shù)-----------------*/main(){unsignedchari,j;unsignedcharled[]={0,0,0,0,0,0,0,0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0,0,0,0,0,0,0};unsignedchar*p; //定義無(wú)符號(hào)字符指針

CTRL8155=0x03; //寫(xiě)入8155控制字C口輸入,A,B口輸出

for(;;){p=led; //數(shù)組首地址賦給指針

for(i=0;i<23;i++,p++) //i增1的同時(shí),指針也增1for(j=0;j<100;j++)scanled(p); //將指針p中的地址作為實(shí)參

}}/*-----------------LED掃描函數(shù)-----------------*/voidscanled(unsignedchar*n){unsignedchari,temp=0x01;for(i=0;i<8;i++) {PORTA8155=0xff; PORTB8155=*(n+i);//B口送段碼

PORTA8155=~temp;//A口選位

temp<<=1;delay(50);}}/*-----------------延時(shí)函數(shù)-----------------*/voiddelay(unsignedchart){unsignedchari;for(i=0;i<t;i++);}

比較例程ex4_1.c和ex4.c會(huì)發(fā)現(xiàn),二者的不同之處在于:ex4.c采用數(shù)組名表示該數(shù)組的首地址,從而訪問(wèn)數(shù)組中的數(shù)據(jù);而ex4_1.c中采用指針訪問(wèn)數(shù)組中的數(shù)據(jù),首先把數(shù)組的首地址賦給指針,然后通過(guò)指針增1來(lái)修改該地址,從而訪問(wèn)數(shù)組中的不同數(shù)據(jù)。指針是C語(yǔ)言的一個(gè)重要概念,也是C語(yǔ)言的重要特色之一。FranklinC51編譯器支持“一般指針”和“基于存儲(chǔ)器”的指針。一般指針和ANSIC語(yǔ)言中的指針定義相同,需3個(gè)字節(jié):第一個(gè)字節(jié)為存儲(chǔ)器類型,第二個(gè)字節(jié)為高字節(jié)偏移地址,第三個(gè)字節(jié)為低字節(jié)偏移地址。一般指針可以被用來(lái)指示MCS-51單片機(jī)存儲(chǔ)器中的任何類型的變量。

例如在例程ex4_1.c中定義的一般指針如下:

unsignedchar*p; //定義無(wú)符號(hào)字符指針一般指針定義時(shí)也可以使用前面介紹過(guò)的data、idata、pdata、xdata等關(guān)鍵字對(duì)指針的存儲(chǔ)位置進(jìn)行聲明,例如:char*xdatastr; //存放在xdata的通用指針int*datanum; //存放在data的通用指針基于存儲(chǔ)器的指針以存儲(chǔ)器類型為參考,一般來(lái)說(shuō),在定義的時(shí)候包含了數(shù)據(jù)類型和數(shù)據(jù)空間的說(shuō)明,例如:chardata*str; //data的字符串指針intxdata*num; //xdata的int指針longcode*pow; //code的long指針

因?yàn)閿?shù)據(jù)類型會(huì)在編譯的時(shí)候處理,所以基于存儲(chǔ)器的指針不需要用來(lái)存放數(shù)據(jù)類型的字節(jié),它只需要一個(gè)字節(jié)(當(dāng)數(shù)據(jù)類型為idata、data、bdata、pdata時(shí))或者兩個(gè)字節(jié)(當(dāng)數(shù)據(jù)類型為code、xdata時(shí))。與一般指針相比,少了一個(gè)字節(jié)來(lái)指示類型,所以在程序執(zhí)行時(shí)速度快一些。同一般指

溫馨提示

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

評(píng)論

0/150

提交評(píng)論