一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

啟動代碼和中斷處理過程

 歲月樓 2011-11-22

啟動代碼和中斷處理過程

27人閱讀 評論(0) 收藏 舉報
阿南ARM訓(xùn)練班課堂總結(jié)(3)2009年03月19日 10:31第三課主要是分析啟動代碼和中斷處理過程

之前有分析過44b0下的這個啟動代碼,差別不是非常大,今天再重新看了一遍。啟動代碼與Bootloader不同,主要是指進(jìn)入C語言之前的匯編代碼,網(wǎng)上都稱為是bootloader的stage1,一般通用的內(nèi)容包括:
1. 定義程序入口點
2. 設(shè)置異常向量表
3. 初始化存儲系統(tǒng)
4. 初始化用戶程序的執(zhí)行環(huán)境
5. 初始化堆棧指針寄存器,改變處理器的模式
6. 設(shè)置FIQ/IRQ中斷處理程序入口
7. 進(jìn)入C程序

1)編譯器選擇
    GBLL     THUMBCODE
     [ {CONFIG} = 16 
THUMBCODE SETL   {TRUE}
         CODE32
         |   
THUMBCODE SETL   {FALSE}
         ]
因為處理器分為16位 32位兩種工作狀態(tài),程序的編譯器也是分16位和32兩種編譯方式,所以這段程序用于根據(jù)處理器工作狀態(tài)確定編譯器編譯方式,程序不難理解,主要解釋一下符號“[     |     ]”的意思,上面的程序是指:
if({CONFIG} = 16 )
     { THUMBCODE SETL {TRUE} 
       CODE32 }
else
       THUMBCODE SETL {FALSE}
還是沒有不明白CONFIG怎么區(qū)分是16位還是32位?哪里決定它的取值?應(yīng)該是編譯器指定的這個值。

2)宏定義
        MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
     sub     sp,sp,#4        
     stmfd     sp!,{r0}        
     ldr      r0,=$HandleLabel
     ldr      r0,[r0]         
     str      r0,[sp,#4]      
     ldmfd    sp!,{r0,pc}    
     MEND
$HandlerLabel是宏的地址標(biāo)號,HANDLER是宏名,$HandleLabel是宏的參數(shù),$標(biāo)號在宏指令展開時,標(biāo)號會替換為用戶定義的符號。在此后,所有遇到$HandlerLabel HANDLER $HandleLabel這種形式的表達(dá)式都會被展開成$HandlerLabel到MEND之間的函數(shù)。
例如:ADC_IRQ HANDLER HandleADC即代表如下函數(shù),語句ldr pc,=ADC_IRQ的作用也就是跳轉(zhuǎn)到這個函數(shù):
ADC_IRQ
     sub     sp,sp,#4        
     stmfd     sp!,{r0}        
     ldr      r0,=HandleADC
     ldr      r0,[r0]         
     str      r0,[sp,#4]      
     ldmfd    sp!,{r0,pc}
這段程序用于把ADC中斷服務(wù)程序的首地址裝載到pc中,跳轉(zhuǎn)到中斷處理函數(shù),稱之為“加載程序”。HandleADC是一個地址標(biāo)號,它的內(nèi)容就是ADC中斷服務(wù)程序的地址標(biāo)號,即文件最后的那個表HandleADC # 4所示,將HandleADC # 4中的4換成中斷服務(wù)程序的地址標(biāo)號即是,程序在這里定義了一個數(shù)據(jù)區(qū),存放各種中斷服務(wù)程序的首地址。每個字空間都有一個標(biāo)號,以Handle***命名。

3)寄存器及堆棧設(shè)置
按照上面的順序,可以比較容易讀懂啟動代碼的作用,主要就是通過設(shè)置特殊功能寄存器來達(dá)到對系統(tǒng)參數(shù)的設(shè)定。依次禁止看門狗,中斷,設(shè)定時鐘控制寄存器,存儲器控制寄存器等等。
由于各個工作模式下的堆棧指針是相互獨立的,所以要分別進(jìn)入各個模式下設(shè)置其堆棧指針,基本上都差不多,比如未定義指令模式下的設(shè)置:
     mrs     r0,cpsr
     bic     r0,r0,#MODEMASK
     orr     r1,r0,#UNDEFMODE|NOINT
     msr     cpsr_cxsf,r1        
     ldr     sp,=UndefStack
UnderStack是在程序后面用UnderStack #   256建立的一個堆??臻g的首地址,這部分空間建立在RAM中,256字節(jié)空間的堆棧大小。

4)初始化用戶程序的執(zhí)行環(huán)境
之前在44B0里的啟動代碼里還有包括把ROM里的程序拷貝到RAM中并跳轉(zhuǎn)到RAM運行程序,也就是把加載狀態(tài)下的程序按照編譯連接時的設(shè)置重新排布成運行時的程序狀態(tài),以達(dá)到符號能夠正確連接的目的,這里是涉及到了所謂的映像文件,但是2410這里沒有這一段,即程序的加載態(tài)就是它的運行態(tài),所以要求燒寫程序時必須要把它燒寫在設(shè)置的RO地址上,否則程序?qū)⒉荒苷_執(zhí)行。下面這段程序?qū)崿F(xiàn)RW數(shù)據(jù)初始化,只是把數(shù)據(jù)段復(fù)制到高地址,如果沒有設(shè)置RW的話這段代碼也不會執(zhí)行。
     ;Copy and paste RW data/zero initialized data
     ldr     r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
     ldr     r1, =|Image$$RW$$Base|   ; and RAM copy
     ldr     r3, =|Image$$ZI$$Base|  
    
     ;Zero init base => top of initialised data
     cmp     r0, r1       ; Check that they are different
     beq     %F2
