作者:彭东林

邮箱:pengdonglin137@163.com

QQ: 405728433

以前一直有个疑问,在U-boot下到底能不能使用中断,为了验证这个问题,于是乎,昨天晚上我在自己的 TQ2440开发板上进行了uboot环境下的按键中断实验,这次使用的我刚移植的最新版Uboot,版本是 u-boot-2014-04,验证的结论是:

U-boot完全能够支持中断

下面就以u-boot-2014-04为例,介绍一下按键中断的实现。

这里分为几部分介绍:

1、异常向量表            ------  由u-boot完成

2、通用中断处理函数       ------  由u-boot完成

3、u-boot自己完成的中断初始化部分  ----- 由u-boot完成

4、用户按键中断中断初始化  ------  由用户完成

5、用户自定义中断处理函数  ------  由用户完成

这里有两篇我在网络上搜集的关于S3C2440中断的文章:

http://files.cnblogs.com/pengdonglin137/S3C2440%E7%B3%BB%E7%BB%9F%E4%B8%AD%E6%96%AD.rar

http://files.cnblogs.com/pengdonglin137/S3C2440%E5%A4%96%E9%83%A8%E4%B8%AD%E6%96%AD%E6%93%8D%E4%BD%9C.pdf

其中介绍了如何使用S3C2440的中断功能以及ARM处理器异常处理。

先简单介绍一下几个知识点:

  • ARM状态下的寄存器组织

在系统上电时,也就是RESET后,处于SVC特权模式

  • ARM状态寄存器

关于状态寄存器的介绍可以参考:

http://www.cnblogs.com/pengdonglin137/p/3819546.html

Control Bits的含义:

Mode bits的含义:

  • 异常向量表

异常向量表是一段特定内存地址空间,每种ARM异常对应一个字长空间(4Bytes),正好是一条32位指令长度,当异常发生时,CPU强制将PC的值设置为当前异常对应的固定内存地址。如表3-4所示是S3C2440的异常向量表。

我们一般都是用的是IRQ异常。下面的按键产生IRQ异常。

  • 异常发生的硬件操作

在异常发生后,ARM内核会自动做以下工作:
        保存执行状态:将CPSR复制到发生的异常模式下SPSR中;

(以按键中断为例,uboot环境下处于SVC模式,中断后,处于irq模式,所以这步完成的动作是:CPSR ----> SPSR_irq)
        模式切换:将CPSR模式位强制设置为与异常类型相对应的值,同时处理器进入到ARM执行模式,禁止所有IRQ中断,当进入FIQ快速中断模式时禁止FIQ中断;

(以按键中断为例,将CPSR的mode bits设置为0x12,将I位置为1,屏蔽IRQ中断,将T位置为1,进入ARM状态)
        保存返回地址:将下一条指令的地址(被打断程序)保存在LR(异常模式下LR_excep)中。

(一条指令的执行分为:取指,译码,执行三个主要阶段, CPU由于使用流水线技术,造成当前执行指令的地址应该是PC – 8(32位机一条指令四个字节),那么执行指令的下条指令应该是PC – 4。在异常发生时,CPU自动会将将PC – 4 的值保存到LR里,但是该值是否正确还要看异常类型才能决定。

快速中断请求和一般中断请求返回处理是一样的。通常处理器执行完当前指令后,查询FIQ/IRQ中断引脚,并查看是否允许FIQ/IRQ中断,如果 某个中断引脚有效,并且系统允许该中断产生,处理器将产生FIQ/IRQ异常中断,当FIQ/IRQ异常中断产生时,程序计数器pc的值已经更新,它指向 当前指令后面第3条指令(对于ARM指令,它指向当前指令地址加12字节的位置;对于Thumb指令,它指向当前指令地址加6字节的位置),当 FIQ/IRQ异常中断产生时,处理器将值(pc-4)保存到FIQ/IRQ异常模式下的寄存器lr_irq/lr_irq中,它指向当前指令之后的第2 条指令,因此正确返回地址可以通过下面指令算出:
SUBS    PC,LR_irq,#4        ; 一般中断
SUBS    PC,LR_fiq,#4                   ; 快速中断
注:LR_irq/LR_fiq分别为一般中断和快速中断异常模式下LR,并不存在LR_xxx寄存器,为方便读者理解加上_xxx)
        跳入异常向量表:强制设置PC的值为相应异常向量地址,跳转到异常处理程序中。

