Tiny6410 + Linux2.6.38 按键驱动改写和说明

四个按键分别接在GPN11, GPL8, GPL9, GPL10,本驱动对应的测试程序test.c可以分别检测这四个按钮的按下和释放状态。

本驱动改写自友善官方驱动,仅供学习参考之用。

驱动程序源码:

/**********************************************基于Tiny6410 + linux-2.6.38 的按键中断驱动程序。**********************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/irq.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <mach/hardware.h>#include <linux/platform_device.h>#include <linux/cdev.h>#include <linux/miscdevice.h>#include <mach/map.h>#include <mach/regs-clock.h>#include <mach/regs-gpio.h>#include <plat/gpio-cfg.h>#include <mach/gpio-bank-n.h>#include <mach/gpio-bank-l.h>#define DEVICE_NAME     "buttons"    struct button_irq_desc {    int irq;    int number;    char *name;};static struct button_irq_desc button_irqs [] = {    {IRQ_EINT(11), 0, "KEY0"},   //IRQ_EINT(11) 对应的管脚是 GPN11    {IRQ_EINT(16), 1, "KEY1"},   //IRQ_EINT(16) 对应的管脚是 GPL8    {IRQ_EINT(17), 2, "KEY2"},   //IRQ_EINT(17) 对应的管脚是 GPL9    {IRQ_EINT(18), 3, "KEY3"}  //IRQ_EINT(18) 对应的管脚是 GPL10};static volatile char key_values [] = {'r', 'r', 'r', 'r'};   //初始状态都是released状态'r',当有按键按下时变为pressed状态'p'。/*****************************************************DECLARE_WAIT_QUEUE_HEAD(name)是一个与等待队列有关的宏展开为:wait_queue_head_t name={.lock=__SPIN_LOCK_UNILOCKED(name.lock),.task_list={&(name), task_list, &(name).task_list}}其中wait_queue_head_t的定义为:typedef struct __wait_queue_head wait_queue_head_t;struct __wait_queue_head{spinlock_t lock;struct list_head task_list;};仅供参考!******************************************************/static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  static volatile int action_flag = 0;     //事件监测,用于在read操作中判断是否有按键按下static irqreturn_t buttons_interrupt(int irq, void *dev_id)   //中断处理函数,结构是固定的。{struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;int down;int number;unsigned tmp;unsigned excp=0;number = button_irqs->number;switch(number) {case 0:tmp = readl(S3C64XX_GPNDAT);down = (tmp & (1 << 11));    //GPN11break;case 1:tmp = readl(S3C64XX_GPLDAT);down = (tmp & (1 << 8));    //GPL8break;case 2:tmp = readl(S3C64XX_GPLDAT);down = (tmp & (1 << 9));    //GPL9break;case 3:tmp = readl(S3C64XX_GPLDAT);down = (tmp & (1 << 10));    //GPL10break;default:excp=1;down = 0;}if(excp==0){   //此处主要是为了防止其他不可预测的误操作。if(down==0){key_values[number]='p';}else{key_values[number]='r';}        action_flag = 1;        wake_up_interruptible(&button_waitq);}    return IRQ_RETVAL(IRQ_HANDLED);}static int s3c64xx_buttons_open(struct inode *inode, struct file *file){    int i;    int err = 0;        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;}/***********************************************************************************************************************************Linux中中断的申请request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);其中:irq表示要申请的中断号;handler表示中断服务程序入口地址;flags表示中断处理的属性,控制上升沿触发、下降沿触发什么的;name是设备描述,表示那一个设备在使用这个中断;dev一般是指向设备数据结构的指针,在调用中断处理程序的时候它就会传递给中断处理程序的void *dev,如果中断没有被共享,dev可以设置为NULL。*************************************************************************************************************************************/        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,                           button_irqs[i].name, (void *)&button_irqs[i]);        if (err)            break;    }    if (err) {        i--;        for (; i >= 0; i--) {    if (button_irqs[i].irq < 0) {continue;    }    disable_irq(button_irqs[i].irq);            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);        }        return -EBUSY;    }    for(i=0; i<sizeof(key_values)/sizeof(key_values[0]); i++)    {key_values[i]='r';    }    action_flag = 0;        return 0;}static int s3c64xx_buttons_close(struct inode *inode, struct file *file){    int i;        for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {    continue;}/*****************************************释放中断:void free_irq(unsigned int irq, void *dev_id);******************************************/free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);    }    return 0;}static int s3c64xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp){    unsigned long err;    if (!action_flag) {if (filp->f_flags & O_NONBLOCK)   //判断设备文件是否以非阻塞模式打开,若是,则马上返回    return -EAGAIN;else    wait_event_interruptible(button_waitq, action_flag);   //等待中断发生。    }        action_flag = 0;    err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));    return err ? -EFAULT : min(sizeof(key_values), count);}static unsigned int s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait){    unsigned int mask = 0;    poll_wait(file, &button_waitq, wait);    if (action_flag)        mask |= POLLIN | POLLRDNORM;    return mask;}static struct file_operations dev_fops = {    .owner   =   THIS_MODULE,    .open    =   s3c64xx_buttons_open,    .release =   s3c64xx_buttons_close,     .read    =   s3c64xx_buttons_read,    .poll    =   s3c64xx_buttons_poll,};static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,};static int __init dev_init(void){int ret;/******************************************************************************(misc device)杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在Linux内核的include/linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备的范畴,所有这些设备采用主编号10,一起归于misc device, 其实misc_register就是主编号10调用register_chrdev()的。也就是说,misc设备其实也就是特殊的字符设备,可自动生成设备节点。*******************************************************************************/ret = misc_register(&misc);      //系统会自动在/dev/下生成buttons设备文件printk (DEVICE_NAME"\tinitialized\n");return ret;}static void __exit dev_exit(void){misc_deregister(&misc);}module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("FlyCloud Inc.");

驱动程序Makefile文件:

obj-m :=buttons.oKERNELDIR :=/Tiny6410/kernel/linux-2.6.38/PWD :=$(shell pwd)all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:rm -rf *.o *.ko *.mod.* *.symvers.PHONY:all clean

测试程序源码:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#define DEVICE_NAME "/dev/buttons"int main(int argc, char* argv[]){int fd;fd = open(DEVICE_NAME, O_RDWR);if(fd < 0){printf("Open %s failed!\n", DEVICE_NAME);exit(-1);}unsigned char key_values[4];unsigned char history_values[4];int i=0;for(i=0;i<4;i++){history_values[i]='r';}for( ; ; ){int i=read(fd, key_values, sizeof(key_values));if(i!=-1){for(i=0;i<4;i++){if(history_values[i]=='r' && key_values[i]=='p'){printf("按键 %d 被按下!\n", i);}if(history_values[i]=='p' && key_values[i]=='r'){printf("按键 %d 被释放!\n", i);}history_values[i]=key_values[i];}}}close(fd);return 0;}

测试效果截图:

(————仅供参考,仅供参考———–)

如你想要拥有完美无暇的友谊,可能一辈子找不到朋友

Tiny6410 + Linux2.6.38 按键驱动改写和说明

相关文章:

你感兴趣的文章:

标签云: