寫在前面
-----------------
在前段時間的工作中,就遇見過全局變量無法初始化的問題,不過之前是在一些C文件中定義的變量能初始化,而其他C文件中不能初始化,當時將這個問題繞過去了,並沒有去深究...而這一週又出現了這個問題。於是就有了這篇文章
這裏不去討論其他情況下全局變量無法初始化的問題,只是針對我所遇見的問題討論....
原因:由於引入庫文件INIT.A51和STARTUP.A51導致的, 這裏之所以要引入這兩個文件,是由我們的應用環境決定的,如果在實際過程中沒有手動引入這兩個文件到工程項目中,則不會出現我這裏提到的類似問題。
簡述如下:
mcu復位後 PC=0 先執行STARTUP.A51的相關文件,該文件主要是對IDATA XDATA PDATA清零以及初始化堆棧指針;附上網絡上一篇文章對其的講解之部分關鍵代碼:
; STARTUP.A51: 用戶上電初始化程序
;------------------------------------------------------------------------------
;
; 用戶定義需上電初始化的內存空間
;
; 使用以下EQU命令可定義在CPU復位時需用0進行初始化的內存空間
;
;;
; IDATA 存儲器的空間的絕對起始地址總是0.;
IDATALEN EQU 80H ; 需用0進行初始化的IDATA存儲器空間的字節數
;
XDATASTART EQU 0H ; XDATA存儲器空間的絕對起始地址
XDATALEN EQU 400H ; 需用0進行初始化的XDATA存儲器的空間字節數.
;
PDATASTART EQU 0H ; PDATA存儲器的空間的絕對起始地址
PDATALEN EQU 0H ; 需用0進行初始化的PDATA存儲器的空間字節數.
;
; 注意: IDATA 存儲器的空間在物理上包括了8051單片機的DATA和BIT存儲器空間.
; 聽 說 至少要保證與C51編譯器運行庫有關的存儲器的空間進行0初始化 不知是否
;------------------------------------------------------------------------------
;
; 再入函數模擬初始化
;
; 以下用EQU指令定義了再入函數模擬堆棧指針的初始化
;
; 使用SMALL存儲器模式時再入函數的堆棧空間 .
IBPSTACK EQU 0 ; 使用SMALL存儲器模式再入函數時將其設置成1.
IBPSTACKTOP EQU 0FFH+1 ; 將堆棧頂設置爲最高地址+1.
;
; 使用LARGE存儲器模式時再入函數的堆棧空間.; 使用LARGE存儲器模式時再入函數的堆棧空間.
XBPSTACK EQU 0 ; 使用LARGE存儲器模式再入函數時將其設置成1.
XBPSTACKTOP EQU 0FFFFH+1; 將堆棧頂設置爲最高地址+1.
;
; 使用COMPACT存儲器模式時再入函數的堆棧空間.; 使用COMPACT存儲器模式時再入函數的堆棧空間.
PBPSTACK EQU 0 ; 使用COMPACT存儲器模式再入函數時將其設置成1.
PBPSTACKTOP EQU 0FFFFH+1; 將堆棧頂設置爲最高地址+1.
;
;------------------------------------------------------------------------------
;
; 使用COMPACT存儲器模式時64K字節XDATA存儲器空間的分頁定義
;
; 以下用EQU指令定義PDATA類型變量在XDATA存儲器空間的頁地址
; 使用EQU指令定義PFAGE時必須與L51連接定位器PDATA指令的控制參數一致
;
PPAGEENABLE EQU 0 ; 使用PDATA類型變量時將其設置成1.
PPAGE EQU 0 ; 定義頁號.
;
;------------------------------------------------------------------------------
.....
.....
.....
; RSEG ?STACK ; 堆棧
; DS 1
.....
.....
.....
CSEG AT 0x0000 ; 定義用戶程序的起始地址
?C_STARTUP: LJMP STARTUP1
.....
.....
.....
; 設置堆棧的起始地址
MOV SP,#?STACK-1 ; 例如 MOV SP,#4FH;
.....
.....
.....
; 跳轉到ININ.A51的初始化入口?C_START
LJMP ?C_START
END
由代碼可知,在執行完清零操作後,程序將跳轉到?C_START標號處,該程序標號定義在INIT.A51文件中,文件INIT.A51完成全局變量的初始化,其中包含有如下代碼:
...
RSEG ?C_C51STARTUP
INITEND: LJMP MAIN
...
...
...
?C_START:
MOV DPTR,#?C_INITSEG
Loop:
WATCHDOG
CLR A
MOV R6,#1
MOVC A,@A+DPTR
JZ INITEND
INC DPTR
MOV R7,A
ANL A,#3FH
...
...
...
...
RSEG ?C_INITSEG
DB 0
...
...
其中#?C_INITSEG爲編譯生成的初始化段,該段保存了需要初始化的變量信息,可以看出上面的代碼是利用該段的信息對相應變量進行初始化,注意其中的'JZ INITEND'(判斷初始化段的相關位置爲零則結束初始化);而INIT.A51文件的最後位置有'RSEG ?C_INITSEG';和'DB 0',所以問題的答案就在這裏了!
問題出在INIT.A51在整個工程文件中的位置,假如在整個工程文件的最開始就編譯INIT.A51,那麼則'?C_INITSEG'第一個字節就是'0',所以接下來的初始化信息都不會被執行,解決辦法就是INIT.A51最後才加入整個工程中,確保在該文件中加入'?C_INITSEG'段中的'0'是加在整個'?C_INITSEG'段的最後一個位置....