S3C2440中断

韦东山老师一期中断课程学习:

总结:

程序启动后工作流程,程序从0地址开始执行Reset  --》 重定位  --》ldr pc,=main [绝对跳转到SDRAM中执行main()函数],main函数中调用各种函数(初始化函数)。

根据S3C2440的Exception Vectors可以知道,  当发生中断时,CPU运行程序跳转到0X18的地方执行指令,该处我们存放中断处理相关内容,CPU运行相应中断内容{保存现场、处理异常(中断)【分辨中断源、调用相应函数】、恢复现场}。

Exception Vectors 如下:

启动文件Start.S程序如下:

.text
.global _start

_start:
    b reset          /* vector 0 : reset */
    ldr pc, und_addr /* vector 4 : und */
    ldr pc, swi_addr /* vector 8 : swi */
    b halt             /* vector 0x0c : prefetch aboot */
    b halt             /* vector 0x10 : data abort */
    b halt             /* vector 0x14 : reserved */
    ldr pc, irq_addr /* vector 0x18 : irq */
    b halt             /* vector 0x1c : fiq */

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

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

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

在分析中断处理函数handle_irq_c()时,我们发现如果中断源过多发生的时候,中断处理函数每次都要重新添加,而且显得不简洁。
正常我们在定义中断处理函数时候代码如下:

void handle_irq_c(void)
{
    /* 分辨中断源 */
    int bit = INTOFFSET;

    /* 调用对应的处理函数 */
    if (bit == 0 || bit == 2 || bit == 5)  /* eint0,2,eint8_23 */
    {
        key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND */
    }
    else if (bit == 10)
    {
        timer_irq();
    }

    /* 清中断 : 从源头开始清 */
    SRCPND = (1<<bit);
    INTPND = (1<<bit);
}

通过学习可以知道一种思想:定义一个函数指针数组,将所有中断函数存在一个数组中,在写某个初始化中断函数的时候,我们将相应的中断函数存到我们定义的函数指针数组中,然后当处理中断函数工作时,即运行数组中相应的中断函数。

以按键中断点亮LED及定时器中断循环点亮LED为例:

相应代码如下:

[interrupt.c]

#include "s3c2440_soc.h"

typedef void(*irq_func)(int);
irq_func irq_array[32];

/* SRCPND 用来显示哪个中断产生了, 需要清除对应位
 * bit0-eint0
 * bit2-eint2
 * bit5-eint8_23
 */

/* INTMSK 用来屏蔽中断, 1-masked
 * bit0-eint0
 * bit2-eint2
 * bit5-eint8_23
 */

/* INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位
 * bit0-eint0
 * bit2-eint2
 * bit5-eint8_23
 */

/* INTOFFSET : 用来显示INTPND中哪一位被设置为1
 */

#if 0
/* 初始化中断控制器 */
void interrupt_init(void)
{
    INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
    INTMSK &= ~(1<<10);  /* enable timer0 int */
}#endif

/* 读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
        {
            /* 按下 */
            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;
}

void handle_irq_c(void)
{
    /* 分辨中断源 */
    int bit = INTOFFSET;

    /* 调用对应的处理函数 */
    irq_array[bit](bit);

    /* 清中断 : 从源头开始清 */
    SRCPND = (1<<bit);
    INTPND = (1<<bit);
}

void register_irq(int irq, irq_func fp)
{
    irq_array[irq] = fp;

    INTMSK &= ~(1<<irq);
}

/* 初始化按键, 设为中断源 */
void key_eint_init(void)
{
    /* 配置GPIO为中断引脚 */
    GPFCON &= ~((3<<0) | (3<<4));
    GPFCON |= ((2<<0) | (2<<4));   /* S2,S3被配置为中断引脚 */

    GPGCON &= ~((3<<6) | (3<<22));
    GPGCON |= ((2<<6) | (2<<22));   /* S4,S5被配置为中断引脚 */

    /* 设置中断触发方式: 双边沿触发 */
    EXTINT0 |= (7<<0) | (7<<8);     /* S2,S3 */
    EXTINT1 |= (7<<12);             /* S4 */
    EXTINT2 |= (7<<12);             /* S5 */

    /* 设置EINTMASK使能eint11,19 */
    EINTMASK &= ~((1<<11) | (1<<19));

    register_irq(0, key_eint_irq);
    register_irq(2, key_eint_irq);
    register_irq(5, key_eint_irq);
}

[timer.c]

#include "s3c2440_soc.h"

void timer_irq(void)
{
    /* 点灯计数 */
    static int cnt = 0;
    int tmp;

    cnt++;

    tmp = ~cnt;
    tmp &= 7;
    GPFDAT &= ~(7<<4);
    GPFDAT |= (tmp<<4);
}

void timer_init(void)
{
    /* 设置TIMER0的时钟 */
    /* Timer clk = PCLK / {prescaler value+1} / {divider value}
                 = 50000000/(99+1)/16
                 = 31250
     */
    TCFG0 = 99;  /* Prescaler 0 = 99, 用于timer0,1 */
    TCFG1 &= ~0xf;
    TCFG1 |= 3;  /* MUX0 : 1/16 */

    /* 设置TIMER0的初值 */
    TCNTB0 = 15625;  /* 0.5s中断一次 */

    /* 加载初值, 启动timer0 */
    TCON |= (1<<1);   /* Update from TCNTB0 & TCMPB0 */

    /* 设置为自动加载并启动 */
    TCON &= ~(1<<1);
    TCON |= (1<<0) | (1<<3);  /* bit0: start, bit3: auto reload */

    /* 设置中断 */
    register_irq(10, timer_irq);
}

