ARM 系列 -- FS2410 开发板上的中断编程

发布者:TranquilMind88最新更新时间:2016-12-02 来源: eefocus关键字:FS2410  中断编程 手机看文章 扫描二维码
随时随地手机看文章

一、目的
   中断服务程序在操作系统中无疑占有非常重要的地位,编写中断程序不仅要会运用底层的汇编语言,还要了解 ARM 的体系架构。那这一节我们就通过中断编程来响应 FS2410开发板上的 16 个按键,实现依次按下16个键时,D9~D12 四个 Led 从 0~15 进行计数,并通过上个实验实现的 uart_printf 向串口发送数据 Kn is pressed!。


二、代码
   我们直接分析代码,代码中只有简略的注释,必要时我会在整个代码文件的后面对相应的细节进行解释。先来分析 head.s:
   
   @文件 head.s
   .text
   .global _start
   _start:
        @ Set vector table for interrupt
    b reset                 
    b HandleIRQ             
    b HandleIRQ             
    b HandleIRQ             
    b HandleIRQ             
    b HandleIRQ             
    b HandleIRQ             @ handle irq interrupt here             
    b HandleIRQ
   reset:
    ldr r0, =0x53000000 @ Close Watch Dog Timer
    mov r1, #0x0
    str r1, [r0]
    
    @ disable all interrupts
    mov r1, #0x4A000000
    mov r2, #0xffffffff
    str r2, [r1, #0x08]     @ set INTMSK
    ldr r2, =0x7ff
    str r2, [r1, #0x1C]     @ set INTSUBMSK
   
    bl  memory_setup @ Initialize memory setting
    bl  flash_to_sdram @ Copy code to sdram
   
    msr cpsr_c, #0xd2 @ set irq mode stack 
        ldr pc, =set_sp         @ jump to addr 0x3000000
   set_sp:
    ldr sp, =0x31000000 
    msr cpsr_c, #0xdf @ set system mode stack 
    ldr sp, =0x32000000 
    bl  init_irq            @ Call init_irq
    msr cpsr_c, #0x5f @ set system mode and open the irq
    
        ldr sp, =0x34000000 @ Set stack pointer
    bl  main
   loop:
    b loop
   HandleIRQ:
    sub   lr,  lr,#4         @ get the return addr
    stmdb sp!, { r0-r12,lr } @ store used registers in stack
    ldr   lr,  =int_return   @ set retrun addr
    ldr   pc,  =EINT_Handle  @ jump to the interrup processing function
   int_return:
    ldmia sp!, { r0-r12,pc }^
  

呵呵,不知不觉 head.s 的代码已经很长了,我们来看一下它的执行流程:
(1) 设置中断向量表。你也许在这里有疑问,为什么一开始就有 8 个分支跳转指令?我们先来研究一下 ARM 如何响应异常/中断,看下表:

      -------------------------------------------------------------
          Exception               Mode              Address
      -------------------------------------------------------------
          Reset                      Supervisor       0x00000000    
          Undefined               Undefined        0x00000004    
          Software Interupt    Supervistor      0x00000008    
          Prefetch Abort        Abort              0x0000000C    
          Data Abort             Abort              0x00000010    
          IRQ (interupt)         IRQ                0x00000018    
          FIQ (fast interupt)   FIQ                0x0000001C    
      -------------------------------------------------------------

    可以看出 ARM 支持 7 种异常/中断,每种异常/中断都有固定的地址,这个地址叫 中断向量,一般我们会在这个地址放一条分支跳转指令,当异常/中断发生时,ARM 就到这个地址执行这个跳转指令,从而调用相应的中断服务程序。

    等等,这里是不是有点问题?呵呵,你也许已经发现了,这里只有 7 种异常/中断,那我们的程序怎么会有 8 条分支跳转指令呢? 因为中断向量即地址 0x00000014 被ARM保留用做将来扩展之用,但我们还需用一条指令(4字节)来填充这个位置,只不过它不会被 ARM 执行。

(2) 关闭看门狗
(3) 暂时屏蔽所有中断。
    1.地址 0x4A000008 是中断屏寄存器 INTMSK 的端口地址,复位 INTMSK 会导致所有的中断源被屏掉。
    2.地址 0x4A00001C 是子中断屏寄存器 INTSUBMSK 的端口地址,它的低 11 位对应外部 11 个中断源,高 21 位保留不用。复位它的低 11 位会导致相应的外部中断被屏。
(4) 初始化内存 SDRAM 设置
(5) Self-copying: 从 Nand Flash 将自身复制到 SDRAM
(6) 进入 IRQ 模式,设置 IRQ 模式下的堆栈寄存器
(7) 进入系统模式,并设置系统模式下的堆栈寄存器
(8) 系统模式下调用 init_irq,这个函数用于初始化一些用于响应按键的中断寄存器
(9) 再次进入系统模式,并打开当前程序状态寄存器 cpsr 的 IRQ 中断位,这样 ARM 就能响应 IRQ 中断了
(10)执行主函数 main 后返回,然后进入死循环,等待中断发生
(11)中断发生时,ARM 响应中断并于 0x00000018 处执行 b HandleIRQ 跳转指令调用中断服务程序,处理完毕后返回循环处再等待下次中断的发生,如此往复...

这就是中断处理的基本流程了 :-), 以下文件的代码在前面随笔均有详细说明,这里就仅附简略注释了


   @ 文件 flash.s
   @ 作用:设置 Nand Flash 的控制寄存器、读取 Nand Flash
   @ 中的代码到 SDRAM 的指定位置,更多细节请参考我前面的随笔
   .equ NFCONF, 0x4e000000 
   .equ NFCMD,  0x4e000004 
   .equ NFADDR, 0x4e000008 
   .equ NFDATA, 0x4e00000c 
   .equ NFSTAT, 0x4e000010 
   .equ NFECC,  0x4e000014
   .global flash_to_sdram 
   flash_to_sdram:
        @ Save return addr
        mov r10,lr
    
        @ Initialize Nand Flash
        mov r0,#NFCONF
        ldr r1,=0xf830 
        str r1,[r0] 
    
        @ First reset and enable Nand Flash
        ldr r1,[r0] 
        bic r1, r1, #0x800 
        str r1,[r0] 
    
        ldr r2,=NFCMD
        mov r3,#0xff 
        str r3,[r2]
    
        @ for delay
        mov r3, #0x0a 
   1: 
        subs r3, r3, #1 
        bne 1b 
   
   @ Wait until Nand Flash bit0 is 1 
   wait_nfstat:
        ldr r2,=NFSTAT 
        ldr r3,[r2] 
        tst r3,#0x01 
        beq wait_nfstat 
           
    @ Disable Nand Flash
    ldr r0,=NFCONF 
    ldr r1,[r0] 
    orr r1,r1,#0x8000 
    str r1,[r0] 
    
    @ Initialzie stack 
    ldr sp,=4096 
   
    @ Set arguments and call 
        @ function nand_read defined in nand_read.c
    ldr r0,=0x30000000 
    mov r1,#0
    mov r2,#1024*40
    bl nand_read
   
        @ return
    mov pc,r10


   /* 文件 nand_read.c
    * 作用:从 Nand Flash 中读取一块数据到 SDRAM 中的指定位置
    */
   #define NFCONF (*(volatile unsigned long *)0x4e000000)
   #define NFCMD  (*(volatile unsigned long *)0x4e000004)
   #define NFADDR (*(volatile unsigned long *)0x4e000008)
   #define NFDATA (*(volatile unsigned long *)0x4e00000c)
   #define NFSTAT (*(volatile unsigned long *)0x4e000010)
   #define NFECC  (*(volatile unsigned long *)0x4e000014)
   
   #define NAND_SECTOR_SIZE 512
   #define NAND_BLOCK_MASK  0x1ff
   
   void wait_idle() {
     int i;
     for (i = 0; i < 50000; ++i) ;
   }
   
   int nand_read(unsigned char *buf, unsigned long start_addr, int size){ 
     int i, j; 
     /* 
      * detect the argument 
      */  
     if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { 
       return -1;     
     } 
    
     /* chip Enable */ 
     NFCONF &= ~0x800; 
     for (i=0; i<10; i++) { 
       ; 
     } 
    
     for (i=start_addr; i < (start_addr + size); i+=NAND_SECTOR_SIZE) { 
       NFCMD = 0; 
    
       /* Write Address */ 
       NFADDR = i & 0xff; 
       NFADDR = (i >> 9)  & 0xff; 
       NFADDR = (i >> 17) & 0xff; 
       NFADDR = (i >> 25) & 0xff; 
    
       wait_idle();
    
       for(j=0; j < NAND_SECTOR_SIZE; j++) { 
         *buf++ = (NFDATA & 0xff); 
       } 
     } 
    
     NFCONF |= 0x800;    /* chip disable */ 
     return 0; 
   }


   /* 头文件 serl.h
    * 作用:定义相关寄存器、UART 初始化函数声明、串口读写函数的声明
    */   
   #ifndef __SERL_H__
   #define __SERL_H__
   
   #define GPHCON  (*(volatile unsigned long *)0x56000070)
   
   /* PORT PULL-UP REGISTER*/
   #define GPHUP   (*(volatile unsigned long *)0x56000078)
   
   /* UART FIFO control register 0*/
   #define UFCON0  (*(volatile unsigned long *)0x50000008)
   
   /* UART line control register 0*/
   #define ULCON0  (*(volatile unsigned long *)0x50000000)
   
   /* UART CONTROL REGISTER 0*/
   #define UCON0   (*(volatile unsigned long *)0x50000004)
   
   /* UART modem control register 0*/
   #define UMCON0  (*(volatile unsigned long *)0x5000000C)
   
   /* UART baud rate divisor register 0*/
   #define UBRDIV0 (*(volatile unsigned long *)0x50000028)
   
   /* UART TX/RX STATUS REGISTER 0*/
   #define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
   
   
   #define UTXH0 (*(volatile unsigned char *)0x50000020)
   #define URXH0 (*(volatile unsigned char *)0x50000024)
   
   
   #define TXD0_READY 0x2
   #define RXD0_READY 0x1
   
   void init_uart();
   unsigned char uart_getc();
   void uart_putc(unsigned char ch);
   void uart_puts(unsigned char* src);
   
   #endif


   /* 文件 serl.c*/
   #include "serl.h"
   
   void init_uart() {
     GPHCON |= 0xa0; /* GPH2, GPH3 used as RXD0, TXD0*/
     GPHUP = 0x0c;   /* GPH2, GPH3 poll-up */
   
     ULCON0 = 0x03;  /* normal mode, no parity, one stop bit, 8-bit*/
     UCON0 = 0x05;   /* Loopback mode*/
     UFCON0 = 0x00;  /* not use FIFO*/
     UMCON0 = 0x00;  /* disable flow control*/
     UBRDIV0 = 12;   /* baud rate 57600*/
   }
   
   void uart_putc(unsigned char ch) {
     while (!(UTRSTAT0 & TXD0_READY));
     UTXH0 = ch;
   }
   
   unsigned char uart_getc(){
     while (! (UTRSTAT0 & RXD0_READY));
     return URXH0;
   }
   
   void uart_puts(unsigned char* src) {
     unsigned char *p = src;
     while (*p != '\0') {
       if (*p == 0x0a)
         uart_putc(0x0d);
       uart_putc(*p);
       p++;
     }
   }
  

   /* 
    * 头文件 printf.h
    * 作用:对外提供调用接口 uart_printf
    */   
   #ifndef __PRINTF_HH__
   #define __PRINTF_HH__
   
   void uart_printf(char *fmt, ...);
   
   #endif

   
   /*
    * 文件 printf.c
    * 文件中大部分代码来自 linux 0.11 内核的 vsprintf.c, 只是作了相应的删减,
    * <> 上有更详细的解释
    */
   #include
   #include
   #include
   #include "printf.h"
   #include "serl.h"
   
   #define ZEROPAD 1 /* pad with zero */
   #define SIGN         2 /* unsigned/signed long */
   #define PLUS         4 /* show plus */
   #define SPACE 8 /* space if plus */
   #define LEFT         16 /* left justified */
   #define SPECIAL 32 /* 0x */
   #define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
   
   #define is_digit(c) ((c) >= '0' && (c) <= '9')

   #define do_div(n,base) ({    \
         int __res;     \
         __res = ((unsigned long) n) % (unsigned) base; \
         n = ((unsigned long) n) / (unsigned) base; \
         __res; })
   
   static unsigned char sprint_buf[1024];
   
   int strnlen(const char * s, int count)
   {
     const char *sc;
   
     for (sc = s; count-- && *sc != '\0'; ++sc)
       /* nothing */;
     return sc - s;
   }
   
   unsigned char _ctype[] = {
     _C,_C,_C,_C,_C,_C,_C,_C,   /* 0-7 */
     _C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
     _C,_C,_C,_C,_C,_C,_C,_C,   /* 16-23 */
     _C,_C,_C,_C,_C,_C,_C,_C,   /* 24-31 */
     _S|_SP,_P,_P,_P,_P,_P,_P,_P,  /* 32-39 */
     _P,_P,_P,_P,_P,_P,_P,_P,   /* 40-47 */
     _D,_D,_D,_D,_D,_D,_D,_D,   /* 48-55 */
     _D,_D,_P,_P,_P,_P,_P,_P,   /* 56-63 */
     _P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
     _U,_U,_U,_U,_U,_U,_U,_U,   /* 72-79 */
     _U,_U,_U,_U,_U,_U,_U,_U,   /* 80-87 */
     _U,_U,_U,_P,_P,_P,_P,_P,   /* 88-95 */
     _P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
     _L,_L,_L,_L,_L,_L,_L,_L,   /* 104-111 */
     _L,_L,_L,_L,_L,_L,_L,_L,   /* 112-119 */
     _L,_L,_L,_P,_P,_P,_P,_C,   /* 120-127 */
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  /* 128-143 */
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  /* 144-159 */
     _S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,   /* 160-175 */
     _P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,       /* 176-191 */
     _U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,       /* 192-207 */
     _U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L,       /* 208-223 */
     _L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,       /* 224-239 */
     _L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L};      /* 240-255 */
   
   static int skip_atoi(const char **s)
   {
     int i=0;
   
     while (is_digit(**s))
       i = i*10 + *((*s)++) - '0';
     return i;
   }
   
   static char * number(char * str, long num, int base, int size, int precision
          ,int type)
   {
     char c,sign,tmp[66];
     const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
     int i;
   
     if (type & LARGE)
       digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     if (type & LEFT)
       type &= ~ZEROPAD;
     if (base < 2 || base > 36)
       return 0;
     c = (type & ZEROPAD) ? '0' : ' ';
     sign = 0;
     if (type & SIGN) {
       if (num < 0) {
         sign = '-';
         num = -num;
         size--;
       } else if (type & PLUS) {
         sign = '+';
         size--;
       } else if (type & SPACE) {
         sign = ' ';
         size--;
       }
     }
     if (type & SPECIAL) {
       if (base == 16)
         size -= 2;
       else if (base == 8)
         size--;
     }
     i = 0;
     if (num == 0)
       tmp[i++]='0';
     else while (num != 0)
     tmp[i++] = digits[do_div(num,base)];
     if (i > precision)
       precision = i;
     size -= precision;
     if (!(type&(ZEROPAD+LEFT)))
       while(size-->0)
         *str++ = ' ';
     if (sign)
       *str++ = sign;
     if (type & SPECIAL) {
       if (base==8)
         *str++ = '0';
       else if (base==16) {
         *str++ = '0';
         *str++ = digits[33];
       }
     }
     if (!(type & LEFT))
       while (size-- > 0)
         *str++ = c;
     while (i < precision--)
       *str++ = '0';
     while (i-- > 0)
       *str++ = tmp[i];
     while (size-- > 0)
       *str++ = ' ';
     return str;
   }
   
   int __vsprintf(char *buf, const char *fmt, va_list args)
   {
     int len;
     unsigned long num;
     int i, base;
     char * str;
     const char *s;
   
     int flags;  /* flags to number() */
   
     int field_width; /* width of output field */
     int precision; /* min. # of digits for integers; max
         number of chars for from string */
     int qualifier; /* 'h', 'l', or 'L' for integer fields */
   
     for (str=buf ; *fmt ; ++fmt) {
       if (*fmt != '%') {
         *str++ = *fmt;
         continue;
       }
      
       /* process flags */
       flags = 0;
     repeat:
       ++fmt;  /* this also skips first '%' */
       switch (*fmt) {
       case '-': flags |= LEFT; goto repeat;
       case '+': flags |= PLUS; goto repeat;
       case ' ': flags |= SPACE; goto repeat;
       case '#': flags |= SPECIAL; goto repeat;
       case '0': flags |= ZEROPAD; goto repeat;
       }
     
       /* get field width */
       field_width = -1;
       if (is_digit(*fmt))
         field_width = skip_atoi(&fmt);
       else if (*fmt == '*') {
         ++fmt;
         /* it's the next argument */
         field_width = va_arg(args, int);
         if (field_width < 0) {
       field_width = -field_width;
       flags |= LEFT;
         }
       }
   
       /* get the precision */
       precision = -1;
       if (*fmt == '.') {
         ++fmt; 
         if (is_digit(*fmt))
       precision = skip_atoi(&fmt);
         else if (*fmt == '*') {
       ++fmt;
       /* it's the next argument */
       precision = va_arg(args, int);
         }
         if (precision < 0)
       precision = 0;
       }
   
       /* get the conversion qualifier */
       qualifier = -1;
       if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
         qualifier = *fmt;
         ++fmt;
       }
   
       /* default base */
       base = 10;
   
       switch (*fmt) {
       case 'c':
         if (!(flags & LEFT))
       while (--field_width > 0)
         *str++ = ' ';
         *str++ = (unsigned char) va_arg(args, int);
         while (--field_width > 0)
       *str++ = ' ';
         continue;
   
       case 's':
         s = va_arg(args, char *);
         if (!s)
       s = "";
   
         len = strnlen(s, precision);
   
         if (!(flags & LEFT))
       while (len < field_width--)
         *str++ = ' ';
         for (i = 0; i < len; ++i)
       *str++ = *s++;
         while (len < field_width--)
       *str++ = ' ';
         continue;
   
       case 'p':
         if (field_width == -1) {
       field_width = 2*sizeof(void *);
       flags |= ZEROPAD;
         }
         str = number(str,
        (unsigned long) va_arg(args, void *), 16,
        field_width, precision, flags);
         continue;
   
   
       case 'n':
         if (qualifier == 'l') {
       long * ip = va_arg(args, long *);
       *ip = (str - buf);
         } else {
       int * ip = va_arg(args, int *);
       *ip = (str - buf);
         }
         continue;
   
         /* integer number formats - set up the flags and "break" */
       case 'o':
         base = 8;
         break;
   
       case 'X':
         flags |= LARGE;
       case 'x':
         base = 16;
         break;
   
       case 'd':
       case 'i':
         flags |= SIGN;
       case 'u':
         break;
   
       default:
         if (*fmt != '%')
       *str++ = '%';
         if (*fmt)
       *str++ = *fmt;
         else
       --fmt;
         continue;
       }
       if (qualifier == 'l')
         num = va_arg(args, unsigned long);
       else if (qualifier == 'h') {
         num = (unsigned short) va_arg(args, int);
         if (flags & SIGN)
    num = (short) num;
       } else if (flags & SIGN)
         num = va_arg(args, int);
       else
         num = va_arg(args, unsigned int);
       str = number(str, num, base, field_width, precision, flags);
     }
     *str = '\0';
     return str-buf;
   }
   
   /* 这就我们的 printf, 注意我们将参数输出到串口 UART0,而非标准输出*/
   void uart_printf(char *fmt, ...)
   {
     va_list args;
   
     va_start(args, fmt);
     __vsprintf(sprint_buf, fmt,args);
     va_end(args);
   
     /* 将缓冲区的字符输出到串口*/
     uart_puts(sprint_buf);
   }


   /*
    * 文件 interrupt.c
    * 作用:设置并响应按键中断
    */
   #include "printf.h"
   
   #define GPECON (*(volatile unsigned long *)0x56000040)
   #define GPEDAT (*(volatile unsigned long *)0x56000044)
   #define GPEUP  (*(volatile unsigned long *)0x56000048)
   #define GPFCON (*(volatile unsigned long *)0x56000050)
   #define GPFDAT (*(volatile unsigned long *)0x56000054)
   #define GPFUP  (*(volatile unsigned long *)0x56000058)
   #define GPGCON (*(volatile unsigned long *)0x56000060)
   #define GPGDAT (*(volatile unsigned long *)0x56000064)
   #define GPGUP  (*(volatile unsigned long *)0x56000068)
   #define EINTMASK (*(volatile unsigned long *)0x560000a4)
   #define INTMSK   (*(volatile unsigned long *)0X4a000008)
   #define PRIORITY (*(volatile unsigned long *)0x4a00000c)
   #define EINTPEND (*(volatile unsigned long *)0x560000a8)
   #define INTPND   (*(volatile unsigned long *)0X4a000010)
   #define SRCPND   (*(volatile unsigned long *)0X4a000000)
   
   #define BIT_EINT0 (0x1 << 0)
   #define BIT_EINT2 (0x1 << 2)
   #define BIT_EINT8_23 (0x1 << 5)
   
   #define SET_KEY_INTERRUPT_REG() ({     \
      GPGCON = (GPGCON & (~((3<<12)|(3<<4)))) | ((1<<12)|(1<<4)) ; \
      GPGDAT = GPGDAT & (~((1<<6)|(1<<2)));    \
      GPECON = (GPECON & (~((3<<26)|(3<<22)))) | ((1<<26)|(1<<22)); \
      GPEDAT = GPEDAT & (~((1<<13)|(1<<11)));    \
      GPGCON = (GPGCON & (~((3<<22)|(3<<6)))) | ((2<<22)|(2<<6)) ; \
      GPFCON = (GPFCON & (~((3<<4)|(3<<0)))) | ((2<<4)|(2<<0)) ; \
    })
   
   __inline void ClearPending(int bit)
   {
     SRCPND = bit;
     INTPND = bit;
   }
   
   void init_irq( ) {
     GPFCON = ((0x1<<8) | (0x1 << 10) | (0x1 << 12) | (0x1 << 14));      // Set the led D9~D12 output
     /*
     GPGCON = (GPGCON & (~((3<<12)|(3<<4)))) | ((1<<12)|(1<<4)) ;  // GPGCON6,2 set output 
                                                                         // GPGCON6:KSCAN1
                                                                         // GPGCON2:KSCAN3
     GPGDAT = GPGDAT & (~((1<<6)|(1<<2)));                   // GPGDAT6,2 output 0
   
     GPECON = (GPECON & (~((3<<26)|(3<<22)))) | ((1<<26)|(1<<22));  // GPECON13,11 set output
     GPEDAT = GPEDAT & (~((1<<13)|(1<<11)));                   // GPEDAT13,11 output 0
   
     GPGCON = (GPGCON & (~((3<<22)|(3<<6)))) | ((2<<22)|(2<<6)) ;  // GPGCON11,3 set EINT
     GPFCON = (GPFCON & (~((3<<4)|(3<<0)))) | ((2<<4)|(2<<0)) ;          // GPFDAT2,0 set EINT
     */
     // Use the defined micro instead of above code
     SET_KEY_INTERRUPT_REG();
   
     GPFUP |= (1<<0) | (1<<2);                       // Up
     GPGUP |= (1<<3) | (1<<11);                      // Up 
   
     EINTPEND |= (1 << 19) | (1 << 11);              // Clear eint 11,19
     EINTMASK &= (~((1 << 19) | (1 << 11)));         // Enable EINT11,19
   
     ClearPending(BIT_EINT0|BIT_EINT2|BIT_EINT8_23); // Enable EINT0,2 and the EINT8_23
     INTMSK &= (~0x25);                        
     return;
   }
   
   int Key_Scan( void )
   {
     int i;
     for(i = 0; i < 1000 ;i++) ;
     GPGDAT = (GPGDAT &(~((1<<6)|(1<<2)))) | (1<<6) | (0<<2) ;  //GPG6,2 output 0
     GPEDAT = (GPEDAT &(~((1<<13)|(1<<11)))) | (1<<13) | (1<<11) ; //GPE13,11 output 0
     if(      (GPFDAT&(1<< 0)) == 0 )  return 16 ;
     else if( (GPFDAT&(1<< 2)) == 0 )  return 15 ;
     else if( (GPGDAT&(1<< 3)) == 0 )  return 14 ;
     else if( (GPGDAT&(1<<11)) == 0 )  return 13 ;
   
     GPGDAT = (GPGDAT &(~((1<<6)|(1<<2)))) | (0<<6) | (1<<2) ;  //GPG6,2 output 0
     GPEDAT = (GPEDAT & (~((1<<13)|(1<<11)))) | (1<<13) | (1<<11) ; //GPE13,11 output 0
     if(      (GPFDAT&(1<< 0)) == 0 )  return 11 ;
     else if( (GPFDAT&(1<< 2)) == 0 )  return 8 ;
     else if( (GPGDAT&(1<< 3)) == 0 )  return 5 ;
     else if( (GPGDAT&(1<<11)) == 0 )  return 2 ;
   
     GPGDAT = (GPGDAT & (~((1<<6)|(1<<2)))) | (1<<6) | (1<<2) ;  //GPG6,2 output 0
     GPEDAT = (GPEDAT & (~((1<<13)|(1<<11)))) | (1<<13) | (0<<11) ; //GPE13,11 output 0
     if(      (GPFDAT&(1<< 0)) == 0 )  return 10 ;
     else if( (GPFDAT&(1<< 2)) == 0 )  return 7 ;
     else if( (GPGDAT&(1<< 3)) == 0 )  return 4 ;
     else if( (GPGDAT&(1<<11)) == 0 )  return 1 ;
   
     GPGDAT = (GPGDAT & (~((1<<6)|(1<<2)))) | (1<<6) | (1<<2) ;  //GPG6,2 output 0
     GPEDAT = (GPEDAT & (~((1<<13)|(1<<11)))) | (0<<13) | (1<<11) ; //GPE13,11 output 0
     if(      (GPFDAT&(1<< 0)) == 0 )  return 12 ;
     else if( (GPFDAT&(1<< 2)) == 0 )  return 9 ;
     else if( (GPGDAT&(1<< 3)) == 0 )  return 6 ;
     else if( (GPGDAT&(1<<11)) == 0 )  return 3 ;
     else return 0xff ;
   }
   
   void EINT_Handle( void ) {
     GPGCON = (GPGCON & (~((3<<22)|(3<<6)))) | ((0<<22)|(0<<6)) ; //GPG11,3 set input
     GPFCON = (GPFCON & (~((3<<4)|(3<<0)))) | ((0<<4)|(0<<0)) ;         //GPF2, 0 set input
   
     if(INTPND==BIT_EINT8_23) {
       if(EINTPEND&(1<<11)) 
         EINTPEND |= 1<< 11;
   
       if(EINTPEND&(1<<19))   
         EINTPEND |= 1<< 19;
   
       ClearPending(BIT_EINT8_23);
     } 
     else if(INTPND==BIT_EINT0) {
       ClearPending(BIT_EINT0); 
     } else if(INTPND==BIT_EINT2) {
       ClearPending(BIT_EINT2);
     }
    
     int key = Key_Scan() ;
     if( key != 0xff ) {
       uart_printf( "K%d is pressed!\n", key ) ;
       GPFDAT = ~(key << 4);
     }
   
     SET_KEY_INTERRUPT_REG();
     return;
   }

这个文件里大部分代码都是依芯片手册来设置,请自行查找对照吧。


   /* 
    * 文件 main.c
    * 作用:测试代码
    */
   #include "serl.h"
   #include "printf.h"
   
   #define GPFCON (*(volatile unsigned long *)0x56000050)
   #define GPFDAT (*(volatile unsigned long *)0x56000054)
   
   int main()
   { 
     init_uart();
     GPFDAT = 0x0;
     uart_printf("starting:\n");
     return 0;
   }

 

   # Makefile for compiling ARM program
   # Author: Jianbin Wang
   CC=arm-linux-gcc
   CFLAGS=-Wall -g -c
   LD=arm-linux-ld
   LDFLAGS:=$(LDFLAGS) -Ttext 0x30000000
   
   INCLUDES=-I./
   CFLAGS:=$(CFLAGS) $(INCLUDES)
   LIBS=-lgcc -L/usr/local/arm/3.3.2/lib/gcc-lib/arm-linux/3.3.2
   
   CONVERT=arm-linux-objcopy
   CVFLAGS=-O binary -S
   RM=rm -f
   
   SRCDIRS=.
   TARGET=main
   TMPOBJ=$(TARGET)_tmp.o
   SRCS=$(foreach dir,$(SRCDIRS),$(wildcard $(dir)/*.c $(dir)/*.s))
   OBJS=head.o mem.o flash.o nand_read.o main.o printf.o serl.o interrupt.o
   
   all:$(TARGET)
   
   $(TARGET):$(OBJS)
    $(LD) $(LDFLAGS) -o $(TMPOBJ) $(OBJS) $(LIBS)
    $(CONVERT) $(CVFLAGS) $(TMPOBJ) $(TARGET)
   
   $(OBJS):$(SRCS)
    $(CC) $(CFLAGS) $(SRCS)
   
   clean:
    $(RM) $(TARGET)
    $(RM) $(TMPOBJ)
    $(RM) $(OBJS)

 


三、编译、烧写、测试
   Make 一下就会生成我们要的文件 main, 将其通过 JTAG 烧入 Nand Flash。用超级终连接到开发板,注意波特率设为 57600,数据位 8,无奇偶校正,停止位1,无数据流控制。现在 Reset 一下的开发板,然后静静的等待吧,生成的二进制文件 main 有 39K 大呢,要等它完全复制到 SDRAM 至少要两三分钟...哈哈,你会发现 D9~D12 四个led 灯被点亮了,并且当你按下某个按键时,这四个灯会指示你按下的是第几个键,你还会发超级终端上有文字显示,例如当你按下按键 2 时:

     K2 is pressed!


关键字:FS2410  中断编程 引用地址:ARM 系列 -- FS2410 开发板上的中断编程

上一篇:s3c2410 board.c分析
下一篇:基于S3C2440的LCD1602驱动的设计

推荐阅读最新更新时间:2024-03-16 15:22

TMS320C32 DSP的中断编程方法
  TMS320C3X系列芯片是美国TI公司推出的第一代浮点DSP芯片,具有丰富的指令集、很高的运算速度、较大的录址空间和较高的性价比,在各领域得到了广泛的应用。TMS320C32是TMS320系列浮点数字信号处理器的新产品,在TMS320C30和TMS320C31的基础上进行了简化和改进。在结构上的改进主要包括可变宽度的存储器接口、更快速的指令周期时间、可设置优先级的双通道DMA处理器、灵活的引导程序装入方式、可重新定位的中断向量表以及可选的边缘/电平触发中断方式等。对TMS320C32的开发可以用汇编语言,也可以用C语言。使用汇编语言的优点在于运行速度快、可以充分利用芯片的硬件特性,但开发速度较慢,程序的可读性差;而C语言的优
[嵌入式]
ARM2200下的中断编程
一. 前言 中断的基本概念: 1. 在ARM中最多有32个中断请求 2. 16个向量IRQ中断(16个向量中断的VIC中断号已经有系统定义好了。在p208中可查看) (16个向量中断有16个优先级,可动态分配给中断请求) 3. 可产生软件中断。 4. 中断的优先级资源:中断的优先级资源就是CPU的中断系统。以ARM7体系的CPU为例,最多可以有32个中断资源。对于每个具体的中断源可以将其设定为FIQ,使其具有最高优先级,但FIQ 最好是分配给唯一的中断源,否则就失去意义;也可以设定为向量IRQ,使其具有中等优先级,但向量IRQ的总数不能超过16个,这些中断源优先级的高低按向量编号从0(最高)到15(最
[单片机]
MCU编程实践之红外中断定时12864逐步讲解一锅烩
这个其实是红外遥控编程过程实验,思路是这样的: 红外接收管采用中断口接收信号; 在中断子程序中,在高低电平的变化时,用计时器来计时其高、低电平维持的时长; 将时长值直接用二进制数显示在12864屏上; 以us为单位,手工计算各显示数值的时长值(这个可以不做); 根据红外遥控NEC标准,分析该遥控器的引导码及比特码的高低电平时长组成,以此获得引导码和比特码的高低电平时长特征值, 依据得到的引导码和比特码的高低电平时长特征值,再编制程序获得该遥控器的用户码值、各键的键值。 依据得到的用户码值、各键的键值,接下来就可以做遥控控制操作编程了。 这一套操作下来,你就拥有了获得各种红外遥控器用户码和键值的工具了,然后,你可以给家
[单片机]
【ARM】按键·查询式控制led灯·fs2410
开发环境 硬件平台:FS2410 主机:Ubuntu 12.04 LTS LED灯原理图 按键原理图 按键的接线资源 KSCAN0 - GPE11 KSCAN1 - GPG6 KSCAN2 - GPE13 KSCAN3 - GPG2 EINT0 - GPF0 EINT2 - GPF2 EINT11 - GPG3 EINT19 - GPG11 程序主要原理 这里实现对 K1,K2,K3,K4 这四个键的查询。 主要涉及到K1,K2, K3, K4这四个按键,要用查询的方式进行判断哪个键被按下去了,因此: 将EINT11, EINT19设置为输入,用于读取;
[单片机]
【ARM】按键·查询式控制led灯·<font color='red'>fs2410</font>
负跳变沿触发中断(汇编程序)
;了解8051的timer0中断的程序写法,用中断法产生定时,是灯明一下,暗一下,如果出现外部中断的时候,在进入外部中断服务程序 COUNT EQU 9217 ;对于11.0592的晶振来说,延时10ms LED1 EQU P1.1 LED2 EQU P1.3 ;作为外部中断0的显示来用 ; ORG 0000H LJMP RESET ;跳转到初始化程序中 ORG 0003H ; INT0(外部中断0) LJMP INT_ET0 ;跳转到外部中断0服务程序中去 (此程序中断就是从这进入的) ORG 000BH ;TI
[单片机]
6502单片机编程--中断
中断: 6502有3个中断IRQ/BRK、NMI和RESET,每个中断都有一个16位的向量,即指针,用来存放该中断发生时中断服务函数的地址。中断发生时CPU都会把状态标志和返回地址压栈,然后调用中断服务程序。 IRQ/BRK中断由一下两种情况产生:一是软件通过BRK指令产生,一是硬件通过IRQ引脚产生。 RESET在开机的时候触发,这是ROM被装入,6502跳到RESET向量指向的地址没有寄存器被修改,没有内存被清空,这些都只在开机是发生。 NMI指不可屏蔽中断,它在VBlank即屏幕刷新时发生,持续时间根据系统(NTSC/PAL)不同而不同。NTSC是每秒60次,而PAL是每秒50次。6502的中断延时是7个时钟周期,也就是说,
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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