一个简单的linux系统下的5*5的键盘驱动

来源:互联网 发布:linux tcp 发送缓冲区 编辑:程序博客网 时间:2024/06/08 05:48

昨天花了点时间为了SEP4020评估版(基于ARM720t内核)写了一个键盘驱动,键盘是一个5*5的键盘,利用5根外部中断和5根gpio口线实现行扫描键盘。

5个外部中断分别为:中断1,2,3,4,5通过对gpio的portA复用得到。

5根口线是gpio的portD的低5位。

一下是我的代码:

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
//#include <linux/irq.h>
#include <linux/interrupt.h>  //不要忘了这个头文件,后面的中断需要用

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/hardware.h>

#define KEY_MAJOR 254 /* 人为分配一个主设备号*/
#define mask_irq(intnum)  *(volatile unsigned long*)INTC_IMR_V |= (1 << intnum)  //屏蔽中断位
#define unmask_irq(intnum)  *(volatile unsigned long*)INTC_IMR_V &= ~(1 << intnum)  //打开屏蔽中断位
#define IrqEnable(intnum)  *(volatile unsigned long*)INTC_IER_V |= (1 << intnum)   //打开中断

int keynum =0;
char *KEY_MAP[]={"KEY_*","KEY_Confirm","KEY_exit","KEY_Trade","KEY_F4",
    "KEY_#","KEY_FeedPaper","KEY_Del","KEY_Right","KEY_F3",
    "KEY_9","KEY_6","KEY_3","KEY_Down","KEY_Up",
    "KEY_8","KEY_5","KEY_2","KEY_Left","KEY_F2",
    "KEY_7","KEY_4","KEY_1","KEY_0","KEY_F1"};

/*给键盘创一个结构体,还可以在key_dev中添加key的信息,
这里为了简单就放了字符设备必须的最基本结构体*/
 cdev
 struct key_dev
{
 struct cdev cdev;

};
 
struct key_dev *dev;

 


static void unmaskkey(void)
{
 unmask_irq(INTSRC_EXTINT0);
 unmask_irq(INTSRC_EXTINT1);
 unmask_irq(INTSRC_EXTINT2);
 unmask_irq(INTSRC_EXTINT3); 
 unmask_irq(INTSRC_EXTINT4);
 
 IrqEnable(INTSRC_EXTINT0);
 IrqEnable(INTSRC_EXTINT1);
 IrqEnable(INTSRC_EXTINT2);
 IrqEnable(INTSRC_EXTINT3);
 IrqEnable(INTSRC_EXTINT4); 
}

static void maskkey(void)
{
 mask_irq(INTSRC_EXTINT0);
 mask_irq(INTSRC_EXTINT1);
 mask_irq(INTSRC_EXTINT2);
 mask_irq(INTSRC_EXTINT3);
 mask_irq(INTSRC_EXTINT4);
}

static void write_row(int index, int HighLow)
{
 switch(index)
 {
  case 0:
   if(HighLow) *(volatile unsigned long*)GPIO_PORTD_DATA_V |= 0x01;
   else *(volatile unsigned long*)GPIO_PORTD_DATA_V &= ~0x01;break;
      case 1:
   if(HighLow) *(volatile unsigned long*)GPIO_PORTD_DATA_V |= 0x02;
   else *(volatile unsigned long*)GPIO_PORTD_DATA_V &= ~0x02;break;
      case 2:
   if(HighLow) *(volatile unsigned long*)GPIO_PORTD_DATA_V |= 0x04;
   else *(volatile unsigned long*)GPIO_PORTD_DATA_V &= ~0x04;break;
     case 3:
   if(HighLow) *(volatile unsigned long*)GPIO_PORTD_DATA_V |= 0x08;
   else *(volatile unsigned long*)GPIO_PORTD_DATA_V &= ~0x08;break;
     case 4:
   if(HighLow) *(volatile unsigned long*)GPIO_PORTD_DATA_V |= 0x10;
   else *(volatile unsigned long*)GPIO_PORTD_DATA_V &= ~0x10;break;
         break;
 }
}

static int read_col(int index)
{
 return ( *(volatile unsigned long*)GPIO_PORTA_DATA_V >>  index   ) & 0x1;
}

static void sep4020_key_setup(void)
{
        *(volatile unsigned long*)GPIO_PORTD_SEL_V  |= 0x1F ;      //for common use
        *(volatile unsigned long*)GPIO_PORTD_DIR_V  &= (~0x1F);    //0 stands for OUT
        *(volatile unsigned long*)GPIO_PORTD_DATA_V &= (~0x1F);    //输出拉低

        *(volatile unsigned long*)GPIO_PORTA_SEL_V |= 0x001F ;       //for common use
        *(volatile unsigned long*)GPIO_PORTA_DIR_V |= 0x001F ;          //1 stands for in
        *(volatile unsigned long*)GPIO_PORTA_INTRCTL_V |= 0x03FF;    //中断类型为低电平解发
        *(volatile unsigned long*)GPIO_PORTA_INCTL_V |= 0x001F;      //中断输入方式
        *(volatile unsigned long*)GPIO_PORTA_INTRCLR_V |= 0x001F;    //清除中断
        *(volatile unsigned long*)GPIO_PORTA_INTRCLR_V = 0x0000;          //清除中断
        unmaskkey();
}

//中断处理函数
static irqreturn_t sep4020_key_irqhandler(int irq, void *dev_id, struct pt_regs *reg)
{
 int i = 0;
 int j = 0;
 int col = 0, row = 0;
 int Init_Col = 0;
 int col_count = 0, row_count = 0, delay;

 maskkey();           //关中断,中断内不允许有中断
 *(volatile unsigned long*)GPIO_PORTA_INTRCLR_V |= 0x001F;
 *(volatile unsigned long*)GPIO_PORTA_INTRCLR_V = 0x0000;

 Init_Col = (*(volatile unsigned long*)(INTC_ISR_V)>>1) & 0x001F;
 for( i = 0 ; i < 5 ; i ++)    //获取列数
 {
  if( (Init_Col >> i) & 0x01 ) 
  {
   col = i ;
   col_count ++;
  }
 }
 if ((!read_col(col)))
 {
  for(i=0;i<5;i++)    //通过对轮流PD0~PD4输出高电平;获取行数。
         {
      write_row(i,1);
        for(delay=0;delay<100;delay++); //delay
        if( read_col( col ) )
         {
         row = i ;
          row_count++;
         }
        write_row(i,0);
                }
        }
 if(col_count==1 && row_count==1)
 {
      keynum = col*5 + row; 
  j = *(volatile unsigned long*)GPIO_PORTA_DATA_V;
//  printk("the value of j %x/r/n",j);
  while((j&0x1f) != 0x1f)
  {
   for(delay=0;delay<10000;delay++); //delay
   j = *(volatile unsigned long*)GPIO_PORTA_DATA_V;
  }

  printk("key_value =%s/r/n",KEY_MAP[keynum]);
 }
 unmaskkey();
// return 0;
 return IRQ_HANDLED;
}

//一下函数是为了对应字符设备的操作,由于key只是一个输入设备,因此这些函数都没写,但是为了完整的表现一个字符设备,这里都列出来了
static ssize_t sep4020_key_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
 return 0;
}

static ssize_t sep4020_key_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
 return 0;
}

int sep4020_key_open(struct inode *inode, struct file *filp)
{
 return 0;
}

int sep4020_key_release(struct inode *inode, struct file *filp)
{
 return 0;
}

//将我们所写的read,write,open,release填充到file_operations结构体中
static const struct file_operations sep4020_key_fops =
{
 .owner = THIS_MODULE,
 .read  = sep4020_key_read,
 .write = sep4020_key_write,
 .open  = sep4020_key_open,
 .release = sep4020_key_release,
};

//申请中断函数,利用这个函数向系统登记中断处理函数
static int sep4020_request_irqs(void)
{
 request_irq(INTSRC_EXTINT0,sep4020_key_irqhandler,SA_INTERRUPT,"4020KEY",NULL);
        request_irq(INTSRC_EXTINT1,sep4020_key_irqhandler,SA_INTERRUPT,"4020KEY",NULL);
        request_irq(INTSRC_EXTINT2,sep4020_key_irqhandler,SA_INTERRUPT,"4020KEY",NULL);
        request_irq(INTSRC_EXTINT3,sep4020_key_irqhandler,SA_INTERRUPT,"4020KEY",NULL);
        request_irq(INTSRC_EXTINT4,sep4020_key_irqhandler,SA_INTERRUPT,"4020KEY",NULL);
 return 0;
}

//释放中断函数
static void sep4020_free_irqs(void)
{
 free_irq(INTSRC_EXTINT0,NULL);
        free_irq(INTSRC_EXTINT1,NULL);
        free_irq(INTSRC_EXTINT2,NULL);
        free_irq(INTSRC_EXTINT3,NULL);
        free_irq(INTSRC_EXTINT4,NULL);
}

//模块加载函数,
static int __init sep4020_key_init(void)
{
 int err,result;
 dev_t devno = MKDEV(KEY_MAJOR, 0);
 if(KEY_MAJOR)
  result = register_chrdev_region(devno, 1, "sep4020_key");//向系统申请设备号
 else
 {
  result = alloc_chrdev_region(&devno, 0, 1, "sep4020_key");//如果不已知设备号,向系统动态申请未被占用的设备号  
 }
 
 if(result < 0)
  return result;
 
 /*动态申请设备结构体的内存*/
 dev = kmalloc(sizeof(struct key_dev),GFP_KERNEL);
 if (!dev)
 {
  result = -ENOMEM;
  goto fail_malloc;
 }
 memset(dev,0,sizeof(struct key_dev));
 /*注册中断函数*/
 
 sep4020_request_irqs();
//键盘初始化
 sep4020_key_setup();
 cdev_init(&(dev->cdev), &sep4020_key_fops);
 dev->cdev.owner = THIS_MODULE;
//cdev_add注册字符设备
 err = cdev_add(&dev->cdev, devno, 1);
 if(err)
  printk("adding err/r/n"); 
 return 0;
 
 //错误返回
 fail_malloc: unregister_chrdev_region(devno,1);
 return result;
}

static void __init sep4020_key_exit(void)
{
 sep4020_free_irqs();
  cdev_del(&dev->cdev);
 kfree(dev);
 unregister_chrdev_region(MKDEV(KEY_MAJOR, 0),1);
}

module_init(sep4020_key_init);
module_exit(sep4020_key_exit);

MODULE_AUTHOR("Leeming Zhang");
MODULE_LICENSE("GPL");

这里的防抖机制还不是很好,打算这两天加上定时器实现防抖,明天实验室出去旅游,回来继续完善它。

原创粉丝点击