這個 2440test里面的中斷寫的向量有些隱蔽,兜了很多個圈,也難怪這么難理解,下面
就對這個東西抽絲剝繭,看清楚這究竟是一個怎么樣的過程。
中斷向量
bHandlerIRQ;handler for IRQ interrupt
很自然,因為所有的單片機都是那樣,中斷向量一般放在開頭,用過單片機的人都會很熟悉
那就不多說了。
異常服務程序
這里不用中斷(interrupt)而用異常(exception),畢竟中斷只是異常的一種情況,呵呵
下面主要分析的是“中斷異?!闭f白了,就是我們平時單片機里面用的中斷?。。∷杏衅骷?/p>
引起的中斷,例如TIMER中斷,UART中斷,外部中斷等等,都有一個統一的入口,那就是中斷
異常 IRQ ! 然后從IRQ的服務函數里面分辨出,當前究竟是什么中斷,再跳轉到相應的中斷
服務程序。這樣看來,ARM比單片機要復雜一些了,不過原理是不變的。
上面說的就是思路,跟著這個思路來接著分析。
HandlerIRQ 很明顯是一個標號,我們找到了
HandlerIRQ HANDLER HandleIRQ
這里是一個宏定義,我們再找到這個宏,看他是怎么定義的:
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
subsp,sp,#4;decrement sp(to store jump address)
stmfdsp!,{r0};PUSH the work register to stack(lr does not push because it return to original
address)
ldr r0,=$HandleLabel ;load the address of HandleXXX to r0
ldr r0,[r0];load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
用 HandlerIRQ 將這個宏展開之后得到的結果實際是這樣的
HandlerIRQ
subsp,sp,#4;decrement sp(to store jump address)
stmfdsp!,{r0};PUSH the work register to stack(lr does not push because it return to original
address)
ldr r0,=HandleIRQ ;load the address of HandleXXX to r0
ldr r0,[r0];load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
至于具體的跳轉原理下面再說
好了,這樣的話就容易看的多了,很明顯,HandlerIRQ 還是一個標號,IRQ異常向量就是跳
轉到這里執行的,這里粗略看一下,應該是保存現場,然后跳轉到真正的處理函數,那么很容易
發現了這么一句 ldr r0,=HandleIRQ ,沒錯,我們又找到了一個標號 HandleIRQ ,看來
真正的處理函數應該是這個 HandleIRQ ,繼續尋找
AREA RamData, DATA, READWRITE
^ _ISR_STARTADDRESS; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset # 4
HandleUndef # 4
HandleSWI# 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ# 4
最后我們發現在這里找到了 HandleIRQ ,^ 其實就是 MAP ,這段程序的意思是,從 _ISR_STARTADDRESS
開始,預留一個變量,每個變量一個標號,預留的空間為 4個字節,也就是 32BIT,其實這里放的是真正
的C寫的處理函數的地址,說白了,就是函數指針 - -
這樣做的話就很靈活了
接著,我們需要安裝IRQ處理句柄,說白了,就是設置處理函數的地址,讓PC指針可以正確的跳轉。
于是我們在接著的找到安裝句柄的語句
; Setup IRQ handler
ldrr0,=HandleIRQ ;This routine is needed
ldrr1,=IsrIRQ;if there is not ‘subs pc,lr,#4’ at 0x18, 0x1c
strr1,[r0]
說白了就是將 IsrIRQ 的地址填到 HandleIRQ對應的地址里面,前面說了 HandleIRQ 放的是中斷處理的
函數的入口地址,我們繼續找 IsrIRQ
IsrIRQ
subsp,sp,#4 ;reserved for PC
stmfdsp!,{r8-r9}
ldrr9,=INTOFFSET
ldrr9,[r9];讀入中斷偏移碼
ldrr8,=HandleEINT0;二級跳轉表的首地址
addr8,r8,r9,lsl #2;R8=R8+R9X4得到相應的中斷入口地址
ldrr8,[r8]
strr8,[sp,#8];中斷入口地址送進SP(第一個代碼留出的4字節空間)
ldmfdsp!,{r8-r9,pc}
要理解這個代碼,得先學學2440的中斷系統了,INTOFFSET存放的是當前中斷的偏移號,根據偏移就知道
當前是哪個中斷源發生的中斷。
注意了,我們說的是中斷,而不是異常,看看原來的表是啥樣子的
^ _ISR_STARTADDRESS; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset # 4
HandleUndef # 4
HandleSWI# 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ# 4
HandleFIQ# 4
HandleEINT0# 4
HandleEINT1# 4
HandleEINT2# 4
HandleEINT3# 4
。..。..。
可以看到,前面幾個是異常,從 HandleEINT0 就是 IRQ異常的向量存放的地方了,這樣就可以理解為
什么上面 IsrIRQ 里面里面要執行那條指令
ldrr8,=HandleEINT0
addr8,r8,r9,lsl #2
道理很簡單, HandleEINT0 就是所有IRQ中斷向量表的入口,在這個地址上面,加上一個適當的偏移量,
INTOFFSET ,那么我們知道現在,到底是哪個IRQ在申請中斷了。
至于具體怎么跳轉的?
首先,我們說了,HandleEINT0 開始的一段內存里面,存放的就是中斷服務函數的函數指針,ARM的體系
的話,每個指針變量就是占4個字節,這里就解釋了,為什么這里為每個標號分配了4個字節的空間,里面
放的就是函數指針!??!下面再看看怎么跳轉,繼續看 IsrIRQ 里面就實現了跳轉了
strr8,[sp,#8]
ldmfdsp!,{r8-r9,pc}
其實最核心就是這兩句了,先查找到當前中斷服務程序的地址,將他放到 R8 里面,然后出棧,彈出給PC
那么PC很自然就跳到中斷服務程序了。至于這里的堆棧問題又是一個非常棘手的,需要好好的參透ARM的
中斷架構,需要了解的可以自己仔細的閱讀 《ARM體系結構與編程》里面說的很詳細。我們這里的重點
是研究怎么跳轉。
最后,我們看看在C代碼中是怎么安裝終端向量的,例如看 按鍵的外部中斷,是怎么具體設置的,參看
/src/keyscan.c 里面的代碼
很簡單,里面只有3個函數
KeyScan_Test 是按鍵測試的主函數
Key_ISR 是按鍵中斷服務函數
在 KeyScan_Test里面,我們發現了有這么一句
pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)Key_ISR;
可以理解否? Key_ISR就是上面提到的按鍵中斷服務函數,函數的名字,代表的就是函數的地址?。。。?/p>
將中斷服務函數的地址,注意了,是地址,這是一個 U32型的變量。送到幾個變量,我們以pISR_EINT0
作為例子,查看頭文件定義,在 2440addr.h 里面找到
// Interrupt vector
#define pISR_EINT0(*(unsigned *)(_ISR_STARTADDRESS+0x20))
_ISR_STARTADDRESS有沒有似曾相識的感覺?沒錯,剛才分析的匯編代碼里面就提到了
^ _ISR_STARTADDRESS; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset # 4
HandleUndef # 4
。..。..
對,地址就是這里,然后 _ISR_STARTADDRESS+0x20 就是跳過前面的異常向量,進入IRQ中斷向量的入口
所以說到尾
pISR_EINT0 = (U32)Key_ISR;
完成的操作就是,將 Key_ISR 的地址存放到
HandleEINT0# 4
這個IRQ向量表里面!?。?!
當按鍵中斷發生的時候,發生IRQ異常中斷
當前PC值-4 保存到LR_IRQ里面,然后執行
bHandlerIRQ
然后是執行
HandlerIRQ
sub
評論
查看更多