一、开发环境
- 主 机:ubuntu10.04
- 开发板:Mini2440--256MB Nand, Kernel:2.6.32.2
- 编译器:arm-linux-gcc-4.3.2
二、实现步骤
1. 硬件原理图分析。由原理图可知每个按键所用到的外部中断分别是EINT8、EINT11、EINT13、EINT14、EINT15、EINT19,所对应的IO口分别是GPG0、GPG3、GPG5、GPG6、GPG7、GPG11。再由按键的接口电路可知,当按键按下时按键接通,中断线上原有的VDD3.3V高电平被拉低,从而触发中断的产生。
2. 开始编写合适mini2440的按键驱动(含去抖动功能),文件名:platButtons.c
首先进行平台设备的定义和注册
//平台资源的定义
static struct resource gitS3cButtonsResource[] = {
[0]={
.start = BUZZER_GPG_ADDR, //GPG管教的数据寄存器,具体值定义在头文件中
.end = BUZZER_GPG_ADDR + 4,
.flags = IORESOURCE_MEM,
},
[1]={
.start = IRQ_EINT8,
.end = IRQ_EINT8,
.flags = IORESOURCE_IRQ,
},
[2]={
.start = IRQ_EINT11,
.end = IRQ_EINT11,
.flags = IORESOURCE_IRQ,
},
[3]={
.start = IRQ_EINT13,
.end = IRQ_EINT13,
.flags = IORESOURCE_IRQ,
},
[4]={
.start = IRQ_EINT14,
.end = IRQ_EINT14,
.flags = IORESOURCE_IRQ,
},
[5]={
.start = IRQ_EINT15,
.end = IRQ_EINT15,
.flags = IORESOURCE_IRQ,
},
[6]={
.start = IRQ_EINT19,
.end = IRQ_EINT19,
.flags = IORESOURCE_IRQ,
}
};
//平台设备定义
static struct platform_device gitButtonDev = {
.name = PLAT_DEV_NAME,
.id = -1,
.num_resources = ARRAY_SIZE(gitS3cButtonsResource),
.resource = gitS3cButtonsResource,
.dev = {
.release = ButtonRelease,
},
};
//平台驱动定义
static struct platform_driver gitButtonDriver = {
.probe = ButtonsProbe,
.remove = ButtonsRemove,
.driver = {
.name = PLAT_DEV_NAME,
.owner = THIS_MODULE,
},
};
static int __init ButtonsInit(void)
{
DPRINTK(banner);
platform_device_register(&gitButtonDev);
platform_driver_register(&gitButtonDriver);
return 0;
}
static void __exit ButtonsExit(void)
{
platform_driver_unregister(&gitButtonDriver);
platform_device_unregister(&gitButtonDev);
}
module_init(ButtonsInit);
module_exit(ButtonsExit);
MODULE_AUTHOR("apple_guet");
MODULE_DESCRIPTION("Mini2440 Buttons Driver");
MODULE_LICENSE("GPL");
static int ButtonsRemove(struct platform_device *dev)
{
iounmap(gipGpgIoDateAdd);
misc_deregister(&gitButtonMiscDev);
return 0;
}
// 设备探测函数
static int ButtonsProbe(struct platform_device *pdev)
{
struct resource *res;
int i = 0, ret = 0;
DPRINTK("probe:%s\n", __func__);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(res == NULL)
{
return -ENOENT;
}
gipGpgIoDateAdd = ioremap(res->start, (res->end - res->start));
if(gipGpgIoDateAdd == NULL)
{
return -EINVAL;
}
DPRINTK(KERN_DEBUG"probe: mapped gipGpgIoDateAdd = %p\n", gipGpgIoDateAdd);
//get irq number
for(i = 0; i < 6; i++)
{
gitpButtonsIrq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if(gitpButtonsIrq == NULL)
{
ret = -ENOENT;
goto err_map;
}
giaButtonIrqs[i] = gitpButtonsIrq->start;
DPRINTK("giaButtonIrqs[%d]=%d\n", i, giaButtonIrqs[i]);
}
ret = misc_register(&gitButtonMiscDev);
return 0;
err_map:
iounmap(gipGpgIoDateAdd);
return ret;
}
static void ButtonRelease(struct device * dev)
{
/* do nothing */
}
static int ButtonsOpen(struct inode *inode, struct file *file)
{
int err = 0;
uint8_t i = 0;
//注册中断
for(i = 0; i < KEY_COUNT; i++)
{
if(giaButtonIrqs[i] < 0)
{
continue;
}
//中断触发方式:上升沿触发
err = request_irq(giaButtonIrqs[i], PlatButtonsIrq, IRQ_TYPE_EDGE_RISING, NULL, NULL);
if(err != 0)
{
break;
}
//初始化并设置6个去抖定时器
setup_timer(&gitKeyTimers[i], ButtonsTimer, i);
}
if(err != 0)
{
i--;
for(; i >= 0; i--)
{
if(giaButtonIrqs[i] < 0)
{
continue;
}
disable_irq(giaButtonIrqs[i]);
free_irq(giaButtonIrqs[i], NULL);
}
return -EBUSY;
}
giButtonStat = 0;
return 0;
}
static int ButtonsClose(struct inode *inode, struct file *file)
{
uint8_t i = 0;
for(i = 0; i < KEY_COUNT; i++)
{
del_timer(&gitKeyTimers[i]);
if(giaButtonIrqs[i] < 0)
{
continue;
}
free_irq(giaButtonIrqs[i], NULL);
}
return 0;
}
static int ButtonsRead(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long ret = 0;
if(giButtonStat == 0)
{
if((filp->f_flags & O_NONBLOCK) != 0)
{
return -EAGAIN;
}
else
{
wait_event_interruptible(gipButtonWaitQue, giButtonStat);
}
}
giButtonStat = 0;
ret = copy_to_user(buff, &giKeyValue, min(sizeof(giKeyValue), count));
return (ret ? -EFAULT : min(sizeof(giKeyValue), count));
}
static unsigned int ButtonsPoll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &gipButtonWaitQue, wait);
if(giButtonStat == 1)
{
mask |= POLLIN | POLLRDNORM;
}
return mask;
}
static struct file_operations gitButtonsFops = {
.owner = THIS_MODULE,
.open = ButtonsOpen,
.release = ButtonsClose,
.read = ButtonsRead,
.poll = ButtonsPoll,
};
static struct miscdevice gitButtonMiscDev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &gitButtonsFops,
};
接下来是比较重要的两个函数:
1、按键触发函数
在此函数中,增加了一个 giInIrqFlg 变量,在调试中 发现,如果没有这个变量,在我们按键过程中,会有抖动,即有可能多次进入此中断函数,那么每进一次此函数,都会设置一次定时器中断,造成程序执行时,报错定时器timer.c kernel panic,加入此变量后,问题解决。
static irqreturn_t PlatButtonsIrq(int irq, void *dev_id)
{
uint8_t i = 0;
if(giInIrqFlg == 0)
{
for(i = 0; i < KEY_COUNT; i++)
{
if(irq == (int)giaButtonIrqs[i])
{
giInIrqFlg = 1;
//设置当前按键按下去抖定时器的延时并启动定时器
gitKeyTimers[i].expires = jiffies + KEY_TIMER_DELAY;
DPRINTK("in platButtonsIrq i = %d\n", i);
add_timer(&gitKeyTimers[i]);
}
}
}/* end if(giInIrqFlg == 0) */
return IRQ_RETVAL(IRQ_HANDLED);
}
static void ButtonsTimer(unsigned long arg)
{
int key = arg, iDate = *gipGpgIoDateAdd;
giInIrqFlg = 0;
DPRINTK(KERN_INFO "this is in ButtonsTimer, key = %d\n", key);
if((iDate & KEY_PRESS_FLG) != 0)
{
//低电平,按键按下
giKeyValue = key;
giButtonStat = 1;
wake_up_interruptible(&gipButtonWaitQue);
}/* end if(iIoStat == 0) */
}
全局变量定义如下:
static volatile int giKeyValue = 0;
static struct resource *gitpButtonsIrq;
static volatile uint8_t giInIrqFlg = 0;
static volatile uint8_t giButtonStat = 0;
static struct timer_list gitKeyTimers[KEY_COUNT]; //定义6个按键去抖动定时器
static DECLARE_WAIT_QUEUE_HEAD(gipButtonWaitQue);
static volatile unsigned long *gipGpgIoDateAdd = NULL;
static volatile uint8_t giaButtonIrqs[KEY_COUNT] = {0};
const static char banner[] __initdata = "Mini2440 Buttons Driver\n";
platButtons.h头文件内容为:
#ifndef _PLAT_BUTTOBS_H
#define _PLAT_BUTTOBS_H
#define KEY_COUNT 0x06
#define KEY_TIMER_DELAY (HZ/50) //按键按下去抖延时20毫秒
#define DEVICE_NAME "HDW_BUTTONS"
#define PLAT_DEV_NAME "hdw-buttons"
#define BUZZER_GPG_ADDR 0x56000064
#define KEY1_POS (0x0001 << 0)
#define KEY3_POS (0x0001 << 3)
#define KEY5_POS (0x0001 << 5)
#define KEY6_POS (0x0001 << 6)
#define KEY7_POS (0x0001 << 7)
#define KEY11_POS (0x0001 << 11)
#define KEY_PRESS_FLG (KEY1_POS | KEY3_POS | KEY5_POS| KEY6_POS | KEY7_POS | KEY11_POS)
#undef DEBUG
#define _DEBUG
#ifdef DEBUG
#define DPRINTK printk
#else
#define DPRINTK(x...) (void)(0)
#endif
#endif
测试程序:
int main(void)
{
fd_set rds;
unsigned int idKeyValue = 0;
int idButtonsFd = 0, ret = 0;
idButtonsFd = open("/dev/HDW_BUTTONS", 0);
if(idButtonsFd < 0)
{
perror("open device buttons");
exit(1);
}
while(1)
{
FD_ZERO(&rds);
FD_SET(idButtonsFd, &rds);
ret = select(idButtonsFd + 1, &rds, NULL, NULL, NULL);
if(ret < 0)
{
perror("select");
exit(1);
}
else if(ret == 0)
{
printf("Timeout.\n");
}
if(FD_ISSET(idButtonsFd, &rds) != 0)
{
ret = read(idButtonsFd, &idKeyValue, sizeof(idKeyValue));
if(ret != sizeof(idKeyValue))
{
if(errno != EAGAIN)
{
perror("errno != EAGAIN, read buttons\n");
}
continue;
}
else
{
printf("buttons_value: %d\n", idKeyValue + 1);
}
}/* end if(FD_ISSET(idButtonsFd, &rds) != 0) */
}/* end while(1) */
close(idButtonsFd);
return 0;
}
经过测试,发现完全可以去除抖动,按键过程中稳定。
首先进行平台设备的定义和注册
//平台资源的定义
static struct resource gitS3cButtonsResource[] = {
[0]={
.start = BUZZER_GPG_ADDR, //GPG管教的数据寄存器,具体值定义在头文件中
.end = BUZZER_GPG_ADDR + 4,
.flags = IORESOURCE_MEM,
},
[1]={
.start = IRQ_EINT8,
.end = IRQ_EINT8,
.flags = IORESOURCE_IRQ,
},
[2]={
.start = IRQ_EINT11,
.end = IRQ_EINT11,
.flags = IORESOURCE_IRQ,
},
[3]={
.start = IRQ_EINT13,
.end = IRQ_EINT13,
.flags = IORESOURCE_IRQ,
},
[4]={
.start = IRQ_EINT14,
.end = IRQ_EINT14,
.flags = IORESOURCE_IRQ,
},
[5]={
.start = IRQ_EINT15,
.end = IRQ_EINT15,
.flags = IORESOURCE_IRQ,
},
[6]={
.start = IRQ_EINT19,
.end = IRQ_EINT19,
.flags = IORESOURCE_IRQ,
}
};
//平台设备定义
static struct platform_device gitButtonDev = {
.name = PLAT_DEV_NAME,
.id = -1,
.num_resources = ARRAY_SIZE(gitS3cButtonsResource),
.resource = gitS3cButtonsResource,
.dev = {
.release = ButtonRelease,
},
};
//平台驱动定义
static struct platform_driver gitButtonDriver = {
.probe = ButtonsProbe,
.remove = ButtonsRemove,
.driver = {
.name = PLAT_DEV_NAME,
.owner = THIS_MODULE,
},
};
static int __init ButtonsInit(void)
{
DPRINTK(banner);
platform_device_register(&gitButtonDev);
platform_driver_register(&gitButtonDriver);
return 0;
}
static void __exit ButtonsExit(void)
{
platform_driver_unregister(&gitButtonDriver);
platform_device_unregister(&gitButtonDev);
}
module_init(ButtonsInit);
module_exit(ButtonsExit);
MODULE_AUTHOR("apple_guet");
MODULE_DESCRIPTION("Mini2440 Buttons Driver");
MODULE_LICENSE("GPL");
static int ButtonsRemove(struct platform_device *dev)
{
iounmap(gipGpgIoDateAdd);
misc_deregister(&gitButtonMiscDev);
return 0;
}
// 设备探测函数
static int ButtonsProbe(struct platform_device *pdev)
{
struct resource *res;
int i = 0, ret = 0;
DPRINTK("probe:%s\n", __func__);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(res == NULL)
{
return -ENOENT;
}
gipGpgIoDateAdd = ioremap(res->start, (res->end - res->start));
if(gipGpgIoDateAdd == NULL)
{
return -EINVAL;
}
DPRINTK(KERN_DEBUG"probe: mapped gipGpgIoDateAdd = %p\n", gipGpgIoDateAdd);
//get irq number
for(i = 0; i < 6; i++)
{
gitpButtonsIrq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if(gitpButtonsIrq == NULL)
{
ret = -ENOENT;
goto err_map;
}
giaButtonIrqs[i] = gitpButtonsIrq->start;
DPRINTK("giaButtonIrqs[%d]=%d\n", i, giaButtonIrqs[i]);
}
ret = misc_register(&gitButtonMiscDev);
return 0;
err_map:
iounmap(gipGpgIoDateAdd);
return ret;
}
static void ButtonRelease(struct device * dev)
{
/* do nothing */
}
static int ButtonsOpen(struct inode *inode, struct file *file)
{
int err = 0;
uint8_t i = 0;
//注册中断
for(i = 0; i < KEY_COUNT; i++)
{
if(giaButtonIrqs[i] < 0)
{
continue;
}
//中断触发方式:上升沿触发
err = request_irq(giaButtonIrqs[i], PlatButtonsIrq, IRQ_TYPE_EDGE_RISING, NULL, NULL);
if(err != 0)
{
break;
}
//初始化并设置6个去抖定时器
setup_timer(&gitKeyTimers[i], ButtonsTimer, i);
}
if(err != 0)
{
i--;
for(; i >= 0; i--)
{
if(giaButtonIrqs[i] < 0)
{
continue;
}
disable_irq(giaButtonIrqs[i]);
free_irq(giaButtonIrqs[i], NULL);
}
return -EBUSY;
}
giButtonStat = 0;
return 0;
}
static int ButtonsClose(struct inode *inode, struct file *file)
{
uint8_t i = 0;
for(i = 0; i < KEY_COUNT; i++)
{
del_timer(&gitKeyTimers[i]);
if(giaButtonIrqs[i] < 0)
{
continue;
}
free_irq(giaButtonIrqs[i], NULL);
}
return 0;
}
static int ButtonsRead(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long ret = 0;
if(giButtonStat == 0)
{
if((filp->f_flags & O_NONBLOCK) != 0)
{
return -EAGAIN;
}
else
{
wait_event_interruptible(gipButtonWaitQue, giButtonStat);
}
}
giButtonStat = 0;
ret = copy_to_user(buff, &giKeyValue, min(sizeof(giKeyValue), count));
return (ret ? -EFAULT : min(sizeof(giKeyValue), count));
}
static unsigned int ButtonsPoll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &gipButtonWaitQue, wait);
if(giButtonStat == 1)
{
mask |= POLLIN | POLLRDNORM;
}
return mask;
}
static struct file_operations gitButtonsFops = {
.owner = THIS_MODULE,
.open = ButtonsOpen,
.release = ButtonsClose,
.read = ButtonsRead,
.poll = ButtonsPoll,
};
static struct miscdevice gitButtonMiscDev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &gitButtonsFops,
};
接下来是比较重要的两个函数:
1、按键触发函数
在此函数中,增加了一个 giInIrqFlg 变量,在调试中 发现,如果没有这个变量,在我们按键过程中,会有抖动,即有可能多次进入此中断函数,那么每进一次此函数,都会设置一次定时器中断,造成程序执行时,报错定时器timer.c kernel panic,加入此变量后,问题解决。
static irqreturn_t PlatButtonsIrq(int irq, void *dev_id)
{
uint8_t i = 0;
if(giInIrqFlg == 0)
{
for(i = 0; i < KEY_COUNT; i++)
{
if(irq == (int)giaButtonIrqs[i])
{
giInIrqFlg = 1;
//设置当前按键按下去抖定时器的延时并启动定时器
gitKeyTimers[i].expires = jiffies + KEY_TIMER_DELAY;
DPRINTK("in platButtonsIrq i = %d\n", i);
add_timer(&gitKeyTimers[i]);
}
}
}/* end if(giInIrqFlg == 0) */
return IRQ_RETVAL(IRQ_HANDLED);
}
static void ButtonsTimer(unsigned long arg)
{
int key = arg, iDate = *gipGpgIoDateAdd;
giInIrqFlg = 0;
DPRINTK(KERN_INFO "this is in ButtonsTimer, key = %d\n", key);
if((iDate & KEY_PRESS_FLG) != 0)
{
//低电平,按键按下
giKeyValue = key;
giButtonStat = 1;
wake_up_interruptible(&gipButtonWaitQue);
}/* end if(iIoStat == 0) */
}
全局变量定义如下:
static volatile int giKeyValue = 0;
static struct resource *gitpButtonsIrq;
static volatile uint8_t giInIrqFlg = 0;
static volatile uint8_t giButtonStat = 0;
static struct timer_list gitKeyTimers[KEY_COUNT]; //定义6个按键去抖动定时器
static DECLARE_WAIT_QUEUE_HEAD(gipButtonWaitQue);
static volatile unsigned long *gipGpgIoDateAdd = NULL;
static volatile uint8_t giaButtonIrqs[KEY_COUNT] = {0};
const static char banner[] __initdata = "Mini2440 Buttons Driver\n";
platButtons.h头文件内容为:
#ifndef _PLAT_BUTTOBS_H
#define _PLAT_BUTTOBS_H
#define KEY_COUNT 0x06
#define KEY_TIMER_DELAY (HZ/50) //按键按下去抖延时20毫秒
#define DEVICE_NAME "HDW_BUTTONS"
#define PLAT_DEV_NAME "hdw-buttons"
#define BUZZER_GPG_ADDR 0x56000064
#define KEY1_POS (0x0001 << 0)
#define KEY3_POS (0x0001 << 3)
#define KEY5_POS (0x0001 << 5)
#define KEY6_POS (0x0001 << 6)
#define KEY7_POS (0x0001 << 7)
#define KEY11_POS (0x0001 << 11)
#define KEY_PRESS_FLG (KEY1_POS | KEY3_POS | KEY5_POS| KEY6_POS | KEY7_POS | KEY11_POS)
#undef DEBUG
#define _DEBUG
#ifdef DEBUG
#define DPRINTK printk
#else
#define DPRINTK(x...) (void)(0)
#endif
#endif
测试程序:
int main(void)
{
fd_set rds;
unsigned int idKeyValue = 0;
int idButtonsFd = 0, ret = 0;
idButtonsFd = open("/dev/HDW_BUTTONS", 0);
if(idButtonsFd < 0)
{
perror("open device buttons");
exit(1);
}
while(1)
{
FD_ZERO(&rds);
FD_SET(idButtonsFd, &rds);
ret = select(idButtonsFd + 1, &rds, NULL, NULL, NULL);
if(ret < 0)
{
perror("select");
exit(1);
}
else if(ret == 0)
{
printf("Timeout.\n");
}
if(FD_ISSET(idButtonsFd, &rds) != 0)
{
ret = read(idButtonsFd, &idKeyValue, sizeof(idKeyValue));
if(ret != sizeof(idKeyValue))
{
if(errno != EAGAIN)
{
perror("errno != EAGAIN, read buttons\n");
}
continue;
}
else
{
printf("buttons_value: %d\n", idKeyValue + 1);
}
}/* end if(FD_ISSET(idButtonsFd, &rds) != 0) */
}/* end while(1) */
close(idButtonsFd);
return 0;
}
经过测试,发现完全可以去除抖动,按键过程中稳定。