linux驱动开发之多路复用poll+按键点灯+中断+标准输入输出
2018-01-26 22:13
453 查看
程序文件包:buttons_drv_v5
/[b]********************[/b]
**实现的功能:
**1.按下左键—–点灯,延时1s,松开左键—-灭灯
**2.按下右键—打开蜂鸣器,延时1s,松开右键—-关闭蜂鸣器
**3.标准输入一个字符串,显示在屏幕上
**4.按下上键,下键,执行相应操作(待定)
[b]********************[/b]/
开发板型号:s5pv210
编译工具:source insight 4.0 ,ubuntu ,putty
设计思路:
一,首先看开发板原理图FS210_DEV_V5
这里查找到KEY1,KEY2,KEY3,KEY4相对应的GPIO脚
继续查看芯片原理图FS210_CORE
看原理图可知
KEY1—XEINT0—GPH0_0—s5pv210_GPH0(0)
KEY2—XEINT1—GPH0_1—s5pv210_GPH0(1)
KEY3—XEINT2—GPH0_2—s5pv210_GPH0(2)
KEY4—XEINT3—GPH0_3—s5pv210_GPH0(3)
接下来写驱动中实现接口的程序(共有3个)
第一个:buttons_drv_v5.c
驱动中实现接口的程序还有led_drv.c和beep_drv.c,详细看压缩包。
接下来写应用空间的程序:buttons_v5.c
执行效果如图:
此处有程序文件包链接:https://pan.baidu.com/s/1kXkUWVd 密码:yif5
/[b]********************[/b]
**实现的功能:
**1.按下左键—–点灯,延时1s,松开左键—-灭灯
**2.按下右键—打开蜂鸣器,延时1s,松开右键—-关闭蜂鸣器
**3.标准输入一个字符串,显示在屏幕上
**4.按下上键,下键,执行相应操作(待定)
[b]********************[/b]/
开发板型号:s5pv210
编译工具:source insight 4.0 ,ubuntu ,putty
设计思路:
一,首先看开发板原理图FS210_DEV_V5
这里查找到KEY1,KEY2,KEY3,KEY4相对应的GPIO脚
继续查看芯片原理图FS210_CORE
看原理图可知
KEY1—XEINT0—GPH0_0—s5pv210_GPH0(0)
KEY2—XEINT1—GPH0_1—s5pv210_GPH0(1)
KEY3—XEINT2—GPH0_2—s5pv210_GPH0(2)
KEY4—XEINT3—GPH0_3—s5pv210_GPH0(3)
接下来写驱动中实现接口的程序(共有3个)
第一个:buttons_drv_v5.c
//头文件 #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/input.h> #include <linux/sched.h> #include <linux/poll.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm-generic/ioctl.h> //定义一个按键的数据包 struct button_event{ int code; //按键的名字----键值 int value; //按键的状态:1---按下,0---松开 }; //定义一个设备驱动的类型 struct s5pv210_button{ dev_t devno; //设备号 struct class *cls; //struct class类型的指针 struct device *device; //自动创建设备节点device_create返回值:成功-- struct device类型的地址,失败--NULL struct cdev *cdev; //申请设备号时用来描述一个字符设备的结构体 unsigned int irqno; //中断号 wait_queue_head_t wq_head; //等待队列头 struct button_event event; //定义一个按键的数据包 int have_data; //0表示没有数据,1表示有数据 }; struct s5pv210_button *button_dev; //定义一个描述按键信息的结构体 struct buttons{ char *name; //按键名称 unsigned int irqno; //中断号 int gpio; //按键对应的gpio口 int code; //键值 unsigned long flags;//中断触发方式 }; //定义一个存储多个按键信息的集合 struct buttons buttons_set[] = { [0] = { .name = "上键", .irqno = IRQ_EINT(0), .gpio = S5PV210_GPH0(0), .code = KEY_UP, .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,//下降沿或者上升沿触发 }, [1] = { .name = "下键", .irqno = IRQ_EINT(1), .gpio = S5PV210_GPH0(1), .code = KEY_DOWN, .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,//下降沿或者上升沿触发 }, [2] = { .name = "左键", .irqno = IRQ_EINT(2), .gpio = S5PV210_GPH0(2), .code = KEY_LEFT, .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,//下降沿或者上升沿触发 }, [3] = { .name = "右键", .irqno = IRQ_EINT(3), .gpio = S5PV210_GPH0(3), .code = KEY_RIGHT, .flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,//下降沿或者上升沿触发 }, }; //中断处理函数 irqreturn_t button_irq_svc(int irqno, void * dev) { int value; struct buttons *p; printk("--------------%s--------------\n",__FUNCTION__); p = (struct buttons*)dev; //区分按下还是松开触发的中断 value = gpio_get_value(p->gpio); if(value){ //松开 printk("kernel:%s--松开\n",p->name); button_dev->event.code = p->code; button_dev->event.value = 0; }else{ //按下 printk("kernel:%s--按下\n",p->name); button_dev->event.code = p->code; button_dev->event.value = 1; } //此时有数据可读 button_dev->have_data = 1; //从等待队列中唤醒阻塞的进程 wake_up_interruptible(&button_dev->wq_head); return IRQ_HANDLED;//默认 } int button_open(struct inode *inode, struct file *filp) { printk("--------------%s--------------\n",__FUNCTION__); button_dev->have_data = 0; return 0; } ssize_t button_write(struct file *filp, const char __user *buf, size_t size, loff_t * flags) { printk("--------------%s--------------\n",__FUNCTION__); return size; } ssize_t button_read(struct file *filp, char __user *buf, size_t size , loff_t *flags) { int ret; printk("--------------%s--------------\n",__FUNCTION__); //在没有数据的前提下,如果调用open时,设置了O_NONBLOCK ,此时让read()直接返回.这就算非阻塞IO if((filp->f_flags & O_NONBLOCK) && !button_dev->have_data); return -EAGAIN; //判断是否有资源可读 wait_event_interruptible(button_dev->wq_head, button_dev->have_data); //将数据传递给应用空间 ret = copy_to_user(buf, &button_dev->event, size); if(ret < 0) { printk("copy_to_user error!\n"); return -EFAULT; } //清空event,同时将have_data置0 memset(&button_dev->event,0,sizeof(button_dev->event)); button_dev->have_data = 0; return size; } long button_ioctl(struct file *flip, unsigned int cmd, unsigned long args) { printk("--------------%s--------------\n",__FUNCTION__); return 0; } //多路复用 unsigned int button_poll(struct file *filp , struct poll_table_struct *pts) { unsigned int mask = 0; printk("--------------%s--------------\n",__FUNCTION__); // 1,将等待队列头注册到系统中---VFS poll_wait(filp, &button_dev->wq_head, pts); // 2,如果有数据可读返回POLLIIN,如果没有数据返回:0 if(button_dev->have_data) mask |= POLLIN; return mask; } int button_close(struct inode *inode, struct file *flip) { printk("--------------%s--------------\n",__FUNCTION__); return 0; } /* **用户进行利用在对设备文件进行诸如read/write操作的时候,系统调用通过设备文件的主设备号找到相对应的设备程序, **然后读取这个数据结构相对应的函数指针,接着把控制权交给该函数,这就是linux的设备驱动程序工作的基本原理。 */ static struct file_operations fops = { .open = button_open, .release = button_close, .write = button_write, .read = button_read, .unlocked_ioctl = button_ioctl, .poll = button_poll, }; //模块加载函数 static int __init button_drv_init(void) { int ret,i; printk("--------------%s--------------\n",__FUNCTION__); // 0.实例化对象 button_dev = kzalloc(sizeof(struct s5pv210_button), GFP_KERNEL); if(IS_ERR(button_dev)){ printk("kzalloc error!\n"); ret = PTR_ERR(button_dev); return -ENOMEM; } // 1.申请设备号 #if 0 //静态方法 button_dev->major = 256; ret = register_chrdev_region(MKDEV(button_dev->major, 0), 1, "button_drv"); if(ret < 0){ printk("register_chrdev_region error!\n"); goto err_kfree; } #else //1.动态申请设备号 ret = alloc_chrdev_region(&button_dev->devno,0,1,"button_drv"); if(ret < 0){ printk("alloc_chrdev_region error!\n"); goto err_kfree; } #endif //创建cdev button_dev->cdev = cdev_alloc(); if(IS_ERR(button_dev->cdev)){ printk("cdev_alloc error!\n"); ret = PTR_ERR(button_dev->cdev ); goto err_unregister; } cdev_init(button_dev->cdev, &fops); cdev_add(button_dev->cdev, button_dev->devno, 1); // 2.创建设备节点 button_dev->cls = class_create(THIS_MODULE, "button_class"); if(IS_ERR(button_dev->cls)){ printk("class_create error!\n"); ret = PTR_ERR(button_dev->cls); goto err_unregister; } button_dev->device = device_create(button_dev->cls, NULL, button_dev->devno,NULL, "button"); if(IS_ERR(button_dev->device)){ printk("device_create error!\n"); ret = PTR_ERR(button_dev->device); goto err_class; } // 3,硬件初始化 -- 申请中断 for(i = 0; i < ARRAY_SIZE(buttons_set); i++){ ret = request_irq(buttons_set[i].irqno,button_irq_svc,buttons_set[i].flags,buttons_set[i].name,&buttons_set[i]); if(ret != 0){ printk(" request_irq error!\n"); goto err_device; } } //初始化等待队列头 init_waitqueue_head(&button_dev->wq_head); return 0; err_device: device_destroy(button_dev->cls, button_dev->devno); err_class: class_destroy(button_dev->cls); err_unregister: unregister_chrdev_region(button_dev->devno, 1); err_kfree: kfree(button_dev); return ret; } //模块卸载函数 static void __exit button_drv_exit(void) { int i; printk("--------------%s--------------\n",__FUNCTION__); for(i = 0; i < ARRAY_SIZE(buttons_set); i++) free_irq(buttons_set[i].irqno, &buttons_set[i]); device_destroy(button_dev->cls, button_dev->devno); class_destroy(button_dev->cls); unregister_chrdev_region(button_dev->devno, 1); kfree(button_dev); } MODULE_AUTHOR("752800373@qq.com"); MODULE_DESCRIPTION("this is a magical driver!\n"); //模块声明和认证 module_init(button_drv_init); module_exit(button_drv_exit); MODULE_LICENSE("GPL");
驱动中实现接口的程序还有led_drv.c和beep_drv.c,详细看压缩包。
接下来写应用空间的程序:buttons_v5.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/input.h> #include <poll.h> /****************************** **实现的功能: **1.按下左键-----点灯,延时1s,松开左键----灭灯 **2.按下右键---打开蜂鸣器,延时1s,松开右键----关闭蜂鸣器 **3.标准输入一个字符串,显示在屏幕上 ******************************/ //定义一个按键的数据包 struct button_event{ int code; //按键的名称 ---- 键值 int value; //按键的状态: 1--- 按下, 0 ---- 松开 }; int main(void) { int fd,fd2,fd3; struct button_event event; int ret; //打开设备 fd = open("/dev/button",O_RDWR); fd2 = open("/dev/led",O_RDWR); fd3 = open("/dev/beep",O_RDWR); if(fd < 0 && fd2 < 0 && fd3 < 0){ perror("open"); exit(1); } //用poll同时监控标准输入和键盘按键 struct pollfd pfds[2]; char buf[128]; pfds[0].fd = STDIN_FILENO; //标准输入 pfds[0].events = POLLIN; //是否可读 pfds[1].fd = fd; //按键的文件描述符 pfds[1].events = POLLIN; //键盘是否有数据可读 //对设备进行操作 while(1){ int on; ret = poll(pfds,2,-1); if(ret < 0) { perror("poll"); exit(1); } if(ret > 0){ //哪个文件描述符可读 if(pfds[0].revents & POLLIN){ fgets(buf,128,stdin); printf("buf:%s",buf); } if(pfds[1].revents & POLLIN){ memset(&event,0,sizeof(event)); ret = read(fd,&event,sizeof(event)); if(ret < 0 ){ perror("read"); //exit(1); } #if 0 if(event.code == KEY_DOWN){ if(event.value){ //按下 printf("user:下键-按下!~\n"); }else{ //松开 printf("user:下键-松开!~\n"); } }else printf("unkown key!\n"); #else switch(event.code){ case KEY_UP: if(event.value) printf("user:按下-上键!\n"); else printf("user:松开-上键!\n"); break; case KEY_DOWN: if(event.value) printf("user:按下-下键!\n"); else printf("user:松开-下键!\n"); break; case KEY_LEFT: if(event.value){ printf("user:按下-左键!\n"); on = 1; write(fd2,&on,sizeof(on)); sleep(1); } else{ printf("user:松开-左键!\n"); on = 0; write(fd2,&on,sizeof(on)); sleep(1); } break; case KEY_RIGHT: if(event.value){ printf("user:按下-右键!\n"); on = 1; write(fd3,&on,sizeof(on));//beep sleep(1); } else{ printf("user:松开-右键!\n"); on = 0; write(fd3,&on,sizeof(on)); sleep(1); } break; default: printf("unknow key!\n"); } #endif } } } //关闭设备 close(fd); close(fd2); close(fd3); return 0; }
执行效果如图:
此处有程序文件包链接:https://pan.baidu.com/s/1kXkUWVd 密码:yif5
相关文章推荐
- Linux驱动开发六:按键中断+poll机制
- Linux驱动开发十:按键中断之输入子系统
- 07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-查询+中断+引入poll机制的按键驱动程序
- Linux驱动开发之七----按键驱动poll实现
- Linux开发--探讨将标准输入输出及错误重定向到/dev/null
- LINUX 网络编程---多路复用和信号驱动I/O(王德仙)2012-04-07 客户端和服务器端编写完成,明天开始学习poll 和epoll
- linux驱动开发之输入子系统编程(一)使用工作队列实现中断下半部
- Linux驱动开发九:按键中断+定时去抖
- Linux按键驱动,中断实现流程
- Linux驱动开发-中断分层机制_工作队列 笔记 7
- Linux应用编程基础之多路复用:select和poll的简单使用示例
- Linux无需开发底层驱动,从应用层获取GPIO中断
- 嵌入式Linux驱动学习之路(十二)按键驱动-poll机制
- tiny6410_按键中断驱动_poll机制
- Linux驱动开发(五)——中断和时钟
- Linux驱动程序开发之三----按键驱动(Tiny6410)
- 字符设备驱动程序开发之基于中断的按键驱动加去抖动
- 2416开发记录十一:按键驱动(platform/中断)
- linux的标准输入输出
- Linux:标准输入、输出、错误