s3c2410矩阵键盘驱动
2010-03-06 16:18
190 查看
在fs2410开发板上矩阵键盘的硬件连接图如下:
连接矩阵键盘的8个IO口与核心板IO依次对应为:
行:EINT0 GPF0 EINT2 GPF2 EINT11 GPF3 EINT19 GPF11
列:KCAN0 GPE11 KCAN1 GPG6 KCAN2 GPE13 KCAN3 GPG2
分析驱动入口函数button_init();
/*
配置GPE,GPF,GPG的控制寄存器GPCON,和数据寄存器GPDAT,通过iremap()
得到相应的IO口的虚拟地址,查数据手册可得,0x04表示32为。
*/
gpecon = ioremap(0x56000040, 0x04);//得到相应IO口的虚拟地址,下同
gpedat = ioremap(0x56000044, 0x04);
gpfcon = ioremap(0x56000050, 0x04);
gpfdat = ioremap(0x56000054, 0x04);
gpgcon = ioremap(0x56000060, 0x04);
gpgdat = ioremap(0x56000064, 0x04);
/*初始化GPIO*/
static void init_gpio(void)
{
//将GPE13 11 设置低位
writel((readl(gpecon) |(~3<<(2*13)|(~3<<(2*11))) , gpecon); //GPE13,11 设置为输出
writel(readl(gpedat) & (~1<<13) |(~1<<11), gpedat); //GPE13,11 输出为0
//将GPG6, 2 设置低位
writel((readl(gpgcon) | (~(3<<(2*6)|(~3<<(2*2)))), gpgcon); //GPG6,2 设置为输出
writel(readl(gpgdat) & 0xffffffbb, gpgdat); //GPG6,2 输出为0
writel((readl(gpfcon) | 0x33) & 0xffffffee, gpfcon); //GPF2, 0 设置为中断
writel((readl(gpgcon) | (3 << 22) | (3 << 6)) & (~((1 << 22) | (1 << 6))), gpgcon); //GPG11,3 设置为中断
/*调用set_irq_type设置中断为下降沿中断*/
set_irq_type(IRQ_EINT0, IRQT_FALLING);
// printk("dddddddddddd=%x/n",EXTINT0);
EXTINT0 = (EXTINT0 & (~0x07)) + 0x02;
set_irq_type(IRQ_EINT2, IRQT_FALLING);
EXTINT0 = (EXTINT0 & (~(0x07 << 8))) + (0x02 << 8);
set_irq_type(IRQ_EINT11, IRQT_FALLING);
EXTINT1 = (EXTINT1 & (~(0x07 << 12))) + (0x02 << 12);
set_irq_type(IRQ_EINT19, IRQT_FALLING);
EXTINT2 = (EXTINT2 & (~(0x07 << 12))) + (0x02 << 12);
进入申请中断函数request_irq:
static int request_irqs()
{
int ret;
/*
IRQ_EINT0为中断号,button_irq为中断处理函数,SA_INTERUPT标志为快速中断方式,最后一个参数设为NULL.
*/
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 irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned char ucKey;
/*屏蔽中断*/
disable_irqs();
printk("in irq/n");
//延迟50毫秒, 屏蔽按键毛刺
mdelay(50);
//进入键盘扫描程序
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();
//printk("in irq! %x/n",EXTINT0);
return IRQ_HANDLED;//2.6内核返回值一般是这个宏。
}
static __inline unsigned char button_scan(int irq)
{
long lGPF, lGPG;
writel((readl(gpfcon) | 0x33) & 0xffffffcc, gpfcon); //GPF2,0 input
writel((readl(gpgcon) | (3 << 22) | (3 << 6)) & (~((3 << 22) | (3 << 6))), gpgcon); //GPG11,3 input
//不利用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 ;
}
进入屏蔽中断函数:
static __inline void disable_irqs(void)
{
disable_irq(IRQ_EINT0);
disable_irq(IRQ_EINT2);
disable_irq(IRQ_EINT11);
disable_irq(IRQ_EINT19);
}
驱动退出函数:
static void __exit button_exit(void)
{
disable_irqs();
free_irqs();
iounmap(gpecon);
iounmap(gpedat);
iounmap(gpfcon);
iounmap(gpfdat);
iounmap(gpgcon);
iounmap(gpgdat);
cdev_del(&SimpleDevs);//删除结构体struct cdev
printk("button_major=%d/n", button_major);
unregister_chrdev_region(MKDEV(button_major, 0), 1);//卸载设备驱动所占有的资源
printk("button device uninstalled/n");
}
进入应用程序app:
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;
}
有fileoperation结构体知道open、ioctl、read对应系统调用函数是button_open、button_ioctl、button_read.
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 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;
}
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;
}
连接矩阵键盘的8个IO口与核心板IO依次对应为:
行:EINT0 GPF0 EINT2 GPF2 EINT11 GPF3 EINT19 GPF11
列:KCAN0 GPE11 KCAN1 GPG6 KCAN2 GPE13 KCAN3 GPG2
分析驱动入口函数button_init();
/*
配置GPE,GPF,GPG的控制寄存器GPCON,和数据寄存器GPDAT,通过iremap()
得到相应的IO口的虚拟地址,查数据手册可得,0x04表示32为。
*/
gpecon = ioremap(0x56000040, 0x04);//得到相应IO口的虚拟地址,下同
gpedat = ioremap(0x56000044, 0x04);
gpfcon = ioremap(0x56000050, 0x04);
gpfdat = ioremap(0x56000054, 0x04);
gpgcon = ioremap(0x56000060, 0x04);
gpgdat = ioremap(0x56000064, 0x04);
/*初始化GPIO*/
static void init_gpio(void)
{
//将GPE13 11 设置低位
writel((readl(gpecon) |(~3<<(2*13)|(~3<<(2*11))) , gpecon); //GPE13,11 设置为输出
writel(readl(gpedat) & (~1<<13) |(~1<<11), gpedat); //GPE13,11 输出为0
//将GPG6, 2 设置低位
writel((readl(gpgcon) | (~(3<<(2*6)|(~3<<(2*2)))), gpgcon); //GPG6,2 设置为输出
writel(readl(gpgdat) & 0xffffffbb, gpgdat); //GPG6,2 输出为0
writel((readl(gpfcon) | 0x33) & 0xffffffee, gpfcon); //GPF2, 0 设置为中断
writel((readl(gpgcon) | (3 << 22) | (3 << 6)) & (~((1 << 22) | (1 << 6))), gpgcon); //GPG11,3 设置为中断
/*调用set_irq_type设置中断为下降沿中断*/
set_irq_type(IRQ_EINT0, IRQT_FALLING);
// printk("dddddddddddd=%x/n",EXTINT0);
EXTINT0 = (EXTINT0 & (~0x07)) + 0x02;
set_irq_type(IRQ_EINT2, IRQT_FALLING);
EXTINT0 = (EXTINT0 & (~(0x07 << 8))) + (0x02 << 8);
set_irq_type(IRQ_EINT11, IRQT_FALLING);
EXTINT1 = (EXTINT1 & (~(0x07 << 12))) + (0x02 << 12);
set_irq_type(IRQ_EINT19, IRQT_FALLING);
EXTINT2 = (EXTINT2 & (~(0x07 << 12))) + (0x02 << 12);
进入申请中断函数request_irq:
static int request_irqs()
{
int ret;
/*
IRQ_EINT0为中断号,button_irq为中断处理函数,SA_INTERUPT标志为快速中断方式,最后一个参数设为NULL.
*/
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 irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned char ucKey;
/*屏蔽中断*/
disable_irqs();
printk("in irq/n");
//延迟50毫秒, 屏蔽按键毛刺
mdelay(50);
//进入键盘扫描程序
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();
//printk("in irq! %x/n",EXTINT0);
return IRQ_HANDLED;//2.6内核返回值一般是这个宏。
}
static __inline unsigned char button_scan(int irq)
{
long lGPF, lGPG;
writel((readl(gpfcon) | 0x33) & 0xffffffcc, gpfcon); //GPF2,0 input
writel((readl(gpgcon) | (3 << 22) | (3 << 6)) & (~((3 << 22) | (3 << 6))), gpgcon); //GPG11,3 input
//不利用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 ;
}
进入屏蔽中断函数:
static __inline void disable_irqs(void)
{
disable_irq(IRQ_EINT0);
disable_irq(IRQ_EINT2);
disable_irq(IRQ_EINT11);
disable_irq(IRQ_EINT19);
}
驱动退出函数:
static void __exit button_exit(void)
{
disable_irqs();
free_irqs();
iounmap(gpecon);
iounmap(gpedat);
iounmap(gpfcon);
iounmap(gpfdat);
iounmap(gpgcon);
iounmap(gpgdat);
cdev_del(&SimpleDevs);//删除结构体struct cdev
printk("button_major=%d/n", button_major);
unregister_chrdev_region(MKDEV(button_major, 0), 1);//卸载设备驱动所占有的资源
printk("button device uninstalled/n");
}
进入应用程序app:
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;
}
有fileoperation结构体知道open、ioctl、read对应系统调用函数是button_open、button_ioctl、button_read.
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 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;
}
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;
}
相关文章推荐
- 在S3C2410的6寸LCD驱动在linux2.6上的移植
- s3c2410的ds18b20驱动(基于linux-2.6.24.4内核)
- S5PV210 Android 矩阵键盘驱动[基于x210开发板]
- S3C2410驱动分析之ADC通用驱动
- LED驱动中s3c2410_gpio_setpin s3c2410_gpio_cfgpin函数的定义
- 引用 SAMSUNG_S3C2410串口驱动
- 基于ARM芯片S3C2410的TFT-LCD驱动方法
- S3C2410驱动分析之触摸屏驱动
- 基于S3C2410的SD卡linux驱动分析二
- Nandflash 驱动深度分析(基于S3C2410)
- Nandflash 驱动深度分析(基于S3C2410)
- S3C2410触摸屏驱动移植的完整过程(论文的操作部分,所以从第七章开始)
- S3C2410触摸屏驱动代码分析1
- 基于S3C2410的CS8900A驱动 for Linux 2.6.27 移植成功
- S3C2410的时钟驱动分析
- s3c2410 lcd驱动分析(1)
- s3c2410 DMA驱动源码分析2
- S3C2410上webcam驱动方法(1)--补丁驱动,模块加载
- 三星s3c2410ARM平台下的按键驱动注释
- 在kernel2.4.18下的s3c2410 AD驱动