接觸單片機有幾年的時間了,一直專注于如何在單片機上寫一些應用,對單片機如何啟動的知之甚少,慚愧慚愧。。。今天得空整理了一下,加深了對單片機的認識,如為什麼定義data區裡的變量重新開機的初始值為0。
單片機在開機上電後,會執行startup.A51檔案的指令,我分析了一下某個項目中這個檔案裡的指令,在這裡單片機會做如下幾件事情:
- 初始化8051硬體堆棧的大小和堆棧指針;
- 初始化中斷向量表,配置設定每個中斷的入口位址和中斷服務函數;
- 初始化内部RAM空間,即DATA/IDATA,将内容清零;
- 初始化外部RAM空間,即XDATA/PDATA,将内容清零;
- 初始化SMALL/COMPACT/LARGE模式下reentrant函數使用的堆棧指針;
- 調用main()函數,去執行我們編寫的代碼。
當用keil作為開發環境,建立一個工程時,需要選擇所使用的單片機型号,然後Keil會将相應單片機的startup.A51檔案拷貝到工程目錄下,在編譯時,該檔案會被編譯到最終的目标檔案中。一般情況下,這個檔案是不需要我們做修改的,保持預設狀态即可,是以可能很多人對此檔案不太熟悉。下面是具體的code以及我的個人分析:
$NOMOD51 ;取消對SFR的預定義,由使用者自行定義。
; 以下定義個SFR
sfr CLKSEL =
sfr P3 =
sfr MMU_SEL =
; 以下初始化IDATA, XDATA和PDATA存儲區
IDATASTART EQU H ; the absolute start-address of IDATA memory
IDATALEN EQU H ; the length of IDATA memory in bytes.
;
XDATASTART EQU H ; the absolute start-address of XDATA memory
XDATALEN EQU F00H ; the length of XDATA memory in bytes.
;
PDATASTART EQU H ; the absolute start-address of PDATA memory
PDATALEN EQU H ; the length of PDATA memory in bytes.
; 定義存儲PLL值的位址。
PLLADDR EQU
;
; 當函數是可重入的(用reentrant關鍵字修飾),以下初始化可重入函數所使用的堆棧, 考慮到了三種編譯模式SMALL/COMPACT/LARGE。
; Stack Space for reentrant functions in the SMALL model.
IBPSTACK EQU ; set to if small reentrant is used.
IBPSTACKTOP EQU FFH+ ; set top of stack to highest location+
;
; Stack Space for reentrant functions in the LARGE model.
XBPSTACK EQU ; set to if large reentrant is used.
XBPSTACKTOP EQU FFFFH+; set top of stack to highest location+
;
; Stack Space for reentrant functions in the COMPACT model.
PBPSTACK EQU ; set to if compact reentrant is used.
PBPSTACKTOP EQU FFFFH+; set top of stack to highest location+
;
;------------------------------------------------------------------------------
; 初始化PDATA區
; Page Definition for Using the Compact Model with KByte xdata RAM
;
; The following EQU statements define the xdata page used for pdata
; variables. The EQU PPAGE must conform with the PPAGE control used
; in the linker invocation.
;
PPAGEENABLE EQU ; set to if pdata object are used.
PPAGE EQU ; define PPAGE number.
PPAGE_SFR DATA A0H
START_GPNVM_CODE EQU H ; Start of Code
;
;------------------------------------------------------------------------------
; Standard SFR Symbols
ACC DATA H
B DATA F0H
SP DATA H
DPL DATA H
DPH DATA H
P2 DATA A0H
NAME ?C_STARTUP ;定義這段彙編代碼在obj檔案中的名字
; 聲明三個在外部定義的中斷函數, 以便在本子產品中調用
EXTRN CODE (TrqIsr)
EXTRN CODE (uartISR)
EXTRN CODE (FlashInterrupt)
; 聲明段C_C51STARTUP和STACK存儲位置
?C_C51STARTUP SEGMENT CODE
?STACK SEGMENT IDATA
; 選擇STACK段,并設定STACK的size
RSEG ?STACK
DS
; 選擇位址(CODE區),并跳轉到(CODE區)的位置
CSEG AT
LJMP
EXTRN CODE (?C_START) ;聲明外部段名 C_START,以便在本子產品中調用
PUBLIC ?C_STARTUP ;聲明在本檔案中定義的段C_STARTUP為public的,以供其他子產品調用
; 選擇位址(CODE區),并跳轉到 STARTUP1(CODE區)的位置
CSEG AT
?C_STARTUP: LJMP STARTUP1
; 以下是中斷向量表,配置設定每個中斷的位址和對應的中斷服務函數。
CSEG AT START_GPNVM_CODE+BH ; IT timer
; gpnvmVectorEtuCnt:
LJMP TrqIsr
RETI
; LJMP InterruptRoutineVectorEtuCnt
CSEG AT START_GPNVM_CODE+H ; MMU COB or DOB or OVD
; gpnvmVectorFault:
LJMP uartISR
RETI
CSEG AT START_GPNVM_CODE+H ; MMU COB or DOB or OVD
; gpnvmVectorFault:
LJMP FlashInterrupt
RETI
; 選擇C_C51STARTUP段所在位址
RSEG ?C_C51STARTUP
STARTUP1:
MOV MMU_SEL,#01H ; 初始化SFR: MMU_SEL
MOV P3,#05H ; 初始化SFR: P3
;初始化單片機的時鐘頻率
MOV DPTR,#PLLADDR
MOVX A,@DPTR
ANL A, #0C0H
MOV CLKSEL, A
; 初始化 IRAM ( - )
IF IDATALEN <>
MOV R0,#IDATALEN - 1
; MOV R1,#IDATASTART
CLR A
IDATALOOP: MOV @R0,A
; INC R1
DJNZ R0,IDATALOOP
ENDIF
; 初始化 XRAM
IF XDATALEN <>
MOV DPTR,#XDATASTART
MOV R7,#LOW (XDATALEN)
IF (LOW (XDATALEN)) <>
MOV R6,#(HIGH (XDATALEN)) +1
ELSE
MOV R6,#HIGH (XDATALEN)
ENDIF
CLR A
XDATALOOP: MOVX @DPTR,A
INC DPTR
DJNZ R7,XDATALOOP
DJNZ R6,XDATALOOP
ENDIF
; 初始化PDATA
IF PPAGEENABLE <>
MOV P2,#PPAGE
ENDIF
IF PDATALEN <>
MOV R0,#PDATASTART
MOV R7,#LOW (PDATALEN)
CLR A
PDATALOOP: MOVX @R0,A
INC R0
DJNZ R7,PDATALOOP
ENDIF
; 初始化reentrant函數使用的堆棧指針(SMALL/COMPACT/LARGE)
IF IBPSTACK <>
EXTRN DATA (?C_IBP)
MOV ?C_IBP,#LOW IBPSTACKTOP
ENDIF
IF XBPSTACK <>
EXTRN DATA (?C_XBP)
MOV ?C_XBP,#HIGH XBPSTACKTOP
MOV ?C_XBP+,#LOW XBPSTACKTOP
ENDIF
IF PBPSTACK <>
EXTRN DATA (?C_PBP)
MOV ?C_PBP,#LOW PBPSTACKTOP
ENDIF
MOV SP,#?STACK-1 ;初始化堆棧指針,指向棧底
; 聲明外部定義的函數B_SWITCH0,并調用之
EXTRN CODE (?B_SWITCH0)
CALL ?B_SWITCH0 ; init bank mechanism to code bank
LJMP ?C_START ;調用main()函數
END
這個是編譯輸出檔案.lst中的部分代碼在code區的配置設定情況,結合彙編代碼,我們可以知道在code區某個位置存放的是什麼指令。
H H H --- OFFS.. CODE ?CO?STTF06? ;此處存放的代碼為 LJMP x0200
H AH H --- --- **GAP**
BH EH H --- OFFS.. CODE ?CO?STTF06? ;此處存放的代碼為 LJMP TrqIsr
FH H H --- --- **GAP**
H H H --- OFFS.. CODE ?CO?STTF06? ;此處存放的代碼為 LJMP uartISR
H H CH --- --- **GAP**
H H H --- OFFS.. CODE ?CO?STTF06? ;此處存放的代碼為 LJMP FlashInterrupt
H FFH D9H --- --- **GAP**
H H H --- OFFS.. CODE ?CO?STTF06? ;此處存放的代碼為 LJMP STARTUP1
H H H BYTE UNIT CODE ?C?LIB_CODE
EAH A0H B7H BYTE UNIT CODE ?C_C51STARTUP ;此處存放的代碼為 段?C_C51STARTUP的内容,對單片機的硬體做初始化
下面是描述startup.A51的流程圖,作為這次學習的總結。