2440啟動(dòng)代碼和中斷處理過(guò)程
(1) 定義程序入口點(diǎn)
(2)設(shè)置異常向量表
(3)初始化存儲(chǔ)系統(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}
]
因?yàn)樘幚砥鞣譃?6位/32位兩種工作狀態(tài),程序的編譯器也是分16位和32兩種編譯方式,所以這段程序用于根據(jù)處理器工作狀態(tài)確定編譯器編譯方式,程序不難理解,主要解釋一下符號(hào)“[ | ]”的意思,上面的程序是指:
if({CONFIG} = 16 )
{ THUMBCODE SETL {TRUE}
CODE32 }
else
THUMBCODE SETL {FALSE}
2、宏定義
MACRO
$HandlerLabelHANDLER$HandleLabel
$HandlerLabel
sub sp,sp,#4
stmfd sp!,{r0}
ldrr0,=$HandleLabel
ldrr0,[r0]
strr0,[sp,#4]
ldmfdsp!,{r0,pc}
MEND
$HandlerLabel是宏的地址標(biāo)號(hào),HANDLER是宏名,$HandleLabel是宏的參數(shù),$標(biāo)號(hào)在宏指令展開(kāi)時(shí),標(biāo)號(hào)會(huì)替換為用戶定義的符號(hào)。在此后,所有遇到$HandlerLabel HANDLER $HandleLabel這種形式的表達(dá)式都會(huì)被展開(kāi)成$HandlerLabel到MEND之間的函數(shù)。
例如:ADC_IRQ HANDLER HandleADC即代表如下函數(shù),語(yǔ)句ldr pc,=ADC_IRQ的作用也就是跳轉(zhuǎn)到這個(gè)函數(shù):
ADC_IRQ
sub sp,sp,#4
stmfd sp!,{r0}
ldrr0,=HandleADC
ldrr0,[r0]
strr0,[sp,#4]
ldmfdsp!,{r0,pc}
這段程序用于把ADC中斷服務(wù)程序的首地址裝載到pc中,跳轉(zhuǎn)到中斷處理函數(shù),稱之為“加載程序”。HandleADC是一個(gè)地址標(biāo)號(hào),它的內(nèi)容就是ADC中斷服務(wù)程序的地址標(biāo)號(hào),即文件最后的那個(gè)表HandleADC # 4所示,將HandleADC # 4中的4換成中斷服務(wù)程序的地址標(biāo)號(hào)即是,程序在這里定義了一個(gè)數(shù)據(jù)區(qū),存放各種中斷服務(wù)程序的首地址。每個(gè)字空間都有一個(gè)標(biāo)號(hào),以Handle***命名。
GBLL THUMBCODE
[ {CONFIG} = 16
THUMBCODE SETL {TRUE}
CODE32
|
THUMBCODE SETL {FALSE}
]
因?yàn)樘幚砥鞣譃?6位/32位兩種工作狀態(tài),程序的編譯器也是分16位和32兩種編譯方式,所以這段程序用于根據(jù)處理器工作狀態(tài)確定編譯器編譯方式,程序不難理解,主要解釋一下符號(hào)“[ | ]”的意思,上面的程序是指:
if({CONFIG} = 16 )
{ THUMBCODE SETL {TRUE}
CODE32 }
else
THUMBCODE SETL {FALSE}
2、宏定義
MACRO
$HandlerLabelHANDLER$HandleLabel
$HandlerLabel
sub sp,sp,#4
stmfd sp!,{r0}
ldrr0,=$HandleLabel
ldrr0,[r0]
strr0,[sp,#4]
ldmfdsp!,{r0,pc}
MEND
$HandlerLabel是宏的地址標(biāo)號(hào),HANDLER是宏名,$HandleLabel是宏的參數(shù),$標(biāo)號(hào)在宏指令展開(kāi)時(shí),標(biāo)號(hào)會(huì)替換為用戶定義的符號(hào)。在此后,所有遇到$HandlerLabel HANDLER $HandleLabel這種形式的表達(dá)式都會(huì)被展開(kāi)成$HandlerLabel到MEND之間的函數(shù)。
例如:ADC_IRQ HANDLER HandleADC即代表如下函數(shù),語(yǔ)句ldr pc,=ADC_IRQ的作用也就是跳轉(zhuǎn)到這個(gè)函數(shù):
ADC_IRQ
sub sp,sp,#4
stmfd sp!,{r0}
ldrr0,=HandleADC
ldrr0,[r0]
strr0,[sp,#4]
ldmfdsp!,{r0,pc}
這段程序用于把ADC中斷服務(wù)程序的首地址裝載到pc中,跳轉(zhuǎn)到中斷處理函數(shù),稱之為“加載程序”。HandleADC是一個(gè)地址標(biāo)號(hào),它的內(nèi)容就是ADC中斷服務(wù)程序的地址標(biāo)號(hào),即文件最后的那個(gè)表HandleADC # 4所示,將HandleADC # 4中的4換成中斷服務(wù)程序的地址標(biāo)號(hào)即是,程序在這里定義了一個(gè)數(shù)據(jù)區(qū),存放各種中斷服務(wù)程序的首地址。每個(gè)字空間都有一個(gè)標(biāo)號(hào),以Handle***命名。
3、寄存器及堆棧設(shè)置
按照上面的順序,可以比較容易讀懂啟動(dòng)代碼的作用,主要就是通過(guò)設(shè)置特殊功能寄存器來(lái)達(dá)到對(duì)系統(tǒng)參數(shù)的設(shè)定。依次禁止看門狗,中斷,設(shè)定時(shí)鐘控制寄存器,存儲(chǔ)器控制寄存器等等。
由于各個(gè)工作模式下的堆棧指針是相互獨(dú)立的,所以要分別進(jìn)入各個(gè)模式下設(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è)堆??臻g的首地址,這部分空間建立在RAM中,256字節(jié)空間的堆棧大小。
按照上面的順序,可以比較容易讀懂啟動(dòng)代碼的作用,主要就是通過(guò)設(shè)置特殊功能寄存器來(lái)達(dá)到對(duì)系統(tǒng)參數(shù)的設(shè)定。依次禁止看門狗,中斷,設(shè)定時(shí)鐘控制寄存器,存儲(chǔ)器控制寄存器等等。
由于各個(gè)工作模式下的堆棧指針是相互獨(dú)立的,所以要分別進(jìn)入各個(gè)模式下設(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è)堆??臻g的首地址,這部分空間建立在RAM中,256字節(jié)空間的堆棧大小。
4、初始化用戶程序的執(zhí)行環(huán)境
之前在44B0里的啟動(dòng)代碼里還有包括把ROM里的程序拷貝到RAM中并跳轉(zhuǎn)到RAM運(yùn)行程序,也就是把加載狀態(tài)下的程序按照編譯連接時(shí)的設(shè)置重新排布成運(yùn)行時(shí)的程序狀態(tài),以達(dá)到符號(hào)能夠正確連接的目的,這里是涉及到了所謂的映像文件,但是2410這里沒(méi)有這一段,即程序的加載態(tài)就是它的運(yùn)行態(tài),所以要求燒寫程序時(shí)必須要把它燒寫在設(shè)置的RO地址上,否則程序?qū)⒉荒苷_執(zhí)行。下面這段程序?qū)崿F(xiàn)RW數(shù)據(jù)初始化,只是把數(shù)據(jù)段復(fù)制到高地址,如果沒(méi)有設(shè)置RW的話這段代碼也不會(huì)執(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)號(hào)跳轉(zhuǎn),F(xiàn)是向后尋找,B是向前尋找。
之前在44B0里的啟動(dòng)代碼里還有包括把ROM里的程序拷貝到RAM中并跳轉(zhuǎn)到RAM運(yùn)行程序,也就是把加載狀態(tài)下的程序按照編譯連接時(shí)的設(shè)置重新排布成運(yùn)行時(shí)的程序狀態(tài),以達(dá)到符號(hào)能夠正確連接的目的,這里是涉及到了所謂的映像文件,但是2410這里沒(méi)有這一段,即程序的加載態(tài)就是它的運(yùn)行態(tài),所以要求燒寫程序時(shí)必須要把它燒寫在設(shè)置的RO地址上,否則程序?qū)⒉荒苷_執(zhí)行。下面這段程序?qū)崿F(xiàn)RW數(shù)據(jù)初始化,只是把數(shù)據(jù)段復(fù)制到高地址,如果沒(méi)有設(shè)置RW的話這段代碼也不會(huì)執(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)號(hào)跳轉(zhuǎn),F(xiàn)是向后尋找,B是向前尋找。
5、說(shuō)說(shuō)映象文件
用ADS編譯產(chǎn)生的映像文件有.axf、.bin、.hex等等格式,就拿要直接燒進(jìn)Flash里的.bin文件來(lái)說(shuō),在其他書上看到有這么句話“映像文件一般由域組成,域由最多三個(gè)輸出段(RO,RW,ZI)組成,輸出段又由輸入段組成。”
對(duì)于這段話,前兩句是比較好理解的,域就是整個(gè)映像文件,對(duì)于大部分程序來(lái)說(shuō)就只有一個(gè)域,也就是燒進(jìn)Flash里的那部分東東,稱作加載域;輸出段就是用AREA定義的RO,Rw,一般就這兩個(gè),拿前面的bootloader來(lái)說(shuō),整體框架是這樣的:
AREA Init,CODE,READONLY ;<--RO段
ENTRY
Entry;<--CODE部分
… …
SMRDATA DATA ;<--DATA部分
… …
AREARamData, DATA, READWRITE ;<--RW段
… …
然而對(duì)于輸出段又由輸入段組成卻著實(shí)糊涂了好一陣,輸入段是指源程序的代碼(CODE)部分和數(shù)據(jù)(DATA)部分。上面這個(gè)框架中,在RO輸出段的Entry下開(kāi)始一系列的匯編指令操作,這個(gè)應(yīng)該是CODE輸入段,而SMRDATA DATA引領(lǐng)DCD用于開(kāi)辟一片數(shù)據(jù)存儲(chǔ)空間,這部分應(yīng)該是DATA輸入段,它與RW里的數(shù)據(jù)不同之處在于這部分?jǐn)?shù)據(jù)不能被修改。
在ADS編譯前在ARM-Linker里的Ro_Base和Rw_Base兩個(gè)地址值,就是指兩個(gè)輸出段的起始地址,即程序是按照你設(shè)置的這種方式排布在內(nèi)存中的,各個(gè)地址標(biāo)號(hào)根據(jù)這兩個(gè)值確定。然而,用Ultraedit打開(kāi)bin文件卻發(fā)現(xiàn)其實(shí)Rw是跟在Ro后面的,也就是說(shuō),這兩個(gè)段并沒(méi)有按照你設(shè)置的地址起始,由此引出映像文件的加載域和運(yùn)行時(shí)域兩個(gè)概念。
加載域就是用Ultraedit打開(kāi)看到的程序最原始的狀態(tài),而運(yùn)行時(shí)域則是程序在執(zhí)行時(shí)按照你設(shè)定的方式排布的狀態(tài),顯然,上面設(shè)置的兩個(gè)地址是針對(duì)運(yùn)行時(shí)域來(lái)設(shè)置的,程序要滿足上面的設(shè)置才能正確連接。也就是程序開(kāi)始階段(加載域狀態(tài))是不能正確連接的,不過(guò)開(kāi)始時(shí)不需要用到Rw里的數(shù)據(jù),程序是可以運(yùn)行的,因此必須在需要用到Rw數(shù)據(jù)之前把它拷貝到上面設(shè)置的位置上,這就是bootloader里初始化用戶程序的執(zhí)行環(huán)境部分的作用,把數(shù)據(jù)移動(dòng)到正確的位置!
拷貝完Rw里的數(shù)據(jù)之后,所有的符號(hào)都可以正確連接,這時(shí)跳轉(zhuǎn)到main函數(shù)里去執(zhí)行程序就可以了。2410的這段啟動(dòng)代碼沒(méi)有進(jìn)行Ro的拷貝,所以如果你把程序燒在0x0地址,那么Ro就必須設(shè)置成0x0,如果你設(shè)置成0x30000000,那么Ro就必須設(shè)置成0x30000000,如果Rw不設(shè)置,它將默認(rèn)跟在Ro后面,否則就執(zhí)行上面的搬遷代碼,挪到正確的位置上。由于本系統(tǒng)是采用NandFlash啟動(dòng)的,最初的啟動(dòng)代碼必須要在0x0處的SRAM里執(zhí)行,所以,如果要把這段啟動(dòng)代碼當(dāng)作NandFlash的啟動(dòng)代碼的話,Ro就必須設(shè)成0x0。
用ADS編譯產(chǎn)生的映像文件有.axf、.bin、.hex等等格式,就拿要直接燒進(jìn)Flash里的.bin文件來(lái)說(shuō),在其他書上看到有這么句話“映像文件一般由域組成,域由最多三個(gè)輸出段(RO,RW,ZI)組成,輸出段又由輸入段組成。”
對(duì)于這段話,前兩句是比較好理解的,域就是整個(gè)映像文件,對(duì)于大部分程序來(lái)說(shuō)就只有一個(gè)域,也就是燒進(jìn)Flash里的那部分東東,稱作加載域;輸出段就是用AREA定義的RO,Rw,一般就這兩個(gè),拿前面的bootloader來(lái)說(shuō),整體框架是這樣的:
AREA Init,CODE,READONLY ;<--RO段
ENTRY
Entry;<--CODE部分
… …
SMRDATA DATA ;<--DATA部分
… …
AREARamData, DATA, READWRITE ;<--RW段
… …
然而對(duì)于輸出段又由輸入段組成卻著實(shí)糊涂了好一陣,輸入段是指源程序的代碼(CODE)部分和數(shù)據(jù)(DATA)部分。上面這個(gè)框架中,在RO輸出段的Entry下開(kāi)始一系列的匯編指令操作,這個(gè)應(yīng)該是CODE輸入段,而SMRDATA DATA引領(lǐng)DCD用于開(kāi)辟一片數(shù)據(jù)存儲(chǔ)空間,這部分應(yīng)該是DATA輸入段,它與RW里的數(shù)據(jù)不同之處在于這部分?jǐn)?shù)據(jù)不能被修改。
在ADS編譯前在ARM-Linker里的Ro_Base和Rw_Base兩個(gè)地址值,就是指兩個(gè)輸出段的起始地址,即程序是按照你設(shè)置的這種方式排布在內(nèi)存中的,各個(gè)地址標(biāo)號(hào)根據(jù)這兩個(gè)值確定。然而,用Ultraedit打開(kāi)bin文件卻發(fā)現(xiàn)其實(shí)Rw是跟在Ro后面的,也就是說(shuō),這兩個(gè)段并沒(méi)有按照你設(shè)置的地址起始,由此引出映像文件的加載域和運(yùn)行時(shí)域兩個(gè)概念。
加載域就是用Ultraedit打開(kāi)看到的程序最原始的狀態(tài),而運(yùn)行時(shí)域則是程序在執(zhí)行時(shí)按照你設(shè)定的方式排布的狀態(tài),顯然,上面設(shè)置的兩個(gè)地址是針對(duì)運(yùn)行時(shí)域來(lái)設(shè)置的,程序要滿足上面的設(shè)置才能正確連接。也就是程序開(kāi)始階段(加載域狀態(tài))是不能正確連接的,不過(guò)開(kāi)始時(shí)不需要用到Rw里的數(shù)據(jù),程序是可以運(yùn)行的,因此必須在需要用到Rw數(shù)據(jù)之前把它拷貝到上面設(shè)置的位置上,這就是bootloader里初始化用戶程序的執(zhí)行環(huán)境部分的作用,把數(shù)據(jù)移動(dòng)到正確的位置!
拷貝完Rw里的數(shù)據(jù)之后,所有的符號(hào)都可以正確連接,這時(shí)跳轉(zhuǎn)到main函數(shù)里去執(zhí)行程序就可以了。2410的這段啟動(dòng)代碼沒(méi)有進(jìn)行Ro的拷貝,所以如果你把程序燒在0x0地址,那么Ro就必須設(shè)置成0x0,如果你設(shè)置成0x30000000,那么Ro就必須設(shè)置成0x30000000,如果Rw不設(shè)置,它將默認(rèn)跟在Ro后面,否則就執(zhí)行上面的搬遷代碼,挪到正確的位置上。由于本系統(tǒng)是采用NandFlash啟動(dòng)的,最初的啟動(dòng)代碼必須要在0x0處的SRAM里執(zhí)行,所以,如果要把這段啟動(dòng)代碼當(dāng)作NandFlash的啟動(dòng)代碼的話,Ro就必須設(shè)成0x0。
6、中斷處理過(guò)程
要使用中斷,首先需要清掉程序狀態(tài)寄存器CPSR里的IRQ位,這個(gè)很容易被忽略了。再之后才是考慮與中斷有關(guān)的相應(yīng)寄存器.
這個(gè)幾個(gè)寄存器比較容易弄混了:
SRCPND/SUBSRCPND:只要中斷產(chǎn)生的條件滿足,例如外部電平,定時(shí)溢出等等,SRCPND/SUBSRCPND的相應(yīng)位就會(huì)被置位,它不管其他地方的設(shè)置如何,所以某一時(shí)刻可能有幾個(gè)位同時(shí)被置位了(幾個(gè)中斷同時(shí)產(chǎn)生)。
INTMSK/INTSUBMSK:這個(gè)是中斷屏蔽位,清零表示允許中斷請(qǐng)求,默認(rèn)是禁止了所有的中斷請(qǐng)求。
INTPND:它表示處理器接下來(lái)就要去處理的那個(gè)中斷,某一時(shí)刻只可能有一個(gè)位被置位。這個(gè)寄存器置位的必要條件是SRCPND/SUBSRCPND已經(jīng)是1,而且INTMSK/INTSUBMSK相應(yīng)位已經(jīng)清零。
SRCPND/SUBSRCPND和INTPND都不會(huì)自動(dòng)清零,要程序向相應(yīng)的位寫1才能清零。
2410不支持中斷嵌套,中斷產(chǎn)生后處理器進(jìn)入到IRQ模式,只有在等到這個(gè)中斷處理完之后才能響應(yīng)下一次中斷。
如果同時(shí)產(chǎn)生多個(gè)中斷,就涉及到了中斷優(yōu)先級(jí)的問(wèn)題。SRCPND寄存器對(duì)應(yīng)的32個(gè)中斷源總共被分為6個(gè)組,每個(gè)組由一個(gè)ARBITER(0~5)對(duì)其進(jìn)行管理。中斷必須先由所屬組的ARBITER(0~5)進(jìn)行第一次優(yōu)先級(jí)判斷然后再到ARBITER6進(jìn)行第二次判斷??梢愿牡闹皇墙M里的優(yōu)先級(jí)順序。
PRIORITY的各個(gè)位被分為兩種類型,一種是ARB_MODE,另一種為ARB_SEL,拿ARBITER0來(lái)說(shuō),這個(gè)組一共包含了四種中斷源:EINT0~EINT3,分別對(duì)應(yīng)Req0~Req3,很明顯ARB_SEL0就是決定了這四種中斷的優(yōu)先順序,如果這個(gè)組里的兩個(gè)中斷同時(shí)產(chǎn)生,將會(huì)把排在前面的先傳遞給ARBITER06進(jìn)行第二次判斷。ARB_MODE0置1代表開(kāi)啟優(yōu)先級(jí)次序旋轉(zhuǎn),當(dāng)該位置為1之后,ARB_SEL0的值會(huì)在每處理完一次中斷后順次改變。
中斷處理流程
啟動(dòng)代碼開(kāi)始是一個(gè)異常向量表,這個(gè)向量表是固定的,由處理器決定,必須要放在0x0地址那個(gè)地方,這個(gè)跟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中斷時(shí),PC首先無(wú)條件地來(lái)到0x18這個(gè)地址處,這個(gè)0x18就是處理器決定的IRQ中斷的入口地址,所以要在這個(gè)地址處放一條跳轉(zhuǎn)指令b HandlerIRQ,PC接著跳轉(zhuǎn)到HandlerIRQ地址標(biāo)號(hào)處,這里存放著一個(gè)宏語(yǔ)句:
HandlerIRQ HANDLER HandleIRQ
按照上面說(shuō)的宏展開(kāi),其實(shí)是執(zhí)行這么一段語(yǔ)句:
sub sp,sp,#4 ;留下堆棧的第一個(gè)位置
stmfd sp!,{r0} ;保護(hù)R0因?yàn)楹竺嬉肦0傳遞值
ldrr0,=HandleIRQ ;將HandleIRQ這個(gè)地址標(biāo)號(hào)的值傳如R0
ldrr0,[r0];取存放在HandleIRQ里的那個(gè)值
strr0,[sp,#4] ;把取到的值壓入棧
ldmfdsp!,{r0,pc};恢復(fù)R0并把PC指向HandleIRQ里存放的地址值
HandleIRQ里存放是什么值呢?代碼最后有個(gè)這樣的表,這個(gè)表就是在SDRAM里的另外一張異常向量表,這張表可以根據(jù)需要修改_ISR_STARTADDRESS的值來(lái)隨意更改它的位置。
^_ISR_STARTADDRESS
HandleReset#4
HandleUndef#4
HandleSWI#4
HandlePabort #4
HandleDabort #4
HandleReserved #4
HandleIRQ#4
HandleFIQ#4
這里實(shí)現(xiàn)結(jié)構(gòu)化一片地址空間的目的,可見(jiàn)在HandleIRQ這里預(yù)留了4個(gè)字節(jié)的空間,但是這個(gè)空間里現(xiàn)在放的是什么東西呢?
在代碼的初始化過(guò)程中有這么一段代碼:
ldr r0,=HandleIRQ ; Setup IRQ handler
ldr r1,=IsrIRQ
str r1,[r0]
原來(lái)是把IsrIRQ所在的地址值放到這個(gè)地方,那就是宏實(shí)現(xiàn)了把PC指向IsrIRQ的目的。程序來(lái)到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中斷來(lái)到了這里,那么PC將會(huì)跳轉(zhuǎn)到HandleEINT0里存放的地址值,與上面的相同,程序里有這個(gè)表:
HandleEINT0#4
HandleEINT1#4
HandleEINT2#4
HandleEINT3#4
HandleEINT4_7 #4
.
.
.
這個(gè)表在2410addr.h頭文件里也有對(duì)應(yīng)的定義,指向的是同樣的一塊地方:
.
.
.
#define pISR_EINT0(*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1(*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_EINT2(*(unsigned *)(_ISR_STARTADDRESS+0x28))
.
.
.
問(wèn)題是HandleEINT0存放的又是什么值呢?這就需要在初始化EINT0的時(shí)候?qū)懮线@么一句:
pISR_EINT0 = (unsigned )_IsrEINT0Service;
也就是把EINT0的中斷處理函數(shù)的地址寫到HandleEINT0地址處存放,那么到此PC就可以跳轉(zhuǎn)到_IsrEINT0Service里去了,這里完成你所需要的中斷處理過(guò)程。
要使用中斷,首先需要清掉程序狀態(tài)寄存器CPSR里的IRQ位,這個(gè)很容易被忽略了。再之后才是考慮與中斷有關(guān)的相應(yīng)寄存器.
這個(gè)幾個(gè)寄存器比較容易弄混了:
SRCPND/SUBSRCPND:只要中斷產(chǎn)生的條件滿足,例如外部電平,定時(shí)溢出等等,SRCPND/SUBSRCPND的相應(yīng)位就會(huì)被置位,它不管其他地方的設(shè)置如何,所以某一時(shí)刻可能有幾個(gè)位同時(shí)被置位了(幾個(gè)中斷同時(shí)產(chǎn)生)。
INTMSK/INTSUBMSK:這個(gè)是中斷屏蔽位,清零表示允許中斷請(qǐng)求,默認(rèn)是禁止了所有的中斷請(qǐng)求。
INTPND:它表示處理器接下來(lái)就要去處理的那個(gè)中斷,某一時(shí)刻只可能有一個(gè)位被置位。這個(gè)寄存器置位的必要條件是SRCPND/SUBSRCPND已經(jīng)是1,而且INTMSK/INTSUBMSK相應(yīng)位已經(jīng)清零。
SRCPND/SUBSRCPND和INTPND都不會(huì)自動(dòng)清零,要程序向相應(yīng)的位寫1才能清零。
2410不支持中斷嵌套,中斷產(chǎn)生后處理器進(jìn)入到IRQ模式,只有在等到這個(gè)中斷處理完之后才能響應(yīng)下一次中斷。
如果同時(shí)產(chǎn)生多個(gè)中斷,就涉及到了中斷優(yōu)先級(jí)的問(wèn)題。SRCPND寄存器對(duì)應(yīng)的32個(gè)中斷源總共被分為6個(gè)組,每個(gè)組由一個(gè)ARBITER(0~5)對(duì)其進(jìn)行管理。中斷必須先由所屬組的ARBITER(0~5)進(jìn)行第一次優(yōu)先級(jí)判斷然后再到ARBITER6進(jìn)行第二次判斷??梢愿牡闹皇墙M里的優(yōu)先級(jí)順序。
PRIORITY的各個(gè)位被分為兩種類型,一種是ARB_MODE,另一種為ARB_SEL,拿ARBITER0來(lái)說(shuō),這個(gè)組一共包含了四種中斷源:EINT0~EINT3,分別對(duì)應(yīng)Req0~Req3,很明顯ARB_SEL0就是決定了這四種中斷的優(yōu)先順序,如果這個(gè)組里的兩個(gè)中斷同時(shí)產(chǎn)生,將會(huì)把排在前面的先傳遞給ARBITER06進(jìn)行第二次判斷。ARB_MODE0置1代表開(kāi)啟優(yōu)先級(jí)次序旋轉(zhuǎn),當(dāng)該位置為1之后,ARB_SEL0的值會(huì)在每處理完一次中斷后順次改變。
中斷處理流程
啟動(dòng)代碼開(kāi)始是一個(gè)異常向量表,這個(gè)向量表是固定的,由處理器決定,必須要放在0x0地址那個(gè)地方,這個(gè)跟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中斷時(shí),PC首先無(wú)條件地來(lái)到0x18這個(gè)地址處,這個(gè)0x18就是處理器決定的IRQ中斷的入口地址,所以要在這個(gè)地址處放一條跳轉(zhuǎn)指令b HandlerIRQ,PC接著跳轉(zhuǎn)到HandlerIRQ地址標(biāo)號(hào)處,這里存放著一個(gè)宏語(yǔ)句:
HandlerIRQ HANDLER HandleIRQ
按照上面說(shuō)的宏展開(kāi),其實(shí)是執(zhí)行這么一段語(yǔ)句:
sub sp,sp,#4 ;留下堆棧的第一個(gè)位置
stmfd sp!,{r0} ;保護(hù)R0因?yàn)楹竺嬉肦0傳遞值
ldrr0,=HandleIRQ ;將HandleIRQ這個(gè)地址標(biāo)號(hào)的值傳如R0
ldrr0,[r0];取存放在HandleIRQ里的那個(gè)值
strr0,[sp,#4] ;把取到的值壓入棧
ldmfdsp!,{r0,pc};恢復(fù)R0并把PC指向HandleIRQ里存放的地址值
HandleIRQ里存放是什么值呢?代碼最后有個(gè)這樣的表,這個(gè)表就是在SDRAM里的另外一張異常向量表,這張表可以根據(jù)需要修改_ISR_STARTADDRESS的值來(lái)隨意更改它的位置。
^_ISR_STARTADDRESS
HandleReset#4
HandleUndef#4
HandleSWI#4
HandlePabort #4
HandleDabort #4
HandleReserved #4
HandleIRQ#4
HandleFIQ#4
這里實(shí)現(xiàn)結(jié)構(gòu)化一片地址空間的目的,可見(jiàn)在HandleIRQ這里預(yù)留了4個(gè)字節(jié)的空間,但是這個(gè)空間里現(xiàn)在放的是什么東西呢?
在代碼的初始化過(guò)程中有這么一段代碼:
ldr r0,=HandleIRQ ; Setup IRQ handler
ldr r1,=IsrIRQ
str r1,[r0]
原來(lái)是把IsrIRQ所在的地址值放到這個(gè)地方,那就是宏實(shí)現(xiàn)了把PC指向IsrIRQ的目的。程序來(lái)到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中斷來(lái)到了這里,那么PC將會(huì)跳轉(zhuǎn)到HandleEINT0里存放的地址值,與上面的相同,程序里有這個(gè)表:
HandleEINT0#4
HandleEINT1#4
HandleEINT2#4
HandleEINT3#4
HandleEINT4_7 #4
.
.
.
這個(gè)表在2410addr.h頭文件里也有對(duì)應(yīng)的定義,指向的是同樣的一塊地方:
.
.
.
#define pISR_EINT0(*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1(*(unsigned *)(_ISR_STARTADDRESS+0x24))
#define pISR_EINT2(*(unsigned *)(_ISR_STARTADDRESS+0x28))
.
.
.
問(wèn)題是HandleEINT0存放的又是什么值呢?這就需要在初始化EINT0的時(shí)候?qū)懮线@么一句:
pISR_EINT0 = (unsigned )_IsrEINT0Service;
也就是把EINT0的中斷處理函數(shù)的地址寫到HandleEINT0地址處存放,那么到此PC就可以跳轉(zhuǎn)到_IsrEINT0Service里去了,這里完成你所需要的中斷處理過(guò)程。
評(píng)論