这个文档时在看了别人的总结和datasheet 之后自己总结的 自己尽量将每一个细节全部讲到并尽量将文档写的有条理 简单易懂
一、中断:
这里简单的说一下什么是中断,就是cpu在运行的时候,突然有突发事件需要处理,这个时候就需要事件来触发通知cpu来首先处理这个突发事件,这种机制就叫做中断。基本上所有的cpu都支持中断机制,只是支持的中断源有多有少而已。6410总共有64个中断源。
在嵌入式软件里除了中断的初始化,就是中断处理函数主要工作就是编写ISR.(Interrupte Service Routing)
Exception(异常),计算机体系结构中,异常或者中断是处理系统中突发事件的一种机制,几乎所有的处理器都提供这种机制。异常主要是从处理器被动接受的角度出发的一种描述,指意外操作引起的异常。而中断则带有向处理器主动申请的意味。但这两种情况具有一定的共性,都是请求处理器打断正常的程序执行流程,进入特定程序的一种机制。
从结构来看,外部设备产生的中断可以看成一种特殊的异常.除了中断之外,还有ARM不少固定的异常.包括以下七种:
1. 复位(Reset)
当按下RESET键后,会产生一个复位异常,此时程序跳转到复位异常处理程序处执行.当CPU重启后.一般刚好跳到这个复位异常来
2. 未定义指令
当ARM的处理器或协处理器遇到不能处理的指令时,产生未定义指令异常,采用这个机制,可以通过软件仿真扩展ARM或Thumb 指令集.
3. 软件中断(SWI)
硬件中断是有固定的硬件产生的中断,而软中断是指没具体的硬件产生,是CPU虚拟出来的.该异常由程序执行汇编SWI产生.软中断的优点,
l 可用于用户模式下的程序调用特权操作指令.Linux 系统调用就是使用这个异常来实现的.
l 有利用程序结构的优化.比如在Linux 驱动里,硬件中断不能长时间运行.但是很多软件的长时间操作依赖的于中断的调用.有时为解决这个冲突,会在驱动设计两级,从硬件存取用硬件中断,而长时间操作用软件中断的模拟.
4. 指令预取中止
若处理预取的指令的地址不存在,或该地址不允许当前指令访问,存储器会向处理器发出中止信号,但当预取的指令被执行时,才会产生指令预取中止的异常.比如用ADS把程序下载到开发板上0x8000地址上.就会产生Abort异常.
5. 数据中止
若处理器数据访问数据的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常
6. IRQ
当外部设备在外部中断脚产生中断信号时.即触发了IRQ中断.这是外部设备使用最常用的一种手段.
在S3C24X0是一个集成的SOC,内部除了ARM模块以外.还其它内部集成的模块,如USB,RTC,等.这一些模块在CPU内部也会有相应的中断线连到ARM920T的内核上.但这一些管脚在CPU外部是不可见的,只能用于寄存器去控制.
还一些GPIO脚就充当外部中断控制线,外部IC可以把自己中断信号线连到相应的中断脚上.当外部产中断信号后,CPU就可以知道,外设有中断发来.
7. FIQ
快速中断,类似于IRQ,但是具有较快响应速度.而且设为FIQ的条件也比较严格,比如一次触发只能有一个FIQ
一句话,中断(IRQ,FIQ,SWI)是异常中的一个特例。当产生外部中断时,大部分CPU会只产生一个异常。在异常处理程序里软件再去读不同的中断寄存器分析后来调用ISR。这里ISR是由软件来执行的。象S3C2440就是这样机制。
在S3C6410中,还可以采用简化的中断处理流程。由CPU直接去调用中断的ISR来处理。 这样中断处理软件的编写难度就大大下降了。
向量(vector)
异常处理函数或中断处理函数的地址都会按中断号的顺离顺序排列在一个连续的内存当中,从C语言的角度来看,可以看成是一个指针数组。数组又称为向量(Vector).
分别介绍每一个步骤
1、设置中断工作在VIC模式,这些代码已经有三星公司提供 ---具体操作那个寄存器 我没有从datasheet中找到,从代码中应该是p15--但是没有找到这个寄存器(有找到的留个言)
介绍一下VIC中断模式,自己下个datasheet看看,VIC模式将64个中断源分成了两个组
第一组:0-31中断源
第二组:32-64中断源
上图:
2、设置中断模式 三星在汇编中也提供了这部分代码
使能IRQ中断模式是操作的cpsr寄存器,cpu可以使用IRQ中断,这个是一个总开关,但是 在每一个中断的设置还需设置
3、中断的初始化:
初始化索要做的操作是:
disable 两个组中的所有中断源,
初始化矢量地址寄存器中的值----矢量地址寄存器中存放的值是中断处理函数的地址
设置你所使用的中断的工作模式————IRQ模式或者FIQ模式
****************操作的寄存器****************
矢量地址寄存器------用来存放中断处理函数地址的
共有64个字来存放 64个中断所触发的中断函数的地址
*******设置你自己使用的中断源的工作模式IRQ还是FIQ
使用这个寄存器就能设置你使用的中断源所工作的中断模式
4、设置某个管脚来触发中断
这个要去看datasheet中GPIO章节的内容,设置管脚工作在特殊模式,并且一定要将上啦电阻和下拉电阻disable了
因为可能使用低电平触发 ,这些寄存器就在GPIO对应管脚的设置寄存器中这里不贴图了
5、设置中断触发方式:跳变沿 还是 低电平触发
这个控制寄存器也是在GPIO章节的后面
还有个是一样的自己看ENT0CON1
6、使能使用的中断源
7、解除中断对应的屏蔽
8、清中断标志位 记得在中断程序中清中断标志位
还要在 初始化的时候清中断标志位
贴代码************6410按键控制led中断
********************
**********************
初始化代码:::
void INTC_Init(void)
{
#if (VIC_MODE==0)
u32 i;
for(i=0;i<32;i++)
Outp32(rVIC0VECTADDR+4*i, i);//初始化 矢量地址寄存器
for(i=0;i<32;i++)
Outp32(rVIC1VECTADDR+4*i, i+32);//初始化 矢量地址寄存器
#endif
Outp32(rVIC0INTENCLEAR, 0xffffffff);//disable 所有的中断
Outp32(rVIC1INTENCLEAR, 0xffffffff);
Outp32(rVIC0INTSELECT, 0x0);//自己使用的中断模式选择 为IRQ
Outp32(rVIC1INTSELECT, 0x0);
INTC_ClearVectAddr();//清空自己要使用公的 矢量地址寄存器
return;
}
//
// Function Name : INTC_ClearVectAddr
// Function Description : This function clears the vector address register
// Input : NONE
// Output : NONE
// Version :
void INTC_ClearVectAddr(void)
{
Outp32(rVIC0ADDR, 0);
Outp32(rVIC1ADDR, 0);
return;
}
/**************主代码部分*********************/
/**************************************************************
*6410 按键中断服务程序
* 6410中断原理
* 功能描述 按键点亮led
*
*tiny 6410 按键资源如下
* KEYINT1 -- XEINT0 --- GPN0
* KEYINT2 -- XEINT1 --- GPN1
* KEYINT3 -- XEINT2 --- GPN2
* KEYINT4 -- XEINT3 --- GPN3
* KEYINT5 -- XEINT4 --- GPN4
* KEYINT6 -- XEINT5 --- GPN5
* KEYINT7 -- XEINT19 --- GPL11
* KEYINT8 -- XEINT20 --- GPL12
*
*AUTHOR kong-hua-sheng
**************************************************************/
#include"gpio.h"
#include"def.h"
#include"system.h"
#include"intc.h"
//给制定地址赋整数值
#define Outp32(addr,data) (*(volatile u32 *)(addr) = (data))//这个就是指针的操作
//读取 制定地址中存储的值
#define Inp32(addr) (*(volatile u32 *)(addr))
//GPIO
#define GPIO_BASE (0x7F008000)//GPIO的起始地址
//oGPIO_REGS结构体类型在gpio.h中定义
#define GPIO ((volatile oGPIO_REGS *)GPIO_BASE)//这样就可以随意的操作其中的寄存器了
//设置led的点亮和熄灭
#define LED_ON ~(1<<4)
#define LED_OFF (1<<4)
#define LED_ALL_OFF (0xf<<4)//熄灭所有的led灯
void delay(int ms);
void LedPortInit(void);
void KeyIntPortInit(u32 uKey,u32 uType);
void EintClrPend(u32 uEint_no);
void EintDisMask(u32 uEINT_no);
void KeyEintInit(void);
void __irq Isr_Eint1(void);
void __irq Isr_Eint6(void);
int main()
{
SYSTEM_EnableVIC();//初始化矢量中断寄存器
SYSTEM_EnableIRQ();//使能中断
INTC_Init();//中断初始化
LedPortInit();
KeyEintInit();
while(1);
}
//延时函数
void delay(int ms)
{
int i;
for(;ms>0;ms--)
for(i=1000;i>0;i--);
}
//led---gpio设置为输出 ---注意管脚的工作模式可以设置为输入 输出 和 中断
void LedPortInit(void)
{
u32 uConValue;
uConValue = GPIO->rGPIOKCON0;//注意每个管脚的设置由4位构成
uConValue &= ~(0xffff<<16);//要想 设置最高的16位为1111,
//首先要做的是将这16位全部设置为0000xxxx-----然后再和11110000相或
uConValue |= (0x1111<<16);//和11110000相或
GPIO->rGPIOKCON0 = uConValue;//led管脚的输出模式设置完毕 工作模式为输出
GPIO->rGPIOKDAT |= LED_ALL_OFF;
}
//设置按键对应的管脚工作模式为 中断模式 还要设置 工作的电平 是低电平还是下降沿 上升沿
void KeyIntPortInit(u32 uKey,u32 uType)
{
u32 uConValue;
if(uKey==0||uKey>6)
return;
//设置按键管脚为中断工作模式
uConValue = GPIO->rGPIONCON;//设置io管脚工作的模式为 特殊功能模式(中断模式)
uConValue &= ~(0x3<<((uKey-1)<<1));//记住 要设置某位为1 则先将这位设为0 在和1或 即可设置为1
uConValue |=(0x2<<((uKey-1)<<1));//由于控制管脚工作方式的是 两位 切10为特殊工作模式
GPIO->rGPIONCON = uConValue;
//因为管脚工作在中断模式 所以必须把上啦下拉电阻给屏蔽掉
uConValue = GPIO->rGPIONPUD;
uConValue &= ~(0x3<<((uKey-1)<<1));
GPIO->rGPIONPUD = uConValue;
//设置对应中断的工作类型 是上升沿 还是下降沿触发中断
uConValue = GPIO->rEINT0CON0;
uConValue &= ~(0x7<<(((uKey-1)/2)<<2));
uConValue |= uType<<(((uKey-1)/2)<<2);
GPIO->rEINT0CON0 = uConValue;
}
//清除中断悬起位
void EintClrPend(u32 uEint_no)
{
GPIO->rEINT0PEND= 1<<uEint_no;
}
//使能中断屏蔽位 0使能中断 1屏蔽
void EintDisMask(u32 uEINT_no)
{
u32 uConValue;
uConValue = GPIO->rEINT0MASK;//中断屏蔽寄存器
uConValue &= ~(0x1<<uEINT_no);//使能 中断
GPIO->rEINT0MASK = uConValue;
}
//
void KeyEintInit(void)
{
u32 uConValue;
//初始化 各Key对应的端口
KeyIntPortInit(1,Falling_Edge);
KeyIntPortInit(6,Rising_Edge);
//清楚中断悬起位
EintClrPend(0);
EintClrPend(5);
//向rVIC0VECTADDR中写入对应的中断服务程序的地址 写入两次 是有两个中断服务子程序
Outp32(rVIC0VECTADDR,(unsigned)Isr_Eint1);
Outp32(rVIC0VECTADDR+4,(unsigned)Isr_Eint6);
//使能中断源
uConValue = Inp32(rVIC0INTENABLE);
uConValue |=(1<<NUM_EINT0)|(1<<NUM_EINT1);
Outp32(rVIC0INTENABLE,uConValue);
//解除中断屏蔽
EintDisMask(0);
EintDisMask(5);
}
void __irq Isr_Eint1(void)
{
//进入的第一件事就是清楚中断标志位
// EintClrPend(0);
GPIO->rGPIOKDAT |= LED_ALL_OFF;
GPIO->rGPIOKDAT &=LED_ON;
//清除rVIC0ADDR,该寄存器按位记录了那个VIC0中断源发生了中断
Outp32(rVIC0ADDR,0);
}
void __irq Isr_Eint6(void)
{
//进入的第一件事就是清楚中断标志位
// EintClrPend(5);
GPIO->rGPIOKDAT |= LED_ALL_OFF;
GPIO->rGPIOKDAT &=LED_OFF;
Outp32(rVIC0ADDR,0);
}
代码传到CSDN资源中去地址: