S3c2440ARM异常与中断体系详解7---按键中断程序示例完善

发布者:码字奇才最新更新时间:2021-09-13 来源: eefocus关键字:S3c2440  ARM异常  中断体系  中断程序 手机看文章 扫描二维码
随时随地手机看文章

首先main.c中

我们初始化中断控制器

初始化中断源


假设按键按键就会产生中断,CPU就会跳到start.S 执行


.text

.global _start


_start:

b reset  /*vector 0 一上电复位,是从0地址开始执行,跳到reset*/

ldr pc, und_addr /*vector 4 如果发生未定义指令异常,硬件就会在自动跳转0x04地址未定义指令异常处,执行do_und*/

    ldr pc,swi_addr /* vector 8 : swi */

在这里插入图片描述

IRQ模式的话跳到0x00000018地方


.text

.global _start


_start:

b reset  /*vector 0 一上电复位,是从0地址开始执行,跳到reset*/

ldr pc, und_addr /*vector 4 如果发生未定义指令异常,硬件就会在自动跳转0x04地址未定义指令异常处,执行do_und*/


/*假设一上电从0地址开始执行,reset,做一系列初始化之后

     *故意加入一条未定义指令

     */

    /*1 添加swi指令*/ 

    ldr pc,swi_addr /* vector 8 : swi */

    b halt          /* vector c : abort */

    b halt          /* vector 10: abort */

    b halt          /* vector 14: reserve */

    ldr pc,irq_addr /* vector 18: IRQ */

irq_addr:

.word do_irq 

do_irq:


/*1、保护现场 2、处理异常 3、恢复现场*/


/*   1、保护现场(硬件自动完成)

*

* 执行到这里之前:

* 1. lr_irq保存有被中断模式中的下一条即将执行的指令的地址

* 2. SPSR_irq保存有被中断模式的CPSR

* 3. CPSR中的M4-M0被设置为10011, 进入到svc模式

* 4. 跳到0x8的地方执行程序 

*/


/*需要从新设置sp栈,指向某一块没有使用的地址*/

/* sp_svc未设置, 先设置它 */


ldr sp,=0x33d00000 /*指向SDRAM的一块内存上*/


/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */

/* 发生异常时,当前被中断的地址会保存在lr寄存器中 先减后存*/

/* lr是异常处理完后的返回地址, 也要保存 */


sub lr,lr,#4           /* IRQ处理完后的返回地址 -4 由手侧可知 */

stmdb sp!,{r0-r12,lr}  /* 先减后存 */


/* 2、处理irq异常(一般是中断处理函数) */


bl handle_irq_c


/*    3、恢复现场    */

ldmia sp!,{r0-r12,pc}^ /* ^ 会把spsr_svc的值恢复到cpsr里 */  

在这里插入图片描述

中断处理函数的书写:

首先,读EINTPEND分辨哪个EINT产生(eint4~23)清除中断时, 写EINTPEND的相应位

在这里插入图片描述

/* 3、中断处理函数 */

void handle_irq_c(void)

{

/*1 分辨中断源 */

    /*读INTOFFSET在芯片手册里找到这个寄存器,它里面的值表示INTPND中哪一位被设置成1*/

    int bit = INTOFFSET;


    /*2 调用对应的处理函数 */

    if (bit == 0 || bit == 2 || bit == 5)  /* 对应eint0,2,eint8_23 */

    {

        /*我们会调用一个按键处理函数*/

        key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND */

    }


    /*3 清中断 : 从源头开始清

     *先清除掉中断源里面的某些寄存器

     *再清 SRCPND

     *再清 INTPND

     */

    SRCPND = (1<    INTPND = (1<

}

void key_eint_irq(int irq)

{

    unsigned int val = EINTPEND;

    unsigned int val1 = GPFDAT;

    unsigned int val2 = GPGDAT;


    if (irq == 0) /* eint0 : s2 控制 D12 */

    {

        if (val1 & (1<<0)) /* s2 --> gpf6 */

        {

            /* 松开 */

            GPFDAT |= (1<<6);

        }

        else

        {

            /* 按下 */

            GPFDAT &= ~(1<<6);

        }


    }

    else if (irq == 2) /* eint2 : s3 控制 D11 */

    {

        if (val1 & (1<<2)) /* s3 --> gpf5 */

        {

            /* 松开 */

            GPFDAT |= (1<<5);

        }

        else

        {

            /* 按下 */

            GPFDAT &= ~(1<<5);

        }


    }

    else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */

    {

        if (val & (1<<11)) /* eint11 */

        {

            if (val2 & (1<<3)) /* s4 --> gpf4 */

            {

                /* 松开 */

                GPFDAT |= (1<<4);

            }

            else

            {

                /* 按下 */

                GPFDAT &= ~(1<<4);

            }

        }

        else if (val & (1<<19)) /* eint19 */

        {

            if (val2 & (1<<11))

            {

                /* 松开 */

                /* 熄灭所有LED */

                GPFDAT |= ((1<<4) | (1<<5) | (1<<6));

            }

            else

            {

                /* 按下: 点亮所有LED */

                GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));

            }

        }

    }


    EINTPEND = val;

}


上传代码编译执行

烧写看是否可以使用

回顾中断处理流程


我们start.s

一上电从

_start:

运行

做一些初始化工作


reset:

    /* 关闭看门狗 */

    ldr r0, =0x53000000

    ldr r1, =0

    str r1, [r0]


    /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */

    /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */

    ldr r0, =0x4C000000

    ldr r1, =0xFFFFFFFF

    str r1, [r0]


    /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */

    ldr r0, =0x4C000014

    ldr r1, =0x5

    str r1, [r0]


    /* 设置CPU工作于异步模式 */

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

    orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA

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


    /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) 

     *  m = MDIV+8 = 92+8=100

     *  p = PDIV+2 = 1+2 = 3

     *  s = SDIV = 1

     *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M

     */

    ldr r0, =0x4C000004

    ldr r1, =(92<<12)|(1<<4)|(1<<0)

    str r1, [r0]


    /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定

     * 然后CPU工作于新的频率FCLK

     */


    /* 设置内存: sp 栈 */

    /* 分辨是nor/nand启动

     * 写0到0地址, 再读出来

     * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动

     * 否则就是nor启动

     */

    mov r1, #0

    ldr r0, [r1] /* 读出原来的值备份 */

    str r1, [r1] /* 0->[0] */ 

    ldr r2, [r1] /* r2=[0] */

    cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */

    ldr sp, =0x40000000+4096 /* 先假设是nor启动 */

    moveq sp, #4096  /* nand启动 */

    streq r0, [r1]   /* 恢复原来的值 */


    bl sdram_init

    //bl sdram_init2     /* 用到有初始值的数组, 不是位置无关码 */


    /* 重定位text, rodata, data段整个程序 */

    bl copy2sdram


    /* 清除BSS段 */

    bl clean_bss


    /* 复位之后, cpu处于svc模式

     * 现在, 切换到usr模式

     */

    mrs r0, cpsr         /* 读出cpsr */

    bic r0, r0, #0xf     /* 修改M4-M0为0b10000, 进入usr模式 */

    bic r0, r0, #(1<<7)  /* 清除I位, 使能中断 */

    msr cpsr, r0


    /* 设置 sp_usr */

    ldr sp, =0x33f00000


    ldr pc, =sdram

sdram:

    bl uart0_init


    bl print1

    /* 故意加入一条未定义指令 */

und_code:

    .word 0xdeadc0de  /* 未定义指令 */

    bl print2


    swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */


    //bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */

    ldr pc, =main  /* 绝对跳转, 跳到SDRAM */


halt:

    b halt


//让后设置CPSR开中断

//让后调到mian函数,做一些中断初始化


int main(void)

{

    led_init();

    interrupt_init();  /* 初始化中断控制器 */

    key_eint_init();   /* 初始化按键, 设为中断源 */


    puts("nrg_A = ");

    printHex(g_A);

    puts("nr");

/*让后在main函数里一直循环输出串口*/

    while (1)

    {


        putchar(g_Char);

        g_Char++;


        putchar(g_Char3);

        g_Char3++;

        delay(1000000);

    }


//这个时候按下按键就会产生中断,让后进入start.s

//跳到0x18 irq模式


ldr pc, irq_addr /* vector 0x18 : irq */


它是一条读内存的执行,从这里读地址赋给pc


irq_addr:

    .word do_irq


就跳到sdram执行do_irq函数

do_irq:

    /* 执行到这里之前:

     * 1. lr_irq保存有被中断模式中的下一条即将执行的指令的地址

     * 2. SPSR_irq保存有被中断模式的CPSR

     * 3. CPSR中的M4-M0被设置为10010, 进入到irq模式

     * 4. 跳到0x18的地方执行程序 

     */


    /* sp_irq未设置, 先设置它 */

    ldr sp, =0x33d00000


    /* 保存现场 */

    /* 在irq异常处理函数中有可能会修改r0-r12, 所以先保存 */

    /* lr-4是异常处理完后的返回地址, 也要保存 */

    sub lr, lr, #4

    stmdb sp!, {r0-r12, lr}  


    /* 处理irq异常 */

    bl handle_irq_c


    /* 恢复现场 */


它怎么处理

/* 读EINTPEND分辨率哪个EINT产生(eint4~23)

 * 清除中断时, 写EINTPEND的相应位

 */


void key_eint_irq(int irq)

{

    unsigned int val = EINTPEND;

    unsigned int val1 = GPFDAT;

    unsigned int val2 = GPGDAT;


    if (irq == 0) /* eint0 : s2 控制 D12 */

    {

        if (val1 & (1<<0)) /* s2 --> gpf6 */

        {

            /* 松开 */

            GPFDAT |= (1<<6);

        }

        else

        {

[1] [2]
关键字:S3c2440  ARM异常  中断体系  中断程序 引用地址:S3c2440ARM异常与中断体系详解7---按键中断程序示例完善

上一篇:S3c2440ARM异常与中断体系详解3---Thumb指令集程序示例
下一篇:S3c2440代码重定位详解1---段的概念重定位的引入

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved