《單片機(jī)原理及應(yīng)用》課件-第4章_第1頁
《單片機(jī)原理及應(yīng)用》課件-第4章_第2頁
《單片機(jī)原理及應(yīng)用》課件-第4章_第3頁
《單片機(jī)原理及應(yīng)用》課件-第4章_第4頁
《單片機(jī)原理及應(yīng)用》課件-第4章_第5頁
已閱讀5頁,還剩72頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第4章單片機(jī)匯編語言程序設(shè)計(jì)4.1匯編語言程序的設(shè)計(jì)基礎(chǔ)4.2匯編語言程序的基本結(jié)構(gòu)形式4.3常用程序設(shè)計(jì)舉例思考與練習(xí)

4.1匯編語言程序的設(shè)計(jì)基礎(chǔ)

程序?qū)嶋H上是一系列計(jì)算機(jī)指令的有序集合。程序設(shè)計(jì)是單片機(jī)應(yīng)用系統(tǒng)設(shè)計(jì)的重要組成部分,單片機(jī)的全部動(dòng)作都是在程序的控制下進(jìn)行的。隨著芯片技術(shù)的發(fā)展,很多標(biāo)準(zhǔn)的或功能型的硬件電路都集成到了芯片中。所以,程序設(shè)計(jì)在單片機(jī)應(yīng)用系統(tǒng)開發(fā)中占的比重越來越大,這一點(diǎn)在有駐留系統(tǒng)程序的高級(jí)嵌入式系統(tǒng)中更加明顯。

4.1.1匯編語言的語句格式

匯編語言的語句有兩種基本類型:指令語句和偽指令語句。

1.指令語句

指令語句是引發(fā)單片機(jī)操作的命令。每一條指令語句在匯編時(shí)都產(chǎn)生一個(gè)指令代碼

(也稱機(jī)器代碼),執(zhí)行該指令代碼對(duì)應(yīng)著單片機(jī)的一種操作。

2.偽指令語句

偽指令語句是控制匯編(翻譯)過程的一些控制命令。在匯編時(shí)沒有機(jī)器代碼與偽指令語句對(duì)應(yīng)。

4.1.2偽指令

匯編時(shí)為了便于匯編器(某些情況下,也可以稱為編譯器)的操作,匯編程序提供了一些本身的操作命令,比如匯編器匯編時(shí)需要知道匯編語言源程序中哪些是數(shù)據(jù)、數(shù)據(jù)的狀態(tài)、程序的起始和終止等。這些匯編器本身的操作指令可以出現(xiàn)在匯編語言源程序中,但它不控制單片機(jī)操作,而是控制匯編器的指令。這些指令稱為偽指令。

偽指令是程序員發(fā)給匯編器的命令,也稱為匯編命令或匯編程序控制指令。

MCS51匯編語言程序中常用的偽指令有以下幾個(gè)。

(1)ORG(ORiGin)匯編起始地址指令:其中,“<地址>”是4位十六進(jìn)制數(shù),這個(gè)十六進(jìn)制數(shù)前不用加“#”,這點(diǎn)與匯編指令中的立即數(shù)前加“#”不同,需要注意。

ORG指令的功能是通知匯編器在把程序“翻譯”成機(jī)器碼的過程中,將ORG后面這段程序的起始地址設(shè)定在ORG規(guī)定的地址上。MCS51系列單片機(jī)執(zhí)行的機(jī)器碼是放在程

序存儲(chǔ)器中的,執(zhí)行是從首地址開始的。

(2)END(ENDofassembly)匯編終止指令:

END指令的功能是通知匯編器在把程序“翻譯”成機(jī)器碼的過程中,遇到END就停止“翻譯”。這條語句是給匯編器的指令,而不是給單片機(jī)的指令,不表示程序運(yùn)行到這里就結(jié)束。

END指令是單指令,一般沒有標(biāo)號(hào)和表達(dá)式。

(3)EQU(EQUate)賦值指令:

EQU指令用于將一個(gè)數(shù)值或寄存器名賦給一個(gè)指定符號(hào)名。經(jīng)過EQU指令賦值的符號(hào)可在程序的其他地方使用,以代替其賦值。

(4)DB(DefineByte)定義數(shù)據(jù)字節(jié)指令:

DB指令的功能是給定表達(dá)式的值以字節(jié)形式初始化代碼空間。通常是在程序存儲(chǔ)器的某個(gè)位置預(yù)置一個(gè)字節(jié)數(shù)(

8位二進(jìn)制數(shù)),可以和ORG配合使用;“<8位二進(jìn)制數(shù)表>”前不加“#”,一般用2位十六進(jìn)制數(shù)表示。

(5)DW(DefineWord)定義數(shù)據(jù)字指令:

DW指令的功能是給定表達(dá)式的值以字形式(雙字節(jié))初始化代碼空間。通常是在程序存儲(chǔ)器的某個(gè)位置預(yù)置兩個(gè)字節(jié)數(shù)(16位二進(jìn)制數(shù)),可以和ORG配合使用;“<16位二進(jìn)制數(shù)表>”前不加“#”,一般用4位十六進(jìn)制數(shù)表示。DW的使用方法和DB類似。

(6)DATA數(shù)據(jù)地址賦值指令:

DATA指令用于將一個(gè)內(nèi)部RAM的地址賦給指定的符號(hào)名。數(shù)值表達(dá)式的值應(yīng)在00H~FFH(0~255)之間。

DATA偽指令的使用與EQU相似,不同之處是:

·EQU定義的標(biāo)識(shí)符必須先定義后使用,DATA定義的符號(hào)名可以先使用后定義;

·EQU可以把匯編符號(hào)賦予標(biāo)識(shí)符,而DATA則不可以。

(7)BIT位定義指令:

BIT指令用于將一個(gè)位地址賦給指定的符號(hào)名。經(jīng)BIT指令定義過的位符號(hào)名不能更改。

(8)DS(DefineStonage)定義存儲(chǔ)區(qū)指令:

DS指令的功能是以字節(jié)為單位在內(nèi)部和外部存儲(chǔ)器保留存儲(chǔ)空間,標(biāo)號(hào)值將是保留區(qū)的第一個(gè)字節(jié)地址。

4.1.3匯編語言程序的結(jié)構(gòu)

完成控制任務(wù)的匯編語言源程序基本上由主程序、子程序、中斷服務(wù)程序等組成。單片機(jī)程序存儲(chǔ)器的某些單元被保留作為特定的程序入口地址,0000H是單片機(jī)的啟動(dòng)地址。由于系統(tǒng)復(fù)位后的PC(程序計(jì)數(shù)器)的內(nèi)容是0000H,所以系統(tǒng)從0000H開始取指令,執(zhí)行程序。如果程序需要用到中斷功能,則程序必須在0000H開始的第一條指令放入一個(gè)無條件轉(zhuǎn)移指令,將主程序引到別處。程序段的起始定位可由偽指令ORG來規(guī)定,主程序的開始地址最好設(shè)在0030H以后。

0003H、000BH、0013H、001BH、0023H分別是五個(gè)中斷服務(wù)程序的入口地址。當(dāng)中斷產(chǎn)生時(shí),PC會(huì)自動(dòng)指向這里,即程序可以從這五個(gè)地址的某一個(gè)開始運(yùn)行。由于每一入口的地址空間較小,一般只有幾個(gè)字節(jié),通常在入口地址存放一條無條件轉(zhuǎn)移指令,將中斷服務(wù)程序引到別處。

單片機(jī)標(biāo)準(zhǔn)匯編程序結(jié)構(gòu)如圖4-1所示。圖4-1標(biāo)準(zhǔn)匯編程序結(jié)構(gòu)

注意事項(xiàng):

①必須有主程序,程序入口地址是0000H。如果有中斷服務(wù)程序,主程序必須是一個(gè)跳轉(zhuǎn)指令,將主程序引出中斷向量區(qū),一般可以從0030H開始。

②必須用偽指令來規(guī)定程序的開始地址。除了主程序,其他一些程序段也可以用偽指令來規(guī)定開始地址,可能會(huì)形成一些不連續(xù)的代碼區(qū),尤其要注意后面的代碼區(qū)不能覆蓋前面的代碼區(qū)。

③用戶可以根據(jù)功能要求設(shè)計(jì)子程序。

④如果使用中斷、定時(shí)器、串口等功能,則單片機(jī)相關(guān)寄存器必須要初始化。

⑤主程序必須是閉環(huán)結(jié)構(gòu),即是一個(gè)閉環(huán)循環(huán),采用LOOP:…LJMPLOOP(類似C語言例程中的while(1){…})表示單片機(jī)的執(zhí)行代碼有一部分是循環(huán)執(zhí)行。實(shí)際應(yīng)用中,單片機(jī)程序先進(jìn)行一次初始化操作,然后使用循環(huán)執(zhí)行其執(zhí)行代碼部分。后面的例程由于部分主程序沒有實(shí)際功能,本書的例題中一般用一條“LJMP$”來代替循環(huán)執(zhí)行代碼。

⑥注釋部分可以用“;注釋內(nèi)容”或“//注釋內(nèi)容”來分割。

4.1.4匯編語言程序的編輯與匯編

匯編語言源程序的編寫要在一個(gè)編輯工具上進(jìn)行,例如,“寫字板”、“Keil集成開發(fā)環(huán)境”(見第9章)等。編寫好的程序,單片機(jī)是不“認(rèn)識(shí)”的,單片機(jī)能夠認(rèn)識(shí)的就是由“0”和“1”組成的機(jī)器碼。匯編指令編寫的匯編語言源程序,需轉(zhuǎn)換成二進(jìn)制代碼表示的機(jī)器語言程序,單片機(jī)才能識(shí)別和執(zhí)行,通常把這一轉(zhuǎn)換(翻譯)工作稱為“匯編”。

完成“翻譯”工作的專用程序稱為匯編程序軟件(即匯編器),經(jīng)匯編程序“匯編”得到的以0、1代碼形式表示的機(jī)器語言程序稱為目標(biāo)程序,這一過程通常是在微機(jī)上進(jìn)行的。

匯編程序從編寫到生成可執(zhí)行機(jī)器碼的過程如圖4-2所示。其中,HEX文件(十六進(jìn)制文件)是由Intel公司定義的一種格式,包括地址、數(shù)據(jù)和校驗(yàn)碼,用ASCII碼存儲(chǔ),可以顯示和打印,通常用于仿真器的集成調(diào)試環(huán)境界面顯示(詳見第9章)。還有一種是BIN(二進(jìn)制)格式的文件,它完全是由編譯器生成的二進(jìn)制文件,是程序的機(jī)器碼。兩種格式都支持寫入單片機(jī)或仿真器調(diào)試的目標(biāo)程序。圖4-2匯編程序從編寫到生成可執(zhí)行機(jī)器碼的過程

4.1.5匯編語言程序的設(shè)計(jì)方法

用匯編語言進(jìn)行程序設(shè)計(jì)的過程和用高級(jí)語言進(jìn)行程序設(shè)計(jì)相類似。在進(jìn)行程序設(shè)計(jì)時(shí),要按照實(shí)際問題的要求和單片機(jī)的特點(diǎn),決定所采用的設(shè)計(jì)方法、邏輯關(guān)系、計(jì)算公

式和步驟,也就是通常所說的算法。合適的算法常??梢云鸬绞掳牍Ρ兜男Ч?然后根據(jù)單片機(jī)的指令系統(tǒng)來編制程序。

1.任務(wù)析(硬件、軟件系統(tǒng)分析)

根據(jù)單片機(jī)系統(tǒng)需要完成的控制任務(wù),在確定好的單片機(jī)的硬件體系、外圍電路條件下,結(jié)合軟件的功能分配,明確軟件應(yīng)該完成的任務(wù)。

2.確定算法和工作步驟

對(duì)一些復(fù)雜的任務(wù),需要提煉數(shù)學(xué)算法,提高編程質(zhì)量,當(dāng)然這些還要和硬件完全配合。也可以說,不同的硬件也許有不同的算法。但對(duì)于一些簡單的功能(多數(shù)情況),則不需要復(fù)雜算法,只要把邏輯關(guān)系弄明白即可。編程前,還需考慮整個(gè)程序的功能模塊劃分。

3.制定程序流程圖

流程圖也稱為程序框圖,就是用各種符號(hào)、圖形、箭頭把程序的流向及過程用圖形表示出來,使程序清晰、結(jié)構(gòu)合理、便于調(diào)試。繪制流程圖是編寫單片機(jī)程序前最重要的工

作,通常程序就是根據(jù)流程圖的指向采用適當(dāng)?shù)闹噶顏砭帉懙摹A鞒虉D一般可以用繪圖工具繪制,常用工具有Visio、AutoCAD等,流程圖也可以手工繪制,但不便于保存和修改。

圖4-3是流程圖的常用格式。圖4-3程序流程圖的常用格式

4.分配內(nèi)存,確定程序與數(shù)據(jù)區(qū)存放地址

分配內(nèi)存工作要根據(jù)程序區(qū)、數(shù)據(jù)區(qū)、堆棧區(qū)等預(yù)計(jì)所占空間大小,對(duì)片內(nèi)外存儲(chǔ)區(qū)進(jìn)行合理分配并確定每個(gè)區(qū)域的首地址,便于編程使用。

5.編寫源程序

根據(jù)任務(wù)要求和程序流程圖編寫程序代碼。盡可能按照節(jié)省數(shù)據(jù)存放單元、縮短程序長度和減少運(yùn)算時(shí)間三個(gè)原則來編制程序。

6.調(diào)試、修改,最終確定程序

通過仿真器或用直接方法進(jìn)行調(diào)試,即發(fā)現(xiàn)錯(cuò)誤和修改錯(cuò)誤的過程。程序錯(cuò)誤分為兩類:一類是語法錯(cuò)誤,比較容易發(fā)現(xiàn),一般編譯時(shí)就可以發(fā)現(xiàn);另一類是邏輯錯(cuò)誤,如指令使用錯(cuò)誤、指令缺失、算法錯(cuò)誤、順序安排錯(cuò)誤等,很難發(fā)現(xiàn),需要在調(diào)試過程中根據(jù)中間結(jié)果或其他現(xiàn)象來判斷,有時(shí)甚至需要利用仿真器的單步執(zhí)行方法去調(diào)試(詳見9.2.5節(jié))。

4.2匯編語言程序的基本結(jié)構(gòu)形式

匯編語言程序設(shè)計(jì)一般采用模塊化編程,基本程序結(jié)構(gòu)有三種:順序結(jié)構(gòu)、分支結(jié)構(gòu)和循環(huán)結(jié)構(gòu)。這三種結(jié)構(gòu)組合起來,可以解決任何復(fù)雜問題。

4.2.1順序程序

順序結(jié)構(gòu)是最簡單、最基本的程序結(jié)構(gòu),其特點(diǎn)是按指令的排列順序一條一條地執(zhí)行,直到全部指令執(zhí)行完畢為止。不管多么復(fù)雜的程序,總是由若干順序程序段所組成的,如果某一個(gè)需要解決的問題可以分解成若干個(gè)簡單的操作步驟,并且可以由這些操作按一定的順序構(gòu)成一種解決問題的算法,則可用簡單的順序結(jié)構(gòu)來進(jìn)行程序設(shè)計(jì)。

【例4-1】三字節(jié)無符號(hào)數(shù)相加,其中被加數(shù)在內(nèi)部RAM的50H、51H和52H單元中(低位在后),加數(shù)在內(nèi)部RAM的53H、54H和55H單元中(低位在后);要求把相加之和存放在50H、51H和52H單元中(低位在后),進(jìn)位存放在位尋址區(qū)的00H位中。

分析1:單片機(jī)累加器A是8位的,本例是3字節(jié)24位的加法,這就需要分成三次從低到高使用加法指令進(jìn)行加法運(yùn)算。需要注意的是:最低8位相加時(shí),無需考慮進(jìn)位,可以采用ADD指令,其他部分因?yàn)樾枰紤]進(jìn)位位,所以采用ADDC指令。

這里采用被加數(shù)放到累加器A中,再直接和內(nèi)存單元中的加數(shù)之間求和,比較直接,但程序?qū)淼臄U(kuò)展性不好。

分析2:可以考慮采用寄存器間接尋址來獲取操作數(shù),這樣程序?qū)肀阌谵D(zhuǎn)化成循環(huán)方式(見例4-7),需要統(tǒng)一使用ADDC指令,因此在進(jìn)行最低8位相加之前,先清除進(jìn)位(進(jìn)位清“0”)。

4.2.2分支程序

在實(shí)際程序設(shè)計(jì)過程中,會(huì)有很多復(fù)雜狀態(tài)和條件,需要根據(jù)這些條件進(jìn)行不同的選擇,這時(shí)就必須對(duì)某一變量的狀態(tài)進(jìn)行判斷,根據(jù)判斷結(jié)果選擇不同的程序流向,這就是分支程序。分支程序有單分支程序、雙分支程序、多分支(散轉(zhuǎn))程序。

在MCS-51單片機(jī)指令系統(tǒng)中,有JZ(JNZ)、CJNE、JC(JNC)及JB(JNB)等豐富的控制轉(zhuǎn)移指令,它們是分支結(jié)構(gòu)程序設(shè)計(jì)的基礎(chǔ),可以完成各種各樣的條件判斷,控制轉(zhuǎn)移方向。

1.單分支程序

有一個(gè)控制轉(zhuǎn)移方向的程序是單分支程序。

【例4-2】兩個(gè)無符號(hào)數(shù)比較(單分支)。內(nèi)部RAM

的30H單元和40H單元各存放了一個(gè)8位無符號(hào)數(shù),比較這兩個(gè)數(shù)的大小。若(30H)≥(40H),則將地址為20的內(nèi)存單

元置0;否則,將地址為20H的內(nèi)存單元置1。

分析:程序比較很簡單,流程圖如圖4-4所示。圖4-4例4-2的程序流程圖

2.多分支程序

有兩個(gè)或兩個(gè)以上控制轉(zhuǎn)移方向的程序是多分支程序。

【例4-3】變量X存放在UNIT1單元內(nèi),函數(shù)值Y

存放在UNIT2單元中,試按下式的要求給Y賦值。

分析1:該例涉及正數(shù)、負(fù)數(shù)和0的判斷,符號(hào)位在最高位ACC.7。可以按照如下思路進(jìn)行編程:

將存放在UNIT1單元的X傳送到累加器A,再利用JZ或者JNZ指令判斷該數(shù)是否為0。如果(A)=0,則將0送入Y中(即UNIT2單元);如果(A)≠0,則需要提取符號(hào)位進(jìn)一步判斷A的正負(fù)性。根據(jù)“邏輯與”操作的性質(zhì),只要將A與10000000B進(jìn)行邏輯與操作,即可保留最高位(符號(hào)位),屏蔽低7位,然后再根據(jù)A的值進(jìn)行判斷:執(zhí)行上述邏輯與操作后,(A)≠0,則最高位為1,代表該數(shù)為負(fù)數(shù),此時(shí)將-1以補(bǔ)碼形式送入Y中,-1的補(bǔ)碼是FFH;否則(A)=0,即最高位為0,代表該數(shù)為正數(shù),此時(shí)將+1送入Y中。

圖4-5(a)實(shí)際是三分支而歸一的流程圖,至少要用兩個(gè)轉(zhuǎn)移指令。

分析2:這個(gè)程序也可以按圖4-5(b)所示的流程圖來編寫,其特征是先賦值,再比較判斷,然后修改賦值并結(jié)束。

在參考程序1中,使用偽指令EQU定義UNIT1和NIT2,在參考程序2中使用偽指令DATA定義UNIT1和UNIT2,最后執(zhí)行結(jié)果相同,說明在本例中兩種偽指令都可以使用。圖4-5例4-3的分支流程圖

3.散轉(zhuǎn)程序

