單片機學習之十二:按鍵控制跑馬燈(中斷)
二極管作左右跑馬燈,當按下外部按鍵K0時,8個二極管全部閃爍5次后從K0按下之前的位置繼續(xù)作跑馬燈。
二、實驗目的
掌握堆棧在中斷程序中的作用
掌握讓程序保護現(xiàn)場的方法
三、實驗任務分析:
有了以前各個試驗的經驗,相信這個試驗對我們來說,難度不是很大。我們唯一接觸到的新的知識點是:讓程序從返回中斷之前的位置繼續(xù)執(zhí)行跑馬燈,那么如何能夠讓程序在進入中斷之前記住當時所處的位置,在執(zhí)行中斷之后,能夠返回這個地方繼續(xù)往下執(zhí)行呢?
我們可以這樣作:在進入中斷之前,把該時刻的程序信息放到一個地方保存下來,在返回中斷之前,再到這個地方把我們存放的程序信息取出來。這樣不就可以從進入中斷的位置開始重新執(zhí)行程序了嗎?那么,這個暫存數(shù)據的地方在哪里呢?
單片機給我們考慮的很周到,允許我們從內部RAM中指定一個空間專門來作這個工作,這個空間就是堆棧。并且單片機還專門給了我們一個8位的堆棧指針,讓我們用它來開辟堆棧空間。(為什么是8位呢?因為內部RAM的地址空間是256字節(jié),所以8位就足夠拉。)
例如:假如我們給堆棧指針賦值:mov sp,#70h,就表示我們把內部數(shù)據RAM的地址為70h開始的單元設為堆棧啦。
那么,我們一般把內部數(shù)據RAM的那些地方作為堆棧呢?讓我們來復習一下內部RAM的結構吧。
前面我們已經說過,內部RAM共有256字節(jié),分為兩組。還記得它們各自的功能嗎?高128字節(jié)是特殊功能寄存器區(qū),我們沒有辦法利用,那就打低128字節(jié)的主意吧。我們再來看看低128字節(jié)的RAM空間分配。
我們發(fā)現(xiàn)在低128字節(jié)中,工作寄存器區(qū)和位尋址區(qū)的地址已經分配好了,我們可以利用的只有30h~7fh的數(shù)據緩沖區(qū)了。所以我們的堆棧指針只能設在這個區(qū)域,從30h以后的范圍為宜。在該程序中,我們把堆棧設在70h的位置。
好啦,知道堆棧設在哪里,下面我們就要考慮如何把程序運行的相關信息放入堆棧拉。那么,程序運行的相關信息在哪里呢?
由于在主程序中,我們讓程序作左右跑馬燈。還記得試驗三嗎,我們的左右跑馬燈是通過把寄存器a中的數(shù),通過進位標志CY(程序狀態(tài)字PSW的最高位),進行左右環(huán)移來實現(xiàn)的。同時,由于寄存器a是單片機中最最常用的寄存器,我們在中斷程序中也要用到它。為了避免中斷程序改變寄存器a的值,所以我們在中斷服務程序開始之前,把a的值放到堆棧中保存起來。同樣我們也要把psw的值也保存起來。在返回主程序之前,再把它們取出來,這樣就可以使得程序從進入中斷之前的位置開始,繼續(xù)作跑馬燈。
把數(shù)據存入堆棧和從堆棧中取出,是通過堆棧操作指令完成的。
例如:如果想把a中的數(shù)據存入堆棧,就:push acc;如果想把a的內容從堆棧中取出,就:pop acc。(一般稱之為:壓入,彈出)。
還需要說明一點的是:堆棧中的數(shù)據是采用“后進先出”的結構方式處理的。就像我們摞盤子一樣,最后摞進去的盤子,取得時候是最先取出的。所以我們壓入數(shù)據后,再彈出的時候要特別注意順序,后壓入的要先彈出,不要弄錯啦。
現(xiàn)在來看看這個試驗的程序吧。
四、實驗程序如下:
org 0000h
ljmp start
org 0013h
ljmp ext1
org 0020h
start: clr p1.5 ;避免蜂鳴器響
setb ea ;CPU開中斷
setb ex1 ;允許外部中斷1申請中斷
setb it1 ;設置外部中斷1跳變方式觸發(fā)
mov sp,#70h ;設置堆棧入口
loop1: lcall light1 ;調用左右跑馬燈子程序
ljmp loop1
;以下是中斷服務程序
ext1: clr ea ;關閉CPU中斷
push acc ;把寄存器a的內容壓入堆棧
push psw ;把程序狀態(tài)字壓入堆棧
lcall keyreader ;調用鍵識別子程序
pass: pop psw ;恢復現(xiàn)場,注意順序,要先彈出程序狀態(tài)字
pop acc ;彈出寄存器a的內容,
setb ea ;CPU開中斷
reti ;中斷返回
light1: mov a,#0ffh ;light1是左右跑馬燈子程序,大家可以參考試驗三的內容
clr c
mov r7,#08h
lloop: rlc a
mov p0,a
lcall del100ms
djnz r7,lloop
mov r6,#06h
rloop: rrc a
mov p0,a
lcall del100ms
djnz r6,rloop
ret
keyreader: mov a,p1 ;keyreader是鍵識別子程序,大家可以參考試驗7
anl a,#0fh
cjne a,#0dh,pass
lcall del10ms
mov a,p1
anl a,#0fh
cjne a,#0dh,pass
lcall light2 ;如果確定K0按鍵按下,調用燈光閃爍子程序
ret
light2: mov a,#00h ;light2是讓燈光閃爍5次的子程序
mov r5,#10
loop2: mov p0,a
call del10ms
cpl a ;把a寄存器中的數(shù)據取反
djnz r5,loop2;
ret
del10ms: mov r4,#15h ;延時10ms子程序
del1: mov r3,#0ffh
del2: djnz r3,del3
djnz r4,del1
ret;
del100ms:mov r2,#
del3: mov r1,#0ffh
del4: djnz r1,del4
djnz r2,del3
ret
end
大家把這個程序下載到學習板上看看,會發(fā)現(xiàn)每次按下按鍵的時候,程序進入中斷后,在返回的時候,會回到那個位置繼續(xù)開始左右循環(huán)。這就是由于我們在進入中斷的時候保護了現(xiàn)場的緣故。
五、幾點說明
主程序是左右跑馬燈,其中用到了r7,r6寄存器,還調用了100ms延時,所以也用到了r2,r1寄存器。所以我們要特別注意,在中斷服務程序中,要避免使用這幾個寄存器。否則,就會導致在中斷程序中,修改了r寄存器的內容,導致返回主程序的時候出現(xiàn)問題。
在中斷服務程序中,用到了10ms延時程序,這個延時程序使用的寄存器是r4,r3。另外,還調用了light2子程序,其中用到了r5寄存器。所以。主程序和中斷服務程序用到的寄存器r就沒有沖突。
那么如果由于條件的限制,使得主程序和中斷程序的寄存器的數(shù)量較多,一組8個寄存器不夠,該怎么辦呢?
我們也可以象保護a寄存器一樣,在進入中斷之后,首先把某一個在中斷服務程序中也要用到的r寄存器的內容壓入堆棧,在退出中斷之前再彈出來。
或者我們就重新選擇寄存器區(qū)吧,由于我們缺省使用的是0區(qū)的寄存器組,所以我們就改變psw程序狀態(tài)字中的rs1和rs0,就可以換另外的一組寄存器區(qū)了。例如,我們在進入中斷服務程序之后,寫這樣的兩條指令:
clr rs1
setb rs0
這樣,我們就用了1區(qū)的8個寄存器,這樣就沒有問題啦。
評論