×
单片机 > 单片机程序设计 > 详情

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

发布时间:2020-06-05 发布时间:
|

一、目的
   中断服务程序在操作系统中无疑占有非常重要的地位,编写中断程序不仅要会运用底层的汇编语言,还要了解 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!



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
AVR熔丝位操作时的要点和需要注意的相关事项