AT9260 GPIO中断

#i nclude <linux/init.h>#i nclude <linux/dma-mapping.h>#i nclude <linux/module.h>#i nclude <linux/sched.h>#i nclude <linux/completion.h>#i nclude <linux/interrupt.h>#i nclude <linux/clk.h>#i nclude <linux/platform_device.h>#i nclude <linux/atmel_pdc.h>#i nclude <asm/io.h>#i nclude <asm/system.h>#i nclude <asm/uaccess.h>#i nclude <linux/semaphore.h>#i nclude <linux/kernel.h>#i nclude <linux/cdev.h>#i nclude <linux/types.h>#i nclude <linux/fs.h>#i nclude <linux/input.h>#i nclude <linux/errno.h>#i nclude <linux/irq.h>#i nclude <linux/debugfs.h>#i nclude <linux/seq_file.h>#i nclude <linux/list.h>#i nclude <mach/board.h>#i nclude <mach/gpio.h>#i nclude <mach/irqs.h>#i nclude <mach/hardware.h>#i nclude <mach/at91_pio.h>#define DEVICE_NAME "button"#define KEY_TIMER_DELAY1 (HZ/50) //按键按下去抖延时20毫秒 #define KEY_TIMER_DELAY2 (HZ/10) //按键抬起去抖延时100毫秒#define KEYSTATUS_DOWN 0 //按键按下 #define KEYSTATUS_UP 1 //按键抬起 #define KEYSTATUS_DOWNX 2 //按键不确定 #define KEY_NUM 4 // 4个按键#define DP_MAJOR 0//主设备号#define DP_MINOR 0 //次设备号unsigned int button_major = DP_MAJOR;// 按键驱动的设备结构体、定时器#define MAX_KEY_BUF 16 // 按键缓存区大小typedef unsigned char KEY_RET;// 设备结构体typedef struct{ unsigned int keyStatus[KEY_NUM]; // 4个按键的按键状态 KEY_RET buf[MAX_KEY_BUF]; unsigned int head,tail; // 按键缓存区头和尾 wait_queue_head_t wq; // 等待队列 struct cdev cdev; // cdev 结构体}KEY_DEV;KEY_DEV key_dev,*key_devp;#define BUF_HEAD (key_dev.buf[key_dev.head]) //缓冲头#define BUF_TAIL (key_dev.buf[key_dev.tail]) //缓冲尾#define INCBUF(x,mod) ((++(x))&((mod)-1))static struct timer_list key_timer[KEY_NUM]; // 4个按键去抖定时器// 按键硬件资源、键值信息结构体static struct key_info{ int irq_no; // 中断号 int irq_type; // 中断类型 unsigned int gpio_port; // GPIO端口 int key_no; // 键值 }key_info_tab[4]=// 按键所使用的CPU资源{ { AT91_PIN_PB0,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB0,1 }, { AT91_PIN_PB1,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB1,2 }, { AT91_PIN_PB2,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB2,3 }, { AT91_PIN_PB3,AT91_AIC_SRCTYPE_FALLING,AT91_PIN_PB3,4 },};// 初始化static void at91sam9260_key_io_init(void){ at91_set_gpio_input(AT91_PIN_PB0, 1); at91_set_deglitch(AT91_PIN_PB0, 1); at91_set_gpio_input(AT91_PIN_PB1, 1); at91_set_deglitch(AT91_PIN_PB1, 1); at91_set_gpio_input(AT91_PIN_PB2, 1); at91_set_deglitch(AT91_PIN_PB2, 1); at91_set_gpio_input(AT91_PIN_PB3, 1); at91_set_deglitch(AT91_PIN_PB3, 1);}/* 记录键值并唤醒等待队列 */static void keyEvent(unsigned int key){ BUF_HEAD = key_info_tab[key].key_no; // 记录键值 key_dev.head = INCBUF(key_dev.head, MAX_KEY_BUF); // 调整缓冲区头指针 wake_up_interruptible(&(key_dev.wq)); // 唤醒等待队列}// 按键设备的中断处理// 键被按下后,将发生中断,在中断处理程序中,应该关闭中断进入查询模式,延迟20ms以实现去抖动// 这个中断处理过程只包含顶半部,无底半部static irqreturn_t at91sam9260_enit_key(int irq,void *dev_id,struct pt_regs *reg){ int key = (int)dev_id; int found = 0; int i; for (i = 0; i < ARRAY_SIZE(key_info_tab); i++) // 查找产生中断的按键 { if (key_info_tab[i].irq_no == irq) { found = 1; break; } } if (!found) // 没找到 { printk(KERN_NOTICE"bad irq %d in button\n", irq); return IRQ_NONE; //错误的中断 } disable_irq(key_info_tab[key].irq_no); // 找到,关闭对应中断 key_dev.keyStatus[key] = KEYSTATUS_DOWNX; // 不确定是否为按下 key_timer[key].expires = jiffies + KEY_TIMER_DELAY1;// 延迟 add_timer(&key_timer[key]); // 启动定时器 return IRQ_HANDLED; //正确的中断}// 定时器处理函数static void key_timer_handler(unsigned long data){ int key = data; //获取当前按键引脚上的电平值来判断按键是按下还是抬起 int up = at91_get_gpio_value(key_info_tab[key].gpio_port); if (!up) { if (key_dev.keyStatus[key] == KEYSTATUS_DOWNX) //从中断进入 { key_dev.keyStatus[key] = KEYSTATUS_DOWN; key_timer[key].expires = jiffies + KEY_TIMER_DELAY2; //延迟 keyEvent(key); //记录键值,唤醒等待队列 add_timer(&key_timer[key]); } else { key_timer[key].expires = jiffies + KEY_TIMER_DELAY2; //延迟 add_timer(&key_timer[key]); } } else //键已抬起 { key_dev.keyStatus[key] = KEYSTATUS_UP; enable_irq(key_info_tab[key].irq_no); }}// 按键设备驱动的中断申请函数// 申请系统中断,中断方式为下降沿触发static int request_irqs(void){ struct key_info *k; int i; int ret; for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++) // for(i = 0; i < 1; i++) { k = key_info_tab + i; //设置4个IO口为中断下降沿触发方式 set_irq_type(key_info_tab[i].irq_no, key_info_tab[i].irq_type); // 设置中断类型 //申请中断(类型为快速中断,中断服务时屏蔽所有外部中断?)将按键序列号作为参数传入中断服务程序 // if(request_irq(k->irq_no,at91sam9260_enit_key,0,DEVICE_NAME,(void *)i)) ret = request_irq(k->irq_no,at91sam9260_enit_key,0,DEVICE_NAME,(void *)i); if(ret) { printk(KERN_NOTICE "buttons:ret is %d\r\n",ret); return -1; } } return 0; }// 释放中断static void free_irqs(void){ struct key_info *k; int i; for(i = 0; i < sizeof(key_info_tab)/sizeof(key_info_tab[1]); i++) { k = key_info_tab + i; free_irq(k->irq_no,(void *)i); } }// 按键设备驱动的打开、释放函数static int at91sam9260_key_open(struct inode *inode, struct file *filp){ key_dev.head = key_dev.tail = 0; //清空按键动作缓冲区 // keyEvent = keyEvent_raw; //函数指针指向按键处理函数keyEvent_raw return 0;}static int at91sam9260_key_release(struct inode *inode, struct file *filp){ // keyEvent = keyEvent_dummy; //函数指针指向空函数 return 0;}// 按键设备驱动读函数static ssize_t at91sam9260_key_read(struct file *filp, char __user *buf, ssize_t count, loff_t*ppos){ unsigned int key_ret; unsigned long flags;retry: if (key_dev.head != key_dev.tail) // 缓冲区有数据? { local_irq_save(flags); // 进入临界区 ,关中断 key_ret = BUF_TAIL; // 读出键值 key_dev.tail = INCBUF(key_dev.tail, MAX_KEY_BUF); // 调整缓冲区尾指针 local_irq_restore(flags); // 退出临界区,开中断 if(copy_to_user(buf, (void *)&key_ret, sizeof(unsigned int))) // 拷贝到用户空间 { return -EFAULT; } else { return sizeof(unsigned int); } } else // 缓冲区没数据 { if (filp->f_flags & O_NONBLOCK) // 若采用非阻塞方式读取则返回错误 { return -EAGAIN; } interruptible_sleep_on(&(key_dev.wq)); // 使进程睡眠 if (signal_pending(current)) //在这里等中断 { // 如果是信号中断 return -ERESTARTSYS; } goto retry; } return sizeof(unsigned int);}// 按键设备驱动文件操作结构体,主要实现打开、释放和读函数static struct file_operations at91sam9260_key_fops ={ .owner = THIS_MODULE, .open = at91sam9260_key_open, // 启动设备 .release = at91sam9260_key_release, // 关闭设备 .read = at91sam9260_key_read, // 读取按键的键值};// 模块加载函数包括:设备号申请、cdev的添加,另:申请中断、初始化定时器和队列;// 模块卸载函数恰好相反static int __init at91sam9260_key_init(void){ int i; int result; // 申请设备号 dev_t dev = MKDEV(button_major, DP_MINOR); /* 申请设备号 */ if (button_major) result = register_chrdev_region(dev, 1, DEVICE_NAME); else /* 动态申请设备号 */ { result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); button_major = MAJOR(dev); printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, button_major); } // 分配设备结构体的内存 key_devp = kmalloc(sizeof(key_dev),GFP_KERNEL); if(!key_devp) { result = – ENOMEM; goto fail_malloc; } // 添加cdev memset(key_devp,0,sizeof(key_dev)); cdev_init(&key_dev.cdev, &at91sam9260_key_fops); if(cdev_add(&key_dev.cdev, dev, 1)){ printk(KERN_ALERT"Add char dev error!\n"); } key_dev.head = key_dev.tail = 0; // 初始化结构体 for(i = 0; i < KEY_NUM; i++) { key_dev.keyStatus[i] = KEYSTATUS_UP; } init_waitqueue_head(&(key_dev.wq)); // 等待队列 // 初始化定时器,实现软件的去抖动 /* 初始化定时器 */ for(i = 0; i < KEY_NUM; i++) { key_timer[i]. = key_timer_handler; key_timer[i].data = i; init_timer(&key_timer[i]); } at91sam9260_key_io_init(); // 初始化 // 注册中断函数 if(-1 == request_irqs()) { printk(KERN_NOTICE "request_irqs failed!\r\n"); } else { printk(KERN_NOTICE "request_irqs success!\r\n"); } /* for(i = 0; i < KEY_NUM; i++) { setup_timer(&key_timer[i],key_timer_handler,i); } */ fail_malloc:unregister_chrdev_region(dev,1); return result; }// 按键设备驱动的模块卸载函数static void __exit at91sam9260_key_exit(void){ free_irqs(); // 注销中断 // 释放设备号,删除cdev cdev_del(&key_dev.cdev); // 删除字符设备结构体 kfree(key_devp); unregister_chrdev_region(MKDEV(button_major,DP_MINOR),1); // 删除字符设备}MODULE_AUTHOR("launch");MODULE_LICENSE("Dual BSD/GPL");module_init(at91sam9260_key_init);module_exit(at91sam9260_key_exit);听他第二十八次提起童年往事,每年的同一天和他庆祝生日,

AT9260 GPIO中断

相关文章:

你感兴趣的文章:

标签云: