市面上常見的介紹EM78系列的參考書中,都給出了一些應(yīng)用實例,但這些實例一般程序代碼量較小,功能單一。雖然這些實例對于新手確實起到了很好的作用,但一個產(chǎn)品可能功能很復雜,程序可能達到幾千行,這就會出現(xiàn)一些短程序中沒有遇見的問題。以筆者的開發(fā)為例,程序總共5千多行,有效的匯編語句代碼有3千多行。由于義隆沒有提供C的編譯環(huán)境,只能以匯編進行編碼,而匯編的結(jié)構(gòu)、條理性與C語言比較不是很清晰,再加上EM78單片機結(jié)構(gòu)的獨特性,所以當程序代碼量較大的時候總會出現(xiàn)一些新的問題。下面將實踐中遇到的問題及其解決方法總結(jié)一下,以供參考。
1 RAM數(shù)據(jù)的讀取方法
EM78系列的RAM既可以認為是普通單片機中的RAM,也可以認為是寄存器(通用和專用寄存器)。以EM78P447為例,RAM共148×8位,其結(jié)構(gòu)如圖1所示。
其中地址為20H一3EH,共有124(31×4)個,分布在4個RAM“體”(Bank)上。4個體分別標記為“BankO(體O)”、“Bankl(體1)”、“Bank2(體2)”、“Bank3(體3)”。當程序中用到的變量不多或者數(shù)據(jù)量不是很大時,這種RAM結(jié)構(gòu)對程序不會有什么影響,即程序變量能夠分配在一個Bank中;但是當變量較多時,操作起來將會有些麻煩,同時有可能因為程序中Bank選擇不當導致程序出錯。筆者覺得當數(shù)據(jù)量較大時,只要是對數(shù)據(jù)操作,就要先進行Bank的選擇。特別是對子程序而言,一進入子程序就要進行Bank的選擇,從而控制所操作數(shù)據(jù)的范圍,時刻要清楚自己操作的數(shù)據(jù)在哪個Bank內(nèi)。通過改變“體選碼”,即RSR寄存器的最高2位,來選擇所要訪問的數(shù)據(jù)Bank。下面給出一個程序來說明。
BS RSR,7 :到Bank2
MOV A,0X2F
AND A,@OB00111111
MOV OX2E,A
CALL LCD _LEAR ;調(diào)用顯示屏清零程序
LCD CLEAR:
BC RSR,7 ;到Bank0
BC RSR,6
MOVA,@OB00000000
MOV LCd address,A
……
如上面程序所示,在調(diào)用LCD_ CLEAR之前訪問了Bank2數(shù)據(jù)區(qū),接著調(diào)用LCD-CLEAR子程序。此時在子程序內(nèi),若沒有注意數(shù)據(jù)的Bank問題,即沒有設(shè)置選擇BankO,則原本要對BankO的數(shù)據(jù)操作則會發(fā)生在Bank2的相應(yīng)數(shù)據(jù)地址上,從而程序發(fā)生錯誤。所以進入子程序后,馬上選擇要使用數(shù)據(jù)的Bank,從而防止發(fā)生誤操作EM78系列單片機的開發(fā)語言為匯編語言,所以當編寫匯編程序時,一定要養(yǎng)成一個良好的編程習慣,這非常有利于程序的開發(fā)和維護。筆者建議程序中對變量按如下操作。
①定義變量表。在程序開始的地方定義好變量表,為每一個變量取名,分配地址空間。
②相關(guān)變量最好定義在同一Bank內(nèi),這樣當對這些相關(guān)變量操作時就可以免去選擇Bank的麻煩。
③以變量名進行操作。不要對變量的地址直接操作,最好以變量名進行操作,這樣當變量需要改變名字或者需要更改分配地址時,直接更改變量表即可,而不用更改具體的程序。這點對于較大的程序非常有利,不但可以增加程序的可讀性,更重要的是提高程序的編寫便利性和維護性。
以上幾點對于編寫較高質(zhì)量的代碼都非常重要,應(yīng)在具體實踐中仔細體會。
2 程序跨頁跳轉(zhuǎn)和跨頁調(diào)用技巧
首先需要介紹一下EM 78系列單片機的程序存儲器ROM結(jié)構(gòu)。EM78系列的程序存儲器ROM容量為4K×13位,采用Page(“頁”)分配原則,就是將4K的程序空間分為4頁,每個頁容量為1K。其結(jié)構(gòu)如圖2所示,其中Page0(000H~3FFH)、Pagel(400H~7FFH)、Page2(800H~BFFH)、Page3(COOH~FFFH)。指令系統(tǒng)中的兩條長距離跳轉(zhuǎn)指令JMP和CALL,所攜帶的地址碼僅僅有l(wèi)O位,210=lK地址空間,即只能在1K的空間內(nèi)跳轉(zhuǎn)。當使用JMP指令時,裝入目標地址到PC程序指針的低10位;使用CALL指令時,裝入目標地址至PC程序指針的低10位,且PC+l壓棧,調(diào)用同1K頁面內(nèi)的任何程序。PC程序指針(寄存器R2)和堆棧的位數(shù)是12位,即尋址空間分別為4K,一個程序頁面為lK。頁面選擇通過設(shè)定狀態(tài)寄存器R3的Bit6(PS1)、Bit5(PSO)來完成。
在EM447中,當需要跳轉(zhuǎn)或調(diào)用不同頁面的子程序時,則需在調(diào)用前修改R3的PSl、PS0,這樣當執(zhí)行IMP指令或CALL指令時會將狀態(tài)寄存器R3的PSl、PS0載入PC的A11、A10,所以PC程序指針可以在4K范圍的地址空間內(nèi)自由跳轉(zhuǎn)。
當編寫的程序代碼量較大(超過1K)時,程序跨頁跳轉(zhuǎn)和跨頁調(diào)用是避免不了的。在使用JMP指令時,一定要知道將要跳到哪個Page;使用CALL指令時,一定要知道要調(diào)用的子程序位于哪個Page中。這樣在使用JMP指令和CALL指令之前必須要設(shè)置PSl和PS0位來選擇將要跳轉(zhuǎn)或調(diào)用程序的存儲空間。例程如下:
BS STATUS,PSl ;到Page3
BS STATUS,PS0
CALL INIT CLEAR ;調(diào)用初始數(shù)據(jù)清零程序
CALL SYS INIT ;調(diào)用系統(tǒng)初始化程序
BC STATUS,PSI ;到Pagel
JMP SYS_BEGIN ;跳轉(zhuǎn)到系統(tǒng)啟動程序
例程中首先設(shè)置PS相應(yīng)位來選擇Page3,然后調(diào)用INIT_CLEAR和SYS_INIT,之后選擇Pagel,跳到Pagel中的SYS BEGIN主程序中。對于較大的程序,這種調(diào)用和跳轉(zhuǎn)是經(jīng)常出現(xiàn)的,所以要求編寫程序前必須熟悉程序地址空間的分配。
3 ROM模塊化編程原則
因為義隆提供的調(diào)試環(huán)境只支持匯編語言,而用匯編語言編寫的程序,結(jié)構(gòu)條理性劣于用C語言編寫的程序,所以編寫程序時應(yīng)盡可能的將子程序功能模塊化,即以完成某個功能來編寫子程序,這樣結(jié)構(gòu)清晰、調(diào)用方便。
筆者在編寫程序時,針對EM78系列單片機ROM的Page結(jié)構(gòu),將子程序按區(qū)分功能原則分別存放到不同的Page內(nèi),如圖3所示為筆者項目中程序的存儲空間分配。
圖3中,PageO存放的主要是比較常用的子程序,如系統(tǒng)初始化程序、鍵盤掃描、液晶顯示等;Pagel存放的是一些數(shù)據(jù)處理程序,如不同Bank的數(shù)據(jù)拷貝,十六進制轉(zhuǎn)為二進制壓縮BCD碼等程序;Page2存放的加密算法程序,這是筆者項目開發(fā)中最重要的部分,所以單獨的放在一個Page內(nèi);Page3主要是對IC存儲器24LC02的一些讀寫操作子程序。這樣將功能相近的子程序放在一個Page內(nèi),從而根據(jù)單片機的結(jié)構(gòu)特點結(jié)合項目開發(fā)的實際來劃分程序存儲空間。在這樣調(diào)試時,很容易發(fā)現(xiàn)問題出現(xiàn)在哪個區(qū)域,使得程序結(jié)構(gòu)清晰明了,調(diào)用方便,易于調(diào)試和維護。
4 采用分區(qū)指令冗余技術(shù)提高軟件抗干擾性
為了確保程序穩(wěn)定可靠運行,有時必須采用軟硬件抗干擾設(shè)計。在某些場臺,大量的干擾源雖然不會造成單片機硬件系統(tǒng)的破壞,卻常常會破壞數(shù)字信號的時序,更改單片機寄存器內(nèi)容,導致程序的“跑飛”或進入死循環(huán)。因此在提高硬件可靠性的基礎(chǔ)上,還需要在程序設(shè)計中采取軟件抗干擾,從而提高軟件的可靠性,減少軟件錯誤的發(fā)生或者在發(fā)生錯誤的情況下仍能使系
統(tǒng)恢復正常運行。
針對EM78系列單片機,筆者采用了分區(qū)指令冗余技術(shù)來防止程序的跑飛。即對程序中沒有使用的程序空間用“NOP_NOP_JMP”指令將其填滿。當程序跑飛到單指令的“NOF”上時,不會發(fā)生將操作數(shù)當作指令來執(zhí)行的錯誤,同時后面增加“JMP”指令使程序跳轉(zhuǎn)到程序跑飛處理程序上。圖4給出圖例。
圖4中,針對EM78系列單片機ROM的結(jié)構(gòu)特點,分別在4個頁里存放一個單獨的跑飛處理程序Error(),程序的功能可以是“踢狗”(外置“狗”)。這樣做的原因如下:如果只編寫一個跑飛處理程序,假設(shè)放在PageO里,如果程序在Page2跑飛后,它不會跳到PageO的跑飛處理程序,反而會跳到Page2中,這樣程序仍然處于一種跑飛的狀態(tài)。所以在每個Page里添加Error()程序,這樣無論程序在哪個Page里跑飛,都會跳到Error()處理程序中。筆者實際中采用了這種方法取得了很好的效果。
5 其它常見問題分析
在使用EM78系列芯片進行開發(fā)時,除了上面要注意的幾點,還會遇見一些其它小問題。下面就提出這些問題及解決方法,希望讀者編程時有所注意。
(1)切記堆棧的深度
匯編語言編程中經(jīng)常調(diào)用子程序,子程序的使用可以大大減少程序的書寫量,提高程序的使用效率。但在對EM78系列芯片進行開發(fā)時,調(diào)用子程序應(yīng)切記堆棧的深度。EM78系列單片機采用的是5級深度的硬件堆棧,它既不占用程序空間也不占用數(shù)據(jù)空間,是獨立的暫存空間,不需要進棧和出棧的堆棧操作專用指令。堆棧的使用是環(huán)行的,若已有5個返回地址被存入堆棧后,若再次調(diào)用子程序,則第6個返回地址將被存入堆棧的第一層中(該層原有的內(nèi)容將被覆蓋),所以設(shè)計應(yīng)用程序時,子程序的嵌套調(diào)用層數(shù)不能超過5層,若嵌套調(diào)用超過5層,則會發(fā)生程序跑飛。
(2)長時間的軟件延時方法
EM 78系列芯片內(nèi)部帶有定時器,但對于較長時間的延時(如幾十s)則不太適用。筆者在開發(fā)中要使用20s的延時,編寫了一段軟件延時程序,采用3層嵌套程序,源代碼如下,程序可以直接移植運行。
;…主循環(huán)………
LOOP0: MOV A,@18H
MOV DATAl,A
LOOF1: MOV A,@07fH
MOV DATA2,A
LOOP2: MOV A,@07IfH
MOV DATA3,A
LOOp3: JMP CHECK ;跳到應(yīng)用程序
CHE(1K BACK:DJZ DATA3 ;應(yīng)用程序退出
JMP LOOP3
DJZ DATA2
JMP LOOP2
DJZ DATAl
IMP LOOP1
;20s后執(zhí)行OTHER程序
IMP OTHER
(3)查表程序中防止程序跑飛的技巧
在單片機開發(fā)應(yīng)用中,經(jīng)常要用到查表程序來實現(xiàn)代碼轉(zhuǎn)換,例如8段LED的譯碼顯示。EM78系列芯片的指令系統(tǒng)中,有查表所需的專用指令TBL和RETL。查表過程是,先通過把被顯數(shù)字(即索引值)作為在數(shù)據(jù)表中的偏移量存入A,通過子程序傳遞參數(shù),子程序?qū)?shù)和當前PC相加,則PC跳到偏移位置,然后經(jīng)由RETL將所列數(shù)據(jù)裝入A,然后退出子程序。假設(shè)所列數(shù)據(jù)表中只有1O項,則傳遞到子程序的參數(shù)最大為9。但如果程序跑飛,則有可能傳入大于9的參數(shù),這樣此數(shù)加上PC指針則跳出數(shù)據(jù)表的范圍,程序出錯。筆者在自己的程序中加上如下措施,從而有效的解決了這個問題。即進入子程序后,首先判斷傳人參數(shù)是否大于9,即偏移量是否正確,不正確則跳到錯誤處理程序ERROR(),正確則繼續(xù)執(zhí)行。這樣可確保顯示的數(shù)據(jù)都是正確的。
;……數(shù)據(jù)譯碼表……
DATA_TPANS_JBL:
AND A,@OXOF;判斷顯示的偏移數(shù)據(jù)是否超過09
MOV Disp_limit,a
BC STATUS,C
SUB A,@OxO9
IBS STATUS,C
JMP ERROR ;若顯示偏移數(shù)據(jù)出錯跳到出錯
;處理程序
……
……
TBL
RETL 0BI 10101 11 ;
“O”段碼AB′ABDEF,“0XD7”
RETL 0800000110 ;“1”段碼:“Bc” ,“0X06”
……
……
RETL 0810110111 ;“9”段碼’′ABCDFG′,"0XB7"以上是筆者在產(chǎn)品開發(fā)中的一些心得,文中所列舉的程序均經(jīng)ELAN WICE。E編譯通過,讀者可直接引用。