UBOOT中斷功能初步分析之----按鍵中斷

前一直有個疑問,在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狀態下的寄存器組織

image

在系統上電時,也就是RESET後,處於SVC特權模式

  • ARM狀態寄存器

image

關於狀態寄存器的介紹可以參考:

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

Control Bits的含義:

image

Mode bits的含義:

image

  • 異常向量表

異常向量表是一段特定內存地址空間,每種ARM異常對應一個字長空間(4Bytes),正好是一條32位指令長度,當異常發生時,CPU強制將PC的值設置爲當前異常對應的固定內存地址。如表3-4所示是S3C2440的異常向量表。

wps_clip_image-15397

我們一般都是用的是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

以上操作可以用下圖來描述

wps_clip_image-26392

接下來分析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中:

image

可以看到,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的內存分佈圖大致如下:

wps_clip_image-29309

 

從圖中可以看到中斷棧的位置。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肯定跑飛了。

 

完!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章