散轉(zhuǎn)程序是一種并行分支程序,它可根據(jù)運(yùn)算結(jié)果或輸入數(shù)據(jù)將程序轉(zhuǎn)入不同的分支。MCS51指令系統(tǒng)中有一條散轉(zhuǎn)指令“JMP@A+DPTR”,用它可以很容易地實(shí)現(xiàn)散轉(zhuǎn)功能。該指令把累加器的8位無符號(hào)數(shù)與16位數(shù)據(jù)指針的內(nèi)容相加,并把相加的結(jié)果裝入程序計(jì)數(shù)器PC,控制程序轉(zhuǎn)向目標(biāo)地址去執(zhí)行。此指令的特點(diǎn)在于:轉(zhuǎn)移的目標(biāo)地址不是在編程或匯編時(shí)預(yù)先確定的,而是在程序運(yùn)行過程中動(dòng)態(tài)地確定的,目標(biāo)地址是以數(shù)據(jù)指針DPTR的內(nèi)容為起始的256個(gè)字節(jié)范圍內(nèi)的指定地址,即由DPTR的內(nèi)容決定分支轉(zhuǎn)移程序的首地址,由累加器A的內(nèi)容來動(dòng)態(tài)選擇其中的某一個(gè)分支轉(zhuǎn)移程序,如圖4-6所示。散轉(zhuǎn)程序適合在鍵盤控制程序中使用。圖4-6散轉(zhuǎn)程序轉(zhuǎn)移

實(shí)現(xiàn)程序散轉(zhuǎn)常用指令表法,即在程序存儲(chǔ)器中建立一個(gè)轉(zhuǎn)移指令表,將表的首地址送DPTR,將特定單元的內(nèi)容送A,再通過散轉(zhuǎn)指令就可以根據(jù)A值轉(zhuǎn)向轉(zhuǎn)移指令表中的某條LJMP指令,然后再執(zhí)行“LJMPPRGi”,把程序轉(zhuǎn)移到指定的分支程序入口PRGi。這種方法實(shí)際上是通過2次轉(zhuǎn)移(1次執(zhí)行散轉(zhuǎn)指令,1次執(zhí)行無條件轉(zhuǎn)移指令)實(shí)現(xiàn)的

【例4-4】已知累加器A中存放著控制程序轉(zhuǎn)向的編號(hào)0~n(n<10),ROM中存有起始地址為TABLE的三字節(jié)長轉(zhuǎn)移指令表,試編程使單片機(jī)能按照累加器A中的編號(hào)轉(zhuǎn)去執(zhí)行相應(yīng)的命令程序,即當(dāng)(A)=0時(shí),執(zhí)行PR0分支程序;當(dāng)(A)=1時(shí),執(zhí)行PR1分支程序;當(dāng)(A)=2時(shí),執(zhí)行PR2分支程序;當(dāng)(A)=n時(shí),執(zhí)行PRn分支程序。

分析:考慮用散轉(zhuǎn)指令“JMP@A+DPTR”來實(shí)現(xiàn),定義散轉(zhuǎn)表首地址是TAB,需注意“LJMP<標(biāo)號(hào)>”是三個(gè)字節(jié),(A)=0對(duì)應(yīng)分支程序標(biāo)號(hào)PR0,地址是TAB+0;(A)=1對(duì)應(yīng)分支程序標(biāo)號(hào)PR1,地址是TAB+3;(A)=n對(duì)應(yīng)分支程序標(biāo)號(hào)PRn,地址是TAB+3×n,所以用A求地址偏移量時(shí)需將A中的數(shù)值“乘以”3。有多種方法能夠獲取偏移量A×3的功能。程序流程圖如圖4-7所示。圖4-7例4-4的程序流程圖

4.2.3循環(huán)程序

在程序設(shè)計(jì)過程中,經(jīng)常會(huì)遇到需要重復(fù)執(zhí)行某一段程序的情況,這時(shí)使用循環(huán)程序結(jié)構(gòu),可以節(jié)省程序存儲(chǔ)空間,提高程序的質(zhì)量。從本質(zhì)上看,循環(huán)程序結(jié)構(gòu)只是分支程序中的一個(gè)特殊形式而已。

循環(huán)程序一般由4部分組成:

(1)設(shè)置循環(huán)初值,即確立循環(huán)開始時(shí)的狀態(tài)。

(2)循環(huán)體(工作部分),要求重復(fù)執(zhí)行的部分。

(3)循環(huán)修改,循環(huán)程序必須在一定條件下結(jié)束,否則就要變成死循環(huán)。

(4)循環(huán)控制部分,根據(jù)循環(huán)結(jié)束條件,判斷是否結(jié)束循環(huán)。

循環(huán)控制的一般方法有:

①循環(huán)次數(shù)已知,利用循環(huán)次數(shù)控制。

②循環(huán)次數(shù)未知,利用關(guān)鍵字控制。

以上4個(gè)部分可以有兩種組織方式,一種是先循環(huán)后判斷,另一種是先判斷后循環(huán),如圖4-8所示。

循環(huán)程序體中不再包含其他循環(huán)程序,即為單循環(huán)程序。如果在循環(huán)體中還有循環(huán),則為循環(huán)嵌套,或多重循環(huán)。在多重循環(huán)中,只允許外重循環(huán)嵌套內(nèi)重循環(huán),不允許循環(huán)相互交叉,也不允許從循環(huán)程序外部跳到循環(huán)程序內(nèi)部。圖4-8循環(huán)程序流程圖

【例4-5】從BLOCK單元開始存放一組無符號(hào)數(shù),一般稱為一個(gè)數(shù)據(jù)塊。數(shù)據(jù)塊長度放在LEN單元,編寫一個(gè)求和程序,將和存入SUM單元。假設(shè)和不超過8位二進(jìn)制數(shù)。

分析:在設(shè)置初值時(shí),將數(shù)據(jù)塊長度置入一個(gè)工作寄存器,將數(shù)據(jù)塊首地址送入另一個(gè)工作寄存器,一般稱它為數(shù)據(jù)塊地址指針。每做一次加法之后,修改地址指針,以便取出下一個(gè)數(shù)來相加,并且使計(jì)數(shù)器減1。到計(jì)數(shù)器減到0時(shí),求和結(jié)束,把和存入SUM即可。程序流程圖如圖4-9所示。

假設(shè)數(shù)據(jù)塊長度存在內(nèi)存單元20H中,數(shù)據(jù)塊起始地址為22H,結(jié)果存入內(nèi)存單元21H中。圖4-9例4-5的程序流程圖

【例4-6】將內(nèi)部RAM中起始地址為data的數(shù)據(jù)串送到外部RAM中起始地址為buffer的存儲(chǔ)區(qū)域中,直到發(fā)現(xiàn)“$”字符,傳送停止。

分析:這是一個(gè)不定循環(huán)次數(shù)的數(shù)據(jù)轉(zhuǎn)移程序。循環(huán)次數(shù)事先不知道,需要先判斷,后執(zhí)行。以特殊字符“$”作為結(jié)束,編程時(shí),在循環(huán)體內(nèi)部開始就進(jìn)行特殊字符判斷,遇到后立即轉(zhuǎn)出循環(huán);否則,完成操作后,進(jìn)行無條件轉(zhuǎn)移,繼續(xù)循環(huán)。程序流程圖如圖4-10所示。圖4-10例4-6的程序流程圖

【例4-7】把例4-1三字節(jié)無符號(hào)數(shù)相加的程序改寫成循環(huán)方式。

分析:針對(duì)例4-1的“參考程序2”,就可以把程序改成循環(huán)方式。因?yàn)槭侨齻€(gè)字節(jié)相加,故循環(huán)次數(shù)為3;因?yàn)檠h(huán)體內(nèi)統(tǒng)一用ADDC,故要在循環(huán)前把進(jìn)位位C置成“0”。用這種方法還可以實(shí)現(xiàn)更多位的加法或減法。

4.2.4子程序

在程序設(shè)計(jì)中,經(jīng)常會(huì)遇到通用問題,同一個(gè)程序的不同地方要求執(zhí)行同樣的操作或運(yùn)算,例如求各種函數(shù)和加減乘除運(yùn)算、代碼轉(zhuǎn)換以及延時(shí)程序等,通常將這些能完成某種基本操作并具有相同操作的程序段單獨(dú)編制成子程序,以供不同程序或同一程序反復(fù)調(diào)用。這樣,一方面,程序結(jié)構(gòu)簡單,程序模塊化、通用化,便于閱讀;另一方面,節(jié)省了程序存儲(chǔ)空間,還利于按照某種功能進(jìn)行調(diào)試。在程序中需要執(zhí)行這種操作的地方執(zhí)行一條調(diào)用指令,轉(zhuǎn)到子程序中完成規(guī)定操作,并返回原來程序中繼續(xù)執(zhí)行下去,這就是所謂的子程序結(jié)構(gòu)。

調(diào)用子程序時(shí)應(yīng)注意以下幾點(diǎn):

(1)子程序調(diào)用由LCALL指令或ACALL指令產(chǎn)生,子程序中執(zhí)行RET返回。

(2)在子程序中,一般應(yīng)包括現(xiàn)場保護(hù)和現(xiàn)場恢復(fù),可通過堆棧實(shí)現(xiàn)保護(hù)和恢復(fù)現(xiàn)場。

(3)如果需要從主程序傳遞參數(shù)到子程序,可以約定交換數(shù)據(jù)的地址單元、寄存器,或者采用堆棧方法。

(4)一個(gè)子程序可以被另一個(gè)子程序調(diào)用,稱之為子程序嵌套。

(5)在子程序調(diào)用過程中,若使用堆棧,應(yīng)注意給堆棧指針SP賦棧底初值,設(shè)置合適大小的棧區(qū)空間,也可以默認(rèn)初始值,這時(shí)(SP)=07H。程序中使用堆棧指令時(shí),PUSH和POP一般要配對(duì)使用。

【例4-8】設(shè)a、b是個(gè)位數(shù),分別存于內(nèi)部RAM

的A1、A2兩個(gè)單元中,編程計(jì)算平方和c=a2+b2,并將計(jì)算結(jié)果存入內(nèi)部RAM的A3單元中。

分析:考慮到a、b是個(gè)位數(shù),我們可以設(shè)定一個(gè)個(gè)位數(shù)平方的表格,利用“MOVCA,@A+DPTR”語句,通過查表的方法來計(jì)算a和b的平方。因?yàn)閏是兩個(gè)數(shù)的平方和,所以兩次求平方可以采用子程序。在子程序中用累加器A來傳遞參數(shù)。

【例4-9】將內(nèi)部RAM從30H開始的8個(gè)單元的十六進(jìn)制數(shù)轉(zhuǎn)換成ASCII碼,并存在內(nèi)部RAM從40H開始的單元中,轉(zhuǎn)換結(jié)果的高位存在前,低位存在后。

因?yàn)?個(gè)單元中,每個(gè)字節(jié)有兩個(gè)十六進(jìn)制數(shù),可以編寫單一字節(jié)的轉(zhuǎn)換子程序,然后循環(huán)調(diào)用8次。轉(zhuǎn)換子程序在例410中已編好,待轉(zhuǎn)換數(shù)需傳入A中,直接調(diào)用子程序HEXASC,轉(zhuǎn)換完成的結(jié)果:高位ASCII碼在B中,低位ASCII碼在A中。我們可以根據(jù)約定好的入口和出口參數(shù)直接調(diào)用。

4.3常用程序設(shè)計(jì)舉例

子程序給定了入口名稱和入口參數(shù),運(yùn)算結(jié)果在出口參數(shù)里。因?yàn)橐岩?guī)定了程序入口標(biāo)號(hào),任務(wù)也是子程序結(jié)構(gòu),最后的結(jié)束語句是RET,所以不再用ORG規(guī)定程序的起始地址。在子程序的設(shè)計(jì)過程中,需要考慮保護(hù)所有用到的SFR、工作寄存器和內(nèi)存單元,除了入口參數(shù)和出口參數(shù)所涉及的以外,有的是隱含在程序里的,比如:指令若影響了進(jìn)位C,就需要保護(hù)程序狀態(tài)字PSW。

4.3.1數(shù)制轉(zhuǎn)換子程序

單片機(jī)能識(shí)別和處理的是二進(jìn)制碼,而輸入/輸出設(shè)備(如:LED顯示器、微型打印機(jī)等)則常使用ASCII碼或十六進(jìn)制數(shù)。為此,在單片機(jī)應(yīng)用系統(tǒng)中經(jīng)常需要通過程序進(jìn)行二進(jìn)制與BCD碼或ASCII碼的相互轉(zhuǎn)換。

【例4-10】編寫將累加器A中存放的十六進(jìn)制數(shù)轉(zhuǎn)換成兩位ASCII碼的子程序。入口名稱:HEXASC。入口參數(shù):A=二位十六進(jìn)制數(shù)。出口參數(shù):B=高位ASCII碼,A=

低位ASCII碼。

分析1:此例適用嵌套子程序,先編寫一個(gè)低四位轉(zhuǎn)換的子程序HEASC,再由HEXASC子程序調(diào)用。

對(duì)于十六進(jìn)制數(shù)0~9,其ASCII碼是30H~39H;對(duì)于十六進(jìn)制數(shù)A~F,其ASCII碼是41H~46H??梢岳谩癕OVCA,@A+DPTR”指令、查表方法,編寫子程序。這種方法更容易理解。

程中的NOP語句主要用于功能模塊的分割,沒有其他意義。.

分析2:通過判斷十六進(jìn)制數(shù)是0~9還是A~F來進(jìn)行轉(zhuǎn)換。判斷方法最好用指令CJNE,然后分別加上30H或37H,編寫子程序。也可以用減法指令SUBB,由于減法指令得到的是差值,原來的數(shù)據(jù)被破壞,所以必須預(yù)先加以保護(hù)。

分析3:利用“DAA”指令進(jìn)行轉(zhuǎn)換。

對(duì)于0~9,其BCD碼是#00H~#09H,加上BCD碼#90H,則分別是BCD碼#90H~#99H。執(zhí)行“DAA”指令后,原數(shù)不變,再加BCD碼#40H,則應(yīng)等于#0C0H~#0C9H;再執(zhí)行“DAA”指令后,變?yōu)?30H~#39H,恰好是0~9的ASCII碼。

對(duì)于A~F,其BCD碼是#10H~#15H,加上BCD碼#90H,則分別是BCD碼#0A0H~#0A5H,進(jìn)位位為1。執(zhí)行“DAA”指令后,變?yōu)?00H~#05H,再加BCD碼#40H和進(jìn)位位,則應(yīng)等于#41H~#46H;再執(zhí)行“DAA”指令后,仍為#41H~#46H,恰好是A~F的ASCII碼。

【例4-11】編寫將單字節(jié)二進(jìn)制數(shù)轉(zhuǎn)換為BCD碼的子程序。入口名稱:BBCD。入口參數(shù):A=待轉(zhuǎn)換8位二進(jìn)制數(shù)。出口參數(shù):B=百位BCD碼,A=十位、個(gè)位BCD碼。

分析1:由于單字節(jié)二進(jìn)制數(shù)最大是255,所以最多是三位BCD碼,可以采用除法指令編程,即先除以100,余數(shù)再除以10。

分析2:采用減法方法,先減100,判斷是否有借位。沒有借位時(shí),B+1,循環(huán);有借位時(shí),多減了100,需再加100。其余類似。

4.3.2延時(shí)子程序