(以按键中断为例,将PC强制设置为0x18)

  • 保存执行现场

异常处理程序最开始,要保存被打断程序的执行现场,程序的执行现场无非就是保存当前操作寄存器里的数据,可以通过下面的栈操作指令实现保存现场:
STMFD  SP_excep!,  {R0 – R12,  LR_excep}
注:LR_abt,SP_excep分别为对应异常模式下LR和SP,为方便读者理解加上_abt
需要注意的是,在跳转到异常处理程序入口时,已经切换到对应异常模式下了,因此这里的SP是异常模式下的SP_excep了,所以被打断程序现场 (寄存器数据)是保存在异常模式下的栈里,上述指令将R0~R12全部都保存到了异常模式栈,最后将修改完的被打断程序返回地址入栈保存,之所以保存该返 回地址就是将来可以通过类似:MOV  PC,  LR的指令,返回用户程序继续执行。
异常发生后,要针对异常类型进行处理,因此,每种异常都有自己的异常处理程序,异常处理过程通过下节的系统中断处理来进行分析。

  • 异常处理的返回

异常处理完成之后,返回被打断程序继续执行,具体操作如下:
      恢复被打断程序运行时寄存器数据
      恢复程序运行时状态CPSR
      通过进入异常时保存的返回地址,返回到被打断程序继续执行
异常发生后,进入异常处理程序时,将用户程序寄存器R0~R12里的数据保存在了异常模式下栈里面,异常处理完返回时,要将栈里保存的的数据再恢复 回原先R0~R12里,毫无疑问在异常处理过程中必须要保证异常处理入口和出口时栈指针SP_excep要一样,否则恢复到R0~R12里的数据不正确, 返回被打断程序时执行现场不一致,出现问题,虽然将执行现场恢复了,但是此时还是在异常模式下,CPSR里的状态是异常模式下状态,因此要恢复 SPSR_excep里的保存状态到CPSR里,SPSR_excep是被打断程序执行时的状态,在恢复SPSR_excep到CPSR的同时,CPU的 模式和状态从异常模式切换回了被打断程序执行时的模式和状态。此刻程序现场恢复了,状态也恢复了,但PC里的值仍然指向异常模式下的地址空间,我们要让 CPU继续执行被打断程序,因此要再手动改变PC的值为进入异常时的返回地址,该地址在异常处理入口时已经计算好,直接将PC = LR_excep即可。
上述操作可以一步一步实现,但是通常我们可以通过一条指令实现上述全部操作:
LDMFD  SP_excp!,  {r0-r12,  pc}^
注:SP_excep为对应异常模式下SP,^符号表示恢复SPSR_excep到CPSR

以上操作可以用下图来描述

接下来分析u-boot代码。

让u-boot支持中断,首先需要在配置文件中定义几个宏,我在我的板子的配置文件include/configs/smdk2440.h中定义了如下几个宏(少定义了在编译时会报错,可以根据出错信息判断少定义了那些宏):

#define CONFIG_USE_IRQ
   #define CONFIG_STACKSIZE_IRQ  (4*1024)    /* IRQ的栈大小*/
   #define CONFIG_STACKSIZE_FIQ  (4*1024)    /* FIQ的栈大小*/

异常向量表

首先分析一下arch/arm/cpu/arm920t/start.S

   1:  .globl _start                                                     指令链接地址                      指令的运行地址     
   2:  _start:    b    start_code                                         0x33f00000                        0x00000000
   3:      ldr    pc, _undefined_instruction                              

0x33f00004                   0x00000004

   4:      ldr    pc, _software_interrupt                                 

0x33f00008 0x00000008

   5:      ldr    pc, _prefetch_abort                                     

0x33f0000c                    0x0000000c

   6:      ldr    pc, _data_abort                                         

0x33f00010 0x00000010

   7:      ldr    pc, _not_used                                           

0x33f00014  0x00000014

   8:      ldr    pc, _irq                                                

0x33f00018 0x00000018

   9:      ldr    pc, _fiq                                                

0x33f0001c  0x0000001c

  10:   
  11:  _undefined_instruction:    .word undefined_instruction             

0x33f00020 0x00000020

  12:  _software_interrupt:    .word software_interrupt                   

0x33f00024 0x00000024

  13:  _prefetch_abort:    .word prefetch_abort                           

0x33f00028 0x00000028

  14:  _data_abort:        .word data_abort                               

