您的位置:首页 > 其它

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: