学习uboot前奏之hardware-IRQ[s3c2440]

来源:互联网 发布:淘宝美食店铺吐血推荐 编辑:程序博客网 时间:2024/06/06 08:12

中断体系结构

ARM体系结构的CPU有7种工作模式

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

以上各个模式可以通过软件切换或者发生各类中断、异常时CPU自动进入相应的模式。
除用户模式外,其他6种模式都属于特权模式。大多数程序运行于用户模式,进入特权
模式是为了处理中断、异常,或者访问被保护的系统资源。

另外,ARM体系的CPU有一下两种工作状态

  • ARM状态:此时处理器执行32位的字对齐的ARM指令
  • Thumb状态:此时处理器执行16位的、半字对齐的Thumb指令

CPU一上电就处于ARM状态。

S3C2440的ARM920T有31个通用的32位寄存器和6个程序状态寄存器,这些寄存器分为7组,进入相应的模式就使用相应的寄存器器组,也就是说有些寄存器在不同的工作模式下有不同的副本。分组情况如下:
这里写图片描述

图中R0~R15可以直接访问,这些寄存器除了R15外都是通用寄存器。另外,R13-R15的情况比较特殊。R13又被称为栈指针寄存器,通常用于保存栈指针。R14又被称为程序连接寄存器或者连接寄存器,当执行BL子程序调用指令时,R14中得到R15(程序计数器PC)的备份。而当发生中断或者异常的时候,对应的R14_svc、R14_irq、R14_fiq、R14_und中保存R15返回值。

每种工作模式处理R0~R15共16个寄存器外,还有第17个寄存器CPSR,即“当前程序状态寄存器”(Current Program Status Register)。CPSR中一些位被用于标识各种状态,一些位用于标识当前处于什么工作模式。
这里写图片描述

CPSR中各位的意义:
T位:置位时,CPU处于Thumb状态,否则处于ARM状态
中断禁止位:I位和F位属于中断禁止位,它们被置位时,IRQ中断、FIQ中断分别被禁止。
工作模式位:表明当前处于什么工作模式,可以编写这些位使CPU进入指定的工作模式。

除CPSR外,还有快速中断模式、中断模式、管理模式、数据访问终止模式和未定义指令中止模式等5中工作模式和一个寄存器—-SPSR,即程序状态保存寄存器“Saved Process Status Register”。当切换进入这些工作模式时,在SPSR中保存前一个工作模式的CPSR值,这样,当返回到前一个工作模式时,可以将SPSR的值恢复到CPSR中。

综上所述,当一个异常发生时,将切换进入相应的异常模式,这是ARM920T CPU核自动完成下面的事情。

  1. 在异常工作模式的连接寄存器R14中保存前一个工作模式的下一条,即将执行的指令的地址,对于ARM状态,这个值是当前PC值加4或者加8
  2. 将CPSR的值复制到异常模式的SPSR.
  3. 将CPSR的工作模式位设为这个异常对应的工作模式。
  4. 令PC的值等于这个异常模式在异常向量的地址,即跳转去执行异常向量表中的相应指令。

相反地,从异常工作模式退出回到之前的工作模式,需要通过软件完成如下事情。

  1. 前面进入异常工作模式时,连接寄存器中保存了前一个工作模式的一个指令地址,将它减去一个适当的值(参考下表)后赋值给PC寄存器
  2. 将SPSR的值复制会CPSR

这里写图片描述

CPU运行过程中,通过中断的方式知道外设发生的某些不可预期的事情:当某事件发生时,硬件会设置某个寄存器;CPU在每执行一个指令时,通过硬件查看这个寄存器,如果关注的事情发生了,则中断当前程序流程,跳转到一个固定的地址处理这个事件。

无论何种CPU中断的处理过程是类似的

  • 中断控制器汇集各类外设发生的中断信号,然后告诉CPU
  • CPU保存当前程序的运行环境,调用中断服务程序(ISR)来处理这些中断
  • 在ISR中通过读取中断控制器、外设的相关寄存器来识别这是哪个中断,并进行相应处理
  • 清除中断:通过读写中断控制器和外设的相关寄存器来实现
  • 最后恢复被中断程序的运行环境,继续执行

S3C2440A 中的中断控制器接受来自60 个中断源的请求。提供这些中断源的是内部外设,如DMA 控制器、UART、IIC 等等。在这些中断源中,UARTn、AC97 和EINTn 中断对于中断控制器而言是“或”关系。当从内部外设和外部中断请求引脚收到多个中断请求时,中断控制器在仲裁步骤后请求ARM920T 内核的FIQ
或IRQ。仲裁步骤由硬件优先级逻辑决定并且写入结果到帮助用户通告是各种中断源中的哪个中断发生了的中断挂起寄存器中。

这里写图片描述

  • 对于不带sub寄存器的中断源,被触发后,SRCPND寄存器中相应位被置1,如果此中断没有被INTMASK寄存器屏蔽的话,它将进一步被处理。
  • 对于带有sub寄存器的请求源,被触发后,SUBSRCPND寄存器中相应位也被置1,如果此中断没有被INTSUBMASK寄存器屏蔽的话,它在SRCPND相应位也会置1,后面的处理过程就和上面的过程类型了。
  • 如果被触发的中断中有快速中断(FIQ),则CPU进入快速中断模式进行处理。
  • 对于一般中断IRQ,可能同时又几个中断被触发,未被INTMASK寄存器的中断屏蔽的中断经过比较后,选出优先级最高的中断,此中断在INTPND寄存器中的相应位被置1,然后CPU进入中断模式(IRQ Mode)进行处理。中断服务程序可以通过读取INTPND寄存器或者INTOFFSET寄存器来确定中断源。

终上所述,使用中断步骤如下。

  • 设置好中断模式和快速中断模式下的栈:当发生中断IRQ时,CPU进入中断模式,这时使用中断模式下的栈。当发生快速中断FIQ时,CPU进入快速中断模式,这时使用快速中断模式下的栈
  • 准备好中断处理函数
    1. 在异常向量表中设置好当进入中断模式或快速中断模式时的跳转函数,它们的异常向量地址分别为0x00000018,0x0000001c 。
    2. 中断服务程序(ISR),IRQ、FIQ的跳转函数,最终将调用具体中断的服务函数。对于IRQ,读取INTPND寄存器或INTOFFSET寄存器的值来确定中断源,然后分别处理。
    3. 清除中断:如果不清除中断,则CPU会误以为中断又发生了一次。清除中断时,从源头开始:首先,需要的话,操作具体外设清除中断信号;其次,清除SUBSRCPND(用到的话)、SRCPND寄存器中的相应位(往相应位写1即可);最后,清除INTPND寄存器中相应位,最简单的方法就是INTPND=INTPND
  • 进入、退出中断模式或者快速中断模式时,需要保存、恢复被中断程序的运行环境

     1. 对于IRQ,进入和退出的代码如下
        sub lr, lr, #4              @计算返回地址        stmdb  sp!, {r0-r12,lr}     @保存使用到的寄存器        ... ...                     @处理中断        ldmia  sp!, {r0-r12,pc}^    @中断返回                            @^表示将spsr的值赋值给cpsr
     2. 对于FIQ,进入和退出的代码如下:
        sub lr, lr, #4              @计算返回地址        stmdb  sp!, {r0-r12,lr}     @保存使用到的寄存器        ... ...                     @处理中断        ldmia  sp!, {r0-r12,pc}^    @中断返回                            @^表示将spsr的值赋值给cpsr
  • 根据具体中断,设置相关外设。比如对于GPIO中断,需要将相应引脚的功能设为“外部中断”、设置中断触发条件
  • 对于有sub寄存器的请求源的中断,还需要将INTSUBMASK相应位置0
  • 确定使用此中断的方式:FIQ和IRQ
  • 设置CPSR寄存器中的I-bit或F-bit为,使能IRQ

下面以代码来说明如果使用中断

