一个简单的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");
这里的防抖机制还不是很好,打算这两天加上定时器实现防抖,明天实验室出去旅游,回来继续完善它。
- 一个简单的linux系统下的5*5的键盘驱动
- linux下的键盘驱动
- 一个嵌入式Linux系统的键盘驱动实现
- 一个嵌入式Linux系统的键盘驱动实现
- 一个简单的Linux驱动
- 一个简单的Linux字符驱动
- 如何写一个简单的linux驱动
- 一个简单的Linux驱动示例
- 如何编写Linux下的USB键盘驱动
- 如何编写Linux 下的 USB 键盘驱动
- 编写Linux下的USB键盘驱动(附源码)
- 如何编写Linux下的USB键盘驱动
- 驱动开发之 一个简单的截取键盘按键的驱动
- linux下简单的设备驱动开发
- Linux下RTC驱动的简单分析
- 在LINUX下编译一个模块(相当于一个简单的驱动)
- linux usb驱动,一个简单的usb驱动,simp_usb
- Linux下简单4*4键盘驱动
- powershell 别名大全
- 如何成为最受欢迎的人,拥有最完美的品质?
- javascript控制ie窗口
- 用JavaScript刷新框架子页面的8种方法
- Spring文件资源操作和Web相关工具类盘点(连载)- 4
- 一个简单的linux系统下的5*5的键盘驱动
- ASP.NET 母版页的加载顺序
- 在MOSS中开发一个模块化的feature
- ALV GRID颜色设置
- AJAX+jsp无刷新验证码实例
- 博客迁移至http://jiyu.wordpress.com.cn
- JIS编码转换(DM格式与JIS)
- sharepoint列表EventHandle的开发
- ADODB.Stream组件Charset属性值