0x33f0002c 0x0000002c

  15:  _not_used:        .word not_used                                   

0x33f00030 0x00000030

  16:  _irq:            .word irq                                         

0x33f00034 0x00000034

  17:  _fiq:            .word fiq                                         

0x33f00038 0x00000038

  18:   
  19:      .balignl 16,0xdeadbeef

上面就是建立异常向量表,其中b start_code指令的地址对应的就是复位异常发生时要赋给PC的值,b 是一条相对跳转指令。其中,我们要关注的是IRQ异常: ldr  pc, _irq  ,这条语句的作用是将_irq中存放的数据放入pc中,可以将_irq看做变量名或者一个*p,而其中存放的是内容就是irq,即中断处理的入口地址(链接地址)。

即当发生按键动作是,pc会指向“ldr pc, _irq”所在的地址,执行这条指令(会被解释成ldr pc, [pc, #offset]),这条指令完成了将irq的地址(链接地址)赋给了pc,从而从异常向量表中直接跳入了中断处理程序(链接时确定的地址处)。

这里需要解释一下,指令的运行地址和链接地址。链接地址是在编译连接时编译器确定的地址,运行地址是实际运行这条指令时,去哪个物理地址去取这条指令,这两个地址一般相同。如果设备支持程序在Flash中运行,那么这两个地址相同,但是对于从NandFlash启动时,他们就不同了,以S3C2440为例,系统会先把NandFlash的前4KB的内容读到SRAM(sram会被映射到物理地址0开始的地方),然后运行这4KB的程序,这段4KB的程序负责把整个程序从NandFlash读到他们的链接地址处(一般在物理内存的末端,S3C2440的物理内存起始地址是0x30000000)。那么对于刚才运行在SRAM中的那4KB程序来说,他们的运行地址(sram中,起始地址0)跟链接地址(内存中,起始地址0x30000000)就不相同了。ARM架构下的异常向量表默认应该存放在0地址处,即要想使用异常,物理地址0处应该存放正确完整的异常向量表。对于从NorFlash启动,自然不是问题,此时NorFlash会被映射到物理地址0开始的地方,NorFlash的中存放的uboot开头便是异常向量表。对于从NandFlash启动时,SRAM被映射到了物理地址0开始的地方,并且前面已经说过,SRAM中的代码来自NandFlash的前4KB,这4KB也就是uboot的前4KB,自然含有异常向量表,也不会出问题,如果你故意在u-boot中通过使用命令mw破坏SRAM中的异常向量表,当发生异常时,u-boot就跑飞了。这里还要提一下被重定向到内存中的u-boot,其中也含有异常向量表,但是异常产生时系统用不到。

通用中断处理函数

通用中断处理函数在u-boot中的实现,还是在start.S中(我做了修改):

   1:      .align    5
   2:  irq:
   3:      sub    lr, lr, #4                @ the return address
   4:      ldr    sp, IRQ_STACK_START        @ the stack for irq
   5:      stmdb    sp!,    { r0-r12,lr }    @ save registers
   6:      
   7:      ldr    lr,    =int_return            @ set the return addr
   8:      ldr    pc,     =do_irq         @ call the isr
   9:  int_return:
  10:      ldmia    sp!,    { r0-r12,pc }^    @ return from interrupt
  11:      

解释:

“sub  lr, lr, #4”的原因在上面已经解释过了。

“ldr sp, IRQ_STACK_START”

这条指令中的sp已经是irq模式下的sp,即r13_irq,意思是将IRQ_STACK_START中存放的数据放入sp,即初始化irq模式下的栈指针。IRQ_STACK_START在什么地方赋值呢?一会儿分析。

“stmdb    sp!,    { r0-r12,lr }”

这条指令负责保存现场,r0~r12是svc模式和irq模式共用的寄存器,同时由于下面在调用用C实现的do_irq时会用到,所以这里要保存。由于lr会被赋予新的值,这里也要保存。

“ldr    lr,    =int_return”

将int_return的链接地址放入lr中,因为在用C实现的do_irq执行结束是会执行 ldr pc, lr 的操作,正好执行到下面将要说的指令。

“ldmia    sp!,    { r0-r12,pc }^”

恢复现场,其中 ^ 表示将SPSR_irq赋值给CPSR。

u-boot自己完成的中断初始化部分

这里u-boot替用户完成的部分有:

1、中断栈的分配

2、设置CPSR的相关位,是I位清零,即使IRQ有效

中断栈的分配

IRQ_STACK_START的定义:

在arch/arm/cpu/arm920t/start.S中:

可以看到,IRQ_STACK_START的初始值给的是0x0badc0de,将来重定向到内存中后,会修改这个值。这里还要明确的是将来会在内存中u-boot的链接地址附近和4G空间的开始4KB内各有一个IRQ_STACK_START,重定向后,u-boot看到的是内存中u-boot链接地址起始地址附近的那个IRQ_STACK_START。也就是说,重定向后,4G空间前4KB处仅仅完成了发生异常后,依赖异常向量表跳转到异常处理程序的链接起始地址(在内存中)处。

IRQ_STACK_START的赋值:

对于u-boot-2014-04是在arch/arm/lib/board.c中的board_init_f函数中完成的:

   1:  unsigned int board_init_f(ulong bootflag)
   2:  {
   3:      bd_t *bd;
   4:      init_fnc_t **init_fnc_ptr;
   5:      gd_t *id;
   6:      ulong addr, addr_sp;
   7:      void *new_fdt = NULL;
   8:      size_t fdt_size = 0;
   9:   
  10:      memset((void *)gd, 0, sizeof(gd_t));
  11:   
  12:      gd->mon_len = (ulong)&__bss_end - (ulong)_start;
  13:      /* Allow the early environment to override the fdt address */
  14:      gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
  15:                          (uintptr_t)gd->fdt_blob);
  16:   
  17:      for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  18:          if ((*init_fnc_ptr)() != 0) {
  19:              hang ();
  20:          }
  21:      }
  22:   
  23:      debug("monitor len: %08lX\n", gd->mon_len);
  24:      /*
  25:       * Ram is setup, size stored in gd !!
  26:       */
  27:      debug("ramsize: %08lX\n", gd->ram_size);
  28:   
  29:      addr = CONFIG_SYS_SDRAM_BASE + get_effective_memsize();          // addr = 0x30000000 + 0x4000000
  30:   
  31:      /* round down to next 4 kB limit */
  32:      addr &= ~(4096 - 1);
  33:      debug("Top of RAM usable for U-Boot at: %08lx\n", addr);
  34:   
  35:      /*
  36:       * reserve memory for U-Boot code, data & bss
  37:       * round down to next 4 kB limit
  38:       */
  39:   
  40:      addr  = CONFIG_SYS_TEXT_BASE;                                  // addr = 0x33f00000
  41:      addr &= ~(4096 - 1);
  42:   
  43:      debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
  44:   
  45:      /*
  46:       * reserve memory for malloc() arena
  47:       */
  48:      addr_sp = addr - TOTAL_MALLOC_LEN;                           
  49:      debug("Reserving %dk for malloc() at: %08lx\n",
  50:              TOTAL_MALLOC_LEN >> 10, addr_sp);
  51:      /*
  52:       * (permanently) allocate a Board Info struct
  53:       * and a permanent copy of the "global" data
  54:       */
  55:      addr_sp -= sizeof (bd_t);
  56:      bd = (bd_t *) addr_sp;
  57:      gd->bd = bd;
  58:      debug("Reserving %zu Bytes for Board Info at: %08lx\n",
  59:              sizeof (bd_t), addr_sp);
  60:   
  61:      

addr_sp -= sizeof (gd_t);

  62:      id = (gd_t *) addr_sp;
  63:      debug("Reserving %zu Bytes for Global Data at: %08lx\n",
  64:              sizeof (gd_t), addr_sp);
  65:   
  66:      /* setup stackpointer for exeptions */
  67:      gd->irq_sp = addr_sp;
  68:  #ifdef CONFIG_USE_IRQ
  69:      

addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); // 在smdk2440.h中这俩个宏都定义为了4K

  70:      debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
  71:          CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
  72:  #endif
  73:      /* leave 3 words for abort-stack    */
  74:      addr_sp -= 12;
  75:   
  76:      /* 8-byte alignment for ABI compliance */
  77:      addr_sp &= ~0x07;
  78:      debug("New Stack Pointer is: %08lx\n", addr_sp);
  79:   
  80:      gd->bd->bi_baudrate = gd->baudrate;
  81:      /* Ram ist board specific, so move it to board code ... */
  82:      dram_init_banksize();
  83:      display_dram_config();    /* and display it */
  84:   
  85:      gd->relocaddr = addr;
  86:      gd->start_addr_sp = addr_sp;
  87:      gd->reloc_off = addr - (ulong)&_start;
  88:      debug("relocation Offset is: %08lx\n", gd->reloc_off);
  89:      if (new_fdt) {
  90:          memcpy(new_fdt, gd->fdt_blob, fdt_size);
  91:          gd->fdt_blob = new_fdt;
  92:      }
  93:      memcpy(id, (void *)gd, sizeof(gd_t));
  94:   
  95:      return (unsigned int)id;
  96:  }

