接觸單片機有幾年的時間了,一直專註於如何在單片機上寫一些應用,對單片機如何啟動的知之甚少,慚愧慚愧。。。今天得空整理了一下,加深了對單片機的認識,如為什麼定義data區裡的變數重新開機的初始值為0。
單片機在開機上電後,會執行startup.A51檔案的指令,我分析了一下某個項目中這個檔案裡的指令,在這裡單片機會做如下幾件事情: 初始化8051硬體堆棧的大小和堆棧指標; 初始化中斷向量表,分配每個中斷的入口地址和中斷服務函數; 初始化內部RAM空間,即DATA/IDATA,將內容清零; 初始化外部RAM空間,即XDATA/PDATA,將內容清零; 初始化SMALL/COMPACT/LARGE模式下reentrant函數使用的堆棧指標; 調用main()函數,去執行我們編寫的代碼。
當用keil作為開發環境,建立一個工程時,需要選擇所使用的單片機型號,然後Keil會將相應單片機的startup.A51檔案拷貝到工程目錄下,在編譯時間,該檔案會被編譯到最終的目標檔案中。一般情況下,這個檔案是不需要我們做修改的,保持預設狀態即可,所以可能很多人對此檔案不太熟悉。下面是具體的code以及我的個人分析:
$NOMOD51 ;取消8051對SFR的預定義,由使用者自行定義。; 以下定義3個SFRsfr CLKSEL = 0x8Fsfr P3 = 0xB0sfr MMU_SEL = 0xC3; 以下初始化IDATA, XDATA和PDATA儲存區IDATASTART EQU 00H ; the absolute start-address of IDATA memoryIDATALEN EQU 100H ; the length of IDATA memory in bytes.;XDATASTART EQU 0H ; the absolute start-address of XDATA memoryXDATALEN EQU 0F00H ; the length of XDATA memory in bytes.;PDATASTART EQU 0H ; the absolute start-address of PDATA memoryPDATALEN EQU 100H ; the length of PDATA memory in bytes.; 定義儲存PLL值的地址。PLLADDR EQU 0xEFFF;; 當函數是可重新進入的(用reentrant關鍵字修飾),以下初始化可重新進入函數所使用的堆棧, 考慮到了三種編譯模式SMALL/COMPACT/LARGE。; Stack Space for reentrant functions in the SMALL model.IBPSTACK EQU 0 ; set to 1 if small reentrant is used.IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1.;; Stack Space for reentrant functions in the LARGE model. XBPSTACK EQU 0 ; set to 1 if large reentrant is used.XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.;; Stack Space for reentrant functions in the COMPACT model. PBPSTACK EQU 0 ; set to 1 if compact reentrant is used.PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1.;;------------------------------------------------------------------------------; 初始化PDATA區; Page Definition for Using the Compact Model with 64 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 1 ; set to 1 if pdata object are used.PPAGE EQU 0 ; define PPAGE number.PPAGE_SFR DATA 0A0HSTART_GPNVM_CODE EQU 00H ; Start of Code;;------------------------------------------------------------------------------; Standard SFR Symbols ACC DATA 0E0HB DATA 0F0HSP DATA 81HDPL DATA 82HDPH DATA 83HP2 DATA 0A0H 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 96; 選擇地址0x00(CODE區),並跳轉到0x0200(CODE區)的位置 CSEG AT 0x00 LJMP 0x0200 EXTRN CODE (?C_START) ;聲明外部段名 C_START,以便在本模組中調用 PUBLIC ?C_STARTUP ;聲明在本檔案中定義的段C_STARTUP為public的,以供其他模組調用; 選擇地址0x0200(CODE區),並跳轉到 STARTUP1(CODE區)的位置 CSEG AT 0x0200 ?C_STARTUP: LJMP STARTUP1; 以下是中斷向量表,分配每個中斷的地址和對應的中斷服務函數。 CSEG AT START_GPNVM_CODE+0BH ; IT timer 0; gpnvmVectorEtuCnt: LJMP TrqIsr RETI ; LJMP InterruptRoutineVectorEtuCnt CSEG AT START_GPNVM_CODE+13H ; MMU COB or DOB or OVD; gpnvmVectorFault: LJMP uartISR RETI CSEG AT START_GPNVM_CODE+23H ; MMU COB or DOB or OVD; gpnvmVectorFault: LJMP FlashInterrupt RETI; 選擇C_C51STARTUP段所在地址 RSEG ?C_C51STARTUPSTARTUP1: 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 (0x00 - 0xFF)IF IDATALEN <> 0 MOV R0,#IDATALEN - 1; MOV R1,#IDATASTART CLR AIDATALOOP: MOV @R0,A; INC R1 DJNZ R0,IDATALOOPENDIF; 初始化 XRAMIF XDATALEN <> 0 MOV DPTR,#XDATASTART MOV R7,#LOW (XDATALEN) IF (LOW (XDATALEN)) <> 0 MOV R6,#(HIGH (XDATALEN)) +1 ELSE MOV R6,#HIGH (XDATALEN) ENDIF CLR AXDATALOOP: MOVX @DPTR,A INC DPTR DJNZ R7,XDATALOOP DJNZ R6,XDATALOOPENDIF; 初始化PDATAIF PPAGEENABLE <> 0 MOV P2,#PPAGEENDIFIF PDATALEN <> 0 MOV R0,#PDATASTART MOV R7,#LOW (PDATALEN) CLR APDATALOOP: MOVX @R0,A INC R0 DJNZ R7,PDATALOOPENDIF; 初始化reentrant函數使用的堆棧指標(SMALL/COMPACT/LARGE)IF IBPSTACK <> 0EXTRN DATA (?C_IBP) MOV ?C_IBP,#LOW IBPSTACKTOPENDIFIF XBPSTACK <> 0EXTRN DATA (?C_XBP) MOV ?C_XBP,#HIGH XBPSTACKTOP MOV ?C_XBP+1,#LOW XBPSTACKTOPENDIFIF PBPSTACK <> 0EXTRN DATA (?C_PBP) MOV ?C_PBP,#LOW PBPSTACKTOPENDIF MOV SP,#?STACK-1 ;初始化堆棧指標,指向棧底; 聲明外部定義的函數B_SWITCH0,並調用之EXTRN CODE (?B_SWITCH0) CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 LJMP ?C_START ;調用main()函數END
這個是編譯輸出檔案.lst中的部分代碼在code區的分配情況,結合彙編代碼,我們可以知道在code區某個位置存放的是什麼指令。
000000H 000002H 000003H --- OFFS.. CODE ?CO?STTF06?3 ;此處存放的代碼為 LJMP 0x0200000003H 00000AH 000008H --- --- **GAP**00000BH 00000EH 000004H --- OFFS.. CODE ?CO?STTF06?5 ;此處存放的代碼為 LJMP TrqIsr00000FH 000012H 000004H --- --- **GAP**000013H 000016H 000004H --- OFFS.. CODE ?CO?STTF06?6 ;此處存放的代碼為 LJMP uartISR000017H 000022H 00000CH --- --- **GAP**000023H 000026H 000004H --- OFFS.. CODE ?CO?STTF06?7 ;此處存放的代碼為 LJMP FlashInterrupt000027H 0001FFH 0001D9H --- --- **GAP**000200H 000202H 000003H --- OFFS.. CODE ?CO?STTF06?4 ;此處存放的代碼為 LJMP STARTUP1000203H 0006E9H 0004E7H BYTE UNIT CODE ?C?LIB_CODE0006EAH 0007A0H 0000B7H BYTE UNIT CODE ?C_C51STARTUP ;此處存放的代碼為 段?C_C51STARTUP的內容,對單片機的硬體做初始化
下面是描述startup.A51的流程圖,作為這次學習的總結。