韦东山嵌入式Linux学习笔记08--中断体系结构
中断是什么?
举个栗子, 系统怎么知道你什么时候插入鼠标这个设备? 可以有两种处理方式:
1. 查询方式:
轮询去检测是否有设备插入;
2. 中断的方式
当鼠标插入这个事件发生时, 置位某个寄存器,告诉CPU去处理这个事件.
对于查询方式, 我们需要一直去监控想要知道的状态, 而中断的处理方式, 使得CPU有空去处理其他的事情, 当插入鼠标这个时间发生时才来处理这个事件, 这样下来,
处理效率会高很多,中断的作用也可见一斑.
下面介绍s3c2440的中断控制器
s3c2440有60个中断源, 这么多中断源, 怎么处理? 下面是中断处理的一个框图:
上面框图中寄存器的介绍:
SUBSRCPND: (0x4A00 0018)
它包含的中断源如下, 当这些中断发生时,对应的位会被自动置1.我们可以往某位写入1来令此位为0, 清中断.写入0无效果,数据保持不变.
SUBMASK: (0x4A00 001C)
SUBSRCPND寄存器后面是SUBMASK寄存器, 这个寄存器用作屏蔽中断, 这个寄存器的对应的位与SUBSRCPND对应. 当这里相应的
位被置一之后,即使相应中断发生了,也会被屏蔽掉. 这就是这个寄存器的作用.
SRCPND: (0x4A00 0000)
SRCPND寄存器, 这里包含了SUBSRCPND中没有的一些中断源, 同样地,当中断被相应之后,相应的位会被置一,如果需要清除中断,往相应的位写入1就可以实现清0的效果.
INTMOD: (0x4A00 0004)
这个寄存器同样由32位组成,每一位对应于一种中断源,如果某一位被设置为一,那么这个中断源就会被当做是快速中断来处理.且只有一个位可以被设置成快速中断,其他都为普通中断.
INTMASK: (0x4A00 0008)
INTMASK寄存器, 这个寄存器也是用于屏蔽某一种中断源.与SRCPND对应.
PRIORITY: (0x4A00 000C)
出现多个普通中断时有可能同时出现的,这时候就需要告诉CPU那种中断源优先级更高,CPU根据优先级高低先后处理中断.
优先级仲裁:
上图中的ARB_SELN(n:0-6)时什么来的?原来CPU有一个仲裁器,用来对中断源进行仲裁,.
如下图, 有6个一级仲裁器和1个二级仲裁器
1.REQ0总是有最高的优先级, REQ5总是有最低的优先级.
2.REQ1-REQ4可以根据上面的寄存器进行设置.
当ARB_MODEn被设置为0时,当这个中断被处理之后,REQn的优先级不会发生变化.
当ARB_MODEn被设置为1时,当这个中断被处理之后,REQn优先级会被置为最低,
INTPND: (0x4A00 0010)
INTPND寄存器, 经过中断优先级仲裁器选出优先级最高的中断后,这个中断的相应的位会被置1, 如果想要清除中断,往这个位写1
插播以下知识点, 因为下面代码会用到:
下面的代码多了一些陌生的知识,比如系统的模式:
ARM体系CPU有7中工作模式:
1.用户模式: ARM处理器正常的程序执行状态. (usr)
2.快速中断模式: 用于高速数据传输或通道处理; (fiq)
3.中断模式: 用于通用的中断处理; (irq)
4.管理模式: 操作系统使用的保护模式; (svc)
5.数据访问终止模式: 当数据或指令预取终止时进入该模式,用于虚拟存储及存储保护; (abt)
6.系统模式:运行具有特权的操作系统任务; (sys)
7.未定义指令终止模式: 当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真. (und)
这些模式的切换可以通过软件设置来进行切换, 或者当CPU发生中断或者异常时自动进入相应的模式. 除用户模式外,其他6种模式都属于特权模式.大多数程序运行与用户模式,进入特权模式是为了处理中断, 异常或者访问被保护的系统资源.
比如, CPU复位上电也属于一种异常, 会自动跳转到0x00地址执行指令, 当CPU发生通用中断时, 会自动进入中断模式并且跳转到0x18地址处执行指令.
如上图, ARM中有16个寄存器, 每种模式下都有这些寄存器, 但是每种模式都有一些"备份寄存器", 这些寄存器是每种模式下与其他模式不同的寄存器,是独立的. 其中
R13被称为栈指针寄存器,又叫SP
R14被称为程序链接寄存器
R15被称为程序计数器
比如你在fiq模式下设置了SP的地址, 又可以切换到irq模式下设置SP的地址,这两个SP地址是互相独立的,在两种模式下设置后都是不同的.
关于切换CPU模式,如果通过软件方式设置, 我们可以更改寄存器CPSR(Current Program Status Register)的值, 这个寄存器的每个位的含义如下, 你可以初步看到一些mode设置位, 快速中断去使能位和普通中断去使能位.
CPSR各个bit的详细解析如下:
进入异常模式时应该按照以下步骤执行:(CPU核自动执行)
1. 在异常工作模式的R14中保存前一个工作模式的下一条指令. 对于ARM状态,这个值是当前PC值加4或者加8,参考下表
2. 将CPSR的值复制到异常模式的SPSR(Saved Program Status Register)
3. 将CPSR设置为相应的异常对应的工作模式.
4.将PC值等于这个异常在异常向量表中的地址(这里的异常向量表指的上面代码0x00-0x1c地址的指令
退出异常模式的操作步骤:(软件完成)
1.将前面保存在R14中的值,将它减去一个值,参考上表
2.将SPSR的值复制回CPSR
下面来看下如何实现代码测试中断:
文件如下:
Makefile:(关于编译选项可以参考: https://www.cnblogs.com/cheyihaosky/p/11562330.html)
objs := head.o init.o interrupt.o main.o int.bin: $(objs) #编译的目标文件是int.bin, 要生成这个文件需要依赖${objs} ->> head.o init.o interrupt.o main.o这几个OBJ文件
arm-linux-ld -Ttext 0x00000000 -o int_elf $^ #$^表示所有依赖文件
arm-linux-objcopy -O binary -S int_elf $@ #$@目标文件的名称, 表示int.bin
arm-linux-objdump -D -m arm int_elf > int.dis #生成反汇编文件 %.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $< #$<表示第一个.c文件 %.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $< clean:
rm -f int.bin int_elf int.dis *.o
测试代码: head.S
.extern main
.text @表明这里为代码段
.global _start
_start: @系统一上电先从这里开始跑 b Reset @这条语句的地址为0x00,上电也算是一种异常,跳到这个地址执行第一条指令
HandleUndef:
b HandleUndef @ 0x04: 这条语句在地址0x04处, 标号就在它上方,实际这里什么都不干
HandleSWI:
b HandleSWI @ 0x08:同0x04
HandlePrefetchAbort:
b HandlePrefetchAbort @ 0x0c:同0x04
HandleDataAbort:
b HandleDataAbort @ 0x10:同0x04
HandleNotUsed:
b HandleNotUsed @ 0x14:同0x04
b HandleIRQ @ 0x18: 这里实际是有相关的代码实现的
HandleFIQ:
b HandleFIQ @ 0x1c:同0x04 Reset: @系统复位,会自动执行0x00地址的b reset跳转到这里
ldr sp, =4096 @下面是调用c函数,所以要先设置栈指针,由于CPU的内部RAM只有4k,所以是指成4096
bl disable_watch_dog @这里只是往控制看门够的一个寄存器写值关闭看门狗, 跳转到init.c执行这个函数 msr cpsr_c, #0xd2 @ 进入中断模式
ldr sp, =3072 @ 然后设置中断模式的栈指针 msr cpsr_c, #0xd5 @进入系统模式
ldr sp, =4096 @设置系统模式栈指针
@ 其实系统复位就处于系统模式
bl init_led @初始化LED相关的GPIO引脚
bl init_irq @ 中断初始化函数
msr cpsr_c, #0x5f @ 开IRQ中断 ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ 调用main函数
halt_loop:
b halt_loop HandleIRQ:
sub lr, lr, #4 @计算返回地址?
stmdb sp!, { r0-r12,lr } @保存使用到的寄存器
@
@ ldr lr, =int_return @ 设置函数的返回地址
ldr pc, =EINT_Handle @ 调用EINT_Handle函数
int_return:
ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将SPSR的值复制到CPSR中
init.c
#include "s3c24xx.h" #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)) #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)) void disable_watch_dog(void)
{
WTCON = 0;
} void init_led(void)
{
GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk);
GPFCON |= GPF4_out | GPF5_out | GPF6_out;
}
void init_irq( )
{
GPFCON &= ~(GPF0_msk | GPF2_msk);
GPFCON |= GPF0_eint | GPF2_eint; GPGCON &= ~GPG3_msk;
GPGCON |= GPG3_eint; EINTMASK &= ~(1<<11); PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ; INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
s3c24xx.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 )
{
case 0:
{
GPFDAT |= (0x7<<4);
GPFDAT &= ~(1<<4);
break;
} // S3±»°ŽÏÂ
case 2:
{
GPFDAT |= (0x7<<4);
GPFDAT &= ~(1<<5);
break;
} // K4±»°ŽÏÂ
case 5:
{
GPFDAT |= (0x7<<4);
GPFDAT &= ~(1<<6);
break;
} default:
break;
} if( oft == 5 )
EINTPEND = (1<<11);
SRCPND = 1<<oft;
INTPND = 1<<oft;
}
main.c
int main()
{
while(1);
return 0;
}
韦东山嵌入式Linux学习笔记08--中断体系结构的更多相关文章
- 韦东山嵌入式Linux学习笔记02--如何给开发板烧录程序
购买韦东山嵌入式开发板jz2440 v3会标配两根usb线和一根网线,OpenJtag需要单独购买, 我暂时还没买到该工具. 下面介绍usb烧录以及通过网线烧录程序. 1.usb烧录程序: 借助DNW ...
- 韦东山嵌入式Linux学习笔记07--Nandflash
常用的flash有两种, Norflash和Nandflash, 前几年市场上的产品比较常见的方案时Norflash和Nandflash搭配使用, 因为norflash比较昂贵,相同的容量norfla ...
- 韦东山嵌入式Linux学习笔记05--存储管理器
SDRAM: 原理图如下: jz2440 v3开发板上面用的内存芯片为钰创科技公司生产的EM63A165TS,一片内存大小为32MB大小,一共有两块,共64MB的大小. SDRAM接 ...
- 韦东山嵌入式Linux学习笔记04--点亮开发板的一个LED灯
搜索开发板原理图LED的走线 LED8是网线接口的指示灯. 在这里我们尝试用汇编代码控制D10, 也就是LED1,它连接到EINT4/GPF4,读取芯片手册 有原理图可知,如果需要 ...
- 韦东山嵌入式Linux学习笔记03--如何搭建软件环境
1. 从网上下一个虚拟机,比如vmvare station 2.下一个ubuntu镜像回来安装, 我下了14.04来安装.参考链接: https://blog.csdn.net/qq1326702 ...
- 韦东山嵌入式Linux学习笔记01--转载: 板子ping不通PC怎么办
请参考链接:https://blog.csdn.net/u013490896/article/details/71250060 我的环境: window 10 jz2440 v3 我采用的连接方式如下 ...
- 韦东山 嵌入式linux教程 笔记
@ 目录 资源链接 一.常用命令 二.shell 三.如何更改PATH? 四.路径 五.vi编辑器 六.进阶命令 七.NAT配置网络 (第2篇-P34) 八.开发板挂载 Ubuntu 的 NFS 目录 ...
- 嵌入式Linux学习笔记之第一阶段---基础篇
嵌入式Linux学习分五个阶段 第一阶段: 01嵌入式环境搭建初期 02C语言语法概述 03C语言内存操作 04c语言函数 05linux基础 06gun基础 第二阶段: 01-linux之io系统编 ...
- 【转】嵌入式Linux学习笔记
一 嵌入式系统定义: 应用于特定环境的硬件体系. 二 两样非常重要的能力: 1. 掌握各种新概念的能力 2. 调试的能力( 包括软件, 硬件 ) 三 需要的基础知识: 1. 操作系统理论基 ...
随机推荐
- Windows下Java JDK环境变量的配置
注意:前提是你已经在电脑上安装了JDK 1.打开控制面板—系统和安全—系统,选择高级系统设置 2.选择环境变量 3. 然后看看用户变量中有没有JAVA_HOME和CLASSPATH变量 4.新建JAV ...
- 虚拟环境mkvirtualenv
python虚拟环境mkvirtualenv使用 安装virtualenvwrapper pip install virtualenvwrapper 修改默认虚拟环境目录: 环境变量中新建: ...
- 一步一步搭建:spark之Standalone模式+zookeeper之HA机制
理论参考:http://www.cnblogs.com/hseagle/p/3673147.html 基于3台主机搭建:以下仅是操作步骤,原理网上自查 :1. 增加ip和hostname的对应关系,跨 ...
- MFC中png格式图片贴图的实现
MFC中png格式图片贴图的实现(2011-07-14 19:10:29) ___转载自新浪 初学vc,正在做五子棋,五子棋中的图片格式都是bmp格式的,所以贴图用CBitmap可以很简单的实现.刚 ...
- StringRedisTemplate与RedisTemplate区别
StringRedisTemplate与RedisTemplate两者的关系是StringRedisTemplate继承RedisTemplate. 两者的数据是不共通的:也就是说StringRedi ...
- Django-给视图加装饰器
给FBV加装饰器 FBV:function based view FBV本身就是一个函数,所以跟普通函数加装饰器是一样的 # 装饰函数是要在APP文件中定义,本例是在app01\templatetag ...
- Samba的认识
Samba服务 samba是一个网络服务器,用于Linux和Windows之间共享文件. samba端口号 samba (启动时会预设多个端口) 数据传输的TCP端口 139.445 进行NetBIO ...
- vue-teach
编译器的工作过程 http://www.ruanyifeng.com/blog/2014/11/compiler.html DNS 原理入门 http://www.ruanyifeng.com/blo ...
- 多线程--原子操作 Interlocked系列函数
[转]原文地址:http://blog.csdn.net/morewindows/article/details/7429155 线程同步与互斥: 互斥主要指多个线程不能同时访问一个资源,如打印机就是 ...
- PostgreSQL编码格式:客户端服务器、客户端、服务器端相关影响
关于字符编码这块,官网链接: https://www.postgresql.org/docs/current/charset.html 刚刚写了几百字的东西因为断网,导致全没有了,重头再写,我就只想记 ...