从上面的代码可以看出u-boot的内存分布图大致如下:

从图中可以看到中断栈的位置。ARM使用的栈是向下增长的,中断栈的栈底地址存放在gd的irq_sp中。

上面只是分配了,还没有赋值给IRQ_STACK_START,它的赋值在arch/arm/lib/board.c中的board_init_r函数中:

void board_init_r(gd_t *id, ulong dest_addr)
{
    ......
    /* set up exceptions */
    interrupt_init();
    /* enable exceptions */
    enable_interrupts();
    .......
    for (;;) {
        main_loop();
    }
}

其中,在interrupt_init中给IRQ_STACK_START赋值,在enable_interrupts中设置CPSR相关位。这两个函数在

interrupts.c (arch\arm\lib) 中实现。

#ifdef CONFIG_USE_IRQ
int interrupt_init (void)
{
    /*
     * setup up stacks if necessary
     */
    IRQ_STACK_START = gd->irq_sp - 4;
    IRQ_STACK_START_IN = gd->irq_sp + 8;
    FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
 
    //return arch_interrupt_init();
    return 0;
    
}
 
/* enable IRQ interrupts */
void enable_interrupts (void)
{
    unsigned long temp;
    __asm__ __volatile__("mrs %0, cpsr\n"
                 "bic %0, %0, #0x80\n"        // 清除I位,使能IRQ
                 "msr cpsr_c, %0"
                 : "=r" (temp)
                 :
                 : "memory");
}
 
 
/*
 * disable IRQ/FIQ interrupts
 * returns true if interrupts had been enabled before we disabled them
 */
