一、目的
中断服务程序在操作系统中无疑占有非常重要的地位,编写中断程序不仅要会运用底层的汇编语言,还要了解 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!
上一篇:s3c2410 board.c分析
下一篇:基于S3C2440的LCD1602驱动的设计
推荐阅读最新更新时间:2024-03-16 15:22
设计资源 培训 开发板 精华推荐
- 算力进化,向智而生: 英特尔为企业智能化发展注入新动力
- 世强硬创入选深圳软件产业扶持计划,赋能电子元器件产业链上下游
- 车规级UFS 4.0将变得愈发重要
- 英飞凌AURIX™ TC3x新增支持FreeRTOS
- 意法半导体推出 STM32WL33 低功耗、长距离无线微控制器,扩展专用生态系统
- 强化PSA安全生态,安谋科技为无处不在的物联网设备筑牢“安全”底座
- 尼得科旗下利莱森玛电机科技(福州)首台国产1E级发电机成功下线
- 艾迈斯欧司朗和弗劳恩霍夫研究团队凭借“数字之光”项目荣获“德国未来奖”
- 介绍一种基于规则的车载网络入侵检测技术
- 基于Nordic nRF54L系列的低功耗蓝牙模块助力下一代物联网应用性能升级