s3c2440裸机编程之中断

上篇说了GPIO的裸机编程,实现了按键+亮灯的功能,但对按键是否按下的检测是通过轮询机制实现的,这种方法太过耗费cpu资源,今天使用中断方式来减轻对cpu的消耗。

实现原理

  • 1.其实要实现功能的原理和按键轮询机制是一样的,不同的是使用中断不会让cpu一直去查询按键是否按下,而是在按键按下后通知cpu去处理。因此,在没有按键按下时cpu可以去处理其他事务而不是一直查询按键状态。
  • 2.中断操作
    1>.中断发生前:初始化中断引脚,使能中断。
    2>.中断发生后:保存当前状态(处理完中断事件后cpu需要返回接着处理未完事务)。
    3>.中断处理:分辨中断,中断处理,清除中断状态(否则cpu会认为中断一直在触发)。
    4>.处理完成:恢复保存的断点,继续执行原先事务。
  • 3.中断寄存器设置
    1>.中断控制寄存器。
    2>.使能/禁止寄存器。
    3>.状态寄存器。
    4>.设置高/低电平触发。
    5>.引脚设置。
    6>.中断优先级设置。

工作模式

  • 1.在arm下,每种工作模式都有16个通用寄存器和1到2个程序状态寄存器,发生中断后就进入了中断模式。
  • 2.各工作模式下的寄存器中R0~R15可以直接访问,他们中除了R15外都是通用寄存器,即既可以保存数据也可以保存地址。
  • 3.R3~R15:
    1>.R13:栈指针寄存器(sp),用于保存栈地址
    2>.R14:程序连接寄存器
  • 4.处于某种模式时,sp即为该模式私有sp,即R13寄存器保存的地址。
  • 5.第17个寄存器CPSR:当前程序状态寄存器,其一些位用于表示状态,一些位用于表示当前处于什么模式。如下:
从第30位到第0位,依次为:
N Z C V ... I F T M4 M3 M2 M1 M0
(置位有效)负数标志,0标志,进位标志,溢出标志 ... 总中断IRQ禁止位,FIQ禁止位,状态位(T),M4~M0的组合表示当前模式

源码(省去寄存器地址配置)

  • 1.head.S
    该部分设置异常向量表,初始化各资源。
@初始化文件:初始化中断向量表,设置中断栈,设置中断处理函数
.extern     main
.text 
.global _start 
_start:
@中断向量表
    b   Reset  @复位(按下复位键或上电),0地址
HandleUndef:
    b   HandleUndef  @0x04地址
HandleSWI:
    b   HandleSWI
HandlePrefetchAbort:
    b   HandlePrefetchAbort
HandleDataAbort:
    b   HandleDataAbort
HandleNotUsed:
    b   HandleNotUsed
    b   HandleIRQ   @快中断
HandleFIQ:
    b   HandleFIQ

Reset:
	ldr sp ,=4096  @设置栈指针(跳过前4k)
	bl  disable_watch_dog  @关闭看门狗

	msr cpsr_c, #0xd2  @设置当前程序状态寄存器,I:1(禁止IRQ),F:1(禁止FIQ),M4:1,M0:1(进入FIQ模式)
	ldr sp,=3072  @设置当前模式栈指针
	
	msr cpsr_c, #0xd3  @进入管理模式
	ldr sp, =4096  @可省略(reset后cpu即处于管理模式,reset中已设置过)
	
	bl  init_led  @初始化led
	bl  init_irq  @初始化可中断
	
	msr cpsr_c, #0x53  @开启IRQ,进入管理模式
	
	ldr lr, =halt_loop  @设置返回地址
	ldr pc, =main

halt_loop:
    b   halt_loop
	
HandleIRQ:
	sub lr, lr, #4  @计算返回地址(被中断出)
	stmdb   sp!,    { r0-r12,lr }  @保存现场(寄存器)
	
	ldr lr, =int_return  @设置调用ISR(中断处理服务)即EINT_Handle后的地址
	ldr pc, =EINT_Handle  @调用中断处理函数

int_return:
    ldmia   sp!,    { r0-r12,pc }^  @中断返回,^表示将spsr(程序状态保存寄存器)的值复制到cpsr
  • 2.init.c
    该部分主要实现led及中断的初始化代码。
#define GPF4OUT  (1<<8)
#define GPF5OUT  (1<<10)
#define GPF6OUT  (1<<12)

#define GPF4MASK  (3<<8) //用作清除当前寄存器状态(置为Reserved状态)
#define GPF5MASK  (3<<10)
#define GPF6MASK  (3<<12)

#define GPF0EIN  (0x2)  //GPF0管脚配置为中断模式
#define GPF2EIN  (0x2<<4)  //GPF2管脚配置为中断模式
#define GPG3EIN  (0x2<<6)  //GPG3管脚配置为中断模式

#define GPF0MASK  (0x3<<0)
#define GPF2MASK  (0x3<<4)
#define GPG3MASK  (0x3<<6)

void disable_watch_dog(void)
{
	WTCON = 0x0;
}

void init_led(void)
{
	GPFCON &= ~(GPF4MASK | GPF5MASK | GPF6MASK); //将寄存器对应位清0
	GPFCON |= (GPF4OUT | GPF5OUT | GPF6OUT); //设置led对应寄存器为输出模式
}

void init_irq(void)
{
	/**配置中断0,2,11引脚为中断模式**/
	GPFCON &= ~(GPF0MASK | GPF2MASK);
	GPFCON |= GPF0EIN | GPF2EIN;
	GPGCON &= ~GPG3MASK;
	GPGCON |= GPG3EIN;

	EINTMASK &= ~(1<<11); //使能EINT11;对于EINT8-23需要设置EINTMASK和INTMSK,对于EINT0-EINT3只需设置INTMSK
	PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7);
	INTMSK   &= (~(1<<0)) & (~(1<<2)) & (~(1<<5)); //EINT0,EINT2,EINT11(第5位为8~23共用使能位)
}
  • 3.interrupt.c
    该部分实现了中断处理函数。
void EINT_Handle(void)
{
	unsigned long oft = INTOFFSET;

	switch(INTOFFSET)
	{
		case 0:
			GPFDAT |= (0x7<<4);
            GPFDAT &= ~(1<<4);
            break;
		case 2:
			GPFDAT |= (0x7<<4);
            GPFDAT &= ~(1<<5);
            break;
		case 5:
			GPFDAT |= (0x7<<4);
            GPFDAT &= ~(1<<6);             
            break;

	}
	//清除中断(否则cpu会认为中断又一次发生了),对于EINT8-23需要清除EINTPEND和SRCPND,对于EINT0-EINT3只需清除SRCPND
	if(oft == 5)
		EINTPEND = 0x1<<11;
	SRCPND = 1<<oft;
    INTPND = 1<<oft;
}
  • 3.main.c
    main函数在此只需将程序阻塞,保证不会退出即可。可使用while(1)死循环。
发布了4 篇原创文章 · 获赞 2 · 访问量 197

猜你喜欢

转载自blog.csdn.net/user_jiang/article/details/100067898