S3C2440 4*4键盘驱动程序

时间:2021-05-23 03:59:22

/********************************************************************
'文件功能: 按键驱动
'文件描述: 开发板上一共有16个按键
 4X4 矩阵键盘
 四个输入引脚: EINT0 -----( GPF0  )----INPUT (键10, 11, 12, 16)
   EINT2 -----( GPF2  )----INPUT   (键 7,  8,  9, 15)
   EINT11-----( GPG3  )----INPUT   (键 4,  5,  6, 14)
   EINT19-----( GPG11 )----INPUT   (键 1,  2,  3, 13)
              |   |   |   |
 四个输出引脚: KEYSCAN0---( GPE11 )----OUTPUT-------   |   |   |
   KEYSCAN1---( GPG6  )----OUTPUT-------------------   |   |
   KEYSCAN2---( GPE13 )----OUTPUT-----------------------   |
   KEYSCAN3---( GPG2  )----OUTPUT---------------------------
 
 当按下按键时分别产生0、2、11、19号外部中断。
*********************************************************************/
#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 <linux/interrupt.h>
#include <linux/time.h>
#include <linux/spinlock.h>

#include <asm/hardware.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/irq.h>

#define DEVICE_NAME "button"
#define MAX_KEY_COUNT  32         //必须是2的n次方

typedef struct
{
 unsigned long jiffy[MAX_KEY_COUNT];  //按键时间, 如果读键时, 5秒钟以前的铵键作废
 unsigned char buf[MAX_KEY_COUNT];  //按键缓冲区
 unsigned int head,tail;     //按键缓冲区头和尾
}KEY_BUFFER;

static KEY_BUFFER g_keyBuffer;  //键盘缓冲区
static spinlock_t buffer_lock;  //缓冲区锁

static int major = 231;    //Define device major
static void *gpecon;
static void *gpedat;
static void *gpfcon;
static void *gpfdat;
static void *gpgcon;
static void *gpgdat;

///////////////////////////////////////////////////////////////
//功能: 获取当前的毫秒数(从系统启动开始, 相当于window 的API GetTickCount()
//入口: 
///////////////////////////////////////////////////////////////
static unsigned long GetTickCount(void)
{
 struct timeval currTick;
 unsigned long ulRet;

 do_gettimeofday(&currTick);
 ulRet = currTick.tv_sec;
 ulRet *= 1000;
 ulRet += (currTick.tv_usec + 500) / 1000;
 return ulRet;
}

///////////////////////////////////////////////////////////////
//功能: 初始化键盘缓冲区
//入口: 
///////////////////////////////////////////////////////////////
static void init_keybuffer(void)
{
 int i;
 spin_lock_irq(&buffer_lock);
 g_keyBuffer.head = 0;
 g_keyBuffer.tail = 0;
 for(i = 0; i < MAX_KEY_COUNT; i++)
 {
  g_keyBuffer.buf[i] = 0;
  g_keyBuffer.jiffy[i] = 0;
 }
 spin_unlock_irq(&buffer_lock);
}

///////////////////////////////////////////////////////////////
//功能: 删除过时(5秒前的按键值)
//入口: 
///////////////////////////////////////////////////////////////
static void remove_timeoutkey(void)
{
 unsigned long ulTick;

 spin_lock_irq(&buffer_lock);
 while(g_keyBuffer.head != g_keyBuffer.tail)
 {
  ulTick = GetTickCount() - g_keyBuffer.jiffy[g_keyBuffer.head];
  if (ulTick  < 5000) //5秒
   break;
  g_keyBuffer.buf[g_keyBuffer.head] = 0;
  g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
  g_keyBuffer.head ++;
  g_keyBuffer.head &= (MAX_KEY_COUNT -1);
 }
 spin_unlock_irq(&buffer_lock);
}

///////////////////////////////////////////////////////////////
//功能: 初始化GPIO, 设置中断0, 2, 11, 19为下降沿中断
//入口: 
///////////////////////////////////////////////////////////////
static void init_gpio(void)
{
 //将GPE13 11 设置低位
 writel((readl(gpecon) | ((3<<26)|(3<<22))) & (~((1<<27)|(1<<23))), gpecon); //GPE13,11 set output
 writel(readl(gpedat) & 0xffffd7ff, gpedat);         //GPE13,11 output 0

 //将GPG6, 2 设置低位
 writel((readl(gpgcon) | 0x3030) & 0xffffdfdf, gpgcon); //GPG6,2 set output
 writel(readl(gpgdat) & 0xffffffbb, gpgdat);    //GPG6,2 output 0

 writel((readl(gpfcon) | 0x33) & 0xffffffee, gpfcon);      //GPF2, 0 set EINT
 writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((1<<22) | (1<<6))), gpgcon); //GPG11,3 set EINT

 set_irq_type(IRQ_EINT0, IRQT_FALLING);
 set_irq_type(IRQ_EINT2, IRQT_FALLING);
 set_irq_type(IRQ_EINT11, IRQT_FALLING);
 set_irq_type(IRQ_EINT19, IRQT_FALLING);
}