1       
     cmp     r1, r3      
     ldrcc     r2, [r0], #4            
     strcc     r2, [r1], #4   
     bcc     %B1
2       
     ldr     r1, =|Image$$ZI$$Limit| 
     mov     r2, #0
3       
     cmp     r3, r1       ; Zero init
     strcc     r2, [r3], #4
     bcc     %B3
b %F1(B1)的意思是在臨近的地址標(biāo)號跳轉(zhuǎn),F(xiàn)是向后尋找,B是向前尋找。 

5)說說映象文件
用ADS編譯產(chǎn)生的映像文件有.axf、.bin、.hex等等格式,就拿要直接燒進(jìn)Flash里的.bin文件來說,在其他書上看到有這么句話“映像文件一般由域組成,域由最多三個輸出段(RO,RW,ZI)組成,輸出段又由輸入段組成?!?BR>對于這段話,前兩句是比較好理解的,域就是整個映像文件,對于大部分程序來說就只有一個域,也就是燒進(jìn)Flash里的那部分東東,稱作加載域;輸出段就是用AREA定義的RO,Rw,一般就這兩個,拿前面的bootloader來說,整體框架是這樣的:
     AREA     Init,CODE,READONLY     ;<--RO段
     ENTRY
Entry                          ;<--CODE部分

… …

SMRDATA DATA               ;<--DATA部分

… …

     AREA          RamData, DATA,             READWRITE     ;<--RW段

… …

然而對于輸出段又由輸入段組成卻著實糊涂了好一陣,輸入段是指源程序的代碼(CODE)部分和數(shù)據(jù)(DATA)部分。上面這個框架中,在RO輸出段的Entry下開始一系列的匯編指令操作,這個應(yīng)該是CODE輸入段,而SMRDATA DATA引領(lǐng)DCD用于開辟一片數(shù)據(jù)存儲空間,這部分應(yīng)該是DATA輸入段,它與RW里的數(shù)據(jù)不同之處在于這部分?jǐn)?shù)據(jù)不能被修改。
在ADS編譯前在ARM-Linker里的Ro_Base和Rw_Base兩個地址值,就是指兩個輸出段的起始地址,即程序是按照你設(shè)置的這種方式排布在內(nèi)存中的,各個地址標(biāo)號根據(jù)這兩個值確定。然而,用Ultraedit打開bin文件卻發(fā)現(xiàn)其實Rw是跟在Ro后面的,也就是說,這兩個段并沒有按照你設(shè)置的地址起始,由此引出映像文件的加載域和運行時域兩個概念。
加載域就是用Ultraedit打開看到的程序最原始的狀態(tài),而運行時域則是程序在執(zhí)行時按照你設(shè)定的方式排布的狀態(tài),顯然,上面設(shè)置的兩個地址是針對運行時域來設(shè)置的,程序要滿足上面的設(shè)置才能正確連接。也就是程序開始階段(加載域狀態(tài))是不能正確連接的,不過開始時不需要用到Rw里的數(shù)據(jù),程序是可以運行的,因此必須在需要用到Rw數(shù)據(jù)之前把它拷貝到上面設(shè)置的位置上,這就是bootloader里初始化用戶程序的執(zhí)行環(huán)境部分的作用,把數(shù)據(jù)移動到正確的位置!
拷貝完Rw里的數(shù)據(jù)之后,所有的符號都可以正確連接,這時跳轉(zhuǎn)到main函數(shù)里去執(zhí)行程序就可以了。2410的這段啟動代碼沒有進(jìn)行Ro的拷貝,所以如果你把程序燒在0x0地址,那么Ro就必須設(shè)置成0x0,如果你設(shè)置成0x30000000,那么Ro就必須設(shè)置成0x30000000,如果Rw不設(shè)置,它將默認(rèn)跟在Ro后面,否則就執(zhí)行上面的搬遷代碼,挪到正確的位置上。由于本系統(tǒng)是采用NandFlash啟動的,最初的啟動代碼必須要在0x0處的SRAM里執(zhí)行,所以,如果要把這段啟動代碼當(dāng)作NandFlash的啟動代碼的話,Ro就必須設(shè)成0x0。

