首页 > 代码库 > jz2440裸板开发之:外部中断

jz2440裸板开发之:外部中断

实验目的:   利用外部中断的方式,来实现点亮对应的LED

实验原理中断的最大好处就是让CPU避免了采用查询的方式来处理中断处理程序要干的事,中断的三个必要元素:中断源、中断控制器、中断处理函数。在arm9上有七种异常,

(这里把重启也包括在内,另外还有一个reversed,加起来应该有八个,只是这个中断向量地址没有用而已)。当中断发生时,CPU会自动跳到中断向量地址处执行程序,由于每个中断向量都只有4字节的地址空间,所以我们经常在此处放一个跳转语句。上面CPU的自动跳转是硬件自动完成的,我们软件要做的事,就是编写好各个向量地址对应的跳转语句和对应的初始化代码(这里的向量初始化代码通常称为核心初始化),要想弄明白中断的原理,能顺利编写中断处理程序,首先要明白arm9的七种工作模式,和其中模式下对应的寄存器,和了解CPSR、SPSR的位格式,在各个模式下跳转时,各个寄存器的值需要变化,因此需要保存通用寄存器。在中断发生时,CPSR的值将保存到对应跳转模式下的SPSR中,等该模式处理完后,再将 CPSR的值恢复(即将保存到SPSR的值,恢复到CPSR)。还要了解中断处理流程,在这个流程和中,设计到各个中断控制器的寄存器(其实中断控制器就是各个中断寄存器组成的,所以中断控制器应该是相应寄存器的统称)。

提示:中断处理流程图,涉及到中断源是怎么得到CPU的执行权的,涉及到中断控制器的寄存器,根据芯片手册一定弄明白。

中断处理流程图

使用中断的步骤

(1):编写中断处理函数:

a、设置好中断向量表(其中中断向量地址为0x100000018,可以通过查数据手册来找到每个向量表的每个地址);

b、在启动模式下,预先设置好中断模式下和管理模式下的栈

c、在中断向量下,预先保存和恢复被中断程序的运行环境

d、编写中断处理函数,在中断处理函数的结束,要把该中断的标志位清除,涉及到SRCPND、INTPND 以及SUBSRCPND 

 (2):初始化中断源                                     

(3):初始化中断控制器寄存器

其中,

SUBSRCPND (子中断源标志位寄存器) :SUB SOURCE PENDING (SUBSRCPND) REGISTER,在中断处理函数响应后,相应位要清零

INTSUBMSK (子中断屏蔽寄存器)      :INTERRUPT SUB MASK (INTSUBMSK) REGISTER       If the mask bit is 0, the interrupt request can be   serviced.

SRCPND (中断标志寄存器)               :SOURCE PENDING (SRCPND) REGISTER                 在中断处理函数响应后,相应位要清零

INTMSK (中断屏蔽寄存器)                 :INTERRUPT MASK (INTMSK) REGISTER                    If the mask bit is 0, the interrupt request can be serviced.

INTMOD (中断模式寄存器)                :INTERRUPT MODE (INTMOD) REGISTER     If a specific bit is set to 1, the corresponding interrupt is processed in the FIQ (fast                                                                                interrupt) mode. Otherwise, it is processed in the IRQ mode(normal interrupt).

PRIORITY(中断优先级寄存器)          :PRIORITY REGISTER (PRIORITY)               有七个中断仲裁器,6个一级仲裁器和1个3二级仲裁器           其三个位控制一个仲裁器

INTPND (中断标志寄存器)                :INTERRUPT PENDING (INTPND) REGISTER         在中断处理函数响应后,相应位要清零

INTOFFSET(这个寄存器的值保存着不同中断源的值,可以从它分辨中断): INTERRUPT OFFSET (INTOFFSET) REGISTER

The value in the interrupt offset register shows which interrupt request of IRQ mode is in the INTPND register.This bit can be cleared automatically by clearing SRCPND and INTPND.

注意还有: EINTMASK:EINTMASK (External Interrupt Mask Register)  ,这个外部中断屏蔽寄存器

下面是实验的代码: Makefile  s3c24xx.h  start.s   init.c       interrupt.c    main.c   

Makefile:

obj:= start.o init.o interrupt.o  main.o

