📄 zxzkeyboard.c
字号:
/********************************************************************'创建日期: 2007-06-21'文件名称: zxzkeyboard.c'文件作者: zhangxianzhu''文件功能: 按键驱动'文件描述: 开发板上一共有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;
/*long lGPG, lGPE;
//设置G2, G6, E11, E13高位
writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat); writel(readl(gpedat) | (1<<13) | (1<<11), gpedat);
//读G2, G6, E11, E13的值
lGPG = readl(gpgdat);
lGPE = readl(gpedat);
switch(irq)
{
case IRQ_EINT0:
{
if ((lGPE & (1 << 11)) == 0) return 10;
if ((lGPG & (1 << 6)) == 0) return 11;
if ((lGPE & (1 << 13)) == 0) return 12;
if ((lGPG & (1 << 2)) == 0) return 16;
}
break;
case IRQ_EINT2:
{
if ((lGPE & (1 << 11)) == 0) return 7;
if ((lGPG & (1 << 6)) == 0) return 8;
if ((lGPE & (1 << 13)) == 0) return 9;
if ((lGPG & (1 << 2)) == 0) return 15;
}
break;
case IRQ_EINT11:
{
if ((lGPE & (1 << 11)) == 0) return 4;
if ((lGPG & (1 << 6)) == 0) return 5;
if ((lGPE & (1 << 13)) == 0) return 6;
if ((lGPG & (1 << 2)) == 0) return 14;
}
break;
case IRQ_EINT19:
{
if ((lGPE & (1 << 11)) == 0) return 1;
if ((lGPG & (1 << 6)) == 0) return 2;
if ((lGPE & (1 << 13)) == 0) return 3;
if ((lGPG & (1 << 2)) == 0) return 13;
}
break;
default:
break;
}*/
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);
MODULE_AUTHOR("ZhangXianZhu <zxzsrh@163.com>");
MODULE_DESCRIPTION("button Driver");
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -