嵌入式开发记录 day30 中断方式获取按键值
1、中断执行
按键按下(产生了中断)→跳转到异常向量入口,执行中断函数
中断函数要做什么:
1.保护现场
2.执行中断处理函数
3.恢复现场
2、向内核申请中断,以及释放中断
// 注册中断: request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev) 参数1:irq,中断号。(和平台架构相关,结合datasheet以及平台文件) 参数2:中断处理函数,回调函数,中断发生后调用的函数 参数3:中断标记。上升/下降沿,高/低电平…… 参数4:中断名字。cat /proc/interrupts 参数5:使用设备的设备结构体或者NULL。 // 释放中断: free_irq(irq,*dev_id)
3、硬件准备
需要注册中断:需要获取中断名称 HOME对应管脚-->UART_RING对应GPIO-->GPX1_1对应宏定义-- ->EXYNOS4_GPX1(1)对应的中断--->XEINT9--->IRQ_EINT(9) Back对应管脚-->SIM_DET对应GPIO---->GPX1_2对应宏定义-- ->EXYNOS4_GPX1(2)对应的中断--->XEINT10-->IRQ_EINT(10)
4、keyirq.c
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> #include <mach/regs-gpio.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/regulator/consumer.h> #include <linux/delay.h> //中断头文件 #include <linux/irq.h> #include <linux/interrupt.h> #define DPRINTK(x...) printk("POLLKEY_CTL DEBUG:" x) #define DRIVER_NAME "keyirq" // 中断处理函数1 static irqreturn_t eint9_interrupt(int irq,void *dev_id) { printk("receive a interrupt 9!\n"); return IRQ_HANDLED; // 中断处理程序已经被处理;IRQ_NONE:中断处理异常 } // 中断处理函数2 static irqreturn_t eint10_interrupt(int irq,void *dev_id) { printk("receive a interrupt 10!\n"); return IRQ_HANDLED; } static int keyirq_probe(struct platform_device *pdev) { char *banner = "keyirq Initialize\n"; printk(banner); // 初始化提示信息 // 注册为中断不需要设置管脚输入输出属性 // 1、 申请中断: // 中断好号 中断处理函数 上升沿下降沿 高低电平 中断名字 设备结构 request_irq(IRQ_EINT(9), eint9_interrupt, IRQ_TYPE_EDGE_FALLING,"my_eint9", pdev); request_irq(IRQ_EINT(10),eint10_interrupt,IRQ_TYPE_EDGE_FALLING,"my_eint10",pdev); return 0; } static int keyirq_remove (struct platform_device *pdev) { // 释放中断: free_irq(IRQ_EINT(9), pdev); free_irq(IRQ_EINT(10),pdev); return 0; } static int keyirq_suspend (struct platform_device *pdev, pm_message_t state) { DPRINTK("keyirq suspend:power off!\n"); return 0; } static int keyirq_resume (struct platform_device *pdev) { DPRINTK("keyirq resume:power on!\n"); return 0; } // 平台驱动结构体 static struct platform_driver keyirq_driver = { .probe = keyirq_probe, .remove = keyirq_remove, .suspend = keyirq_suspend, .resume = keyirq_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; static int __init keyirq_init(void) { // 平台驱动注册 return platform_driver_register(&keyirq_driver); } static void __exit keyirq_exit(void) { // 驱动卸载 platform_driver_unregister(&keyirq_driver); } module_init(keyirq_init); module_exit(keyirq_exit); MODULE_LICENSE("Dual BSD/GPL");
5、在平台文件中注册设备:keyirq,(平台文件)vim arch/arm/mach-exynos/mach-itop4412.c
struct platform_device s3c_device_keyirq_ctl = { .name = "keyirq", .id = -1, }; // 在另一段添加 &s3c_device_keyirq_ctl,
6、测试
1、注册为中断,在中断回调函数中没有实现设备的读写,所以只触发中断,执行中断函数就行;
2、正常加载模块后,按下按键HOME和Back,看到打印信息,一切正常;
3、查看注册的中断, cat /proc/interrupts查看中断
7、中断处理程序编写注意事项:
1、中断处理函数不能调用可能睡眠的函数;copy_to_user() copy_from_user()
2、不能把自己调度出去,没有current指针,保存不了堆栈信息;
3、不能调用enable irq或者disable irq之类的函数,
4、力求简单、快速,中断执行程序是在关闭中断的场景下运行的;
8、中断
中断上半部和中断下半部:在中断上半部执行一些需要快速处理的操作,追求简洁;不能有睡眠;中断中断下半部处理一些复杂,耗时的操作;
8.1、中断下半部:
下半部处理复杂任务有两种实现机制:tasklet(小任务机制)和workqueue
通常使用tasklet_struct结构来描述小任务;小任务机制不能被线程调度,可睡眠;
// 1、根据GPIO请求一个中断号; gpio_to_irq(unsigned gpio) // 2. 根据中断号注册中断处理函数,此时的中断处理函数也称为中断上半部; request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * name, void * dev) struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; }; // 初始化并声明一个tasklet #define DECLARE_TASKLET(name, func, data) \ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } #define DECLARE_TASKLET_DISABLED(name, func, data) \ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } // 3、初始化tasklet中断下半部,此处的func为中断下半部; tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data) // 4、tasklet中断下半部调度函数,一般在中断的上半部处理完的最后调度;执行中断下半部; tasklet_schedule(struct tasklet_struct * t)
8.2、工作队列机制:可睡眠可调度
struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; #define INIT_WORK(_work, _func) \ __INIT_WORK((_work), (_func), 0) // 工作队列回调函数 void (*work_func_t)(struct work_struct *work); schedule_work(struct work_struct * work) // 内核异步数据处理 kfifo_alloc(fifo, size, gfp_mask) struct __kfifo { unsigned int in; unsigned int out; unsigned int mask; unsigned int esize; void *data; }; #define kfifo_free(fifo) kfifo_in(fifo, buf, n) kfifo_out(fifo, buf, n) kfifo_len(fifo) kfifo_size(fifo) kfifo_is_empty(fifo) kfifo_is_full(fifo)

浙公网安备 33010602011771号