ARM920T中斷體系結構

再開始ARM920t的中斷體系分析前,我們先來回憶一箇中斷觸發時8051單片機的處理流程:
(1)保護現場
(2)cpu的pc指針自動跳轉到中斷向量表處找出對應的中斷處理入口,即中斷處理函數
(3)進入中斷處理函數後首先要分辨中斷源,然後進行對應的中斷處理
(4)中斷處理完,清除中斷
(5)返回現場
中斷程序的設置流程:
(1)建立中斷向量表
(2)初始化中斷:打開開關、設置優先級、中斷模式下的堆棧指針等
(3)建立中斷處理函數:彙編調用C函數

開始分析ARM920t的中斷處理過程,原理和51單片機類似直接開始分析我的實驗代碼。

進入代碼時間:

File:head.S

.extern     main
.text 
.global _start 
_start:
@******************************************************************************       
@ (1)建立中斷異常向量表:每一條指令佔用4個字節的空間
@******************************************************************************       
    b   Reset         		@ 0x00:(有使用)啓動復位也是一種特殊的“異常”
HandleUndef:
    b   HandleUndef   		@ 0x04: 未定義指令中止模式的向量地址
HandleSWI:
    b   HandleSWI     		@ 0x08: 管理模式的向量地址,通過SWI指令進入此模式
HandlePrefetchAbort:
    b   HandlePrefetchAbort @ 0x0c: 指令預取終止導致的異常的向量地址
HandleDataAbort:
    b   HandleDataAbort		@ 0x10: 數據訪問終止導致的異常的向量地址
HandleNotUsed:
    b   HandleNotUsed		@ 0x14: 保留
    b   HandleIRQ 			@ 0x18: (有使用)中斷模式的向量地址
HandleFIQ:
    b   HandleFIQ			@ 0x1c: 快中斷模式的向量地址

Reset:                  
    ldr sp, =4096           @ 設置棧指針,以下都是C函數,調用前需要設好棧
    bl  disable_watch_dog   @ 關閉WATCHDOG,否則CPU會不斷重啓
    
    msr cpsr_c, #0xd2       @ 進入中斷模式,只有在中斷模式下纔可以設置中斷的堆棧指針
    ldr sp, =3072           @ 設置中斷模式棧指針

    msr cpsr_c, #0xd3       @ 進入(返回)管理模式
    ldr sp, =4096           @ 設置管理模式棧指針,
                            @ 其實復位之後,CPU就處於管理模式,
                            @ 前面的“ldr sp, =4096”完成同樣的功能,此句可省略

    bl  init_led            @ 初始化LED的GPIO管腳
    bl  init_irq            @ 調用中斷初始化函數,在init.c中
    msr cpsr_c, #0x53       @ 設置I-bit=0,開IRQ中斷
    
    ldr lr, =halt_loop      @ 進入c函數main前設置返回地址
    ldr pc, =main           @ 調用main函數
halt_loop:
    b   halt_loop

HandleIRQ:                 			@ 定義中斷異常處理函數
    sub lr, lr, #4                  @ 計算返回地址
    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器
                                    @ 注意,此時的sp是中斷模式的sp
                                    @ 初始值是上面設置的3072
    
    ldr lr, =int_return             @ 設置調用ISR即EINT_Handle函數後的返回地址  
    ldr pc, =EINT_Handle            @ 調用中斷服務函數,在interrupt.c中
int_return:
    ldmia   sp!,    { r0-r12,pc }^  @ 中斷返回, ^表示將spsr的值複製到cpsr
代碼分析:慢慢體會和51單片機的一點差別

當一個異常中斷髮生時ARM920T的硬件自動處理的工作:保護一部分現場
(1)將前一個工作模式的“下一條即將執行的指令地址”即PC值保存到下一個異常模式下的R14即LR寄存器中(此時還未進入該異常模式)
(2)將前一個工作模式的下的CPSR保存到下一個異常模式下的SPSR中
(3)強制將下一個異常模式的CPSR工作模式位設置成對應值
(4)強制讓PC值等於中斷向量表中保存的跳轉地址
進入到中斷後軟件要完成的工作有:
(1)計算中斷返回地址保存到LR:上面我們講到硬件自動幫我們把
注意這裏有一點很有意思:根據2440使用手冊可知平時PC指針是指向當前執行指令的下兩條指令(即PC=“PC”+8),剛纔硬件幫我們保存的地址並不是我們中斷返回時真正想要的地址!!!
例如從IRQ返回前,我必須要矯正這個返回地址才行,即讓LR=PC+4,這很簡單,之前LR=PC+8,現在只要將LR=LR-4就可以了。
(2)保護剩餘的現場:將所有的工作寄存器,比如R0-R12、LR等
(3)調用異常處理函數並清除中斷
(4)恢復現場,將保存過得寄存器值恢復
【非常建議自己去看s3c2440英文版的使用手冊,裏面有講到中斷異常到來時是CPU是如何操作的】


