Tiny6410 按键中断驱动笔记

1. 先查看《Tiny6410SDK-1103 底板原理图》,找到按键部分:

从上图可知,当按键按下时,相当于接地,即低电平,从而产生一个由高电平到低电平的跳变。

Tiny6410的底板有8个按键:

2. 查看《Tiny6410-1170 CPU核心板原理图》,找到EINT0的连接图:

从上图可知:

EINT0 接 GPN0

EINT1 接 GPN1

EINT2 接 GPN2

ENIT3 接 GPN3

EINT4 接 GPN4

EINT5 接 GPN5

EINT19 接 GPL11

EINT20 接 GPL12

知识点:由s3c6410外部触发的中断就是外部中断,由s3c6410内部触发的是内部中断,像watch dog就是内部中断,像key,wm9717触发的就是外部中断。

外部中断从外设到cpu的具体流程:

外设——>GPIO——>VIC——>ARM1176

我们编写关于中断的程序也是这个过程:

配置外设——>配置GPIO——>配置VIC——>配置ARM协处理器等

其实就是要打造一个通路能够让中断的电平变化能顺顺利利的传送到ARM里!

3. 中断设置流程

3.1 GPIO及其配置

外设都是连到GPIO上的。s3c6410具有187个多功能I/O端口,其实有127个用于外部中断。这127个引脚可以分为10个分组:

Group    外部中断

EINT0    GPN0—>GPN15        GPL8—>GPL14         GPM0—>GPM4

EINT1    GPA0—>GPA7         GPB0—>GPB6

EINT2    GPC0—>GPC7

EINT3    GPD0—>GPD5

EINT4    GPF0—>GPF14

EINT5    GPG0—>GPG7

EINT6    GPH0—>GPH9

EINT7    GPO0—>GPO15

EINT8    GPP0—>GPP14

EINT9    GPQ0—>GPQ9

    每个引脚可以对应一个外部中断。那么当外部中断电平变化传GPIO里,除了对应端口的那几个寄存器(CON,PUD,etc)GPIO里又有哪些寄存器会对这个中断信号造成影响呢?如下所示:

EINTXCON :配置触发方式,低电平,高电平,上升沿,下降沿。

EINTXPEND :这个现在用不到,一会儿中断处理程序会用到,这个是pending register.

EINTXMASK :这里可以屏蔽某个外部中断,要通过需要clear一下对应的中断位。默认是全屏蔽的。

EINTXFLTCON :这里设置滤波方式,可以去毛刺。

我们这里要设置的是EINTXCON,EINTXMASK,EINTXFLTCON。这样我们的中断信号就可以顺利通过GPIO了,然后就是VIC 向量中断控制器了。

上面说的这些外部中断在GPIO里分成了九组,具体反应到VIC里他们占用中断号如下:

No

Sources

Description

Group

0

 INT_EINT0

External interrupt 0 ~ 3 

VIC0

1

 INT_EINT1 

External interrupt 4 ~ 11  

VIC0

32

 INT_EINT2  

External interrupt 12 ~ 19     

VIC1

33

 INT_EINT3 

External interrupt 20 ~ 27    

VIC1

53

 INT_EINT4

External interrupt Group 1 ~ Group 9  

VIC1

这里我们使用VIC,当然要先开启VIC,这个是在协处理器里设置的 VE位

mrc p15,0,r0,c1,c0,0

orr r0,r0,#(1<<24)

mcr p15,0,r0,c1,c0,0

主要是通过CP15协处理器特定的寄存器来控制VIC的使能:查看arm6410手册,arm1176JZF-S.pdf 第3-14页如下图所示,


因为op1=0,CRn=c1,CRm=c0,op2=0,跳转到3-44页图所示,查看对应寄存器。


首先,mrc  p15,0,r0,c1,c0,0将协处理器cp15中op1=0,CRn=c1,CRm=c0,

op2=0所对应的寄存器的值传给r0寄存器。

其次,orr  r0,r0,#(1<<24)将上面得到的r0寄存器的值与1左移24位得到的值进行按位或运算,将得到的结果放入r0中。

最后,mcr  p15,0,r0,c1,c0,0将运算后的r0的值重新放回cp15协处理器对应的寄存器当中。

这样我们的VIC就能用了。

然后就是开启总中断:

mrs r0,cpsr
bic r0,r0,#0x80
msr cpsr_c,r0


然后是VIC的设置,几个重要的寄存器:

VICXINTSELECT:选择中断方式FIQ or IRQ。

VICXVECTADDR:设置中断处理程序的地址。

VICXINTENABLE:使能GPIO传过来的中断信号。

其实设置到这里外部中断就能正确运行了,但是还有许多别的寄存器,比如设置什么优先级的。

ARM得知来了个中断,就和VIC进行一系列的握手,得到VICADDRESS,就开始执行我们的中断处理程序了。最后要清除一下EINTXPEND和VICXADDRESS。

4. 程序实现:

inter.s :

.text
.code 32
.global _start
.global asm_handle_k1_irq
.extern interrupt_test
.extern handle_k1_irq

_start:
    @disable watch dog 
    ldr r0, =0x7E004000
    mov r1, #0
    str r1, [r0]    

    @enable vic 
    mrc p15,0,r0,c1,c0,0
    orr r0,r0,#(1<<24)
    mcr p15,0,r0,c1,c0,0

    @enable interrupt
    mrs r0,cpsr
    bic r0,r0,#0x80
    msr cpsr_c,r0

    ldr sp, = 0x0C001000

    @sp_irq mode 
    msr cpsr_cxsf,#0xd2
    ldr sp, = 0x0C001000

    @return back to svc mode
    msr cpsr_cxsf, #0x13

    bl interrupt_test

loop:
    b loop

asm_handle_k1_irq:
    stmfd sp!, {r0-r3,r12,lr}
    ldr lr,=int_return
    bl handle_k1_irq

int_return:
    ldmfd sp!, {r0-r3,r12,lr}
    subs pc,lr,#4

inter_func.c:

#define GPKCON0 *((volatile unsigned int*)0x7F008800)
#define GPKDAT *((volatile unsigned int*)0x7F008808)
#define GPKPUD *((volatile unsigned int*)0x7F00880C)
#define GPNCON *((volatile unsigned int*)0x7F008830)
#define GPNPUD *((volatile unsigned int*)0x7F008838)

#define EINT0CON0 *((volatile unsigned int*)0x7F008900)
#define EINT0MASK *((volatile unsigned int*)0x7F008920)
#define EINT0PEND *((volatile unsigned int*)0x7F008924)
#define EINT0FLTCON0 *((volatile unsigned int*)0x7F00891C)

#define VIC0INTSELECT *((volatile unsigned int*)0x7120000C)
#define VIC0VECTADDR  *((volatile unsigned int*)0×71200100)  
#define VIC0INTENABLE *((volatile unsigned int*)0×71200010)  
#define VIC0INTENCLEAR *((volatile unsigned int*)0×71200014)  
#define VIC0ADDRESS *((volatile unsigned int*)0x71200F00)  
#define VIC1ADDRESS *((volatile unsigned int*)0x71300F00) 

typedef void (isr) (void);
extern void asm_handle_k1_irq();

void led_init()
{
    //init gpkcon
    GPKCON0 &= 0x0000ffff;
    GPKCON0 |= 0x11110000;

    //set output
    //light led1

    GPKDAT = 0xffef;
    //set pull-up register
    //GPKPUD = 0x000aa00;
}

void handle_k1_irq()
{
    //reverse led1
    GPKDAT ^= 0×0010;
    // clear K1 irq  
    EINT0PEND = 1;

    //clear irq
    VIC0ADDRESS = 0;
    VIC1ADDRESS = 0;
}

void key_io_init()
{
    //configure k1 as Ext.Interrupt
    GPNCON &= (~0x03);
    GPNCON |= 0x02;
    GPNPUD &= ~(0x3);

    //configure k1 as falling edge trigged
    EINT0CON0 &= (~0x03);
    EINT0CON0 |= 0x3;

    //EINT0FLTCON0 |= 0x40;
    //Enable EINT0 irq
    EINT0MASK &= (~0x1);

    // Select INT_EINT0 mode as irq  
    VIC0INTSELECT = 0;

    // init the isr addr  
    isr** isr_array = (isr**)(0x71200100);
    isr_array[0] = (isr*)asm_handle_k1_irq;

    //EINT0PEND = 0xffffffff;  
    //VIC0INTENCLEAR = 0xffffffff;

    //enable 
    VIC0INTENABLE |= 0x01;
}

void interrupt_test()
{
    led_init();
    key_io_init();
}

makefile:

CC=arm-linux-gcc
LD=arm-linux-ld
OBJCOPY=arm-linux-objcopy      
CFLAG=-c
LDFLAG=-e _start -Ttext 0x0c000000

inter.bin: inter
    $(OBJCOPY) -O binary $< $@

inter: inter.o inter_func.o
    $(LD) $(LDFLAG) $? -o $@

inter.o:inter.s                
    $(CC) $(CFLAG) $< -o $@    

inter_func.o:inter_func.c
    $(CC) $(CFLAG) $< -o $@

clean:
    rm *.o
    rm inter
    rm inter.bin

5. 问题记录    

(1) 设置GPNCON时,网上的文章写成GPNCON &= (~0×2),实际上应该是GPNCON &= (~0×03),导致中断无反应。

(2) 直接设置VIC向量的地址为C中的中断函数,忘记要在中断模式下进行堆栈的保存与恢复,导致程序只能触发一次中断。

asm_handle_k1_irq:

    stmfd sp!, {r0-r3,r12,lr}

    ldr lr,=int_return

    bl handle_k1_irq @一开始直接作为VIC向量的地址

int_return:

    ldmfd sp!, {r0-r3,r12,lr}

    subs pc,lr,#4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值