/********************************************************************
'文件功能: 按键驱动
'文件描述: 开发板上一共有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;
}