附加文件:

(1)File:interrupt.c   真正中斷處理函數

#include "s3c24xx.h"

void EINT_Handle()
{
    unsigned long oft = INTOFFSET;//判斷外部中斷源
    unsigned long val;
    
    switch( oft )
    {
        // S4被按下
        case 0: 
        {   
            GPBDAT |= (0x7<<5);   // 所有LED熄滅
            GPBDAT &= ~(1<<5);      // LED1點亮
            break;
        }
        
        // S1被按下
        case 1:
        {   
            GPBDAT |= (0x7<<5);   // 所有LED熄滅
            GPBDAT &= ~(1<<6);      // LED2點亮
            break;
        }

        // K3被按下
        case 2:
        {   
            GPBDAT |= (0x7<<5);   // 所有LED熄滅
            GPBDAT &= ~(1<<7);      // LED3點亮                
            break;
        }
        // K2被按下
        case 4:
        {   
            GPBDAT |= (0x7<<5);   // 所有LED熄滅
            GPBDAT &= ~(1<<8);      // LED3點亮                
            break;
        }

        default:
            break;
    }

    //清中斷
    if( oft == 5 ) 
        EINTPEND = (1<<11);   // EINT8_23合用IRQ5
    SRCPND = 1<<oft;
    INTPND = 1<<oft;
}

(2)File:init.c   一些初始化函數,比較懶所以就用c函數代替

#include "s3c24xx.h"

#define	GPB5_out	(1<<(5*2))
#define	GPB6_out	(1<<(6*2))
#define	GPB7_out	(1<<(7*2))
#define	GPB8_out	(1<<(8*2))

#define	GPB5_msk	(3<<(5*2))
#define	GPB6_msk	(3<<(6*2))
#define	GPB7_msk	(3<<(7*2))
#define	GPB8_msk	(3<<(8*2))

#define GPF0_eint   (0x2<<(0*2))
#define GPF1_eint   (0x2<<(1*2))
#define GPF2_eint   (0x2<<(2*2))
#define GPF4_eint   (0x2<<(4*2))

#define	GPF0_msk	(3<<(0*2))
#define	GPF1_msk	(3<<(1*2))
#define	GPF2_msk	(3<<(2*2))
#define	GPF4_msk	(3<<(4*2))

void disable_watch_dog(void)
{
    WTCON = 0;  // 關閉WATCHDOG很簡單,往這個寄存器寫0即可
}

void init_led(void)
{
    // LED1,LED2,LED4對應的3根引腳設爲輸出
    GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk);
    GPFCON |= GPF4_out | GPF5_out | GPF6_out;
}

/*
 * 初始化GPIO引腳爲外部中斷
 * GPIO引腳用作外部中斷時,默認爲低電平觸發、IRQ方式(不用設置INTMOD)
 */ 
void init_irq( )
{
    //配置中斷引腳
    GPFCON &= ~(GPF0_msk | GPF1_msk | GPF2_msk |GPF4_msk);
    GPFCON |= GPF0_eint | GPF1_eint | GPF2_eint | GPF4_eint;
        
    /*
     * 設定優先級:
     *  ARB_MODE0 = 0, ARB_SEL0 = 00b
     *  ARB_MODE6 = 0, ARB_SEL6 = 00b
     *  ARB_MODE1、ARB_SEL1無需設置:因爲這一組仲裁器只用了一箇中斷源
     *  優先級最終排名:EINT0 > EINT1 > EINT2 > EINT4
     */
    PRIORITY = (PRIORITY & ( (~0x01) | (0x0<<7) | (~(0x1<<6))| (0x0<<19) ) );

    // EINT0、EINT1、EINT2、EINT4 使能
    INTMSK   &= (~(1<<0)) & (~(1<<1)) & (~(1<<2)) & (~(1<<4));
}

代碼分析:

中斷優先級的設置:
ARM920t的中斷優先級主要由7個仲裁器arbiter來控制,每個仲裁器連接的中斷信號如圖,最終0-5仲裁器選出來的信號還要6仲裁器來進行最後一輪的排優。

PRIORITY寄存器共有有20bit,被分成兩部分功能:看2440手冊,記得是英文版
[0-6]ARB_MODE:設置仲裁寄存器的工作模式,主要有自動輪流優先級和關閉該功能。一個仲裁器使用1bit
[7-20]ARB_SEL:控制輸入中斷信號的優先級。一個仲裁器使用2bit

(3)File:main.c   等待中斷觸發

int main()
{
    while(1);
    return 0;
}

搞定生氣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章