原文地址:https://www.cnblogs.com/cxl-93/p/11052425.html

时间: 2024-10-07 04:51:07

S3C2440中断的相关文章

S3C2440 中断

本文转自http://blog.csdn.net/mr_raptor/article/details/6556186 http://blog.csdn.net/zhaocj/article/details/5309938   感谢两位老师 CPU和外设构成了计算机系统,CPU和外设之间通过总线进行连接,用于数据通信和控制,CPU管理监视计算机系统中所有硬件,通常以两种方式来对硬件进行管理监视: 查询方式:CPU不停的去查询每一个硬件的当前状态,根据硬件的状态决定处理与否.好比是工厂里的检查员,不

LCD实验学习笔记(八):中断控制器

s3c2440有60个中断源(其中15个为子中断源). 程序状态寄存器(PSR)的F位设为1,禁用快速中断(FRQ). 程序状态寄存器(PSR)的I位设为1,禁用普通中断(IRQ). 相反,PSR寄存器F位为0开FRQ中断,I位为0开IRQ中断. s3c2440中断控制器中有五个控制寄存器:中断源等待寄存器(SRCPND),中断模式寄存器(INTMOD),屏蔽寄存器(INTMSK),优先级寄存器(PRIORITY),中断等待寄存器(INTPND). SRCPND寄存器各位对应不同的中断,其中外部

中断控制器

一.ARM中断体系结构 1.7种工作模式 不同工作模式下,对应不一样的寄存器 2.异常 中断属于一种异常,按键.串口等中断 异常向量入口 二.S3C2440中断体系结构 1.中断处理过程 2.相关寄存器 三.源码分析

(6)s3c2440用I2C接口访问EEPROM

在前面阅读理解了I2C的官方协议文档后,就拿s3c2440和EEPROM来验证一下. 本来是想用s3c2440的SDA和SCL管脚复用为GPIO来模拟的,但在没有示波器的情况下搞了一周,怎么都出不来,最后还是放弃了.甚至参考了linux下i2c-algo-bit.c和i2c-gpio.c,依然没调出来.如果有示波器,可能很快就能找到原因,现在完全不知道问题出在哪里.其实想用GPIO模拟I2C的目的很简单,以一种简单而又深刻的方式来理解I2C. 既然这条路暂时没法走,退而求其次,用s3c2440的

JZ2440开发板之中断体系

ARM架构的CPU有7种工作模式: 1. 用户模式--usr 2. 管理模式--svc 3. 系统模式--sys 4. 快中断模式--fiq 5. 中断模式--irq 6. 数据访问终止模式--abt 7. 未定义指令终止模式--und ARM架构的CPU有2种工作状态: 1. ARM状态--处理器执行32位的字对齐的ARM指令 2. Thumb状态--处理器执行16位的半字对齐的Thumb指令 ARM架构的CPU的寄存器: 1. ARM有31个通用的32位寄存器和6个状态寄存器 2. 每种AR

五、中断控制器

5.1 中断体系 ARM 体系的CPU 有 7 种工作模式 用户模式(usr):ARM 处理器正常的程序执行状态 快速中断模式(fiq):用于高速数据传输或通道处理 中断模式(irq):用户通用的中断处理 管理模式(svc):操作系统使用的保护模式 数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护 系统模式(sys):运行具有特权的操作系统任务 未定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真 可通过软件来进行模

s3c2440外部中断

上文用到了pwm控制蜂鸣器,实现了蜂鸣器频率的改变,有一点不好就是蜂鸣器一直响,可以用一个交互的方式来关闭蜂鸣器.这里就选择按键,开发板上一共4个按键,对应这4个外部中断EINT0,EINT1,EINT2和EINT4(为什么不是EINT0~3呢,让人蛋疼) 要使用外部中断,首先要初始化外部中断.外部中断跟外部引脚F和G复用.要把相应的引脚配置为中断模式: 配置完引脚后,还需要配置具体的中断功能.我们要打开某一中断的屏蔽,这样才能响应该中断,相对应的寄存器为INTMSK  有些位对应着多个外部中断

S3C2440的中断

S3C2440一共有60个中断源,其中有15个子中断源,它们与SUBSRCPND寄存器中的每一位相对应,其他45个中断源与SRCPND中的每一位相对应.要注意的是EINT4~7对应的是同一位SRCPND[4],而EINT8~23对应的也是SRCPND[5]一位. 1 S3C2440的中断寄存器 中断分两大类:外部中断和内部中断. 1.1 外部中断寄存器 24个外部中断占用GPF0-GPF7(EINT0-EINT7),GPG0-GPG15(EINT8-EINT23).用这些脚做中断输入,则必须配置

S3c2440的中断体系结构

概述 S3C2440A中的中断控制器接受来自60个中断源的请求.提供这些中断源的可以是内部外设,如DMA控制器.UART.IIC等等.在这些中断源中,UARTn.AC97和EINTn中断对于中断控制器而言是"或"关系(在这几个源中还可以有中断分支). 当从内部外设和外部中断请求引脚收到多个中断请求时,中断控制器在仲裁步骤后请求ARM920T内核的FIQ或IRQ. 总流程图如下: 程序状态寄存器(PSR)的 F 位和 I 位 如果 ARM920T CPU 中的 PSR 的 F 位被置位为