///////////////////////////////////////////////////////////////
//功能: 激活中断
//入口: 
///////////////////////////////////////////////////////////////
static __inline void enable_irqs(void)
{
 enable_irq(IRQ_EINT0);
 enable_irq(IRQ_EINT2);
 enable_irq(IRQ_EINT11);
 enable_irq(IRQ_EINT19);
}

///////////////////////////////////////////////////////////////
//功能: 屏蔽中断
//入口: 
///////////////////////////////////////////////////////////////
static __inline void disable_irqs(void)
{
 disable_irq(IRQ_EINT0);
 disable_irq(IRQ_EINT2);
 disable_irq(IRQ_EINT11);
 disable_irq(IRQ_EINT19);
}

///////////////////////////////////////////////////////////////
//功能: 进入中断后, 扫描铵键码
//入口: 
//返回: 按键码(1-16), 0xff表示错误
///////////////////////////////////////////////////////////////
static __inline unsigned char button_scan(int irq)
{
 long lGPF, lGPG;
 //不利用irq号, 直接扫描键盘
 //设置G2低位, G6, E11, E13高位
 writel((readl(gpgdat) | (1<<6)) & (~(1<<2)), gpgdat);
 writel(readl(gpedat) | (1<<11) | (1<<13), gpedat);
 //取GPF0, GPF2, GPG3, GPG11的值
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 //判断按键
 if ((lGPF & (1<<0)) == 0)  return 16;
 else if((lGPF & (1<<2)) == 0) return 15;
 else if((lGPG & (1<<3)) == 0) return 14;
 else if((lGPG & (1<<11)) == 0) return 13;

 //设置G6低位, G2, E11, E13高位
 writel((readl(gpgdat) | (1<<2)) & (~(1<<6)), gpgdat);
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 if ((lGPF & (1<<0)) == 0)  return 11;
 else if((lGPF & (1<<2)) == 0) return 8;
 else if((lGPG & (1<<3)) == 0) return 5;
 else if((lGPG & (1<<11)) == 0) return 2;

 //设置E11低位, G2, G6, E13高位
 writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
 writel((readl(gpedat) | (1<<13)) & (~(1<<11)), gpedat);
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 if ((lGPF & (1<<0)) == 0)  return 10;
 else if((lGPF & (1<<2)) == 0) return 7;
 else if((lGPG & (1<<3)) == 0) return 4;
 else if((lGPG & (1<<11)) == 0) return 1;

 //设置E13低位, G2, G6, E11高位
 //writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
 writel((readl(gpedat) | (1<<11)) & (~(1<<13)), gpedat);
 lGPF = readl(gpfdat);
 lGPG = readl(gpgdat);
 if ((lGPF & (1<<0)) == 0)  return 12;
 else if((lGPF & (1<<2)) == 0) return 9;
 else if((lGPG & (1<<3)) == 0) return 6;
 else if((lGPG & (1<<11)) == 0) return 3;
 return 0xff ;
}

///////////////////////////////////////////////////////////////
//功能: 中断函数,
//入口: irq 中断号
//
///////////////////////////////////////////////////////////////
static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
{
 unsigned char ucKey;
 disable_irqs();
 //延迟50毫秒, 屏蔽按键毛刺
 udelay(50000);
 ucKey = button_scan(irq);
 if ((ucKey >= 1) && (ucKey <= 16))
 {
  //如果缓冲区已满, 则不添加
  if (((g_keyBuffer.head + 1) & (MAX_KEY_COUNT - 1)) != g_keyBuffer.tail)
  {
   spin_lock_irq(&buffer_lock);
   g_keyBuffer.buf[g_keyBuffer.tail] = ucKey;
   g_keyBuffer.jiffy[g_keyBuffer.tail] = GetTickCount();
   g_keyBuffer.tail ++;
   g_keyBuffer.tail &= (MAX_KEY_COUNT -1);
   spin_unlock_irq(&buffer_lock);
  }
 }
 init_gpio();
 enable_irqs();
 return 0;
}

///////////////////////////////////////////////////////////////
//功能: 申请中断
//入口: 
//
///////////////////////////////////////////////////////////////
static __inline int request_irqs()
{
 int ret;
 ret = request_irq(IRQ_EINT0, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
 if (ret < 0)
  return ret;
 ret = request_irq(IRQ_EINT2, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
    if (ret >= 0)
 {
     ret = request_irq(IRQ_EINT11, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
  if (ret >= 0)
  {
      ret = request_irq(IRQ_EINT19, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
   if (ret >= 0)
    return ret;
   free_irq(IRQ_EINT11, button_irq);
  }
  free_irq(IRQ_EINT2, button_irq);
 }
 free_irq(IRQ_EINT0, button_irq);
 return ret;
}

///////////////////////////////////////////////////////////////
//功能: 释放中断
//入口: 
//
///////////////////////////////////////////////////////////////
static __inline void free_irqs()
{
 free_irq(IRQ_EINT0, button_irq);
 free_irq(IRQ_EINT2, button_irq);
 free_irq(IRQ_EINT11, button_irq);
 free_irq(IRQ_EINT19, button_irq);
}

///////////////////////////////////////////////////////////////
//功能: 打开文件, 开始中断
//入口: 
//
///////////////////////////////////////////////////////////////
static int button_open(struct inode *inode,struct file *filp)
{
 int ret = nonseekable_open(inode, filp);
 if (ret >= 0)
 {
  init_keybuffer();
  enable_irqs();
 }
 return ret;
}

///////////////////////////////////////////////////////////////
//功能: 关闭文件, 屏蔽中断
//入口: 
//
///////////////////////////////////////////////////////////////
static int button_release(struct inode *inode,struct file *filp)
{
    disable_irqs();
    return 0;
}

///////////////////////////////////////////////////////////////
//功能: 读键盘
//入口: 
//
///////////////////////////////////////////////////////////////
static ssize_t button_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
 ssize_t ret = 0;

 remove_timeoutkey();
 spin_lock_irq(&buffer_lock);
 while((g_keyBuffer.head != g_keyBuffer.tail) && (((size_t)ret) < count) )
 {
  buffer[ret] = (char)(g_keyBuffer.buf[g_keyBuffer.head]);
  g_keyBuffer.buf[g_keyBuffer.head] = 0;
  g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
  g_keyBuffer.head ++;
  g_keyBuffer.head &= (MAX_KEY_COUNT -1);
  ret ++;
 }
 spin_unlock_irq(&buffer_lock);
 return ret;
}

///////////////////////////////////////////////////////////////
//功能: 清空键盘缓冲区
//入口: 
//
///////////////////////////////////////////////////////////////
static int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
 init_keybuffer();
 return 1;
}

static struct file_operations button_fops =
{
 .owner = THIS_MODULE,
 .ioctl = button_ioctl,
 .open = button_open,
 .read = button_read,
 .release = button_release,
};

///////////////////////////////////////////////////////////////
//功能: 驱动初始化
//入口: 
//
///////////////////////////////////////////////////////////////
static int __init button_init(void)
{
 int ret;

 gpecon = ioremap(0x56000040, 0x04);
 gpedat = ioremap(0x56000044, 0x04);
 gpfcon = ioremap(0x56000050, 0x04);
 gpfdat = ioremap(0x56000054, 0x04);
 gpgcon = ioremap(0x56000060, 0x04);
 gpgdat = ioremap(0x56000064, 0x04);

 init_gpio();
 ret = request_irqs();
 if (ret < 0) return ret;

 ret = register_chrdev(major, DEVICE_NAME, &button_fops);
 if (ret < 0)
 {
  free_irqs();
  return ret;
 }
 devfs_mk_cdev(MKDEV(major, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, DEVICE_NAME);

 disable_irqs();
 printk("button initialized.\n");
 return 0;
}

///////////////////////////////////////////////////////////////
//功能: 驱动释放
//入口: 
//
///////////////////////////////////////////////////////////////
static void __exit button_exit(void)
{
 disable_irqs();
 free_irqs();
 devfs_remove(DEVICE_NAME);
 unregister_chrdev(major, DEVICE_NAME);
}

module_init(button_init);
module_exit(button_exit);

 

测试程序:

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/delay.h>

main()
{
 int fd;
 char key = 0;
 fd = open("/dev/button", O_RDWR);
 if (fd == -1)
 {
  printf("open device button errr!\n");
  return 0;
 }

 ioctl(fd, 0, 0); //清空键盘缓冲区, 后面两个参数没有意义,
 while(key != 16)
 {
  if (read(fd, &key, 1) > 0)
  {
   printf("*********************Key Value = %d*****************************\n", key);
  }
 }
 close(fd);
 return 0;
}