




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第3章C51應用實訓實訓3.1C51程序開發環境實訓3.2定時器/計數器C51程序設計實訓3.3數碼顯示和矩陣式鍵盤C51程序設計實訓3.4字符型LCD顯示模塊C51程序設計實訓3.5單片機串行通信C51程序設計
實訓3.6數字鐘C51程序的實現實訓3.1C51程序開發環境1.實訓目的
(1)了解利用C51控制單片機系統的軟硬件環境及C51程序的開發過程。
(2)了解C51程序的結構。
(3)了解8051特殊功能寄存器SFR和位名稱在C51中的定義方法。
2.實訓設備與器件實訓設備:單片機集成開發環境、綜合實訓板。3.項目設計要求編制C51程序使目標板上連接在P1口的8個LED閃動。4.實訓內容目標板上連接在P1口的8個LED閃動的C51程序源代碼如下://ex1.c#include"REG51.H" //頭文件為REG51.H,定義了51單片機的SFR和位名稱/*-----------------延時函數-----------------*/delay(intt) {inti,j;for(i=0;i<t;i++)for(j=0;j<10;j++);}/*-----------------main函數-----------------*/main() {inti;while(1){P1=0xff; //熄滅8個LED,0xff為十六進制數0ffH,在C語言中用0x前綴表示十六進制數delay(1000);P1=0x00; //點亮8個LEDdelay(1000); }}
5.調試方法與步驟本實訓教材采用FranklinC51編譯器和MedWin中文版集成單片機開發環境,并假定FranklinC51編譯器已經安裝在計算機的D:\FC子目錄下。
(1)將開發系統和目標板連接好,并接上電源。
(2)啟動MedWin中文版,進入MedWin集成開發環境。
(3)設置編譯環境。第一次在MedWin中使用C51編譯環境需進行“編譯、匯編、連接配置”(以后使用時不需再配置)。單擊“設置”菜單項,如圖3.1.1所示。選擇“設置向導”,彈出如圖3.1.2所示的“編譯/匯編/連接配置”窗口1。圖3.1.1設置菜單項圖3.1.2“編譯/匯編/連接配置”窗口1圖3.1.3“編譯/匯編/連接配置”窗口2
圖3.1.4“Newfiles”窗口(4)編輯源程序。打開“文件”菜單,選擇“新建”選項,在“Newfiles”窗口中輸入新建的文件名,例如“ex1.c”(注意一定要輸入文件擴展名),如圖3.1.4所示。單擊“打開”,進入源程序編輯窗口。
(5)輸入源程序,并存盤。
(6)編譯源程序。編譯后在編輯窗口下面將出現狀態窗口,如圖3.1.5所示。
(7)連接程序并下載到仿真器中。
(8)在調試窗口中可以對程序進行單步運行、斷點運行、全速運行等測試,如圖3.1.6所示為程序全速運行時的畫面。圖3.1.5編譯后出現的狀態窗口區域圖3.1.6全速運行程序
6.實訓分析與總結
1)FranklinC51編譯器簡介
FranklinC51編譯器的安裝非常簡單,直接點擊setup,按照提示選擇安裝目錄即可。安裝完成后,在安裝目錄下有以下四個子目錄:
LIB:庫文件目錄,包含了所有標準函數;
INC:頭文件目錄(#include);
BIN:可執行文件目錄;
EXAMPLES:樣例程序子目錄。
這里對主要的可執行文件簡介如下:
A51:MCS-51匯編程序,它可將按照A51匯編格式編制的匯編源程序編譯成以Intel目標模塊格式產生的可再定位目標代碼。
C51:可將C51高級語言編制的源程序編譯成以Intel目標模塊格式產生的可再定位目標代碼。
L51:連接定位器,可將幾個不同程序模塊復合為一個模塊,并自動從庫文件中挑選模塊嵌入目標文件,同時定義絕對地址,并計算再定位段的地址。
LIB51:C51函數庫管理器,可以進行函數庫的生成與處理。2)C51程序結構圖3.1.7C程序基本結構C51中的函數與匯編語言中的子程序概念是一樣的。本例中包含delay和main兩個函數,main函數是必不可少的主函數,也是程序開始執行的函數,delay函數的功能是延時,用于控制燈的閃動速度。LED的閃動過程是:點亮→延時→熄滅→延時
延時函數在很多程序設計中都會用到,本程序中使用了雙重循環,外循環的循環次數由形式參數t提供,總的循環次數是10×t,循環體是空操作。for語句的使用方法與ANSIC語言相同。
C51程序設計中,函數的定義和調用方法同ANSIC語言相同。3)8051單片機的SFR寄存器和位名稱及其C51定義在8051單片機中,除了程序計數器PC和四組通用寄存器組之外,其他所有的寄存器均稱為特殊功能寄存器(SFR)。
MCS-51系列單片機內部定義了21個特殊功能寄存器,它們分散在片內RAM區的高128字節中,地址為80H~0FFH,SFR只能用直接尋址方式。
SFR中有11個寄存器具有位尋址能力(這些寄存器的字節地址都能被8整除,即字節地址是以8或0作為尾數的),且每一位均定義了位名稱。在匯編語言程序設計中可以直接使用這些寄存器名稱和位地址名稱,例如: MOV A,#0ffh CLR P1.1
那么,在C51程序設計中,如何使用這些寄存器和位名稱呢?在頭文件“REG51.H”已經預定義了所有SFR和位名稱。在REG51.H頭文件中使用了“sfr”和“sbit”兩個關鍵字。
(1)關鍵字“sfr”用于定義特殊功能寄存器的地址,其格式為:
sfr特殊功能寄存器名=特殊功能寄存器地址;注意后面的地址必須為常數,范圍是0x80到0xff,例如:sfrPSW=0xD0;sfrACC=0xE0;sfrB=0xF0;(2)關鍵字“sbit”與“sfr”類似,用于定義一些特殊的位,其格式為:
sbit位名稱=位地址例如:
sbitCY=0xD7; sbitAC=0xD6; sbitF0=0xD5;也可以寫成:
sbitCY=0xD0^7; sbitAC=0xD0^6; sbitF0=0xD0^5;
如果在前面已定義了特殊功能寄存器PSW,那么上面的定義也可以寫成:sbitCY=PSW^7;sbitAC=PSW^6;sbitF0=PSW^5;
在C51程序設計中,編程員可以直接在自己的程序中利用關鍵字sfr和sbit來定義這些特殊功能寄存器和特殊位名稱,也可以把“REG51.H頭文件包含在自己的程序中,直接使用SFR名稱和位名稱。頭文件REG51.H的內容如下:7.思考題(1)修改源程序,使8個LED模擬霓虹燈的各種顯示方式。(2)利用對P1口某一位的操作設計霓虹燈的顯示方式。(3)以單步、斷點等各種方式運行程序。(4)在程序運行過程中如何查看單片機各種資源的狀態。實訓3.2定時器/計數器C51程序設計1.實訓目的(1)復習MCS-51單片機定時器/計數器和中斷的知識。(2)掌握利用C51進行單片機內部資源控制的方法。(3)掌握利用C51進行中斷編程的方法。(4)了解C51的基本語法。2.實訓設備與器件實訓設備:單片機集成開發環境,綜合實訓板。
3.項目設計要求
(1)利用定時器查詢方式,編制C51程序使目標板上連接在P1口的8個LED循環顯示,時間間隔為1s。
(2)利用定時器中斷方式,編制C51程序使目標板上連接在P1口的8個LED循環顯示,時間間隔為1s。
4.實訓內容
1)定時器查詢方式C51程序設計
(1)程序設計方法:用定時器1的方式1編制1s的延時程序,假定系統采用12MHz晶振,定時器1、方式1定時50ms,再循環20次即可定時到1s。(2)源程序如下://ex2.c#include"REG51.H"/*-----------------延時函數-----------------*/voiddelay(){intj;for(j=0;j<0x14;j++) //設置20次循環次數
{TH1=0x3c; //置定時器初值
TL1=0xb0;TR1=1; //啟動定時器1while(!TF1); //查詢計數是否溢出,即定時50ms時間到,TF=1TF1=0; //50ms時間到,將定時器溢出標志位TF清零
}}/*-----------------main函數-----------------*/main(){inti,w;TMOD=0x10; //置定時器1為方式1while(1){w=0x01; //燈的位置初值為01hfor(i=0;i<8;i++){P1=~w; //循環點亮燈
w<<=1; //點亮燈的位置移動
delay(); //調用1ms延時
}}}2)定時器中斷方式C51程序設計
(1)程序設計方法:用定時器1的方式1編制1s的延時程序,假定系統采用12MHz晶振,定時器1、方式1定時50ms,采用中斷方式編程,需中斷20次,外部變量count作為計數次數,位變量flag為1s定時到標志位。
(2)源程序如下://ex3.c#include"REG51.H"intcount; //定義外部變量bitflag; //1s時間到標志/*-----------------中斷函數-----------------*/voiddelay()interrupt3//interrupt3表示該函數為中斷號3的中斷函數{TH1=0x3c; //重新置定時器1初值
TL1=0xb0;TR1=1; //開定時器1中斷
count--; //中斷次數減1if(count==0)flag=1; //若20次中斷已完成,則置1s延時時間到標志
}/*-----------------main函數-----------------*/main(){intj,w;TMOD=0x10; //初始化定時器1TH1=0x3c;TL1=0xb0;EA=1; //開總中斷ET1=1; //定時器1開中斷
TR1=1; //啟動定時器1while(1){w=0x01;for(j=0;j<8;j++){flag=0; //初始標志
count=0x14; //設置中斷次數
P1=~w; //循環點亮燈
while(flag==0); //等待1s定時時間到
w<<=1; //點亮燈的位置移動
}}}
5.實訓分析與總結
1)C51程序基本結構
C51的程序結構同ANSIC語言相同。C語言是一種結構化編程語言。結構化程序由若干模塊組成,每個模塊中包含著若干個基本結構,而每個基本結構中有若干條語句。總的來說,C語言有3種基本結構:順序結構、選擇結構和循環結構。順序結構是一種最基本、最簡單的程序結構,程序由低地址到高地址順序執行程序代碼。選擇結構也稱為分支結構,根據條件測試結果選擇不同的程序執行方向,常用的選擇語句有:if,elseif,switch/case語句。在程序ex3.c的delay的斷函數中有下面的選擇語句:
if(count==0)flag=1;//若20次中斷已完成,則置1s延時時間到標志
循環結構是指重復執行某一程序段的程序結構,是選擇結構的一種特殊情形,程序設計中使用非常廣泛。C語言中用于循環的語句有:while,dowhile,for語句。前面的例程中使用了很多循環語句,舉例如下:
(1)單片機控制程序的主程序,即main函數中都有一個后臺無限循環語句,結構如下:main(){...... //初始化部分
while(1){ //無限循環
}}(2)在例程ex1.c中,延時函數delay采用了雙重循環結構,其代碼如下:delay(intt) //延時函數{inti,j;for(i=0;i<t;i++)for(j=0;j<10;j++);}
在上面的雙重循環結構中,循環體是空的,表示什么都不做,僅用于延時的功能。(3)在例程ex2.c中,延時函數采用了查詢方式編程,查詢語句如下:
while(!TF1);//查詢計數是否溢出,即定時50ms時間到,TF=1
當定時時間未到時,TF=0,此時測試條件!TF的結果為1,即條件為真,繼續重復執行循環體(此處循環體為空);當定時時間到時,TF=1,條件!TF的結果為0,即條件為假,退出while循環,繼續執行下面的語句。在單片機程序設計中,查詢方式編程經常采用上面的結構,例如:
while((P1&0x01)==0);該語句用于測試P1口的P1.0的電平狀態。當P1.0=0時,條件成立,繼續等待;若P1口的P1.0由0變為1,則循環終止,繼續執行下面的語句。2)C51的數據與運算法則簡介(1)C51的數據類型。表3.2.1FranklinC51編譯器支持的數據類型
當計算結果隱含著另外一種數據類型時,數據類型可以自動轉換。例如將一個位變量賦給一個整型變量時,位值自動轉換為整型值,有符號變量的符號也能自動進行處理。
FranklinC51編譯器也支持符號常量和變量,符號常量的定義方法如下:
#defineCOUNT20
符號常量在整個程序中,其值不可變,通常是具有一定含義的英文單詞或符號。因此,采用符號常量可以增加程序的可讀性和可修改性。
下面結合本實訓中的兩個例程,對變量定義應注意的幾個問題加以討論。①變量數據類型的選擇。在C51程序設計中,變量數據類型的定義極其重要,因為在所有數據類型中只有bit和unsignedchar兩種數據類型可以直接支持機器指令,對于其他的數據類型C51編譯器都要進行一系列復雜的變量數據和變量類型的處理,而這種處理將會對應很長一段機器指令,最終會使程序變得復雜、龐大,運行速度降低。由此可見,在C51程序設計過程中,在滿足數據要求的情況下,應盡可能使用unsignedchar變量和bit變量。signedchar變量雖然也只占用一個字節,但需要進行額外的操作來測試代碼的符號位,因此會降低代碼執行效率。
在例程ex2.c的main函數中定義了整型變量i和w,仔細分析發現,i的取值范圍是0~8,而表示亮燈位置的變量w的取值也在00H~0ffH之間,所以,這兩個變量應該定義成unsignedchar,而不是int。同樣,在該程序的delay函數中,也定義了整型變量j,分析一下,最好是將j定義成什么數據類型呢?同樣,在例程ex3.c的main函數中的變量i和w也應該定義成unsignedchar,外部變量count也應該定義成unsigedchar。②全局變量和局部變量。這里應該注意的是:在例程ex3.c中,整型變量count和位變量flag是定義在所有函數外部的變量,稱為外部變量,即全局變量;而變量i和w是定義在main函數內部的,稱為內部變量,即局部變量。全局變量與局部變量的區別是:前者可以在本文件的所有函數中使用,其有效范圍是從定義變量的位置開始到本源文件結束為止。例如count和flag,不僅在delay函數中使用,也在main函數中使用,后者只在定義本函數的范圍內有效,即只能在本函數內使用。③變量的定義。
bit數據類型是ANSIC語言所沒有的數據類型,在此做如下說明。位變量的定義格式為:
bit變量名;在程序ex3.c中,定義了位變量flag作為定時時間到的標志,若定時時間未到,則flag=0,若定時時間到,則flag=1。在單片機C語言程序設計中,為了使程序更加優化,當需要設定只有兩種狀態的標志變量時,應該將其定義成位變量,而不要定義成字符或整型變量。④用于訪問sfr的數據類型。
FranklinC51編譯器的數據類型除了包括ANSIC語言所具有的位尋址、字符、整數、長整、浮點數、指針等之外,還有3種用于訪問SFR的數據類型,如表3.2.2所示。表3.2.2訪問SFR的數據類型(2)C51的運算符。
C51編譯器所支持的運算符與ANSIC語言相同,分為算術運算符、關系運算符、邏輯運算符、位操作符、自增減運算符和復合運算符等。算術運算符:+、-、*、/、%。關系運算符:<、>、<=、>=、==、!=。邏輯運算符:&&、||、!。位操作符:&、|、^、~、<<、>>。自增減運算符:++、--。復合運算符:凡是二目運算符,都可以與賦值運算符“=”一起組成復合賦值運算符,包括+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=。
尤其值得注意的是:由于單片機C語言程序和單片機硬件緊密相關,因此會用到大量的位運算或邏輯運算。在例程ex2.c和ex3.c中,為了使亮燈的順序移動,采用了取反和左移操作,具體操作如下:初值:w=0x01;w:00000001(初值)P1=~w;11111110(“1”使相應燈熄滅,“0”使相應燈點亮)W<<=1;00000010(左移一位)思考:如果將該程序中初值直接寫成:“11111110”,應該如何修改程序?3)中斷函數的編寫方法
FranklinC51編譯器支持在C源程序中直接以函數形式編寫中斷過程。常用的中斷函數定義語法如下:
void函數名()interruptnC51編譯器允許0~31個中斷,下列中斷及其相關地址為8051控制器所提供的外部中斷:
0:EXTERNAL0 地址:0003H 1:TIMER/COUNTER0 地址:000BH 2:EXTERNAL1 地址:0013H 3:TIMER/COUNTER1 地址:001BH 4:SERIALPORT 地址:0023H
在例程ex3.c中,使用了TIMER/COUNT1中斷,中斷號為3,因此該中斷函數的結構如下:voiddelay()interrupt3//interrupt3表示該函數為中斷號3的中斷函數{
::}
編寫中斷函數時應遵循下列規則:
(1)不能進行參數傳遞,如果中斷過程包括任何參數聲明,則編譯器將產生一個錯誤信息。
(2)無返回值,如果想定義一個返回值將產生錯誤,但是,如果返回整型值編譯器將不產生錯誤信息,因為整型值是默認值,編譯器不能清楚識別。
(3)在任何情況下不能直接調用中斷函數,否則編譯器會產生錯誤。由于退出中斷過程是由指令RETI完成的,該指令影響MCS-51單片機的硬件中斷系統,直接調用中斷函數時硬件上沒有中斷請求存在,因而這個指令的結果是不定的并且通常是致命的。(4)編譯器從絕對地址8n+3處產生一個中斷向量,其中n為中斷號,該向量包括一個中斷過程的跳轉,向量的產生可由編譯器控制指令NOINTVECTOR壓縮,因而程序員可以從獨立的匯編模塊中提供中斷向量。
(5)可以在中斷函數定義中使用using指定當前使用的寄存器組,格式如下:
void函數名([形式參數])interruptn[usingm]MCS-51單片機有四組寄存器R0~R7,程序具體使用哪一組寄存器由程序狀態字PSW中的兩位RS1和RS0來確定。在中斷函數定義時,可以用using指定該函數具體使用哪一組寄存器,m在0,1,2,3這4個數中取值,對應四組寄存器組。例如:
voiddelay()interrupt3using2(6)在中斷函數中調用的函數所使用的寄存器組必須與中斷函數相同,當沒有使用using指令時,編譯器會選擇一個寄存器組作絕對寄存器訪問。程序員必須保證按要求使用相應寄存器組,C編譯器不會對此檢查。
(7)如果在中斷函數中執行浮點運算,必須保存浮點寄存器狀態,當沒有其他程序執行浮點運算時,可以不保存。4)C51的數據存儲類型與8051存儲器結構由于MCS-51單片機的存儲器結構特點是:程序存儲器(ROM)和數據存儲器(RAM)分開,并各自有其獨立的尋址方式。具體來說,MCS-51單片機的存儲空間分為四部分:片內數據存儲器空間(256字節)、片外數據存儲器空間(64K字節)、片內程序存儲器空間和片外程序存儲器空間,那么程序中定義的變量在哪個存儲區域呢?FranklinC51編譯器通過將變量、常量定義成不同的存儲類型的方法,將它們定位在不同的存儲區中。C51編譯器支持的存儲器類型如表3.2.3所示。表3.2.3C51編譯器支持的存儲器類型存儲器類型可以和數據類型一起使用,例如:intdatai;//整數i為內部數據存儲器中的變量intxdataj;//整數j定義在外部數據存儲器(64K字節)內一般在定義變量時經常省略存儲器類型的定義,采用默認存儲器類型,而默認存儲器類型和存儲器模式有關。5)C51編譯器支持的存儲器模式表3.2.4C51編譯器支持的存儲器模式SMALL模式:所有缺省變量參數均裝入內部RAM(與使用顯式的data關鍵字來定義結果相同)。使用該模式的優點是訪問速度快,缺點是空間有限,而且分配給堆棧的空間比較少,遇到函數嵌套調用和函數遞歸調用時必須小心,該模式適用于較小的程序。
COMPACT模式:所有缺省變量均位于外部RAM區的一頁(與使用顯式的pdata關鍵字來定義結果相同),最多能夠定義256字節變量。使用該模式的優點是變量定義空間比SMALL模式大,但運行速度比SMALL模式慢。使用本模式時,程序通過@R0和@R1來訪問變量。LARGE模式:所有缺省變量可放在多達64K字節的外部RAM區(與使用顯式的xdata關鍵字來定義結果相同),均使用數據指針DPTR來尋址。該模式的優點是空間大,可定義變量多,缺點是速度較慢,一般用于較大的程序,或擴展了大容量外部RAM的系統中。存儲模式決定了變量的默認存儲類型、參數傳遞區和無明確存儲類型的說明。例如若定義chars,則在SMALL存儲模式下,s被定位在DATA存儲區;在COMPACT存儲模式下,s被定位在IDATA存儲區;在LARGE存儲模式下,s被定位在XDATA存儲區。
存儲模式定義關鍵字SMALL、COMPACT和LARGE屬于C51編譯器控制指令,可以在命令行輸入,也可以在源文件的開始直接使用下面的預處理語句:
#pragmaSMALL//定義為SMALL模式除非特殊說明,本書中的C51程序均運行在SMALL模式下。
6.思考題
(1)例程ex3.c進行優化修改后的程序如下,試指出哪些地方做了修改?修改后有什么好處?//ex3_1.c#include"REG51.H"#defineTIME20unsignedcharcount; //定義外部變量bitflag; //1s時間到標志/*-----------------中斷函數-----------------*/voiddelay()interrupt3 //interrupt3表示該函數為中斷號3 的中斷函數{TH1=0x3c; //重新置定時器1初值
TL1=0xb0;TR1=1; //開定時器1中斷
count--; //中斷次數減1if(count==0)flag=1; //若20次中斷已完成,則置1s延時時間到標志}/*-----------------main函數-----------------*/main(){unsignedcharj,w;TMOD=0x10; //初始化定時器1TH1=0x3c;TL1=0xb0;EA=1; //開總中斷
ET1=1; //定時器1開中斷
TR1=1; //啟動定時器1while(1){w=0x01;for(j=0;j<8;j++){flag=0; //初始標志
count=TIME; //設置中斷次數
P1=~w; //循環點亮燈
while(flag==0); //等待1s定時時間到
w<<=1; //點亮燈的位置移動
}}}(2)修改實訓中的兩個例程,分別采用定時器/計數器0的方式0和方式2實現定時功能。實訓3.3數碼顯示和矩陣式鍵盤C51程序設計
1.實訓目的
(1)復習MCS-51單片機擴展可編程接口芯片8155的方法。
(2)復習8155編程方法及矩陣式鍵盤的硬件接口及掃描方法,復習LED的動態接口方法。
(3)掌握利用C51進行鍵盤和LED顯示的編程思路。
(4)掌握利用C51進行絕對地址訪問的方法。
2.實訓設備與器件實訓設備:單片機集成開發環境、綜合實訓板。
3.項目設計要求
(1)編制C51程序使實驗板上的8個數碼管移動顯示“0~F”。
(2)編制C51程序使實驗板上的8個數碼管移動顯示“0~F”,移動速度是1s移動一個字符。
(3)編制C51矩陣式鍵盤掃描程序,當按下實驗板上任意鍵時,在某個數碼管上能夠顯示其鍵值,當按鍵釋放時,顯示關閉。
4.實訓內容
1)LED顯示程序設計初步
(1)硬件電路分析。當單片機提供的并行I/O口不夠用戶使用時,常常需要擴展I/O口,8155和8255是擴展I/O口時使用較多的I/O芯片。在實訓電路中,用擴展的8155連接了8個LED數碼管和16個按鍵。通過擴展8155可以提供3個并行I/O口A、B、C口,3個I/O口的工作方式、輸入/輸出方向是由用戶的編程來確定的。8位LED顯示的位選碼由8155的A口控制,段選碼由8155的B口控制,LED為共陰極數碼管,接口方式為動態顯示接口方式。8155的地址如下:控制口:4400HA口:4401HB口:4402HC口:4403H(2)軟件設計思路。
LED動態顯示的硬件連接特點是各位數碼管的段選線相應并聯在一起,由同一個I/O口控制,各位的位選線(公共陰極或陽極)由另外的I/O口線控制。當程序向數碼管傳送一個顯示碼時,該顯示碼會同時到達每個數碼管,到底哪個數碼管會顯示,由位選線確定。當以動態方式顯示時,各數碼管分時輪流選通,要使其穩定顯示,必須采用掃描方式,利用人眼睛的視覺暫留效應,每一時刻顯示一個字符,一位一位輪流顯示,只要每位顯示間隔時間足夠短,就可以給人以同時顯示的感覺。圖3.3.1LED顯示示意圖
在上述移動顯示方式中,要分別顯示23屏不同的數據,就可以達到移動顯示的效果。每屏顯示數據之間有一定的邏輯關系,把這些顯示字符順序寫成下面的格式:
×××××××0123456789AbCdEF×××××××(×表示不顯示)
led
可以看到,第1屏顯示數據為“×××××××0”,第二屏顯示數據為“××××××01”,依此類推,第23屏顯示數據為“F×××××××”。
如果把以上顯示數據的顯示碼按順序存放到內部存儲器中,首地址為led,那么第1屏顯示碼的首地址為led,第2屏顯示碼的首地址為led+1,第3屏顯示碼的首地址為led+2,依此類推,可以得到第i屏顯示碼的首地址為led+i-1。如果顯示屏從第0屏開始數起,那么第i屏顯示碼的首地址即為led+i。(3)C51源程序代碼如下://ex4.c#include"REG51.H" //定義頭文件#include"absacc.h"#defineCTRL8155XBYTE[0x4400] //采用絕對地址訪問方式定義8155口 地址#definePORTA8155XBYTE[0x4401]#definePORTB8155XBYTE[0x4402]#definePORTC8155XBYTE[0x4403]voidscanled(unsignedcharn[]); //函數聲明,LED掃描函數,該函數 將8個LED //輪流掃描一遍,入口參數為8個顯示碼存放的首地址voiddelay(unsignedchart);//延時函數,入口參數t確定延時時間,用于控制
//每位顯示間隔時間/*-----------------main函數-----------------*/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}; //定義顯示碼數組,其中顯示碼0沒有任何顯示CTRL8155=0x03; //寫入8155控制字,C口輸入,A,B口輸出for(;;){for(i=0;i<23;i++) //共顯示23屏
for(j=0;j<100;j++) //每屏掃描100遍,用于控制每屏顯示時間
//同時也控制了移動顯示的速度
scanled(led+i); //調用LED掃描函數
}}/*-----------------LED掃描函數-----------------*/voidscanled(unsignedcharn[]){unsignedchari,temp=0x01;for(i=0;i<8;i++) //控制8個LED{PORTA8155=0xff; //位碼送0xff,關閉所有顯示
PORTB8155=n[i]; //B口送顯示碼,根據不同的位依次送顯示 碼n[0],n[1],…n[7]PORTA8155=~temp; //A口選位
temp<<=1; //位碼左移一位,選擇下一位LEDdelay(20); //每位顯示時間
}}/*-----------------延時函數-----------------*/voiddelay(unsignedchart){unsignedchari;for(i=0;i<t;i++);}2)LED定時顯示程序設計
(1)軟件設計思路。1s定時采用實訓3.2中的設計思路,用定時器1的方式1編制1s的延時程序。假定系統采用12MHz晶振,定時器1、方式1定時50ms,再循環20次即可定時到1s,定時器編程采用查詢方式實現。本程序設計的重點是8位LED動態掃描函數如何同1s定時函數合理結合起來。動態掃描顯示的基本原理要求調用掃描函數的時間間隔不能太長,否則顯示就會出現閃爍的現象。那么,把掃描函數放到什么地方調用才能保證每次調用的時間間隔不能太長呢?現在試著把它放到主函數main中調用,調用程序段如下:voidscanled(unsignedcharn[]); //8位LED掃描函數,參見ex4.cvoiddelay(); //定時器1延時1s函數,參見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); //調用8位LED掃描函數
delay(); //調用1s延時函數
} }}
把上面程序補充完整,運行該程序會出現什么現象呢?LED無法正常顯示。因為在主函數main中每隔1s調用一次掃描函數,掃描間隔時間太長(按照視覺暫留原理,一般要保證1s內掃描次數在24次以上才可以保證顯示不閃爍),所以把掃描函數直接放到main函數中調用是無法完成顯示功能的。分析delay函數發現,執行它的絕大部分時間是在查詢等待定時時間到,查詢語句如下:
while(!TF1);
把掃描函數放到查詢語句中調用,是否可以保證能正確穩定顯示結果呢?調用語句如下:
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函數-----------------*/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();}}/*-----------------延時1s函數-----------------*/voiddelay(){unsignedchardataj;for(j=0;j<0x14;j++){ //設置20次循環次數
TH1=0x3c; //置定時器初值
TL1=0xb0;TR1=1; //啟動定時器1while(!TF1){scanled(p);};//查詢計數是否溢出,即定時50ms時間到,TF=1TF1=0; //50ms時間到,將定時器溢出標志位TF清0TH1=0x3c; //重新置計數器初值
TL1=0xb0;}}/*-----------------LED掃描函數-----------------*/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);}}/*-----------------延時函數-----------------*/voiddelay1(unsignedchart){unsignedchardatai;for(i=0;i<t;i++);}3)矩陣式鍵盤程序設計
(1)硬件電路連接。矩陣式鍵盤由行線和列線組成,按鍵位于行列的交叉點上。實驗板中4×4矩陣式鍵盤由可編程并行接口芯片8155控制,行線與8155的PC0~PC3相連,列線與8155的PA0~PA3相連,值得注意的是PA口同時又是8位動態LED的位選線。
(2)軟件設計思路。矩陣式鍵盤掃描一般采用逐列掃描法,即將鍵盤的列線逐一清0,然后讀取行線的值。如果行線的值都為1,則表示該列沒有鍵按下;否則表示該列有鍵按下,但該列有4個按鍵,到底是哪個按鍵按下了,由行值中為0的位確定。
針對實驗板上的電路,將列值逐一清0,也就是分別往8155的A口依次寫入下列值:0xfe、0xfd、0xfb、0xf7,將其定義成如下數組:unsignedcharlie[]={0xfe,0xfd,0xfb,0xf7};0xfe:將PA0連接的列清0,列號為0;0xfd:將PA1連接的列清0,列號為1;0xfb:將PA2連接的列清0,列號為2;0xf7:將PA3連接的列清0,列號為3。定義變量unsignedchara存放列號,a的取值為0~3。
如果某一列有鍵按下,那么從C口讀取的行值可能為下面的值:0x0e,0x0d,0x0b,0x07,將其定義成如下數組:unsignedcharsum[]={0x0e,0x0d,0x0b,0x07};0x0e:與PC0連接的行有鍵按下,行號為0;0x0d:與PC1連接的行有鍵按下,行號為1;0x0b:與PC2連接的行有鍵按下,行號為2;0x07:與PC3連接的行有鍵按下,行號為3。定義變量unsignedcharb存放行號,b的取值為0~3。
把16個按鍵對應的鍵值定義成如下的二維數組:unsignedcharkey[4][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}};
該數組的第一個下標為行號,第二個下標為列號,即key[0][0]為第0行第0列的鍵值,k[1][3]為第1行第3列的鍵值。鍵盤掃描程序一般包括以下3步:第一步:判斷是否有鍵按下。將列值(全0)寫入8155的A口,從C口讀取行值,若行值不全為1,則表示有鍵按下,軟件延時去抖動,再次用同樣的方法判斷是否有鍵按下。若第二次判斷也有鍵按下,則可以確定有鍵按下;若第二次判斷無鍵按下,則可能第一次為誤判斷,確定為無鍵按下,繼續下一次寫入列值。
值得注意的是,軟件延時去抖動這一步不要省略,否則對按鍵的判斷可能會產生誤判斷。在使用動態掃描顯示的程序中,一般采用動態掃描顯示程序作為去抖動的延時。第二步:求按鍵位置并得到鍵值。將四列依次置為0,送到8155的A口,讀取行值,若行值不全為1,則確定該列有鍵按下,假設此列號為a;將讀取的行值采用逐一查詢的方法,從行號b=0開始,判斷行值與預先定義的行值數組中的元素是否相等,若相等即可得到行號b,同列號a一起確定按鍵位置,從而將鍵值數組key中相應的鍵值取出來,該程序段如下:for(b=0;b<4;b++){ //查詢按下的行號
if(c==sum[b]){ //c中為讀取的行值
save=key[b][a]; //得到鍵值保存到變量save中
break;}
由于保存鍵值的變量save的值在main函數中修改,又在顯示函數display中使用,因此將其定義成外部變量。
第三步:判斷閉合的按鍵是否釋放。判斷按鍵是否釋放的算法同判斷是否有鍵按下的算法正好相反,判斷有鍵按下的標志為:讀取的行值不全為1,而判斷按鍵是否釋放的標志是:讀取的行值全為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函數-----------------*/main(){unsignedchara,b,c;unsignedcharlie[]={0xfe,0xfd,0xfb,0xf7}; //逐次選中鍵盤的列的碼
unsignedcharsum[]={0x0e,0x0d,0x0b,0x07}; //相應行值
unsignedcharkey[4][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}};//鍵值
CTRL=0x03; //寫控制字,C口輸入,A、B輸出
PB=0x00; //屏蔽B口
save=0x00;while(1){PB=0x00; //關LED顯示
PA=0x00; //A口輸出全0c=PC; //讀C口
c=c&0x0f; //屏蔽掉C口高4位
if(c==0x0f)continue; //若列線為全1,則表示沒有鍵按下,重新判斷
else{ //否則,延時去抖,第二次判斷是否有鍵按下
delay(20);PA=0x00;c=PC;c=c&0x0f;if(c==0x0f)continue;//第二次判斷無鍵按下,重新判斷
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++){ //查詢按下的行號
if(c==sum[b]){save=key[b][a];break; //得到鍵值保存到變量save中
}}do{display(); //調用顯示函數,并等待按鍵是否釋放
PB=0x00; //關顯示
PA=0x00; //判斷按鍵是否釋放
c=PC; //讀C口行值
c=c&0x0f; }while(c!=0x0f);//屏蔽高四位
}}}}/*-----------------顯示函數-----------------*/voiddisplay(){unsignedchardatai;for(i=0;i<10;i++){PA=0x7f; //無鍵按下,送LED顯示位碼
PB=ledd[save];} //將鍵值對應的顯示碼送LED}/*-----------------延時函數-----------------*/voiddelay1(unsignedchart){unsignedchardatai;for(i=0;i<t;i++);}
5.實訓分析與總結
1)外部RAM與擴展I/O地址的C51定義
MCS-51單片機擴展外部I/O口采用與片外RAM相同的尋址方法,所有擴展的I/O口以及通過擴展I/O口連接的外設都與片外RAM統一編址,在匯編語言程序設計中,使用以下指令訪問外部I/O口地址:MOVX@DPTR,A ;尋址外部64K字節地址范圍0000H~FFFFHMOVX A,@DPTRMOVX @Ri,A ;尋址低256字節地址范圍00H~FFHMOVX A,@Ri
而在C51程序設計中,如何定義外部RAM和擴展I/O口的地址呢?首先在程序中必須包含“absacc.h”絕對地址訪問頭文件,然后用關鍵字XBYTE來定義I/O口地址或外部RAM地址。#include“absacc.h"#defineCTRL8155XBYTE[0x4400] //定義8155控制口地址#definePORTA8155XBYTE[0x4401] //定義8155的A口地址#definePORTB8155XBYTE[0x4402] //定義8155的B口地址#definePORTC8155XBYTE[0x4403] //定義8155的C口地址有了以上定義后,就可以直接在程序中對已定義的I/O口名稱進行讀寫了,例如:
CTRL8155=0x43;
在絕對地址訪問頭文件absacc.h中,定義了MCS-51單片機所有存儲區域的絕對地址訪問關鍵字CBYTE、DBYTE、PBYTE和XBYTE,可以對相應的存儲區域的絕對地址進行字節尋址。其中包括,CBYTE尋址CODE區,DBYTE尋址DATA區,PBYTE尋址分頁XDATA區(低256字節),XBYTE尋址XDATA區。如果要訪問外部數據存儲區域0x2000處的內容,可以使用如下語句:unsignedcharval;val=XBYTE[0x2000];
也可以像例程ex4.c中一樣,將絕對地址先預定義成一個易于識別的符號,如CTRL8155、PORTA8155等。2)C51中數組定義
FranklinC51編譯器支持ANSIC語言中的構造數據類型,包括數組、結構、共用體、枚舉等。在C51程序中定義和使用數組的方法與ANSIC語言中相同。例程ex4.c中將顯示碼定義成了一個一維數組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};
該數組在存儲器中的存放方式:占據了以符號地址led為首地址的一串連續的字節位置。在例程中,由于LED掃描函數voidscanled(unsignedcharn[])的入口參數是8個顯示碼的首地址,因此在main函數中調用LED掃描函數scanled的格式如下: scanled(led+i);其中,led+i(i是0~22之間的整數)表示該數組元素的地址,當i=0時,led+i就是該數組的首地址;當i=1時,led+i就是該數組的第一個元素的地址,依此類推。利用指針訪問數組也是一個比較好的方法。3)C51中指針定義修改例程ex4.c的源代碼如下://ex4_1.c,本程序中的斜體部分為修改內容#pragmaSMALL#include"REG51.H" //定義頭文件#include"absacc.h"#defineCTRL8155XBYTE[0x4400] //采用絕對地址訪問方式定義8155口地址#definePORTA8155XBYTE[0x4401]#definePORTB8155XBYTE[0x4402]#definePORTC8155XBYTE[0x4403]voidscanled(unsignedchar*n); //函數聲明,LED掃描函 數,該函數將8個 //LED輪流掃描一遍,入口參數為8個顯示碼存放的首地址voiddelay(unsignedchart); //延時函數,入口參數t確定延時時間,用 于控制每位顯示間隔時間/*-----------------main函數-----------------*/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; //定義無符號字符指針
CTRL8155=0x03; //寫入8155控制字C口輸入,A,B口輸出
for(;;){p=led; //數組首地址賦給指針
for(i=0;i<23;i++,p++) //i增1的同時,指針也增1for(j=0;j<100;j++)scanled(p); //將指針p中的地址作為實參
}}/*-----------------LED掃描函數-----------------*/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);}}/*-----------------延時函數-----------------*/voiddelay(unsignedchart){unsignedchari;for(i=0;i<t;i++);}
比較例程ex4_1.c和ex4.c會發現,二者的不同之處在于:ex4.c采用數組名表示該數組的首地址,從而訪問數組中的數據;而ex4_1.c中采用指針訪問數組中的數據,首先把數組的首地址賦給指針,然后通過指針增1來修改該地址,從而訪問數組中的不同數據。指針是C語言的一個重要概念,也是C語言的重要特色之一。FranklinC51編譯器支持“一般指針”和“基于存儲器”的指針。一般指針和ANSIC語言中的指針定義相同,需3個字節:第一個字節為存儲器類型,第二個字節為高字節偏移地址,第三個字節為低字節偏移地址。一般指針可以被用來指示MCS-51單片機存儲器中的任何類型的變量。
例如在例程ex4_1.c中定義的一般指針如下:
unsignedchar*p; //定義無符號字符指針一般指針定義時也可以使用前面介紹過的data、idata、pdata、xdata等關鍵字對指針的存儲位置進行聲明,例如:char*xdatastr; //存放在xdata的通用指針int*datanum; //存放在data的通用指針基于存儲器的指針以存儲器類型為參考,一般來說,在定義的時候包含了數據類型和數據空間的說明,例如:chardata*str; //data的字符串指針intxdata*num; //xdata的int指針longcode*pow; //code的long指針
因為數據類型會在編譯的時候處理,所以基于存儲器的指針不需要用來存放數據類型的字節,它只需要一個字節(當數據類型為idata、data、bdata、pdata時)或者兩個字節(當數據類型為code、xdata時)。與一般指針相比,少了一個字節來指示類型,所以在程序執行時速度快一些。同一般指
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 音樂課中國古典課件
- 急救方法培訓課件
- 油田開發項目質量管理方案
- 高效節能電機項目社會穩定風險評估報告(范文參考)
- 2025年砂洗機項目發展計劃
- 2025年碾米機械項目合作計劃書
- 2025年家用制冷電器具項目發展計劃
- 2025年政府引導基金項目合作計劃書
- 維修表揚信范文
- 2025年旅游景區開發建設項目社會穩定風險評估與管理規范報告
- 《無人機介紹》課件
- 2025-2030中國硼酸行業市場發展現狀及競爭格局與投資研究報告
- 學校中層干部選拔聘用實施方案中層干部選聘實施方案2
- 生物必修1教師用書
- 園藝植物育種學知到課后答案智慧樹章節測試答案2025年春浙江大學
- 《電力機車制動系統檢修與維護》課件 項目二任務四檢修中繼閥
- GB/T 15683-2025糧油檢驗大米直鏈淀粉含量的測定
- 2025吉林省安全員C證考試(專職安全員)題庫及答案
- 電鉆清洗消毒流程
- 裝修貸款申請書
- 造林安全文明施工方案
評論
0/150
提交評論