按键驱动(平台文件、中断)
来源:互联网 发布:网络管理(兼职) 编辑:程序博客网 时间:2024/06/05 06:28
1. 基本的思路(两种方法)
方法一:直接以驱动的方式,在一个文件中将设备资源写好,驱动直接使用。
方法二:以 设备 和 驱动 分离的思想,驱动中用获取函数的方式使用设备。方法二的实现
2. 实现方法一
文件结构:
.
├── dri
│ ├── key_irq.c
│ └── Makefile
└── test
└── test.c
key_irq.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/poll.h>#include <linux/interrupt.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <linux/wait.h>#include <linux/device.h>#include <mach/gpio.h>#include <linux/interrupt.h>#include <linux/sched.h>#include <linux/timer.h>#define DEVICE_NAME "jz2440_button_drv"static unsigned char key_vals[4]; /* 保存要传递给用户空间的4个按键值 */static struct timer_list key_timers[4];#define KEYDOWN_DELAY (HZ/100) /* 按键按下时的去抖延时是10ms */#define KEYUP_DELAY (HZ/200) /* 按键抬起时的去抖延时是5ms */struct jz2440_key_t{ const char *name; unsigned int pin; unsigned char key_val; unsigned int irq; /*按键的中断号 */ unsigned int setting; /* 按键对应的功能的掩码 */ unsigned int id;};/* * key s2 is GPF0 -- EINT0 * key s3 is GPF2 -- EINT2 * key s4 is GPG3 -- EINT11 * key s5 is GPG11-- EINT19 */struct jz2440_key_t key_table[] ={ {"Button S2", S3C2410_GPF(0), 0, IRQ_EINT(0), S3C2410_GPF0_EINT0, 0}, {"Button S3", S3C2410_GPF(2), 0, IRQ_EINT(2), S3C2410_GPF2_EINT2, 1}, {"Button S4", S3C2410_GPG(3), 0, IRQ_EINT(11), S3C2410_GPG3_EINT11, 2}, {"Button S5", S3C2410_GPG(11), 0, IRQ_EINT(19), S3C2410_GPG11_EINT19, 3},};static struct class *button_drv_class;int major;unsigned int key_pressed;unsigned int key_pressed_middle_flag;static DECLARE_WAIT_QUEUE_HEAD(button_waitq);irqreturn_t irq_handler(int irq, void *dev_id){ int key_num = *((int *)dev_id); unsigned int pinval = s3c2410_gpio_getpin(key_table[key_num].pin); if (!pinval) { key_pressed_middle_flag = 1; mod_timer(&key_timers[key_num], jiffies + KEYDOWN_DELAY); }else { key_pressed_middle_flag = 0; mod_timer(&key_timers[key_num], jiffies + KEYUP_DELAY); } return IRQ_RETVAL(IRQ_HANDLED);}void fun_timer(unsigned long arg){ int pinval = s3c2410_gpio_getpin(key_table[arg].pin); if (!pinval) { if (key_pressed_middle_flag == 1) { /*printk("pree down\n");*/ key_pressed = 1; key_table[arg].key_val = 1; wake_up_interruptible(&button_waitq); /* 唤醒由 poll_wait 而引起的阻塞 */ } }else { if (key_pressed_middle_flag == 0) { /*printk("pree up\n");*/ key_pressed = 0; key_table[arg].key_val = 0; } }}static int jz2440_button_drv_open(struct inode *inode, struct file *filp){ int i; for (i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(key_table[i].pin, key_table[i].setting); /* 将芯片引脚设置成中断 */ irq_set_irq_type(key_table[i].irq, IRQ_TYPE_EDGE_BOTH); /* 下降沿触发 */ request_irq(key_table[i].irq, irq_handler, IRQF_DISABLED, key_table[i].name, &key_table[i].id); /* 向系统内核申请快速中断 */ setup_timer(&key_timers[i], fun_timer, i); } printk("button_drv_open\n"); return 0;}static int jz2440_button_drv_close(struct inode *inode, struct file *filp){ int i; for (i = 0; i < 4; i++) { del_timer(&key_timers[i]); /*disable_irq(key_table[i].irq);*/ free_irq(key_table[i].irq, &key_table[i].id); } return 0;}/* 读的时候按键的 size 是字节的个数 ,返回成功读取的个数 */ssize_t jz2440_button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){ int j; unsigned long ret; if (size != sizeof(key_vals)) return -EINVAL; /* 如果是非阻塞的读 */ if (file->f_flags & O_NONBLOCK) { if (!key_pressed) /* 非阻塞的读,按键没有按下过 */ return -EAGAIN; } else if (!key_pressed) /* 阻塞的读,按键没有按下过 */ { wait_event_interruptible(button_waitq, key_pressed); } key_pressed = 0; for (j = 0; j < 4; j++) { key_vals[j] = key_table[j].key_val; } ret = copy_to_user(buf, key_vals, size); /* 返回 key_vals 数组数据到用户空间 */ return ret ? -EFAULT : min(sizeof(key_vals), size);}unsigned int jz2440_button_drv_poll (struct file *filp, struct poll_table_struct *wait){ unsigned int mask = 0; poll_wait(filp, &button_waitq, wait); /* 将此线程放入等待队列头(此处还要看select传递过来的参数) */ if (key_pressed) mask |= POLLIN | POLLRDNORM; /* 标示数据可获得 */ return mask; }static struct file_operations button_drv_fops ={ .owner = THIS_MODULE, .open = jz2440_button_drv_open, .release = jz2440_button_drv_close, .read = jz2440_button_drv_read, .poll = jz2440_button_drv_poll,};int button_drv_init(void){ /* auto choice major */ major = register_chrdev(0, DEVICE_NAME, &button_drv_fops); /* 自动分配设备号,并注册字符设备 */ if (major < 0) { printk("jz2440 button driver can't register major number!\n"); return major; } button_drv_class = class_create(THIS_MODULE, DEVICE_NAME); /* 创建一个类 */ if (IS_ERR(button_drv_class)) { printk("error, fail to init button_drv_class"); return -1; } device_create(button_drv_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); /* 创建设备节点 */ printk("BUTTON_DRV initialized! \n"); return 0;}void button_drv_exit(void){ device_destroy(button_drv_class, MKDEV(major, 0)); //删掉设备节点 class_destroy(button_drv_class); //注销类 unregister_chrdev(major, DEVICE_NAME); //卸载驱动 printk("exit is finished.\n");}module_init(button_drv_init);module_exit(button_drv_exit);MODULE_LICENSE("GPL");
说明:
【1】中断说明,向系统内核申请了4个中断,可以用 cat /proc/interrupts 中查看到,按键设置成了中断方式,上升和下降沿触发的中断
【2】消抖,按下 向系统中添加了一个10ms的定时器,定时到了后,看是不是状态跟中断中的相同,定时器自动失效,抬起 向系统中添加了一个5ms的定时器 ...
【3】read 中用等待对列的方式,提供阻塞读的支持,如果用户程序以阻塞的方式读取,就会进去等待队列中,当有按键按下的时候,将等待队列中的按键唤醒,并将数据返回给用户,但是程序中不是 read 引起的等待,是由 select 引起的等待
【4】有4个按键,这四个按键对应着4个定时器,可以同时按下
【5】如果出现中断号不能释放掉的情况,并且用的是mini2440 的配置文件,很可能是你的mini2440的配置文件中有冲突,你需要去掉默认的按键的配置,这个部分可以查看jz2440:编译内核 这篇文章
Makefile
ifeq ($(KERNELRELEASE),)#KERNELDIR ?= /lib/modules/$(shell uname -r)/build KERNELDIR ?= ~/wor_lip/linux-3.4.112PWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_installclean:rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules* Module*.PHONY: modules modules_install cleanelseobj-m := key_irq.oendif
说明:
【1】一定要注意 KERNELDIR 这个变量,一定要设置成下载的内核源码的路径,不要用Ubuntu的路径,否则会出现找不到头文件的编译错误
test.c
#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/select.h>#include <fcntl.h>#include <unistd.h>#include <signal.h>int fd;fd_set rds;void stop(int signo){ close(fd); FD_CLR(fd, &rds); _exit(0);}int main(int argc, const char *argv[]){ unsigned char data_buf[4]; signal(SIGINT, stop); /* 按下ctrl+c,调用stop函数 */ /* 阻塞的读 */ if ((fd = open("/dev/jz2440_button_drv", O_RDONLY)) < 0) { perror("fail to open"); return -1; } while (1) { int ret, i; FD_ZERO(&rds); FD_SET(fd, &rds); /* 将fd文件描述符添加到rds这个文件描述符集中去 */ ret = select(fd + 1, &rds, NULL, NULL, NULL); printf("doing\n"); if (ret < 0) { printf("read jz2440 button fail!\n"); continue; }else if (ret == 0) { printf("read jz2440 button time out!\n"); continue; } if (FD_ISSET(fd, &rds)) { ret = read(fd, data_buf, sizeof(data_buf)); if (ret < 4) { printf("read not complete.\n"); break; } for (i = 0 ; i < 4; i++) { printf("S%d:%d ", 2 + i, data_buf[i]); } printf("\n"); } } return 0;}
说明:
这个文件编译的命令是
arm-none-linux-gnueabi-gcc test.c -o button -march=armv4t
打印的结果是
[root@lip ~]# insmod key_irq.ko
BUTTON_DRV initialized!
[root@lip ~]# ./button
button_drv_open
doing
S2:0 S3:1 S4:0 S5:0
doing
S2:0 S3:0 S4:1 S5:0
doing
S2:1 S3:0 S4:0 S5:0
doing
S2:0 S3:0 S4:0 S5:1
- 按键驱动(平台文件、中断)
- 中断处理(按键驱动)
- 按键驱动(一)-中断
- linux驱动之按键(中断)
- 外部中断按键驱动
- fl2440按键中断驱动
- 按键中断驱动实例
- s3c6410中断按键驱动
- s5pc100中断按键驱动
- TQ2440 中断按键驱动
- OK6410 按键中断驱动
- 按键中断驱动
- 按键中断驱动
- 按键中断驱动
- mtk 按键中断驱动
- 中断按键驱动
- 驱动-按键-中断模式
- S3C6410 按键驱动(二) ---按键中断的基本流程
- 动态规划
- cdsn ——博客
- 低频RFID读卡流程
- [转]xcode提示App Transport Security has blocked a cleartext HTTP (http://) resource load的解决办法
- android面试题(未完)
- 按键驱动(平台文件、中断)
- DragonBones
- spark二次排序
- 仿大街网-堆栈式卡片布局-SwipeCardLayout
- C++学习之重载、覆盖与隐藏
- 面经
- elasticsearch中的doc_values
- 第八周项目3-顺序串算法
- LeetCode 310. Minimum Height Trees题解