@******************************************************************************@ File:head.S@ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数@******************************************************************************       .extern     main.text .global _start _start:@******************************************************************************       @ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用@******************************************************************************           b   Reset@ 0x04: 未定义指令中止模式的向量地址HandleUndef:    b   HandleUndef @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式HandleSWI:    b   HandleSWI@ 0x0c: 指令预取终止导致的异常的向量地址HandlePrefetchAbort:    b   HandlePrefetchAbort@ 0x10: 数据访问终止导致的异常的向量地址HandleDataAbort:    b   HandleDataAbort@ 0x14: 保留HandleNotUsed:    b   HandleNotUsed@ 0x18: 中断模式的向量地址    b   HandleIRQ@ 0x1c: 快中断模式的向量地址HandleFIQ:    b   HandleFIQReset:                      ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启    msr cpsr_c, #0xd2       @ 进入中断模式    ldr sp, =3072           @ 设置中断模式栈指针    msr cpsr_c, #0xd3       @ 进入管理模式    ldr sp, =4096           @ 设置管理模式栈指针,                            @ 其实复位之后,CPU就处于管理模式,                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略    bl  init_led            @ 初始化LED的GPIO管脚    bl  init_irq            @ 调用中断初始化函数,在init.c中    msr cpsr_c, #0x5f       @ 设置I-bit=0,开IRQ中断    ldr lr, =halt_loop      @ 设置返回地址    ldr pc, =main           @ 调用main函数halt_loop:    b   halt_loopHandleIRQ:    sub lr, lr, #4                  @ 计算返回地址    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器                                    @ 注意,此时的sp是中断模式的sp                                    @ 初始值是上面设置的3072    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址      ldr pc, =EINT_Handle            @ 调用中断服务函数,在interrupt.c中int_return:    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

在上面程序开始的部分,分布了7条指令,地址分别为0x00,0x04,…….,0x1c,这些地址上的指令被称为“异常向量”,当发生各类异常或者中断时,CPU进入相应的工作模式,并跳转去执行对应的“异常向量”。比如当复位时,CPU进入系统模式,并跳转到0x00地址开始执行;发生中断时,CPU进入中断模式,并跳转到0x18地址开始执行。

本程序中,只有复位和中断对应的异常向量有作用,其他没有实际作用。

0x00地址处的指令为 “b Reset”,直接跳转到标号为Reset开始的代码。

Reset:                      ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈    bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启    msr cpsr_c, #0xd2       @ 进入中断模式    ldr sp, =3072           @ 设置中断模式栈指针    msr cpsr_c, #0xd3       @ 进入管理模式    ldr sp, =4096           @ 设置管理模式栈指针,                            @ 其实复位之后,CPU就处于管理模式,                            @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略    bl  init_led            @ 初始化LED的GPIO管脚    bl  init_irq            @ 调用中断初始化函数,在init.c中    msr cpsr_c, #0x5f       @ 设置I-bit=0,开IRQ中断    ldr lr, =halt_loop      @ 设置返回地址    ldr pc, =main           @ 调用main函数halt_loop:    b   halt_loop

这里先说下msr cpsr_c, #0x5f中的 cpsr_c,CPSR寄存器共32位被分为4个域如下:

CPSR

所以cpsr_c是对应操作CPSR寄存器的低8位,设置完成中断模式的栈指针以后,让我们来看下发生中断时候的处理流程:

HandleIRQ:    sub lr, lr, #4                  @ 计算返回地址    stmdb   sp!,    { r0-r12,lr }   @ 保存使用到的寄存器                                    @ 注意,此时的sp是中断模式的sp                                    @ 初始值是上面设置的3072    ldr lr, =int_return             @ 设置调用ISR即EINT_Handle函数后的返回地址      ldr pc, =EINT_Handle            @ 调用中断服务函数,在interrupt.cint_return:    ldmia   sp!,    { r0-r12,pc }^  @ 中断返回, ^表示将spsr的值复制到cpsr

上面的程序首先计算返回地址,然后将现场环境r0-r12以及lr保存到中断模式的栈中,然后进入中断ISR中,最后返回。

#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;}

以上就是中断部分的内容,这篇内容拖了太久,很多内容我自己都记不清除了,以后不能这样了