interrupt.bin :$(obj)
	arm-linux-ld -Ttext 0x00000000 -o  interrupt_elf $^
	arm-linux-objcopy -O binary -S interrupt_elf  $@
	arm-linux-objdump -D -m arm interrupt_elf > int.dis

%.o:%.S
	arm-linux-gcc -Wall -O2 -c -o $@ $<

%.o:%.c
	arm-linux-gcc -Wall -O2 -c -o $@ $<

.PHONY:clean
clean:
	rm -f *.o  *.elf int.dis  *.bin

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)


 start.s:
@********************************************************************************************
@  根据s3c2440芯片手册中的PROGRAMMER‘S MODEL这章可查询到其中中断异常的向量地址
@ 注意:其中有一个是保留未使用的,在本程序中,只使用了irq中断和Reset(复位也算是一种异常)
@	0x00000000 Reset Supervisor
@	0x00000004 Undefined instruction Undefined
@	0x00000008 Software Interrupt Supervisor
@	0x0000000C Abort (prefetch) Abort
@	0x00000010 Abort (data) Abort
@	0x00000014 Reserved Reserved
@	0x00000018 IRQ IRQ
@	0x0000001C FIQ FIQ
@********************************************************************************************

.extern main
.text
.global _start
_start:

	b  Reset

HandleUndef:
	b  HandleUndef

HandleSWI:
	b  HandleSWI

HandlePrefetchAbort:
	b  HandlePrefetchAbort

HandleDataAbort:
	b  HandleDataAbort
	
HandleNotUsed:
	b  HandleNotUsed
	
	b HandleIRQ
	
HandleFIQ:
	b  HandleFIQ
	
	
Reset:
	ldr sp,=4096           @系统启动时,默认是以管理模式启动,这个值在后面也设置了,所以这里可以省略
	bl disable_watch_dog
	
	msr cpsr_c ,#0xd2       @中断模式
	ldr sp, =3072
	
	msr cpsr_c ,#0xd3       @管理模式
        ldr sp,=4096
        
        bl init_led
        bl init_irq
@************************************************************************************************
@N Z C V  * * * * * * ...****      I F T M4 M3 M2 M1 M0
@                                        1  0  1  1  1  Abort
@************************************************************************************************
           msr cpsr_c,#0x53    @开启中断总开关
           ldr lr ,=halt_loop
           ldr pc, =main
halt_loop:
	b  halt_loop
	
	
	
HandleIRQ:
	sub lr ,lr,#4           @这里lr为什么要减4,是因为arm体系结构是这样的,lr保存的值时下一个预取指令的地址
	stmdb sp! ,{r0-r12,lr}   @将管理模式和中断模式下的公用寄存器保存到栈里,“!“:表示sp自动加4	
	ldr lr, =int_return
	ldr pc, =EINT_Handle
		
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 = 0;  // 关闭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 &= ~(1<<11);
        
    /*
     * 设定优先级:
     * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2
     * 仲裁器1、6无需设置
     * 最终:
     * EINT0 > EINT2 > EINT11即K2 > K3 > K4
     */
    PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ;

    // EINT0、EINT2、EINT8_23使能
    INTMSK   &= (~(1<<0)) & (~(1<<2)) & (~(1<<5));
}


interrupt.c:

#include "s3c24xx.h"

void EINT_Handle()
{
    unsigned long oft = INTOFFSET;
    unsigned long val;

    switch( oft )
    {
        // S2被按下
        case 0:
        {
             GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<4);      // LED1点亮
            break;

        }

        // S3被按下
        case 2:
        {
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<5);      // LED2点亮
            break;
        }

        // K4被按下
        case 5:
        {
            GPFDAT |= (0x7<<4);   // 所有LED熄灭
            GPFDAT &= ~(1<<6);      // LED4点亮
            break;


        }

        default:
            break;
    }

    //清中断
    if( oft == 5 )
        EINTPEND = (1<<11);   // EINT8_23合用IRQ5
    SRCPND = 1<<oft;
    INTPND = 1<<oft;
}

main.c:

int main()
{
    while(1);
    return 0;
}

注意:中断分两种,外部中断和定时器中断,这只是对外部中断的分析。

                                            转载本文转载请标明出处。