STEP 1:分析电路图.打开开发板底板的电路图,注意一般开发板厂商会提供2个电路图,一个是核心板的电路图,一个是开发板从核心板外接设备的电路图,后者就简称为底板电路图吧。可以从底板电路图中找到2个按键的电路图,当键被按下时,会产生按键中断信号。按键产生的中断信号经过CPLD逻辑处理后连接到CPU的中断引脚。
(什么是CPLD?CPLD是一种用户根据各自需要而自行构造逻辑功能的数字集成电路,具体作用可以查看CPLD内部逻辑图,本开发板的CPLD有4个锁存,StatusReg0,StatusReg1,CtrReg0,CtrReg1,这个4个锁存分别由4个片选信号CS0,CS1,CS2,CS3打开,这个4个片选信号是由一个3:8译码器引出,3:8译码器的输入的三个信号是来自地址总线上的A18,A19,A20.而3:8译码器则由cpu直接控制。同时,这4个锁存是挂在数据总线上面的,当某个锁存的片选信号有效,选中相应锁存,锁存就可以从数据总线上面读取或写入数据。通过CPLD,可以使N个外设通过CPLD的控制共用一个中断信号了!)
按键中断EINT0是由状态寄存器StatusReg1与中断控制器CtrReg1来控制的,并且按键中断EINT0与外部中断共享了一个cpu中断,在初始状态,这些引脚信号为高电平。
中断控制寄存器CtrReg1是8位只写寄存器。它的每个位分别控制了一个外部中断。其中按键中断EINTT0位于BIT1位。往寄存器相应位写1,则相应中断被屏蔽;相应位清零,则相应中断被打开。
状态寄存器StatusReg1是8位只读寄存器。它的每个位分别实时反映了一个外部中断信号的状态,比如,当按键KEY2没有按下,则中断信号引脚EINT0为高电平,此时寄存器StatusReg1的BIT1位为高电平;当有键按下,EINT0变为低电平,则StatusReg1的BIT1位也变为低电平。
总之,CPLD的2种寄存器,CtrReg寄存器控制着打开某个信号还是屏蔽某个信号,StatusReg寄存器记录着外部设备输入的信号。最后,CtrReg与StatusReg经过逻辑运算,输出信号到CPU。
STEP 2:编写驱动程序dri.c.以下代码均在一个文件dri.c中.
#include <linux/config.h> #include <linux/module.h> #include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <asm/hardware.h> #include <asm/delay.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm-arm/arch-s3c2410/regs-gpio.h> #include <asm-arm/arch-s3c2410/regs-mem.h> #include <asm/arch/regs-gpio.h> #include <asm/io.h> #include <asm-arm/arch-s3c2410/irqs.h> #include <asm-arm/irq.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/cdev.h> #include <linux/devfs_fs_kernel.h>
|
#define DEVICE_NAME "key" #define keyRAW_MINOR 1 #define KEY_UP 0 #define KEY_DOWN 1 #define MAX_KEY_BUF 4 #define KEY_TIMER_DELAY (HZ/100) #define BUF_HEAD (keydev.buf[keydev.head]) #define BUF_TAIL (keydev.buf[keydev.tail]) #define INCBUF(x,mod) ((++(x)) & ((mod) - 1)) #define CPLD_BANK0_BASE (0xE0000000)
|
static int irq_no = 0; typedef struct { unsigned int keystatus; int buf[MAX_KEY_BUF]; unsigned int head, tail; wait_queue_head_t wq; struct timer_list key_timer; spinlock_t lock; } KEY_DEV; static KEY_DEV keydev; static int keyMajor = 0; #undef DEBUG //#define DEBUG
#ifdef DEBUG #define DPRINTK(x...) {printk(__FUNCTION__"(%d): ",__LINE__);printk(##x);} #else #define DPRINTK(x...) (void)(0) #endif #define MAX_TIME 5 //MAX LED wait time
void init_gpio(void);
|
static int iskey_down(unsigned long irq){ int reg,gpio_no; irq_no = (int)irq; irq_no = irq_no - 44;//EINT4~9,20~23 if (irq_no < 8){ reg = __raw_readl(S3C2410_GPFDAT); gpio_no = irq_no; } else{ reg = __raw_readl(S3C2410_GPGDAT); gpio_no = irq_no - 8; } if (reg & (1 << gpio_no)){ return 0;//key up } else{ return 1;//key still down } }
|
static irqreturn_t key_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned char vector,tmp; vector = inb(CPLD_BANK0_BASE + 0x02200000); tmp = inb(CPLD_BANK0_BASE + 0x02600000); if(0 == (vector & 0x4)){ printk("key1\n"); outb(tmp | (1<<2), CPLD_BANK0_BASE+0x02600000); inb(CPLD_BANK0_BASE + 0x02600000); outb(tmp & (~(1<<2)), CPLD_BANK0_BASE+0x02600000); } else if(0 == (vector & 0x2)){ printk("key2\n"); outb(tmp | (1<<1), CPLD_BANK0_BASE+0x02600000); inb(CPLD_BANK0_BASE + 0x02600000); outb(tmp & (~(1<<1)), CPLD_BANK0_BASE+0x02600000); } return IRQ_HANDLED; }
|
static int s3c2410_key_open(struct inode *inode, struct file *filp) { int ret; DPRINTK("open\n"); printk("open\n"); set_irq_type(IRQ_EINT9,IRQT_FALLING); ret = request_irq(IRQ_EINT9, key_interrupt, SA_SHIRQ, DEVICE_NAME, inode); if(ret) { printk("IRQ_EINT9: could not register interrupt\n"); return ret; } printk("register IRQ_EINT9 success\n"); return 0; }
|
static int s3c2410_key_release(struct inode *inode, struct file *filp) { DPRINTK("release\n"); free_irq(IRQ_EINT9,NULL); return 0; }
|
static struct file_operations s3c2410_key_fops = { .owner = THIS_MODULE, .open = s3c2410_key_open, .release = s3c2410_key_release, };
|
static int __init s3c2410_key_init(void) { int ret; unsigned int bswcon = inl((unsigned int)S3C2410_BWSCON); bswcon = (bswcon & 0xFFFCFFFF) | 0x00000000; outl(bswcon,(unsigned int)S3C2410_BWSCON); bswcon = inl((unsigned int)S3C2410_BWSCON); outb(0xFF,0xE0000000+0x02600000);//关闭cpld的网卡中断控制
outb(0xF9,0xE0000000+0x02600000);//打开cpld的网卡中断控制
ret = register_chrdev(keyMajor, DEVICE_NAME, &s3c2410_key_fops); if (ret < 0) { printk(DEVICE_NAME " can't get major number\n"); return ret;
}
keyMajor = ret; #ifdef CONFIG_DEVFS_FS devfs_mk_cdev(MKDEV(keyMajor,keyMajor),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME); #endif printk(DEVICE_NAME " initialized\n"); return 0; }
|
static void __exit s3c2410_key_exit(void) { #ifdef CONFIG_DEVFS_FS devfs_remove(DEVICE_NAME); #endif unregister_chrdev(keyMajor, DEVICE_NAME); printk(DEVICE_NAME " removed\n"); }
|
module_init(s3c2410_key_init); module_exit(s3c2410_key_exit); MODULE_ALIAS("key"); MODULE_DESCRIPTION("S3C2410 KEY DRIVER"); MODULE_LICENSE("GPL");
|
STEP 3:编写Makefile.并执行make,生成dri.ko模块。
obj-m := dri.o
KERNELBUILD := /usr/src/kernels/linux-2.6.14/
default:
make -C $(KERNELBUILD) M=$(shell pwd) modules
clean: rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions
|
STEP 4:编写测试驱动程序testdri.c.并交叉编译arm-linux-gcc testdri.c -o testdri.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> int main(int argc, char *argv[]) { int time; int key_fd; key_fd = open("/dev/key", 0); if (key_fd < 0) { perror("open device key"); exit(1); } return 0; }
|
STEP 5:连接开发板到PC。在开发板的终端输入:
mount -o nolock 192.192.192.192:/home /mnt
|
将主机home目录挂载到开发板上,进入模块所在目录,在开发板的终端输入:
STEP 6:进入测试应用程序所在目录,输入:
参考:《linux device drivers》
《英蓓特EduKit文档》
阅读(1272) | 评论(0) | 转发(0) |