一、目的
中斷服務(wù)程序在操作系統(tǒng)中無(wú)疑占有非常重要的地位,編寫(xiě)中斷程序不僅要會(huì)運(yùn)用底層的
匯編語(yǔ)言,還要了解 ARM 的體系架構(gòu)。那這一節(jié)我們就通過(guò)中斷編程來(lái)響應(yīng) FS2410開(kāi)發(fā)板
上的 16 個(gè)按鍵,實(shí)現(xiàn)依次按下16個(gè)鍵時(shí),D9~D12 四個(gè) Led 從 0~15 進(jìn)行計(jì)數(shù),并通
過(guò)上個(gè)實(shí)驗(yàn)實(shí)現(xiàn)的 uart_printf 向串口發(fā)送數(shù)據(jù) Kn is pressed!。
二、代碼
我們直接分析代碼,代碼中只有簡(jiǎn)略的注釋,必要時(shí)我會(huì)在整個(gè)代碼文件的后面對(duì)相應(yīng)的細(xì)節(jié)
進(jìn)行解釋。先來(lái)分析 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 }^
呵呵,不知不覺(jué) head.s 的代碼已經(jīng)很長(zhǎng)了,我們來(lái)看一下它的執(zhí)行流程:
(1) 設(shè)置中斷向量表。你也許在這里有疑問(wèn),為什么一開(kāi)始就有 8 個(gè)分支跳轉(zhuǎn)指令?我們
先來(lái)研究一下 ARM 如何響應(yīng)異常/中斷,看下表:
-------------------------------------------------------------
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 種異常/中斷,每種異常/中斷都有固定的地址,這個(gè)地址叫
中斷向量,一般我們會(huì)在這個(gè)地址放一條分支跳轉(zhuǎn)指令,當(dāng)異常/中斷發(fā)生時(shí),ARM 就
到這個(gè)地址執(zhí)行這個(gè)跳轉(zhuǎn)指令,從而調(diào)用相應(yīng)的中斷服務(wù)程序。
等等,這里是不是有點(diǎn)問(wèn)題?呵呵,你也許已經(jīng)發(fā)現(xiàn)了,這里只有 7 種異常/中斷,那
我們的程序怎么會(huì)有 8 條分支跳轉(zhuǎn)指令呢? 因?yàn)橹袛嘞蛄考吹刂?0x00000014 被ARM
保留用做將來(lái)擴(kuò)展之用,但我們還需用一條指令(4字節(jié))來(lái)填充這個(gè)位置,只不過(guò)它不會(huì)
被 ARM 執(zhí)行。
(2) 關(guān)閉看門(mén)狗
(3) 暫時(shí)屏蔽所有中斷。
1.地址 0x4A000008 是中斷屏寄存器 INTMSK 的端口地址,復(fù)位 INTMSK 會(huì)導(dǎo)致所有
的中斷源被屏掉。
2.地址 0x4A00001C 是子中斷屏寄存器 INTSUBMSK 的端口地址,它的低 11 位對(duì)應(yīng)外
部 11 個(gè)中斷源,高 21 位保留不用。復(fù)位它的低 11 位會(huì)導(dǎo)致相應(yīng)的外部中斷被屏。
(4) 初始化內(nèi)存 SDRAM 設(shè)置
(5) Self-copying: 從 Nand Flash 將自身復(fù)制到 SDRAM
(6) 進(jìn)入 IRQ 模式,設(shè)置 IRQ 模式下的堆棧寄存器
(7) 進(jìn)入系統(tǒng)模式,并設(shè)置系統(tǒng)模式下的堆棧寄存器
(8) 系統(tǒng)模式下調(diào)用 init_irq,這個(gè)函數(shù)用于初始化一些用于響應(yīng)按鍵的中斷寄存器
(9) 再次進(jìn)入系統(tǒng)模式,并打開(kāi)當(dāng)前程序狀態(tài)寄存器 cpsr 的 IRQ 中斷位,這樣 ARM 就能
響應(yīng) IRQ 中斷了
(10)執(zhí)行主函數(shù) main 后返回,然后進(jìn)入死循環(huán),等待中斷發(fā)生
(11)中斷發(fā)生時(shí),ARM 響應(yīng)中斷并于 0x00000018 處執(zhí)行 b HandleIRQ 跳轉(zhuǎn)指令調(diào)用中斷服
務(wù)程序,處理完畢后返回循環(huán)處再等待下次中斷的發(fā)生,如此往復(fù)...
這就是中斷處理的基本流程了 :-), 以下文件的代碼在前面隨筆均有詳細(xì)說(shuō)明,這里就僅附
簡(jiǎn)略注釋了
@ 文件 flash.s
@ 作用:設(shè)置 Nand Flash 的控制寄存器、讀取 Nand Flash
@ 中的代碼到 SDRAM 的指定位置,更多細(xì)節(jié)請(qǐng)參考我前面的隨筆
.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 中讀取一塊數(shù)據(jù)到 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
* 作用:定義相關(guān)寄存器、UART 初始化函數(shù)聲明、串口讀寫(xiě)函數(shù)的聲明
*/
#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
* 作用:對(duì)外提供調(diào)用接口 uart_printf
*/
#ifndef __PRINTF_HH__
#define __PRINTF_HH__
void uart_printf(char *fmt, ...);
#endif
/*
* 文件 printf.c
* 文件中大部分代碼來(lái)自 linux 0.11 內(nèi)核的 vsprintf.c, 只是作了相應(yīng)的刪減,
* <<Linux 內(nèi)核完全注釋>> 上有更詳細(xì)的解釋
*/
#include <stdarg.h>
#include <linux/types.h>
#include <linux/ctype.h>
#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 = "<NULL>";
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, 注意我們將參數(shù)輸出到串口 UART0,而非標(biāo)準(zhǔn)輸出*/
void uart_printf(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
__vsprintf(sprint_buf, fmt,args);
va_end(args);
/* 將緩沖區(qū)的字符輸出到串口*/
uart_puts(sprint_buf);
}
/*
* 文件 interrupt.c
* 作用:設(shè)置并響應(yīng)按鍵中斷
*/
#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;
}
這個(gè)文件里大部分代碼都是依芯片手冊(cè)來(lái)設(shè)置,請(qǐng)自行查找對(duì)照吧。
/*
* 文件 main.c
* 作用:測(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)
三、編譯、燒寫(xiě)、測(cè)試
Make 一下就會(huì)生成我們要的文件 main, 將其通過(guò) JTAG 燒入 Nand Flash。用超級(jí)終
連接到開(kāi)發(fā)板,注意波特率設(shè)為 57600,數(shù)據(jù)位 8,無(wú)奇偶校正,停止位1,無(wú)數(shù)據(jù)流控制。現(xiàn)
在 Reset 一下的開(kāi)發(fā)板,然后靜靜的等待吧,生成的二進(jìn)制文件 main 有 39K 大呢,要等它
完全復(fù)制到 SDRAM 至少要兩三分鐘...哈哈,你會(huì)發(fā)現(xiàn) D9~D12 四個(gè)led 燈被點(diǎn)亮了,并且
當(dāng)你按下某個(gè)按鍵時(shí),這四個(gè)燈會(huì)指示你按下的是第幾個(gè)鍵,你還會(huì)發(fā)超級(jí)終端上有文字顯示,
例如當(dāng)你按下按鍵 2 時(shí):
K2 is pressed!
呵呵,是不是很酷,你明白為 ARM 編寫(xiě)中斷的流程了嗎 :-)