嵌入式linux 添加中断,《嵌入式linux应用程序开发完全手册》中断控制器操作(外部中断)学习笔记...
《嵌入式linux應用程序開發(fā)完全手冊》中斷控制器操作(外部中斷)學習筆記
一.ARM中斷體系
當一個“異常”發(fā)生時,或者說當收到一個中斷觸發(fā)信號時,ARM9將會自動完成如下一些工作:
(1)在異常工作模式的連接寄存器R14中保存前一個工作模式的下一條指令地址,即是pc+4或者pc+8。
(2)將CPSR的值復制到異常模式的SPSR中。
(3)將CPSR的工作模式位設為這個異常對應的工作模式。
(4)令PC值等于這個異常模式在異常向量表中的地址,即跳轉(zhuǎn)去執(zhí)行異常向量表中的相應指令。既是執(zhí)行中斷服務程序。
當從異常模式恢復至之前的工作模式時,軟件需要完成如下工作:
(1)將前面進入異常時保存在R14中的指令地址減去適當值后賦給PC寄存器。
異常模式
退出異常時PC值
進入異常模式時R14值
Svc
MOVS PC,R14
Und
MOVS PC,R14
PC+4
Fiq
SUBS PC,R14,#4
PC+4
Irq
SUBS PC,R14,#4
PC+4
Abt
指令預取終止
SUBS PC,R14,#4
PC+4
指令讀取終止
SUBS PC,R14,#8
PC+8
(2)將SPSR的值復制回CPSR。
二. S3C2440中斷控制器
其實,無論哪種CPU,中斷處理過程都是類似的:
1.中斷控制器匯集各類外設發(fā)出的中斷信號,然后告訴CPU。
2.CPU保存當前程序的運行環(huán)境,調(diào)用中斷服務程序來處理中斷。
3.在ISR(interrupt Service Routine)中通過讀取中斷控制器,外設的相關寄存器來識別這是哪個中斷,并進行相應的處理。
4.清除中斷:通過讀寫中斷控制器和外設的相關寄存器來實現(xiàn)。
5.最后恢復被中斷程序的運行環(huán)境,繼續(xù)執(zhí)行中斷前的程序。
當然,對于不同的CPU體系結(jié)構,具體的寄存器操作自然就不同了。
對于S3C2440,寄存器SUBSRCPND和SRCPND表明有哪些中斷被觸發(fā)了,正在等待處理(pending),INTSUMMSK和INTMSK寄存器用于屏蔽某些中斷
基本中斷處理過程如下:
中斷源被觸發(fā)過后,SUBSRCPND寄存器中的相應位會被置位,如果此中斷沒有被INTMSK寄存器屏蔽掉,它將會進一步處理。在SRCPND寄存器中,被觸發(fā)的中斷相應位被置1,等待處理。如果被觸發(fā)的中斷源中有FIQ,則CPU進入FIQ Mode進行處理。如果被觸發(fā)的是一般的IRQ,可能同時有幾個中斷被觸發(fā),未被INTMSK寄存器屏蔽的中斷經(jīng)過比較,選出優(yōu)先級最高的中斷,此中斷在INTPND寄存器中的相應位被置1,然后CPU進入IRQ Mode進行處理。中斷服務程序可以通過讀取INTPND寄存器或者INTOFFSET寄存器來確定中斷源。
綜上,使用中斷的步驟如下:
1.設置好中斷模式和快速中斷模式下的棧。
2.準備好中斷處理函數(shù)。包括異常向量表,ISR,清除中斷。
清除中斷的方法:
(1)操作具體外設清除中斷信號。
(2)清除SUBSRCPND,SRCPND寄存器中的相應位。
(3)清除INTPND寄存器中的相應位。
3.進入和退出時需要對中斷程序的運行環(huán)境進行保護和恢復。
(1)進入和退出IRQ的一般處理:
sub lr, lr, #4 @ 計算返回地址
stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
……@中斷處理
ldmia sp!, { r0-r12,pc }^ @ 中斷返回, ^表示將spsr的值復制到cpsr
(2)進入和退出FIQ的一般處理:
sub lr, lr, #4 @ 計算返回地址
stmdb sp!, { r0-r7,lr } @ 保存使用到的寄存器
……@快速中斷處理
ldmia sp!, { r0-r7,pc }^ @ 中斷返回, ^表示將spsr的值復制到cpsr
4.根據(jù)具體中斷,設置相關外設,比如對于GPIO中斷,需要將相應引功能設置為“外部中斷”,設置中斷觸發(fā)條件等等。
5.將INTSUBMSK寄存器相應位設為0,如果需要的話。
6.確定使用中斷的方式:FIQ或者IRQ
7.如果是FIQ,則INTMOD寄存器中設置相應位為1;如果是IRQ,則在PRIORITY寄存器中設置優(yōu)先級。
8.如果是IRQ,將INTMSK寄存器相應位設為0,FIQ不受INTMSK寄存器控制。
9.設置CPSR寄存器中的I位(IRQ)或者F位(FIQ),全能IRQ或者FIQ。
中斷控制寄存器:
(1)子中斷源未決寄存器SUBSRCPND,地址:0x4A000018
(2)子中斷屏蔽寄存器INTSUBMSK,地址:0x4A00001C
(3)中斷源未決寄存器SRCPND,地址:0x4A000000
(4)中斷屏蔽寄存器INTMSK,地址:0x4A000008
(5)中斷模式寄存器INTMOD,地址:0x4A000004
(6)優(yōu)先級寄存器PRIORITY,地址:0x4A00000C
(7)中斷未決寄存器INTPND,地址:0x4A000010
(8)中斷偏移寄存器INTOFFSET,地址:0x4A000014
各個寄存器的具體操作說明請參考S3C2440的datasheet。
三.KEY對應的中斷連接
EINT19(GPG11)-------K1
EINT11(GPG3)-------K2
EINT2GPF2)-------K3
EINT0GPF0)-------K4
將這幾個I/O口設置成外部中斷功能。如果某個key按下時就產(chǎn)生中斷,點亮對應的led。
四.按鍵中斷實驗代碼
(1)初始化,設置中斷模式、系統(tǒng)模式的棧,設置好中斷處理函數(shù)
@******************************************************************************
@ File:head.S
@ 功能:初始化,設置中斷模式、系統(tǒng)模式的棧,設置好中斷處理函數(shù)
@******************************************************************************
.extern main
.text
.global _start
_start:
@******************************************************************************
@ 中斷向量,本程序中,除Reset和HandleIRQ外,其它異常都沒有使用
@******************************************************************************
b Reset
@ 0x04: 未定義指令中止模式的向量地址
HandleUndef:
b HandleUndef
@ 0x08: 管理模式的向量地址,通過SWI指令進入此模式
HandleSWI:
b HandleSWI
@ 0x0c: 指令預取終止導致的異常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10: 數(shù)據(jù)訪問終止導致的異常的向量地址
HandleDataAbort:
b HandleDataAbort
@ 0x14: 保留
HandleNotUsed:
b HandleNotUsed
@ 0x18: 中斷模式的向量地址
b HandleIRQ
@ 0x1c: 快中斷模式的向量地址
HandleFIQ:
b HandleFIQ
Reset:
ldr sp, =4096 @ 設置棧指針,以下都是C函數(shù),調(diào)用前需要設好棧
bl disable_watch_dog @ 關閉WATCHDOG,否則CPU會不斷重啟
msr cpsr_c, #0xd2 @ 進入中斷模式,M[4:0]=10010 , [IFT]=110
ldr sp, =3072 @ 設置中斷模式棧指針
msr cpsr_c, #0xdf @ 進入系統(tǒng)模式
ldr sp, =4096 @ 設置系統(tǒng)模式棧指針,
@ 其實復位之后,CPU就處于系統(tǒng)模式,
@ 前面的“l(fā)dr sp, =4096”完成同樣的功能,此句可省略
bl init_led @ 初始化LED的GPIO管腳
bl init_irq @ 調(diào)用中斷初始化函數(shù),在init.c中
msr cpsr_c, #0x5f @ 設置I-bit=0,開IRQ中斷,[IFT]=010
ldr lr, =halt_loop @ 設置返回地址
ldr pc, =main @ 調(diào)用main函數(shù)
halt_loop:
b halt_loop
HandleIRQ:
sub lr, lr, #4 @ 計算返回地址
stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
@ 注意,此時的sp是中斷模式的sp
@ 初始值是上面設置的3072
ldr lr, =int_return @ 設置調(diào)用ISR即EINT_Handle函數(shù)后的返回地址
ldr pc, =EINT_Handle @ 調(diào)用中斷服務函數(shù),在interrupt.c中
int_return:
ldmia sp!, { r0-r12,pc }^ @ 中斷返回, ^表示將spsr的值復制到cpsr
(2)初始化,關閉watchdog,,初始化GPIO引腳為外部中斷。
* init.c: 進行一些初始化
*/
#include "s3c24xx.h"
/*
* LED1-4對應GPB5、GPB6、GPB7、GPB8
*/
#define GPB5_out (1<
#define GPB6_out (1<
#define GPB7_out (1<
#define GPB8_out (1<
/*
* K1-K4對應GPG11、GPG3、GPF2、GPF0
*/
#define GPG11_eint (2<
#define GPG3_eint (2<
#define GPF2_eint (2<
#define GPF0_eint (2<
/*
* 關閉WATCHDOG,否則CPU會不斷重啟
*/
void disable_watch_dog(void)
{
WTCON = 0; // 關閉WATCHDOG很簡單,往這個寄存器寫0即可
}
void init_led(void)
{
GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out ;
}
/*
* 初始化GPIO引腳為外部中斷
* GPIO引腳用作外部中斷時,默認為低電平觸發(fā)、IRQ方式(不用設置INTMOD)
*/
void init_irq( )
{
GPFCON = GPF0_eint | GPF2_eint;
GPGCON = GPG3_eint | GPG11_eint;
// 對于EINT11、19,需要在EINTMASK寄存器中使能它們
EINTMASK &= (~(1<<11)) & (~(1<<19));
/*
* 設定優(yōu)先級:
* ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
* 仲裁器1、6無需設置
* 最終:
* EINT0 > EINT2 > EINT11,EINT19,即K4 > K3 > K1,K2
* EINT11和EINT19的優(yōu)先級相同
*/
PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;
// EINT0、EINT2、EINT8_23使能
INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
}
(3)真正的ISR
void EINT_Handle()
{
unsigned long oft = INTOFFSET;
unsigned long val;
switch( oft )
{
// K4被按下
case 0:
{
GPBDAT |= (0x0f<<5); // 所有LED熄滅
GPBDAT &= ~(1<<8); // LED4點亮
break;
}
// K3被按下
case 2:
{
GPBDAT |= (0x0f<<5); // 所有LED熄滅
GPBDAT &= ~(1<<7); // LED3點亮
break;
}
// K1或K2被按下
case 5:
{
GPBDAT |= (0x0f<<5); // 所有LED熄滅
// 需要進一步判斷是K1還是K2,或是K1、K2被同時按下
val = EINTPEND;
if (val & (1<<11))
GPBDAT &= ~(1<<6); // K2被按下,LED2點亮
if (val & (1<<19))
GPBDAT &= ~(1<<5); // K1被按下,LED1點亮
break;
}
default:
break;
}
//清中斷
if( oft == 5 )
EINTPEND = (1<<11) | (1<<19); // EINT8_23合用IRQ5
SRCPND = 1<
INTPND = 1<
}
總結(jié)
以上是生活随笔為你收集整理的嵌入式linux 添加中断,《嵌入式linux应用程序开发完全手册》中断控制器操作(外部中断)学习笔记...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 查看java最大内存配置,L
- 下一篇: linux标准分发版的描述信息,如何查看