單片機(jī)工作時(shí)有時(shí)需要一些時(shí)間的延時(shí),但不用十分精確。比如:控制一個(gè)指示燈的亮滅用于提示,或A/D轉(zhuǎn)換器啟動(dòng)后的等待時(shí)間等。這種情況下,用指令運(yùn)行時(shí)間的累積

來做延時(shí)比較便捷。

【例4-12】已知單片機(jī)的fosc=12MHz,設(shè)計(jì)延時(shí)1ms的子程序。入口名稱:DELAY。入口參數(shù):無。出口參數(shù):無。

分析:單片機(jī)指令在執(zhí)行時(shí)需要一定的時(shí)間,這樣我們就可以利用運(yùn)行若干條指令的方式(一般是循環(huán)執(zhí)行),來獲取一段延時(shí),但這樣的延時(shí)準(zhǔn)確性和靈活性稍差。我們可以利用單片機(jī)內(nèi)部的定時(shí)器/計(jì)數(shù)器,來獲取精確的時(shí)間,具體方法見第5章的有關(guān)內(nèi)容。

這里采用運(yùn)行指令延時(shí)的方法。晶振頻率為fosc=12MHz時(shí),一個(gè)機(jī)器周期為12/fosc=1μs,因此延時(shí)1ms的子程序的執(zhí)行時(shí)間是1000個(gè)機(jī)器周期(1000μs)。

需要執(zhí)行的時(shí)間為(2+1+(1+1+2)×248+2+2)μs=999μs≈1ms,循環(huán)次數(shù)為248。

通常,為了簡單起見,我們只考慮循環(huán)體內(nèi)的執(zhí)行時(shí)間:(

1+1+2)×250≈1ms,循環(huán)次數(shù)為250。

4.3.3均值濾波子程序

在應(yīng)用系統(tǒng)中有時(shí)需要對(duì)數(shù)據(jù)進(jìn)行采樣(比如:測溫),為了克服偶然因素引起的波動(dòng),一般需要采集N個(gè)值,求算術(shù)平均,這就是算術(shù)平均值濾波法。

【例4-13】已知采樣值為單字節(jié),連續(xù)采樣n次,編寫對(duì)采樣值進(jìn)行算術(shù)平均值濾波的子程序。入口名稱:FILTER。入口參數(shù):R0=采樣首地址,R7=采樣次數(shù)n(

n只能取1、2、4、8、16)。出口參數(shù):A=平均值。

分析1:先實(shí)現(xiàn)n次數(shù)據(jù)求和,和的值可能會(huì)大于255,因此需用兩個(gè)存儲(chǔ)器保存。然后再將和除以n,由于n的取值是1、2、4、8、16,為2的整數(shù)冪,因此通過右移位代替除法,可簡化算法,提高運(yùn)行效率。

分析2:當(dāng)n為任意數(shù)時(shí),如果還用先求和再除的方法,編程比較困難,因?yàn)閰R編指令只提供了單字節(jié)除法。我們可以考慮采用先除以n,再求和的方法。所有商數(shù)直接相加作為平均值,余數(shù)在相加過程中減n,沒有借位,則平均值+1,循環(huán)得到結(jié)果。也可以在余數(shù)相加過程中除以n。

4.3.4數(shù)據(jù)極值查找子程序

【例4-14】編寫外部存儲(chǔ)器中最大數(shù)查找子程序。入口名稱:MAXIM。入口參數(shù):DPTR=數(shù)據(jù)塊首地址,R7=數(shù)據(jù)塊長度。出口參數(shù):A=最大數(shù)。

分析:極值查找操作的主要內(nèi)容是進(jìn)行數(shù)值大小的比較。假定在比較過程中,以B單元存放比較出來的最大數(shù),與之逐個(gè)比較的另一個(gè)數(shù)隨時(shí)取出放在A中。比較結(jié)束后,把查找到的最大數(shù)放在A中。

采用循環(huán)程序結(jié)構(gòu),必須先設(shè)一個(gè)“0”,放到B中,數(shù)據(jù)區(qū)長度計(jì)數(shù)器是R7,其他取數(shù)過程在循環(huán)體內(nèi)。程序流程圖如圖4-11所示。圖4-11極值查找程序流程圖

4.3.5算術(shù)運(yùn)算子程序

MCS-51單片機(jī)指令集里只提供了簡單的8位無符號(hào)數(shù)二進(jìn)制加、減、乘、除指令,

在實(shí)際的數(shù)據(jù)采集和控制系統(tǒng)中還需要更復(fù)雜的算術(shù)運(yùn)算。

【例4-15】編寫帶符號(hào)雙字節(jié)二進(jìn)制數(shù)的加減法運(yùn)算子程序。入口名稱:BSUB為減法程序入口,BADD為加法程序入口。入口參數(shù):R2R3=(被加數(shù)或被減數(shù)),R4R5=(加數(shù)或減數(shù)),其中R2和R4的最高位為兩數(shù)的符號(hào)位。出口參數(shù):R6R7=(和或差),R6的最高位是符號(hào)位。

分析:有符號(hào)數(shù)的加減運(yùn)算比較復(fù)雜,首先把減法轉(zhuǎn)換成加法,即如果是減法,就把減數(shù)的符號(hào)位取反;其次,判斷兩數(shù)的符號(hào)位

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(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)論