目前在8051 單片機(jī)應(yīng)用開發(fā)中主要有兩種編程語(yǔ)言:匯編語(yǔ)言和c51 語(yǔ)言。c51 語(yǔ)言是一種結(jié)構(gòu)化的編程語(yǔ)言,采用c51 編寫的應(yīng)用程序結(jié)構(gòu)清晰、模塊化程度高、可讀性強(qiáng)、并容易移植。但c51 語(yǔ)言也有缺點(diǎn),就是編譯后生成的目標(biāo)代碼空間要比匯編的大。
而且目前單片機(jī)的教材還是側(cè)重于匯編語(yǔ)言。因此學(xué)習(xí)用單片機(jī)匯編語(yǔ)言程序進(jìn)行結(jié)構(gòu)化設(shè)計(jì)還是很有必要的。我們知道c51 語(yǔ)言是函數(shù)式語(yǔ)言,其程序由函數(shù)構(gòu)成,每一個(gè)源程序有且只有一個(gè)主函數(shù)main() 和若干個(gè)函數(shù)組成。其中每一個(gè)函數(shù)都用于完成某一特定任務(wù)。也就是說(shuō),一個(gè)項(xiàng)目若具有幾個(gè)功能,實(shí)現(xiàn)這些功能就會(huì)需要由若干個(gè)任務(wù)來(lái)完成,那么它的源程序中就會(huì)有若干個(gè)或以上的函數(shù)。而在匯編語(yǔ)言中,源程序中只有程序和子程序。那么我們能否以子程序?yàn)榛締挝唬靡粋€(gè)子程序?qū)崿F(xiàn)一種功能來(lái)做到模塊化編程呢?實(shí)踐證明是可行的。但在編制程序中不要忘記匯編語(yǔ)言的特點(diǎn),注意子程序之間對(duì)單片機(jī)資源的使用,避免不同子程序交叉共用同一資源引起程序的錯(cuò)誤執(zhí)行。子程序嵌套調(diào)用的級(jí)數(shù)等。本文以“60秒倒計(jì)時(shí)電路”為例談一談51 單片機(jī)匯編語(yǔ)言模塊化編程的一點(diǎn)技巧。
一、60秒倒計(jì)時(shí)電路及編程
1. 功能要求
所謂倒計(jì)時(shí),就是首先給定一個(gè)初始值,然后對(duì)初始值進(jìn)行減“1”操作,直到該值為“0”為止。60 秒倒計(jì)時(shí)就是對(duì)給定的初始值“60”每隔1 秒鐘對(duì)其進(jìn)行減“1”,一直減到該值為“0”為止。
該倒計(jì)時(shí)電路要求有兩個(gè)按鈕。一個(gè)是“復(fù)位”按鈕,按下按鈕設(shè)置倒計(jì)時(shí)初始值,并把指示燈熄滅;另一個(gè)是“開始”按鈕,按下按鈕開始倒計(jì)時(shí)。并用兩位led 數(shù)碼管顯示當(dāng)前倒計(jì)時(shí)值。計(jì)時(shí)時(shí)間到,指示燈點(diǎn)亮。
2. 電路組成
實(shí)現(xiàn)上述功能要求的單片機(jī)接口電路如圖1 所示。
圖1單片機(jī)接口電路
圖中用按鈕sb1 作為“置初值”按鈕,按鈕sb2 作為“開始”按鈕。按下sb1 按鈕,將顯示值設(shè)置為“60”。
按下按鈕sb2,每隔一秒顯示值減“1”,直到值為“0”
停止計(jì)數(shù)。按鈕和指示燈接在p0 口上,p0.0 為初始按鈕,p0.1 為開始按鈕,p0.7 為指示燈。十位led 數(shù)碼管接p2 口,個(gè)位led 數(shù)碼管接p1 口。圖2 為單片機(jī)基本系統(tǒng)電路。
圖2 單片機(jī)基本系統(tǒng)電路
3. 功能分析
根據(jù)60 秒倒計(jì)時(shí)的功能要求,需要單片機(jī)完成以下任務(wù):
⑴ 按鍵掃描。用來(lái)判斷有沒(méi)有鍵被按下,是哪個(gè)鍵被按下?根據(jù)不同的鍵,給出相應(yīng)的鍵值。
⑵ 計(jì)時(shí)顯示。這里時(shí)間值使用的是兩位數(shù),故需要將被顯示的時(shí)間值取出個(gè)位數(shù)和十位數(shù),然后才能進(jìn)行顯示。
⑶ 被顯示數(shù)轉(zhuǎn)換成7 段碼。由于單片機(jī)中的數(shù)據(jù)都是以二進(jìn)制形式存放或運(yùn)算的。而這里輸出顯示使用了兩位led 數(shù)碼管來(lái)顯示計(jì)時(shí)數(shù)值的,一個(gè)被顯示的數(shù)要點(diǎn)亮數(shù)碼管的某幾段才能顯示出這個(gè)數(shù),不同的數(shù)需要點(diǎn)亮數(shù)碼管的不同段。因此需要將被顯示的這個(gè)數(shù)轉(zhuǎn)換成相應(yīng)的顯示段碼,才能被正確顯示出來(lái)。
⑷ 延時(shí)。包括1秒鐘延時(shí)和按鍵消抖的10毫秒延時(shí)。
⒋ 程序編制
程序按實(shí)現(xiàn)功能采用模塊化結(jié)構(gòu),有一個(gè)主程序和若干個(gè)子程序組成。每個(gè)子程序分別是完成某個(gè)任務(wù)的獨(dú)立模塊,有時(shí)會(huì)用到調(diào)用參數(shù)。本實(shí)例共有5 個(gè)子程序,分別是按鍵掃描子程序、10ms 延時(shí)子程序、1s 延時(shí)子程序、顯示子程序、取段碼子程序。
⑴ 按鍵掃描子程序
按鍵掃描子程序完成對(duì)按鍵進(jìn)行掃描,確定有沒(méi)有鍵被按下,當(dāng)有鍵被按下并抬起后將相關(guān)鍵值返回給主程序的任務(wù)。其流程如圖3 所示。該子程序沒(méi)有入口參數(shù),但有一個(gè)出口參數(shù),即按鍵的鍵值,存放在寄存器r3 中。寄存器r3 中的值為“60h”表示sb1 鍵被按下;寄存器r3 中的值為“00h”表示sb2 鍵被按下。
圖3 按鍵掃描子程序流程圖
按照?qǐng)D3 的流程圖和51 單片機(jī)的指令系統(tǒng)編制的子程序如下:
;----------- 按鍵掃描描--------------
; 出口參數(shù)鍵值存放在寄存器r3 中,用于識(shí)別哪個(gè)鍵。
;r3=60h, 說(shuō)明sb1 被按下;r3=00h, 說(shuō)明sb2 被按下
key_scan: jnb kb_init, k1check ; sb1 按下轉(zhuǎn)移
jnb kb_begin, k2check ; sb2 按下轉(zhuǎn)移
sjmp ksr ;
k1check: acall del10 ; 調(diào)用毫秒延時(shí),去抖
jb kb_init, ksr ; 干擾,返回
jnb kb_init,$ ; 等待按鍵釋放
mov r3, #60h; 是sb1,鍵值“60h”送寄存器r3
sjmp ksr ; 是,不進(jìn)行任何操作返回
k2check: acall del10 ; 調(diào)用毫秒延時(shí),去抖
jb kb_begin, ksr ; 干擾,返回
jnb kb_begin,$ ; 等待按鍵釋放
mov r3, #00h; 是sb2,鍵值“00h”送寄存器r3
ksr: ret ; 返回
;---------------------------------
⑵ 顯示子程序
顯示子程序完成從被顯示值中取出十位數(shù)將其轉(zhuǎn)換成顯示斷碼,并送單片機(jī)的p2 口;從被顯示值中取出個(gè)位數(shù)將其轉(zhuǎn)換成顯示斷碼,并送單片機(jī)的p1 口任務(wù)。其流程如圖4 所示。該子程序有一個(gè)入口參數(shù),即被顯示的值,存放在寄存器r2 中。
圖4 顯示子程序流程圖
按照?qǐng)D4 的流程圖和51 單片機(jī)的指令系統(tǒng)編制的子程序如下:
;------------ 顯示子程序------------
; 入口參數(shù)存放在寄存器r2 中
display:mov a, r2 ; 取被顯示值
mov b, #10; 取被顯示值的十位數(shù)
div ab;
acall seg7; 調(diào)用轉(zhuǎn)換子程序,取顯示斷碼
mov p2, a ; 十位數(shù)段碼送p2 口
mov a, b; 取個(gè)位數(shù)
acall seg7 ; 調(diào)用轉(zhuǎn)換子程序,取顯示斷碼
mov p1, a ; 個(gè)位數(shù)段碼送p1 口
ret ; 返回
;---------------------------------
⑶ 取段碼子程序
取段碼子程序完成將被顯示的數(shù)轉(zhuǎn)換成7 段共陽(yáng)led 數(shù)碼管對(duì)應(yīng)數(shù)的段碼的任務(wù)。其流程如圖5 所示。
圖5 取段碼子程序流程圖
該子程序有一個(gè)入口參數(shù)和一個(gè)出口參數(shù)。入口參數(shù)就是被顯示的數(shù),出口參數(shù)就是該數(shù)的段碼(相應(yīng)位=0表示亮),都存放在累加器a 中。
按照?qǐng)D5 的流程圖和51 單片機(jī)的指令系統(tǒng)編制的子程序如下:
;-------------- 取段碼--------------
; 對(duì)累計(jì)器a 中的值由查表得到顯示斷碼
; 入口和出口參數(shù)存放在累計(jì)器a 中
seg7: inc a ; 取被顯示數(shù),累加器a 加1
movc a, @a+pc ; 查表
ret ; 返回
db 0c0h,0f9h,0a4h,0b0h;0123
db 99h,92h,82h,0f8h;4567
db 80h,90h,88h,83h;89ab
db 0c6h,0a1h,86h,8eh;cdef
;---------------------------------
⑷ 延時(shí)子程序
延時(shí)子程序完成一定的延時(shí)時(shí)間任務(wù)。這里有兩個(gè)延時(shí)時(shí)間不同的子程序(也可以調(diào)用100 次10ms 做1s 延遲),其流程如圖6 所示。延時(shí)子程序沒(méi)有入口和出口參數(shù)。
圖6 延時(shí)子程序流程圖
按照?qǐng)D6 的流程圖和51 單片機(jī)的指令系統(tǒng)編制的子程序如下;
;----------- 延時(shí)10ms 程序----------
; 用到寄存器組1 中的r6 和r7 寄存器
del10: setb psw.3 ; 切換至第1 組寄存器
mov r7, #0bh ;
dl1: mov r6, #0ffh ;
dl2: djnz r6, dl2 ;
djnz r7, dl1;
clr psw.3 ; 切換至第0 組寄存器
ret ;
;---------------------------------
;------------- 延時(shí)1s 程序-----------
; 用到寄存器組1 中的r1、r2 和r3 寄存器
del1s: setb psw.3 ; 選用寄存器區(qū)1
mov r1 , #46; 立即數(shù)46 送寄存器r1
del0: mov r2 , #100; 立即數(shù)100 送寄存器r2
del1: mov r3 , #100 ; 立即數(shù)100 送寄存器r3
djnz r3 , $ ; 寄存器r3 中的內(nèi)容減1,不為零轉(zhuǎn)移到當(dāng)
前指令
djnz r2 , del1; 寄存器r2 中的內(nèi)容減1,不為零轉(zhuǎn)移到
del1
djnz r1 , del0; 寄存器r1 中的內(nèi)容減1,不為零轉(zhuǎn)移到
del0
clr psw.3 ; 選用寄存器區(qū)0
re