Chinaunix首页 | 论坛 | 博客
  • 博客访问: 429047
  • 博文数量: 123
  • 博客积分: 2686
  • 博客等级: 少校
  • 技术积分: 1349
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-23 22:11
文章分类
文章存档

2012年(3)

2011年(10)

2010年(100)

2009年(10)

我的朋友

分类: LINUX

2010-04-05 13:09:17

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目录挂载到开发板上,进入模块所在目录,在开发板的终端输入:

insmod dri


STEP 6:进入测试应用程序所在目录,输入:

./testdri


 

参考:《linux device drivers》

     《英蓓特EduKit文档》

阅读(1243) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~