一、ARM中断体系结构
arm有7中异常工作模式

用户模式、快中断模式、管理模式、数据访问终止模式、中断模式、系统模式、未定义指令终止模式。

几种模式有什么不同呢,
1、不同的寄存器
2、不同的权限
3、触发条件

对于不同的寄存器,ARM920T有31个通用的32位寄存器和6个程序状态寄存器。这37个寄存器分为7组,进入某个工作模式时就使用他那组的寄存器。有些寄存器,不同的工作模式下有他的副本,当切换到另一个工作模式时,那个工作模式的寄存器副本将被使用:这些寄存器被称为备份寄存器(图中灰色部分的寄存器)

在ARM状态下,每种工作模式都有16个通用的寄存器和一个(或两个,这取决于不同的工作模式)程序状态寄存器。图中R0-R15可以直接访问,这些寄存器除了R15外都是通用寄存器,即他们既可以保存数据又可以保存地址。另外R13-R15稍有特殊。R13是栈指针寄存器,通常被用来保存栈指针。14被称为程序连接寄存器,当执行BL子程序调用指令时,R14中得到R15(程序计数器PC)的备份。而当发生中断或异常时,对应的R14_svc、R14_irq、R14_fiq、R14_abt、R14_und中保存R15的返回值。

快速中断模式有7个备份寄存器R8-R14,这使得进入快速中断模式执行很大部分程序时,只要不改变R0-R7,不用保存任何寄存器。七种工作模式都有自己独占的寄存器副本R13和R14,这使得每个模式都有自己的栈指针寄存器和连接寄存器。

每种工作模式除了R0-R15外,还有第17个寄存器CPSR,即当前程序状态寄存器。CPSR中一些位被用来标识各种状态,一些位被用来标识当前处于什么工作模式。
如图CPSR


CPSR各位的意义:
(1)T位:置位时,CPU处Thumb状态,否则处于ARM状态
(2)中断禁止位:I位和F位属于中断禁止位。他们被置位时IRQ中断和FIQ中断分别被禁止。
(3)工作模式位:标识CPU当前处于什么工作模式。可以编写这些位,使CPU进入指定的工作模式。
除CPSR外,还有快速中断模式、中断模式、管理模式、数据访问终止模式和未定义指令终止魔兽等5中工作模式都有独占的寄存器SPSR,即程序状态保存寄存器。当切换进入这些工作模式时,在SPSR中保存前一个工作模式的CPSR的值,这样,当返回前一个工作模式时,可以将SPSR中的值恢复到CPSR中。

综上所述,当一个异常发生时,将切换进入相应的工作模式,这时ARM920T CPU核将自动完成如下的事情:
(1)、在异常工作模式的连接寄存器R14中保存前一个工作模式的下一条即将执行指令的地址,对于ARM状态,这个值是当前pc值加4或加8。
(2)、将CPSR的值复制到异常模式的SPSR
(3)、将CPSR的工作模式位设置为这个异常模式对应的工作模式。
(4)、令PC值等于这个异常模式在异常向量表的地址。即跳转去执行异常向量表中的相应的指令。
相反地、从异常工作模式退出返回之前的工作模式时,需要通过软件完成以下事情:
(1)、前面进入异常工作模式时,连接寄存器保存了前一工作模式的一个指令地址,将他减去一个适当的值后赋给PC寄存器。
(2)、将SPSR的值复制回CPSR。

CPSR模式位设置如下图:

二、s3c2440的中断控制器

CPU工作的过程中如何知道各类外设发生了某些不预期的事件呢,比如串口接收到了新数据、USB接口插入了设备、按下了某个按键等。主要有两种方法:
(1)是查询方式:程序循环地查询各设备的状态并作出相应的反应。实现比较简单,常用在功能相对单一的系统中,比如在一个温度检测系统对温度的不断检查。缺点是占用CPU资源,不适用于多任务系统。
(2)、中断方式:当某事件发生时,硬件会设置某个寄存器;CPU在每执行完一个指令时,通过硬件查看这个寄存器,如果发现所关注的时
间发生了,则中断当前程序流程,跳转到一个固定的地址处理这件事,最后返回继续执行被中断的程序。他的实现相对复杂,但是效率很高,是常用的方法。

中断体系外设、内部外设与CPU核的硬件框图:

不论何种CPU,中断的处理是相似的。
(1)、中断控制器汇集各类外设发出的中断信号,然后告诉CPU。
(2)、CPU保存当前程序的运行环境,调用中断服务程序来处理这些中断。
(3)、在ISR中通过读取中断控制器,外设的相关寄存器来识别这是哪个中断,并进行相应的处理。
(4)、清楚中断:通过读写中断控制器和外设的相关寄存器来实现。
(5)、最后恢复被中断程序的运行环境(即上面保存的各个寄存器等),继续执行。

对于不同的CPU而言,中断的处理只是细节的不同。

中断处理框图:

从中断控制器结构图看出
SUBSRCPND和SRCPND寄存器表明有哪些中断被触发,正在等待处理;SUBMASK(INTSUBMASK寄存器)和MASK(INTMASK寄存器)用于屏蔽某些中断。
图中的“Request sources(with sub-register)”表示INT_RXD0、INT_TXD0等中断源(这类中断源S3C2440有15个)。他们不同于“Request sources(without sub-register”。

附图s3c2440异常向量表:

综上所述,使用中断的步骤如下:
(1)设置好中断模式和快速中断模式下的栈:当发生中断IRQ时,CPU进入中断模式,这时使用中断模式下的栈;当发生快速中断时,CPU进入快速中断模式,这时使用快速中断模式下的栈。
(2)准备好中断处理函数。
a、异常向量:
在异常向量表中设置好进入中断模式或快速中断模式时的跳转函数,他们的异常向量分别是0x00000018、0x0000001c。
b、中断服务程序(ISR)
IRQ、FIQ的跳转函数,具体将调用具体中断的服务函数、

对于IRQ,读取INTPND寄存器或INTOFFSET寄存器的值来判断中断源,然后分别处理。
对FIQ,由于只有一个中断可以设为FIQ,无需判断中断源。

c、清除中断:如果不清除中断,则CPU会误以为这个中断又一次发生。

清除中断时从源头开始:首先,需要的话,操作具体外设清除中断信号,比如EINTPEND;其次,清除SUBSRCPND(用到的话)、SRCPND寄存器中的相应的位;最后清除INTPND寄存器中相应的位。
(3)、进入、退出中断模式或快速中断模式时,需要保存、恢复被中断程序的运行环境。
(4)、根据具体中断设置相关外设,比如GPIO中断,需要将相应的引脚功能设置为外部中断、设置中断触发条件等,一些中断有自己的屏蔽寄存器,还要开启它,比如,GPIO的外设中断,EINTMASK。
(5)、对于“Request sources(without sub-register)”的中断,将INTSUBMASK寄存器中相应的位设为0.
(6)、确定使用中断的方式:FIQ或IRQ
a、如果是FIQ,则在INTMOD寄存器中设置相应位为1
b、如果是IRQ,则在RIORITY寄存器中设置优先级
(7)、如果是IRQ,将INTMAK寄存器中相应的位设为0(FIQ不受INITMSK寄存器的控制)。
(8)、设置CPSR寄存器中的I位(对于IRQ)或F位(对于FIQ)为0,使能IRQ或FIQ、

在s3c2440中中断控制器相关的8个寄存器:

1、SUBSRCPND
SUBSRCPND被用来标识INT_RXD0、INT_TXD0等中断是否已经发生,每位对应一个中断。当这些中断发生且没有被INTSUBMSK寄存器屏蔽,则他们的若干位将汇集出现在SRCPND寄存器的一位上,比如、只要INT_RXD0、INT_TXD0或INT_ERR0一个发生且没有被屏蔽,则SRCPND寄存器INT_UART0位被置1.
要清除中断时,往SUBSRCPND中相应的位写1

2、INTSUBMSK
用来屏蔽SUBSRCPND寄存器所标识的中断
3、SRCPND
SRCPND寄存器每一位用来标识一个或一类中断是否已经发生,清除中断时与SUBSRCPND类似。
4、INTMSK
INTMSK寄存器用来屏蔽SRCPND寄存器所标识的中断。
5、INTMOD
当INTMOD寄存器中某位写入1时,它对应的中断被设为FIQ,同一时间里,INTMOD寄存器中只能有一位写入1。
6、PRIORITY
优先级寄存器,有多个中断同时发生时,中断控制器将选出优先级最高的中断源

7、INTPND
经过优先级仲裁器选出优先级最高的中断后,这个中断在INTPND寄存器中相应的位被置1,随后,CPU将进入中断模式处理它。同一时间段里,此寄存器只有一位被置1:在ISR中,可以根据这个位确定是哪个中断。清除中断时,往这个位写1.
8、INTOFFSET寄存器
这个寄存器被用来表示INTPND寄存器中哪个位被置1了,即INTPND寄存器中位【x】位1时,INTOFFSET寄存器的值为x(x为0-31)。
在清除SRCPND、INTPND寄存器时,INTOFFSET寄存器被自动清除。

三、实现按键中断,led灯亮程序

head.S

@******************************************************************************
@ File:head.S
@ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数
@****************************************************************************** .extern main
.text
.global _start
_start:
@******************************************************************************
@ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************
b Reset @ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
b HandleUndef @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI @ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort @ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort @ 0x14: 保留
HandleNotUsed:
b HandleNotUsed @ 0x18: 中断模式的向量地址
b HandleIRQ @ 0x1c: 快中断模式的向量地址
HandleFIQ:
b HandleFIQ Reset:
ldr sp, = @ 设置栈指针,以下都是C函数,调用前需要设好栈
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启 msr cpsr_c, #0xd2 @ 进入中断模式
ldr sp, = @ 设置中断模式栈指针 msr cpsr_c, #0xd3 @ 进入管理模式
ldr sp, = @ 设置管理模式栈指针,
@ 其实复位之后,CPU就处于管理模式,
@ 前面的“ldr sp, =”完成同样的功能,此句可省略 bl init_led @ 初始化LED的GPIO管脚
bl init_irq @ 调用中断初始化函数,在init.c中
msr cpsr_c, #0x5f @ 设置I-bit=,开IRQ中断 ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ 调用main函数
halt_loop:
b halt_loop HandleIRQ:
sub lr, lr, # @ 计算返回地址
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 init.c /*
* init.c: 进行一些初始化
*/ #include "s3c24xx.h" /*
* LED1,LED2,LED4对应GPF4、GPF5、GPF6
*/
#define GPF4_out (1<<(4*2))
#define GPF5_out (1<<(5*2))
#define GPF6_out (1<<(6*2)) #define GPF4_msk (3<<(4*2))
#define GPF5_msk (3<<(5*2))
#define GPF6_msk (3<<(6*2)) /*
* S2,S3,S4对应GPF0、GPF2、GPG3
*/
#define GPF0_eint (0x2<<(0*2))
#define GPF2_eint (0x2<<(2*2))
#define GPG3_eint (0x2<<(3*2)) #define GPF0_msk (3<<(0*2))
#define GPF2_msk (3<<(2*2))
#define GPG3_msk (3<<(3*2)) /*
* 关闭WATCHDOG,否则CPU会不断重启
*/
void disable_watch_dog(void)
{
WTCON = ; // 关闭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( )
{
// S2,S3对应的2根引脚设为中断引脚 EINT0,ENT2
GPFCON &= ~(GPF0_msk | GPF2_msk);
GPFCON |= GPF0_eint | GPF2_eint; // S4对应的引脚设为中断引脚EINT11
GPGCON &= ~GPG3_msk;
GPGCON |= GPG3_eint; // 对于EINT11,需要在EINTMASK寄存器中使能它
EINTMASK &= ~(<<); /*
* 设定优先级:
* ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
* 仲裁器1、6无需设置
* 最终:
* EINT0 > EINT2 > EINT11即K2 > K3 > K4
*/
PRIORITY = (PRIORITY & ((~0x01) | (0x3<<))) | (0x0 << ) ; // EINT0、EINT2、EINT8_23使能
INTMSK &= (~(<<)) & (~(<<)) & (~(<<));
}
s3c2440.h
/* WOTCH DOG register */
#define WTCON (*(volatile unsigned long *)0x53000000) /* SDRAM regisers */
#define MEM_CTL_BASE 0x48000000
#define SDRAM_BASE 0x30000000 /* NAND Flash registers */
#define NFCONF (*(volatile unsigned int *)0x4e000000)
#define NFCMD (*(volatile unsigned char *)0x4e000004)
#define NFADDR (*(volatile unsigned char *)0x4e000008)
#define NFDATA (*(volatile unsigned char *)0x4e00000c)
#define NFSTAT (*(volatile unsigned char *)0x4e000010) /*GPIO registers*/
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014) #define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPFUP (*(volatile unsigned long *)0x56000058) #define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
#define GPGUP (*(volatile unsigned long *)0x56000068) #define GPHCON (*(volatile unsigned long *)0x56000070)
#define GPHDAT (*(volatile unsigned long *)0x56000074)
#define GPHUP (*(volatile unsigned long *)0x56000078) /*UART registers*/
#define ULCON0 (*(volatile unsigned long *)0x50000000)
#define UCON0 (*(volatile unsigned long *)0x50000004)
#define UFCON0 (*(volatile unsigned long *)0x50000008)
#define UMCON0 (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
#define UTXH0 (*(volatile unsigned char *)0x50000020)
#define URXH0 (*(volatile unsigned char *)0x50000024)
#define UBRDIV0 (*(volatile unsigned long *)0x50000028) /*interrupt registes*/
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTMOD (*(volatile unsigned long *)0x4A000004)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
#define PRIORITY (*(volatile unsigned long *)0x4A00000c)
#define INTPND (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK (*(volatile unsigned long *)0x4A00001c) /*external interrupt registers*/
#define EINTMASK (*(volatile unsigned long *)0x560000a4)
#define EINTPEND (*(volatile unsigned long *)0x560000a8)
interrupt.c
#include "s3c24xx.h"

void EINT_Handle()
{
unsigned long oft = INTOFFSET;
unsigned long val; switch( oft )
{
// S2被按下
case :
{
GPFDAT |= (0x7<<); // 所有LED熄灭
GPFDAT &= ~(<<); // LED1点亮
break;
} // S3被按下
case :
{
GPFDAT |= (0x7<<); // 所有LED熄灭
GPFDAT &= ~(<<); // LED2点亮
break;
} // K4被按下
case :
{
GPFDAT |= (0x7<<); // 所有LED熄灭
GPFDAT &= ~(<<); // LED4点亮
break;
} default:
break;
} //清中断
if( oft == )
EINTPEND = (<<); // EINT8_23合用IRQ5
SRCPND = <<oft;
INTPND = <<oft;
}
main.c
int main()
{
while();
return ;
}
Makefile
objs := head.o init.o interrupt.o main.o

int.bin: $(objs)
arm-linux-ld -Ttext 0x00000000 -o int_elf $^
arm-linux-objcopy -O binary -S int_elf $@
arm-linux-objdump -D -m arm int_elf > int.dis %.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $< %.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $< clean:
rm -f int.bin int_elf int.dis *.o
	


												

s3c2440中断控制器操作的更多相关文章

  1. ARM9的中断控制器

    简要复习一下ARM9中断控制器的控制过程: 1.首先能识别触发的中断(对应中断源必须打开,然后查询当前中断状态寄存器),硬件会操控PC跳到中断向量入口(IRQ_HANDLE,硬件控制的只要是IRQ中断 ...

  2. S3C2440中断

    韦东山老师一期中断课程学习: 总结: 程序启动后工作流程,程序从0地址开始执行Reset  --> 重定位  -->ldr pc,=main [绝对跳转到SDRAM中执行main()函数] ...

  3. 【原创】Linux中断子系统(一)-中断控制器及驱动分析

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  4. x86架构中的外部中断结构-Part 1:中断控制器的演化

    本文主要讲解了x86体系架构从外部设备接受中断的过程,本文是系列文章的第一部分,试图回答以下问题: 什么是PIC以及它的用途是什么? 什么是APIC以及它的用途是什么?LAPIC和I/O APIC的目 ...

  5. ASP.NET Core 中文文档 第四章 MVC(4.2)控制器操作的路由

    原文:Routing to Controller Actions 作者:Ryan Nowak.Rick Anderson 翻译:娄宇(Lyrics) 校对:何镇汐.姚阿勇(Dr.Yao) ASP.NE ...

  6. thinkphp使用模块/控制器/操作访问时出现No input file specified.解决方式

    thinkphp使用 http://serverName/index.php/模块/控制器/操作 访问时,出现了 No input file specified. 的错误 解决办法: 一: 开启cgi ...

  7. Linux中断子系统:级联中断控制器驱动

    Linux中断子系统 Linux中断子系统是个很大的话题,如下面的思维导图所示,包含硬件.驱动.中断上半部.中断下半部等等.本文着眼于中断控制器(PIC),特别是级联中断控制器驱动部分,对驱动的设计和 ...

  8. 关于ck中断控制器

    一.中断控制器 中断控制器模块包括 其使能寄存器,状态寄存器等. 中断使能寄存器为32bit,每一个bit 对应一个中断源,具体对应到硬件上线的连接 二.cpu怎样调用到中断 (1). 前置工作 首先 ...

  9. threejs绕轴转,粒子系统,控制器操作等(二)

    前言:threejs系列的第二篇文章,也是一边学习一边总结: 1,一个物体绕着另一个物体转动 上一篇文中主要是物体自转,为了描述一个一个物体绕另一个物体转,这里我描述了一个月球绕地球公转,并且自转的场 ...

随机推荐

  1. GTID复制的搭建和问题处理

    首先看一下什么是GTID: GTID(Global Transaction ID)是对于一个已提交事务的编号,并且是一个全局唯一的编号. GTID实际上是由UUID+TID组成的.其中UUID是一个M ...

  2. git error:【fatal: unable to access 'https://github.com/userId/prjName.git/': err or setting certificate verify locations:】

    $ git pull origin master fatal: unable to access 'https://github.com/userId/prjName.git/': err or se ...

  3. laravel 使用EasyWechat 3分钟完成微信支付(以APP支付为例)

    上一篇写了支付宝支付,然后这段时间我又把微信支付给接上了,作为萌新的我还是很有成就感的,哈哈~~好了,该写正事了. 第一步:创建应用及配配置  首先到微信的官方平台注册应用https://pay.we ...

  4. easyui学习笔记9—手风琴格子的增,删和选择

    这一篇中我们将看看如何给手风琴动态的增加,删除格子,怎样选择某一个格子的. 1.先看看引用的资源 <link rel="stylesheet" href="jque ...

  5. Java Web技术经验总结

    接口的权限认证,使用拦截器(HandlerInterceptorAdapter),参考:第五章 处理器拦截器详解——跟着开涛学SpringMVC.注意:推荐能使用servlet规范中的过滤器Filte ...

  6. TB5上正常使用msfconsole

    在TB上使用系统自带的msfconsole,给出以下错误 [-] Failed to connect to the database: could not connect to server: Con ...

  7. Vue-Resource请求PHP数据失败的原因

    在写一个Vue项目的时候发现在使用Vue-Resource的post方法请求PHP数据时,完全没有反应,查阅资料才知道没有加配置参数: { emulateJSON:true } 这个配置参数的意思是: ...

  8. JAVA中commons-collections-3.2.1.jar包是干什么用的?

    类似C++中的Boost库,对Java容器类型和算法的补充

  9. 异常:Neither BindingResult nor plain target object for bean name 'command' available as request attribute

    Neither BindingResult nor plain target object for bean name 'command' available as request attribute ...

  10. new ,malloc

    特征 new/delete malloc/free 分配内存的位置 自由存储区 堆 内存分配失败返回值 完整类型指针 void* 内存分配失败返回值 默认抛出异常 返回NULL 分配内存的大小 由编译 ...