int disable_interrupts (void)
{
    unsigned long old,temp;
    __asm__ __volatile__("mrs %0, cpsr\n"
                 "orr %1, %0, #0xc0\n"      // 将I和F置位,屏蔽FIQ和RIQ
                 "msr cpsr_c, %1"
                 : "=r" (old), "=r" (temp)
                 :
                 : "memory");
    return (old & 0x80) == 0;
}
#else
 
......
 
#endif

用户按键中断中断初始化

这部分由用户自己完成,我们要实现的是按键中断,然后再在中断处理函数中点亮某个LED灯,关闭其他的LED灯。这部分应该放在u-boot已经完成了系统的初始化工作,这里我把它放在了执行main_loop之前。

void board_init_r(gd_t *id, ulong dest_addr)
{
    ......
    {
        #define GPBCON              (*(volatile unsigned long *)0x56000010)
        #define GPFCON              (*(volatile unsigned long *)0x56000050)
        #define EINTMASK            (*(volatile unsigned long *)0x560000a4)
        #define EXTINT0             (*(volatile unsigned long *)0x56000088)
        #define INTMSK              (*(volatile unsigned long *)0x4A000008)
        
        /*
         * LED1,LED2,LED3,LED4 分别对应 GPB5、 GPB6、 GPB7、 GPB8
         */
        #define    GPB5_out    (1<<(5*2))
        #define    GPB6_out    (1<<(6*2))
        #define    GPB7_out    (1<<(7*2))
        #define    GPB8_out    (1<<(8*2))
        
        #define    GPB5_msk    (3<<(5*2))
        #define    GPB6_msk    (3<<(6*2))
        #define    GPB7_msk    (3<<(7*2))
        #define    GPB8_msk    (3<<(8*2))
        
        /*
         * 按键S1,S2,S3,S4 分别对应GPF0、GPF2、GPF0、GPF4
         */
        #define GPF0_eint     (0x2<<(0*2))
        #define GPF1_eint     (0x2<<(1*2))
        #define GPF2_eint     (0x2<<(2*2))
        #define GPF4_eint     (0x2<<(4*2))
        
        #define GPF0_msk    (3<<(0*2))
        #define GPF1_msk    (3<<(1*2))
        #define GPF2_msk    (3<<(2*2))
        #define GPF4_msk    (3<<(4*2))
 
        //将控制LED的引脚设置为输出
        GPBCON &= ~(GPB5_msk | GPB6_msk | GPB7_msk | GPB8_msk);    
        GPBCON |= GPB5_out | GPB6_out | GPB7_out | GPB8_out;
        
        // 将按键部分的引脚设置为外部中断模式
        GPFCON &= ~(GPF0_msk | GPF2_msk | GPF1_msk | GPF4_msk);
        GPFCON |= GPF0_eint | GPF2_eint | GPF1_eint | GPF4_eint;
        
        // 外部中断4到7共用一个中断EINT4_7,将外部中断4对应的屏蔽位清除
        EINTMASK &= ~(1<<4);
            
        // EINT0、EINT2 EINT1、EINT4_7 清除屏蔽位 0 2 1 4
        INTMSK     &= (~(1<<0)) & (~(1<<2)) & (~(1<<1) & (~(1<<4)));
        
        //设置为下降沿触发
        EXTINT0 &= ~0xffff;
        EXTINT0 |= ((2<<0) | (2<<4) | (2<<8) | (2<<16));
 
    }
    /* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
        main_loop();
    }
}

用户自定义中断处理函数

这部分是用户自己实现的。完成当按键中断发生后,用户期望完成的功能。我们所要的功能是:点亮某个LED灯,关闭其他的LED灯。

这部分的实现我放在了interrupts.c (arch\arm\cpu\arm920t\s3c24x0)中。

void do_irq (struct pt_regs *pt_regs)
{
    #define INTOFFSET           (*(volatile unsigned long *)0x4A000014)
    #define GPBDAT              (*(volatile unsigned long *)0x56000014)
    #define EINTPEND            (*(volatile unsigned long *)0x560000a8)
    #define SRCPND              (*(volatile unsigned long *)0x4A000000)
    #define INTPND              (*(volatile unsigned long *)0x4A000010)
    
    unsigned long oft = INTOFFSET;
 
    switch( oft )
        {
    // 按键0
    case 0: 
    {   
        GPBDAT |= (0xf<<5);     // 熄灭所有LED
        GPBDAT &= ~(1<<5);      // LED1亮
        printf("EINT0\n");
        break;
    }
    
    // 按键1
    case 1:
    {   
        GPBDAT |= (0xff<<5);   // 熄灭所有LED
        GPBDAT &= ~(1<<7);      // LED2亮
        printf("EINT1\n");
        break;
    }
 
 
    // 按键2
    case 2:
    {   
        GPBDAT |= (0xff<<5);   // 熄灭所有LED
        GPBDAT &= ~(1<<6);      // LED3亮
        printf("EINT2\n");
        break;
    }
 
    //按键3
    case 4:
    {   
        GPBDAT |= (0xff<<5);   // 熄灭所有LED
        GPBDAT &= ~(1<<8);      // LED4亮  
        printf("EINT4\n");
        break;
    }
 
    default:
        break;
    }
 
    //清中断
    if( oft == 4 ) 
        EINTPEND = (1<<4);   // EINT4_7合用IRQ4
    SRCPND = 1<<oft;         // 对应位写1
    INTPND = 1<<oft;         // 对应位写1
 
}

至此,u-boot下就实现了按键中断。无论从NorFlash还是NandFlash启动都可以,大家还可以验证一下,当从NandFlash启动后,手动将SRAM(物理起始地址从0开始的4KB空间)全部清零,然后再按键,看看现象,此时u-boot肯定跑飞了。

完!!

u-boot中断功能初步分析之---------按键中断的更多相关文章

  1. STM32—中断详解(配合按键中断代码,代码亲测)

    在STM32中执行中断主要分三部分: 1.配置NVIC_Config()函数 2.配置EXTI_Config()函数 3.编写中断服务函数 (注:本文章所用代码为中断按键代码,实现了按键进入中断从而控 ...

  2. ok6410按键中断编程,linux按键裸机

    6410按键中断编程 一.流程分析 外部中断控制寄存器(s3c6410x  359页) 1.EINTxCONy: 外部中断组x的第y个控制器.这个就是设置中断的触发方式.有5种触发方式. 2.EINT ...

  3. 2、CC2541芯片中级教程-OSAL操作系统(进一步了解-OLED && 普通按键和5方向按键-中断!!!)这个系统驱动层和应用层不一样~

    本文根据一周CC2541笔记汇总得来—— 适合概览和知识快速索引—— 全部链接: 中级教程-OSAL操作系统\OSAL操作系统-实验01 OSAL初探 [插入]SourceInsight-工程建立方法 ...

  4. DirectUI的初步分析-转

    DirectUI的初步分析(一) 最近由于项目的需要学习了一下DirectUI方面的东西,主要借鉴的是一个国外程序员写的代码(见引用一),看了后发现它更多的是探讨一种实现的可能性和思路,和实际应用还是 ...

  5. 鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断 | 百篇博客分析OpenHarmony源码 | v44.02

    百篇博客系列篇.本篇为: v44.xx 鸿蒙内核源码分析(中断管理篇) | 江湖从此不再怕中断 | 51.c.h .o 硬件架构相关篇为: v22.xx 鸿蒙内核源码分析(汇编基础篇) | CPU在哪 ...

  6. Azure底层架构的初步分析

    之所以要写这样的一篇博文的目的是对于大多数搞IT的人来说,一般都会对这个topic很感兴趣,因为底层架构直接关乎到一个公有云平台的performance,其实最主要的原因是我们的客户对此也非常感兴趣, ...

  7. Linux mips64r2 PCI中断路由机制分析

    Linux mips64r2 PCI中断路由机制分析 本文主要分析mips64r2 PCI设备中断路由原理和irq号分配实现方法,并尝试回答如下问题: PCI设备驱动中断注册(request_irq) ...

  8. Linux内核中断和异常分析(上)

    中断,通常被定义为一个事件.打个比方,你烧热水,水沸腾了,这时候你要去关掉烧热水的电磁炉,然后再去办之前手中停不下来的事情.那么热水沸腾就是打断你正常工作的一个信号机制.当然,还有其它的情况,我们以后 ...

  9. Linux内核中断和异常分析(下)

    这节,我们继续上,中(以前的日志有)篇目进行分析,结合一个真实的驱动案例来描述linux内核中驱动的中断机制,首先我们先了解一下linux内核中提供的中断接口. 这个接口我们需要包含一个头文件:#in ...

随机推荐

  1. Tarjan缩点【p4819】[中山市选]杀人游戏

    Description 一位冷血的杀手潜入Na-wiat,并假装成平民.警察希望能在\(N\)个人里面,查出谁是杀手.警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀 ...

  2. Map泛型集合-显示企鹅信息

    package collection; /** * 宠物类 * @author * */ public class Pet { private String name; private String ...

  3. Tiny6410下的第一个Linux驱动程序

    Linux系统环境是照着友善之臂的教程搭建的 //Hello  World驱动程序源文件 #include <linux/miscdevice.h> #include <linux/ ...

  4. Jenkins配置Publish Junit test result report(转)

    参考这篇文章:http://www.yiibai.com/jenkins/jenkins_unit_testing.html 插件:JUnit Plugin

  5. 【教训】null == '',改造ThinkSNS 系统里面的一个缓存管理函数S()后,留下一个大bug

    本来想简化 ThinkSNS 系统里面的一个缓存管理函数: <?php /** * 用来对应用缓存信息的读.写.删除 * $expire = null/0 表示永久缓存,否则为缓存有效期 */ ...

  6. 开始使用 Docker (Linux 上运行 SQL Server) 上的 SQL Server 容器 - SQL Server | Microsoft Docs

    原文:开始使用 Docker (Linux 上运行 SQL Server) 上的 SQL Server 容器 - SQL Server | Microsoft Docs 快速入门:使用 Docker ...

  7. T-SQL语言基础(转载)

    本文转自http://www.cnblogs.com/Jolinson/p/3552786.html 这里的摘抄来自<Microsoft SQL Server 2008技术内幕:T-SQL语言基 ...

  8. vuejs -- 如何使一个自定义函数在加载时自动执行

  9. C++之类成员所占内存大小问题总结

    1.空类所占字节数为1,可见代码如下 #include <iostream> using namespace std; class Parent { }; class Child:publ ...

  10. Makefile中的“-I”(大写i),“-L”(大写l),“-l”(小写l)

    用gcc编译程序时,可能会用到“-I”(大写i),“-L”(大写l),“-l”(小写l)等参数, “-I”(大写i):表示包含头文件: “-L”(大写l):表示库文件目录: “-l”(小写l):表示链 ...