新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > 分析2440test中的中斷處理部分

        分析2440test中的中斷處理部分

        作者: 時間:2016-11-20 來源:網(wǎng)絡(luò) 收藏
        這個2440test里面的中斷寫的向量有些隱蔽,兜了很多個圈,也難怪這么難理解,下面

        就對這個東西抽絲剝繭,看清楚這究竟是一個怎么樣的過程。

        中斷向量
        bHandlerIRQ;handlerforIRQinterrupt
        很自然,因為所有的單片機都是那樣,中斷向量一般放在開頭,用過單片機的人都會很熟悉
        那就不多說了。

        異常服務(wù)程序
        這里不用中斷(interrupt)而用異常(exception),畢竟中斷只是異常的一種情況,呵呵
        下面主要分析的是“中斷異常”說白了,就是我們平時單片機里面用的中斷!??!所有有器件
        引起的中斷,例如TIMER中斷,UART中斷,外部中斷等等,都有一個統(tǒng)一的入口,那就是中斷
        異常IRQ!然后從IRQ的服務(wù)函數(shù)里面分辨出,當前究竟是什么中斷,再跳轉(zhuǎn)到相應(yīng)的中斷
        服務(wù)程序。這樣看來,ARM比單片機要復(fù)雜一些了,不過原理是不變的。

        上面說的就是思路,跟著這個思路來接著分析。

        HandlerIRQ很明顯是一個標號,我們找到了
        HandlerIRQHANDLERHandleIRQ

        這里是一個宏定義,我們再找到這個宏,看他是怎么定義的:

        MACRO
        $HandlerLabelHANDLER$HandleLabel

        $HandlerLabel
        subsp,sp,#4;decrementsp(tostorejumpaddress)
        stmfdsp!,{r0};PUSHtheworkregistertostack(lrdoesnotpushbecauseitreturntooriginal

        address)
        ldrr0,=$HandleLabel;loadtheaddressofHandleXXXtor0
        ldrr0,[r0];loadthecontents(serviceroutinestartaddress)ofHandleXXX
        strr0,[sp,#4];storethecontents(ISR)ofHandleXXXtostack
        ldmfdsp!,{r0,pc};POPtheworkregisterandpc(jumptoISR)
        MEND

        HandlerIRQ將這個宏展開之后得到的結(jié)果實際是這樣的

        HandlerIRQ
        subsp,sp,#4;decrementsp(tostorejumpaddress)
        stmfdsp!,{r0};PUSHtheworkregistertostack(lrdoesnotpushbecauseitreturntooriginal

        address)
        ldrr0,=HandleIRQ;loadtheaddressofHandleXXXtor0
        ldrr0,[r0];loadthecontents(serviceroutinestartaddress)ofHandleXXX
        strr0,[sp,#4];storethecontents(ISR)ofHandleXXXtostack
        ldmfdsp!,{r0,pc};POPtheworkregisterandpc(jumptoISR)
        至于具體的跳轉(zhuǎn)原理下面再說
        好了,這樣的話就容易看的多了,很明顯,HandlerIRQ還是一個標號,IRQ異常向量就是跳
        轉(zhuǎn)到這里執(zhí)行的,這里粗略看一下,應(yīng)該是保存現(xiàn)場,然后跳轉(zhuǎn)到真正的處理函數(shù),那么很容易
        發(fā)現(xiàn)了這么一句ldrr0,=HandleIRQ,沒錯,我們又找到了一個標號HandleIRQ,看來
        真正的處理函數(shù)應(yīng)該是這個HandleIRQ,繼續(xù)尋找

        AREARamData,DATA,READWRITE

        ^_ISR_STARTADDRESS;_ISR_STARTADDRESS=0x33FF_FF00
        HandleReset#4
        HandleUndef#4
        HandleSWI#4
        HandlePabort#4
        HandleDabort#4
        HandleReserved#4
        HandleIRQ#4

        最后我們發(fā)現(xiàn)在這里找到了HandleIRQ,^其實就是MAP,這段程序的意思是,從_ISR_STARTADDRESS
        開始,預(yù)留一個變量,每個變量一個標號,預(yù)留的空間為4個字節(jié),也就是32BIT,其實這里放的是真正
        的C寫的處理函數(shù)的地址,說白了,就是函數(shù)指針--
        這樣做的話就很靈活了

        接著,我們需要安裝IRQ處理句柄,說白了,就是設(shè)置處理函數(shù)的地址,讓PC指針可以正確的跳轉(zhuǎn)。
        于是我們在接著的找到安裝句柄的語句

        ;SetupIRQhandler
        ldrr0,=HandleIRQ;Thisroutineisneeded
        ldrr1,=IsrIRQ;ifthereisnotsubspc,lr,#4at0x18,0x1c
        strr1,[r0]

        說白了就是將IsrIRQ的地址填到HandleIRQ對應(yīng)的地址里面,前面說了HandleIRQ放的是中斷處理
        函數(shù)的入口地址,我們繼續(xù)找IsrIRQ

        IsrIRQ
        subsp,sp,#4;reservedforPC
        stmfdsp!,{r8-r9}
        ldrr9,=INTOFFSET
        ldrr9,[r9]
        ldrr8,=HandleEINT0
        addr8,r8,r9,lsl#2
        ldrr8,[r8]
        strr8,[sp,#8]
        ldmfdsp!,{r8-r9,pc}

        要理解這個代碼,得先學學2440的中斷系統(tǒng)了,INTOFFSET存放的是當前中斷的偏移號,根據(jù)偏移就知道
        當前是哪個中斷源發(fā)生的中斷。
        注意了,我們說的是中斷,而不是異常,看看原來的表是啥樣子的

        ^_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里面里面要執(zhí)行那條指令
        ldrr8,=HandleEINT0
        addr8,r8,r9,lsl#2
        道理很簡單,HandleEINT0就是所有IRQ中斷向量表的入口,在這個地址上面,加上一個適當?shù)钠屏浚?br />INTOFFSET,那么我們知道現(xiàn)在,到底是哪個IRQ在申請中斷了。

        至于具體怎么跳轉(zhuǎn)的?
        首先,我們說了,HandleEINT0開始的一段內(nèi)存里面,存放的就是中斷服務(wù)函數(shù)的函數(shù)指針,ARM的體系
        的話,每個指針變量就是占4個字節(jié),這里就解釋了,為什么這里為每個標號分配了4個字節(jié)的空間,里面
        放的就是函數(shù)指針?。?!下面再看看怎么跳轉(zhuǎn),繼續(xù)看IsrIRQ里面就實現(xiàn)了跳轉(zhuǎn)了
        strr8,[sp,#8]
        ldmfdsp!,{r8-r9,pc}
        其實最核心就是這兩句了,先查找到當前中斷服務(wù)程序的地址,將他放到R8里面,然后出棧,彈出給PC
        那么PC很自然就跳到中斷服務(wù)程序了。至于這里的堆棧問題又是一個非常棘手的,需要好好的參透ARM的
        中斷架構(gòu),需要了解的可以自己仔細的閱讀《ARM體系結(jié)構(gòu)與編程》里面說的很詳細。我們這里的重點
        是研究怎么跳轉(zhuǎn)。
        最后,我們看看在C代碼中是怎么安裝終端向量的,例如看按鍵的外部中斷,是怎么具體設(shè)置的,參看
        /src/keyscan.c里面的代碼
        很簡單,里面只有3個函數(shù)

        KeyScan_Test是按鍵測試的主函數(shù)
        Key_ISR是按鍵中斷服務(wù)函數(shù)

        KeyScan_Test里面,我們發(fā)現(xiàn)了有這么一句

        pISR_EINT0=pISR_EINT2=pISR_EINT8_23=(U32)Key_ISR;

        可以理解否?Key_ISR就是上面提到的按鍵中斷服務(wù)函數(shù),函數(shù)的名字,代表的就是函數(shù)的地址?。。?!
        將中斷服務(wù)函數(shù)的地址,注意了,是地址,這是一個U32型的變量。送到幾個變量,我們以pISR_EINT0
        作為例子,查看頭文件定義,在2440addr.h里面找到

        //Interruptvector
        #definepISR_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向量表里面?。。。?br />

        當按鍵中斷發(fā)生的時候,發(fā)生IRQ異常中斷
        當前PC值-4保存到LR_IRQ里面,然后執(zhí)行
        bHandlerIRQ
        然后是執(zhí)行

        HandlerIRQ
        subsp,sp,#4;預(yù)留一個用來存放PC地址
        stmfdsp!,{r0};保存R0,因為下面使用了
        ldrr0,=HandleIRQ;將HandleIRQ(服務(wù)程序)的地址裝載到R0
        ldrr0,[r0]
        strr0,[sp,#4];保存到剛才預(yù)留的地方
        ldmfdsp!,{r0,pc};彈出堆棧,恢復(fù)R0,并且將剛才計算好的HandleIRQ地址彈出到PC

        堆棧是向下生長的,所以SUBSP,SP,#4就相當于PUSHXX,但是這個XX這個時候并沒有用,因為這里
        用的是強制移動SP指針實現(xiàn)的。然后得到服務(wù)程序的地址,再將這個值放回剛才預(yù)留的棧的空位上面,最后
        就是POP出R0恢復(fù),并且將剛才得到的服務(wù)程序的地址送到PC,那么實現(xiàn)的效果就是跳轉(zhuǎn)到HandleIRQ里面了。

        接著看剛才是怎么安裝的HandleIRQ
        ;SetupIRQhandler
        ldrr0,=HandleIRQ;Thisroutineisneeded
        ldrr1,=IsrIRQ;ifthereisnotsubspc,lr,#4at0x18,0x1c
        strr1,[r0]
        可以看出,這里將IsrIRQ的地址的值保存到HandleIRQ中,也就是說,上面的IRQ服務(wù)程序,這個時候?qū)嶋H
        上就是指IsrIRQ

        所以接著的事情就是轉(zhuǎn)移到IsrIRQ中執(zhí)行:

        IsrIRQ
        subsp,sp,#4;預(yù)留一個值來保存PC
        stmfdsp!,{r8-r9}
        ldrr9,=INTOFFSET;計算偏移量,下面解釋
        ldrr9,[r9]
        ldrr8,=HandleEINT0
        addr8,r8,r9,lsl#2
        ldrr8,[r8]
        strr8,[sp,#8];因為保存了2個寄存器R8R9,所以SP下移了8位
        ldmfdsp!,{r8-r9,pc};恢復(fù)寄存器,彈出到PC,同上面的一樣
        怎么保存,操作SP,跟最后彈出到PC的部分和上面的例子一樣,下面說說中間的計算部分
        計算偏移量,其實原理很簡單,首先INTOFFSET保存著當前是哪個IRQ中斷,例如0代表著HandleEINT0,1代表
        HandleEINT1.....等等,這不是亂來,有一個表的,這個是由S3C2440的datasheet說的,自己可以去查看。
        然后得到中斷處理函數(shù)的向量表,這個表的首地址就是HandleEINT0,那么很自然的想到,怎么查表?那還不簡
        單?HandleEINT0+INTOFFSET不就完了?基地址加偏移量就得到表中某項了,當然,因為這里是中斷處理向量
        每一項占用4個字節(jié),所以用lsl#2處理一下,左移2位相當于乘以4,偏移量乘以4,這應(yīng)該很好理解的。

        我們這個例子找到的就是HandleEINT0,將里面的值讀出來,里面放的是HandleEINT0服務(wù)函數(shù)的地址,這個
        地址怎么來的?是在C程序里面設(shè)置的。我們看keyscan.c程序,找到一個voidKeyScan_Test(void)函數(shù),
        里面有這么一句:
        pISR_EINT0=pISR_EINT2=pISR_EINT8_23=(U32)Key_ISR;
        這里是安裝了3個按鍵中斷服務(wù)程序,我們只關(guān)注0號中斷,也就是
        pISR_EINT0=(U32)Key_ISR;
        這句話什么意思?先看看pISR_EINT0的定義,在2440addr.h中定義
        #definepISR_EINT0(*(unsigned*)(_ISR_STARTADDRESS+0x20))

        看到?jīng)]有?_ISR_STARTADDRESS不就是剛才說的那個異常向量的入口地址?加上一個0x20
        之后實際上指向的,就是HandleEINT0?。?!這么說來,上面的意思就是,將Key_ISR處理函數(shù)的入口地址,送
        HandleEINT0中。
        再來看Key_ISR,這是一個典型的服務(wù)程序,加了_irq作為編譯關(guān)鍵字,告訴編譯器,這個函數(shù)是中斷服務(wù)程序
        得保存需要的寄存器,免得被破壞。具體可以參考《ARM體系結(jié)構(gòu)與編程》P283頁的描述。

        staticvoid__irqKey_ISR(void)
        {
        .......
        }

        加上_irq關(guān)鍵字之后,編譯器就會處理好所有的保存動作了,并不需要多關(guān)心。但是這個是ARM-CC編譯器的關(guān)
        鍵字,GCC中并沒有這個東西,所以GCC處理中斷的時候最好還是自己保存一下。

        到這里為止,整個中斷的過程就解釋完畢。分析的過程中確實學習了很多。

        本文引用地址:http://www.104case.com/article/201611/318875.htm


        關(guān)鍵詞: 2440test中斷處

        評論


        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 义马市| 方山县| 文化| 靖安县| 海原县| 景宁| 建湖县| 遵化市| 邓州市| 当阳市| 孟连| 厦门市| 漯河市| 武陟县| 乃东县| 拉萨市| 白水县| 蓝田县| 和田县| 驻马店市| 沭阳县| 昔阳县| 乐亭县| 湛江市| 郑州市| 浮山县| 乐至县| 于都县| 阜阳市| 阿瓦提县| 子长县| 镇赉县| 天等县| 扶余县| 富民县| 宕昌县| 嘉兴市| 那曲县| 静乐县| 宣化县| 卢龙县|