您的位置:首页 > 运维架构 > Linux

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

//头文件
#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: