新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM簡單啟動代碼及中斷處理分析

        ARM簡單啟動代碼及中斷處理分析

        作者: 時間:2016-11-24 來源:網絡 收藏
        前言:本篇分析的是一個最精簡的啟動代碼,并且包含一個簡單的中斷處理,C程序部分省略,重點分析匯編部分,這是因為對于我來說,匯編代碼實在是讓人厭煩,但是又不能不用。
        下面是對代碼的分析,紅色部分是分析結果
        .extern main //.extern 表示main在另外的文件中定義,在這里要引用,至于為什么只聲明了extern而沒 //有聲明別的,目前還不知道,不過都聲明一下,應該沒問題
        .text //.text 表示后面的內容編譯出來放在代碼段
        .global _start //.global 告訴編譯器后面聲明的是一個全局可見的名字,由于這是啟動代碼,所以cpu上電后 //必須要找到_start函數,所以_start必須聲明為全局的,當然這個名字可以改,無所謂
        _start: //查看反匯編地址就可以發現,_start的地址就是0x0
        b Reset //不帶返回的跳轉到Reset中,為什么不用返回呢,因為沒必要,reset中直接進入到主函數內了
        HandleUndef: //查看反匯編地址可知HandleUndef的地址位0x04,這個很好理解,因為一條指令4個字節
        b HandleUndef //在這里由于是精簡的啟動代碼,所以一旦發生了未定義異常時,就讓CPU自己玩死自己吧
        HandleSWI: //反匯編地址0x08,其他分析同上
        b HandleSWI
        HandlePrefetchAbort: //反匯編地址0x0C,其他分析同上
        b HandlePrefetchAbort
        HandleDataAbort: //反匯編地址0x10,其他分析同上
        b HandleDataAbort
        HandleNotUsed: //反匯編地址0x14,其他分析同上
        b HandleNotUsed
        HandleIRQ:
        b HandlerIRQ //反匯編地址0x18,看到這里相比就有點感覺了,0x18是普通中斷向量地址,這個就有很 //多地方可說了,首先這里依然是使用B指令進行跳轉,這個之所以不直接用BL的原因 ///是,中斷還不太算是調用函數那么簡單,發生中斷的時候,要保存現場,而現場的數 //據有很多,具體為R0-R12、PC、CPSR的內容,這些都需要進棧保存,所以這些的內容不 //是BL一條命令能完成的,所以保存現場的內容就直接放到了中斷程序中了。其次要特別 //注意這個里面跳轉的程序時HandlerIRQ不是HandleIRQ,如果還像上面那樣,那么發生中 //斷時,CPU就會不停的循環來回跳了,所以這點需要特別留意,當然還有一種方法,就是 //不要這條命令的標號HandleIRQ,直接使用一條 b HandlerIRQ即可。
        HandleFIQ:
        b HandleFIQ //反匯編地址位0x1C,由于本程序只分析簡單的普通中斷,所以認為發生快速 //中斷的時候,讓CPU自己玩
        Reset:
        ldr sp,=4096 //由于要在匯編中調用C函數,所以要先設置堆棧指針,具體為什么,就 //不詳細分析了,簡單的說,C函數要使用很多的中間內存存放運算數據,所以 //要開辟一段堆棧用。這里還要特別提到一點,由于ARM工作模式的特點,此時 //設置的堆棧指針SP,即R14,對應的是系統模式下的堆棧指針寄存器,即R14_svc
        bl disable_watch_dog //關閉看門狗
        msr cpsr_c,# 0xd2 //設置CPU進入到中斷模式
        ldr sp,=3072 //由于發生中斷時,要用到堆棧用于保存現場,所以需要先設置中斷模式下的堆 //棧指針地址(設置堆棧指針就相當于開辟堆棧了,因為堆棧就是有堆棧指針的 //一片普通內存區域),即設置R14_irq,特別注意R14_irq和R14_svc是不一樣 //的,只是同名,但是指向的物理地址是完全不同的。
        msr cpsr_c,# 0xdf //從普通中斷模式返回到系統模式
        bl init_led //這個非常值得分析,首先這里使用了BL指令進行跳轉,BL指令的特點就是,跳轉 //的同時,會自動的將PC的值放到LR中,而且,如果后面跳轉的是C函數的話,C函 //數中就不用設置返回命令了,而如果BL后面跟的是一個匯編子程序,那就需要程 //序員自己添加返回命令,這是為什么呢?如果用專業術語說,如果調用C函數, //函數運行結束后,編譯器會自動的將上面保存的LR中的內容減去4重新傳遞給 //PC(為什么減4下面分析),也就實現了自動返回的功能,但是匯編函數不行,因 //為匯編的編譯器沒有這項功能,所以通俗的講,為什么C語言是高級語言,除了語 //法高級人性化,配套的編譯器也比較人性化,會幫程序員做一些通用的工作。而 //匯編語言就比較一根筋,所有的事兒,哪怕是最簡單的事兒,都要程序員一條一 //條的把代碼敲上去告訴它怎么干(這也是我為什么討厭匯編的原因,比較懶,呵 //呵),至于調用匯編子程序怎么返回怎么做,分析在下面
        bl init_irq //分析同上
        msr cpsr_c,# 0x5f //開中斷,即允許普通中斷IRQ
        ldr lr,=halt_loop //這個也很有意思,在這里為什么不直接用 bl main代替這兩條命令呢,因 //為。。。。。想嘚瑟一下(玩笑了),主要是想學習另外一種常見常用的跳轉方 //法。BL命令很好用,但是有缺點,就是跳轉的范圍,因為它只能跳轉區區32MB, //換算成ARM的命令,也就只有8M的地址(因為一條命令4個字節),也就 //是-4MB~+4MB,所以范圍有限,而嵌入式程序,尤其是有操作系統時,程序存放地 //址跨度比較大,所以呢,需要有一個長跳轉指令,所以就引出了LDR命令,這個命 //令的跳轉范圍是4G,所以知道為什么LDR長用了吧,因為無論地址在哪,它都能跳 //到,跳轉范圍大是LDR命令的優點,但是它也有缺點,就是它負責跳轉,不能保存 //PC,如果在跳轉前不往LR中放返回地址,就會出現,跳到了子程序的位置,子程序 //執行完后,跳不回來了,所以如果使用ldr調用子程序,一定要在調用前給LR一個 //返回地址。
        ldr pc,=main
        halt_loop:
        b halt_loop //main函數的返回地址,或者說是返回操作,讓CPU原地踏步
        HandlerIRQ:
        sub lr,lr,# 4 //這條指令用于計算中斷處理完畢后的返回地址,在這里有個隱含的地方,即當發生中 //斷時,處理器會自動的向LR寄存器中存放被中斷指令的地址加4,或者也可以理解成,
        //存放的是PC的值,而PC的值是當前運行命令地址加8,雖然發生了中斷,但是CPU仍然會 //運行完當前命令后才去處理中斷,所以LR-4的值,正是被中斷指令的下一條命令,當中 //斷處理完畢以后,返回到被中斷指令的下一條指令進行執行。
        stmdb sp!,{r0-r12,lr} //入棧指令,表示將R0~R12,以及LR寄存器的內容入棧,這個命令格式暫不分析,具體的可以參考 //相關手冊
        ldr lr,=int_return //這兩條命令的分析跟上面跳轉到main函數原理是一樣的,可以使用一條命令
        //bl EINT_Handle代替,因為這個程序本身并不大,不會超過4Kb
        ldr pc,=EINT_Handle
        int_return:
        ldmia sp!,{r0-r12,pc}^ //出棧指令,跟上面的入棧指令相呼應,表示將sp對應的地址內容遞增(因為ARM的堆棧式滿遞減 //形式)的依次存放到r0~r12,以及pc中,而且這個很有意思,按照順序,sp即r13肯定不包括在 //內,所以到pc的時候,賦值的內容寄存器正好是LR,而LR已經在中斷開頭處理了,而且^表示將 //spsr的值復制到cpsr中,至此完成了中斷的返回



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 广水市| 赫章县| 瑞安市| 潜江市| 金门县| 浮山县| 承德市| 布拖县| 滨海县| 宜章县| 建瓯市| 珠海市| 临桂县| 比如县| 贵南县| 成安县| 滦南县| 日喀则市| 大庆市| 广安市| 柳江县| 汉寿县| 安远县| 浮梁县| 盐城市| 霍州市| 临城县| 灵台县| 八宿县| 连江县| 家居| 江北区| 清新县| 石楼县| 桐梓县| 离岛区| 东台市| 开封市| 璧山县| 榆中县| 涿州市|