6).中斷處理過程
要使用中斷,首先需要清掉程序狀態(tài)寄存器CPSR里的IRQ位,這個很容易被忽略了。再之后才是考慮與中斷有關(guān)的相應(yīng)寄存器.

這個幾個寄存器比較容易弄混了:
SRCPND/SUBSRCPND:只要中斷產(chǎn)生的條件滿足,例如外部電平,定時溢出等等,SRCPND/SUBSRCPND的相應(yīng)位就會被置位,它不管其他地方的設(shè)置如何,所以某一時刻可能有幾個位同時被置位了(幾個中斷同時產(chǎn)生)。
INTMSK/INTSUBMSK:這個是中斷屏蔽位,清零表示允許中斷請求,默認(rèn)是禁止了所有的中斷請求。
INTPND:它表示處理器接下來就要去處理的那個中斷,某一時刻只可能有一個位被置位。這個寄存器置位的必要條件是SRCPND/SUBSRCPND已經(jīng)是1,而且INTMSK/INTSUBMSK相應(yīng)位已經(jīng)清零。
SRCPND/SUBSRCPND和INTPND都不會自動清零,要程序向相應(yīng)的位寫1才能清零,這個有點奇怪。

2410不支持中斷嵌套,中斷產(chǎn)生后處理器進(jìn)入到IRQ模式,只有在等到這個中斷處理完之后才能響應(yīng)下一次中斷。
如果同時產(chǎn)生多個中斷,就涉及到了中斷優(yōu)先級的問題。SRCPND寄存器對應(yīng)的32個中斷源總共被分為6個組,每個組由一個ARBITER(0~5)對其進(jìn)行管理。中斷必須先由所屬組的ARBITER(0~5)進(jìn)行第一次優(yōu)先級判斷然后再到ARBITER6進(jìn)行第二次判斷??梢愿牡闹皇墙M里的優(yōu)先級順序。
PRIORITY的各個位被分為兩種類型,一種是ARB_MODE,另一種為ARB_SEL,拿ARBITER0來說,這個組一共包含了四種中斷源:EINT0~EINT3,分別對應(yīng)Req0~Req3,很明顯ARB_SEL0就是決定了這四種中斷的優(yōu)先順序,如果這個組里的兩個中斷同時產(chǎn)生,將會把排在前面的先傳遞給ARBITER06進(jìn)行第二次判斷。ARB_MODE0置1代表開啟優(yōu)先級次序旋轉(zhuǎn),當(dāng)該位置為1之后,ARB_SEL0的值會在每處理完一次中斷后順次改變。

中斷處理流程
啟動代碼開始是一個異常向量表,這個向量表是固定的,由處理器決定,必須要放在0x0地址那個地方,這個跟51單片機(jī)的中斷向量表相類似。
    b     ResetHandler  
     b     HandlerUndef     ;handler for Undefined mode
     b     HandlerSWI     ;handler for SWI interrupt
     b     HandlerPabort     ;handler for PAbort
     b     HandlerDabort     ;handler for DAbort
     b     .         ;reserved
     b     HandlerIRQ     ;handler for IRQ interrupt 
     b     HandlerFIQ     ;handler for FIQ interrupt
當(dāng)產(chǎn)生IRQ中斷時,PC首先無條件地來到0x18這個地址處,這個0x18就是處理器決定的IRQ中斷的入口地址,所以要在這個地址處放一條跳轉(zhuǎn)指令b HandlerIRQ,PC接著跳轉(zhuǎn)到HandlerIRQ地址標(biāo)號處,這里存放著一個宏語句:
HandlerIRQ       HANDLER HandleIRQ
按照上面說的宏展開,其實是執(zhí)行這么一段語句:
     sub     sp,sp,#4         ;留下堆棧的第一個位置
    stmfd     sp!,{r0}         ;保護(hù)R0因為后面要用R0傳遞值
    ldr      r0,=HandleIRQ   ;將HandleIRQ這個地址標(biāo)號的值傳如R0
     ldr      r0,[r0]          ;取存放在HandleIRQ里的那個值
    str      r0,[sp,#4]       ;把取到的值壓入棧
    ldmfd    sp!,{r0,pc}      ;恢復(fù)R0并把PC指向HandleIRQ里存放的地址值
HandleIRQ里存放是什么值呢?代碼最后有個這樣的表,這個表就是在SDRAM里的另外一張異常向量表,這張表可以根據(jù)需要修改_ISR_STARTADDRESS的值來隨意更改它的位置。
         ^    _ISR_STARTADDRESS
HandleReset      #    4
HandleUndef      #    4
HandleSWI        #    4
HandlePabort     #    4
HandleDabort     #    4
HandleReserved   #    4
HandleIRQ        #    4
HandleFIQ        #    4
這里實現(xiàn)結(jié)構(gòu)化一片地址空間的目的,可見在HandleIRQ這里預(yù)留了4個字節(jié)的空間,但是這個空間里現(xiàn)在放的是什么東西呢?
在代碼的初始化過程中有這么一段代碼:
    ldr     r0,=HandleIRQ     ; Setup IRQ handler  
     ldr     r1,=IsrIRQ          
     str     r1,[r0]
原來是把IsrIRQ所在的地址值放到這個地方,那就是宏實現(xiàn)了把PC指向IsrIRQ的目的。程序來到IsrIRQ:
IsrIRQ  
     sub     sp,sp,#4        ;預(yù)留堆棧
     stmfd     sp!,{r8-r9}     ;保護(hù)R8,R9
    
     ldr     r9,=INTOFFSET   ;找出產(chǎn)生哪種中斷
     ldr     r9,[r9]
     ldr     r8,=HandleEINT0
     add     r8,r8,r9,lsl #2 
     ldr     r8,[r8]
     str     r8,[sp,#8]
     ldmfd     sp!,{r8-r9,pc}   ;將PC指向相應(yīng)的中斷處理地址
假如產(chǎn)生了EINT0中斷來到了這里,那么PC將會跳轉(zhuǎn)到HandleEINT0里存放的地址值,與上面的相同,程序里有這個表:
HandleEINT0        #    4
HandleEINT1        #    4
HandleEINT2        #    4
HandleEINT3        #    4
HandleEINT4_7     #    4
    .
    .
    .
這個表在2410addr.h頭文件里也有對應(yīng)的定義,指向的是同樣的一塊地方:
    .
    .
    .
#define pISR_EINT0      (*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1      (*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_EINT2      (*(unsigned *)(_ISR_STARTADDRESS+0x28))
    .
    .
    .
問題是HandleEINT0存放的又是什么值呢?這就需要在初始化EINT0的時候?qū)懮线@么一句:
pISR_EINT0 = (unsigned )_IsrEINT0Service;
也就是把EINT0的中斷處理函數(shù)的地址寫到HandleEINT0地址處存放,那么到此PC就可以跳轉(zhuǎn)到_IsrEINT0Service里去了,這里完成你所需要的中斷處理過程。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产一级一片内射视频在线| 欧美日韩精品视频在线| 在线观看视频日韩精品 | 激情五月天免费在线观看| 亚洲三级视频在线观看免费| 欧美一级黄片欧美精品| 亚洲精品成人综合色在线| 神马午夜福利一区二区| 日韩美成人免费在线视频 | 日本午夜免费观看视频| 欧美特色特黄一级大黄片| 国产一级内片内射免费看| 中文字幕在线五月婷婷| 五月的丁香婷婷综合网| 精品一区二区三区中文字幕| 欧美日韩免费观看视频| 日韩精品中文在线观看| 在线观看免费视频你懂的| 日韩人妻av中文字幕| 国产精品不卡高清在线观看| 午夜福利国产精品不卡| 久一视频这里只有精品| 最近的中文字幕一区二区| 欧美一区二区三区高潮菊竹| 手机在线不卡国产视频| 午夜国产福利在线播放| 99香蕉精品视频国产版| 性欧美唯美尤物另类视频| 日韩成人午夜福利免费视频| 久久福利视频在线观看| 91日韩在线观看你懂的| 麻豆国产精品一区二区| 日本人妻的诱惑在线观看| 五月婷婷六月丁香狠狠| 免费观看在线午夜视频| 一区二区日本一区二区欧美| 日韩中文无线码在线视频| 亚洲国产黄色精品在线观看| 老鸭窝精彩从这里蔓延| 国产亚洲欧美日韩国亚语| 久热香蕉精品视频在线播放|