一、linux驱动开发-7.2-阻塞IO实验

本文档介绍了一种优化Linux应用程序的方法,通过使用中断和定时器来降低CPU利用率。当按键未被按下时,程序会进入休眠状态,只有在检测到按键事件时才会唤醒并处理。文中详细展示了如何配置GPIO、中断处理函数、定时器回调以及设备驱动的相关操作,从而实现按键事件的高效处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

       上一节在应用程序中通过read不断读取按键状态,这样就会占用很高的CPU使用率,用top指令查看下:

       现在换种方法,当按键没被按下的时候休眠,当有按键事件发生的时候才运行,并打印按键值,这样就可以降低CPU利用率。

#include <linux/types.h>

#include <linux/kernel.h>

#include <linux/delay.h>

#include <linux/ide.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/errno.h>

#include <linux/gpio.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/of.h>

#include <linux/of_address.h>

#include <linux/of_gpio.h>

#include <linux/semaphore.h>

#include <linux/timer.h>

#include <linux/of_irq.h>

#include <linux/irq.h>

#include <asm/mach/map.h>

#include <asm/uaccess.h>

#include <asm/io.h>

#include <linux/string.h>

#include <linux/wait.h>



#define IMX6UIRQ_CNT     	1     

#define IMX6UIRQ_NAME    	"blockio"

#define KEY0VALUE			0X01

#define INVAKEY				0xFF 

#define KEY_NUM				1



struct irq_keydesc{

	int gpio;				//gpio编号

	unsigned int irqnum;	//中断号

	irqreturn_t (*irq_handler)(int, void*);		//中断处理函数

};



//设备结构体

struct imx6uirq_dev{

	dev_t devid;

	struct cdev cdev;

	struct class *class;

	struct device *device;

	int major;

	int minor;

	struct device_node *nd;		//设备节点

	atomic_t keyvalue;			//有效的按键值

	atomic_t releasekey;		//标记是否完成一次完整的按键

	struct timer_list timer;	//定时器

	struct irq_keydesc irq_keydesc[KEY_NUM];	//按键描述数组



	wait_queue_head_t r_wait;	//等待队列项头

};



struct imx6uirq_dev imx6uirq;  



static int imx6uirq_open(struct inode *inode, struct file *file)

{

	file->private_data = &imx6uirq;

	return 0;

}



ssize_t imx6uirq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

{

	unsigned char keyvalue = 0;

	unsigned char releasekey = 0;

	struct imx6uirq_dev *dev = file->private_data;



	int ret = 0;



	//定义并初始化一个等待队列项

	DECLARE_WAITQUEUE(wait, current);



	if (atomic_read(&dev->releasekey) == 0)

	{

		//按键未按下,将等待队列项添加到等待队列头

		add_wait_queue(&dev->r_wait, &wait);

		__set_current_state(TASK_INTERRUPTIBLE);/*  设置任务状态 */

		//任务切换,进入睡眠态

		schedule();

		if(signal_pending(current)) { /*  判断是否为信号引起的唤醒 */

			ret = -ERESTARTSYS;

			goto wait_error;

		}



		__set_current_state(TASK_RUNNING); /* 设置为运行状态 */

		//按键按下了,将等待队列项从等待队列头移除

		remove_wait_queue(&dev->r_wait, &wait);

	}



	keyvalue = atomic_read(&dev->keyvalue);

	releasekey = atomic_read(&dev->releasekey);

	

	if (releasekey)

	{

		copy_to_user(buf, &keyvalue, sizeof(keyvalue));

		atomic_set(&dev->releasekey, 0);	//清除按下标志位

	}

	else

	{

		return -EINVAL;

	}

	

	return 0;



wait_error:

	set_current_state(TASK_RUNNING); /*  设置任务为运行态 */

	remove_wait_queue(&dev->r_wait, &wait); /*  将等待队列移除 */

	return ret;

}



static const struct file_operations imx6uirq_fops = {

	.owner = THIS_MODULE,

	.open  = imx6uirq_open,

	.read  = imx6uirq_read,

};



//定时器回调函数

static void timer_function(unsigned long arg)

{

	int value;

	struct irq_keydesc *keydesc;

	struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;

	keydesc = &dev->irq_keydesc[0];



	value = gpio_get_value(keydesc->gpio);

	if (value == 0)

		atomic_set(&dev->keyvalue, value);

	else {

		atomic_set(&dev->keyvalue, value);

		atomic_set(&dev->releasekey, 1);	//标记按键松开

	}



	if (atomic_read(&dev->releasekey))

	{

		//按键按下,唤醒等待队列项中的进程

		wake_up_interruptible(&dev->r_wait);

	}

}



static irqreturn_t key0_irq_handler(int irq, void *dev_id)

{

	int value;

	struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;



	dev->timer.data = (volatile long)dev_id;

	//启动定时器

	mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));



	return IRQ_HANDLED;

}



static int key_io_init(void)

{

	int ret;



	//通过节点名字查找节点

	imx6uirq.nd = of_find_node_by_name(NULL, "gpiokey");

	if (imx6uirq.nd == NULL)

	{

		printk("find %s fail\r\n", "gpiokey");

		return -EINVAL;

	}



	//通过节点获取GPIO编号

	imx6uirq.irq_keydesc[0].gpio = of_get_named_gpio(imx6uirq.nd, "key-gpio", 0);

	if (imx6uirq.irq_keydesc[0].gpio < 0)

	{

		printk("get %s fail\r\n", "key-gpio");

		return -EINVAL;

	}

	//初始化key-gpio

	ret = gpio_request(imx6uirq.irq_keydesc[0].gpio, "gpio_irq");

	if (ret != 0)

	{

		printk("request %s fail\r\n", "gpio_irq");

		return -EINVAL;

	}

	ret = gpio_direction_input(imx6uirq.irq_keydesc[0].gpio);

	if (ret < 0)

	{

		printk("set key status fail\r\n");

		return -EINVAL;

	}



	//通过节点获取中断号

	imx6uirq.irq_keydesc[0].irqnum = irq_of_parse_and_map(imx6uirq.nd, 0);

#if 0

	//通过GPIO编号获取中断号

	imx6uirq.irq_keydesc[0].irqnum = gpio_to_irq(imx6uirq.irq_keydesc[0].gpio);

#endif

	//设置中断处理函数

	imx6uirq.irq_keydesc[0].irq_handler = key0_irq_handler;

		

	//通过中断号申请中断

	ret = request_irq(imx6uirq.irq_keydesc[0].irqnum,	

				imx6uirq.irq_keydesc[0].irq_handler,

				IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,

				IMX6UIRQ_NAME,

				&imx6uirq);	

	if (ret < 0)

	{

		printk("request irq fail\r\n");

		return -EINVAL;

	}



	//初始化定时器

	init_timer(&imx6uirq.timer);

	imx6uirq.timer.function = timer_function;

}



static int __init imx6uirq_init(void)

{

	int ret = 0;

	int val = 0;	



	//初始化等待队列头

	init_waitqueue_head(&imx6uirq.r_wait);



	//分配设备号

	if ( imx6uirq.major) {

		 imx6uirq.devid = MKDEV( imx6uirq.major, 0);

		register_chrdev_region( imx6uirq.devid,  IMX6UIRQ_CNT,  IMX6UIRQ_NAME);

	} else {

		alloc_chrdev_region(&imx6uirq.devid, 0,  IMX6UIRQ_CNT,  IMX6UIRQ_NAME);

		 imx6uirq.major = MAJOR(imx6uirq.devid);

		 imx6uirq.minor = MINOR(imx6uirq.devid);

	}



	//初始化cdev

	imx6uirq.cdev.owner = THIS_MODULE;

	cdev_init(&imx6uirq.cdev, &imx6uirq_fops);

	//添加cdev

	cdev_add(&imx6uirq.cdev, imx6uirq.devid,  IMX6UIRQ_CNT);

	//创建类

	imx6uirq.class = class_create(THIS_MODULE,  IMX6UIRQ_NAME);

	//创建设备

	imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL,  IMX6UIRQ_NAME);



	//按键gpio初始化

	key_io_init();

	

	return 0;

}



static void __exit imx6uirq_exit(void)

{

	//删除定时器

	del_timer_sync(&imx6uirq.timer);

	//释放中断

	free_irq(imx6uirq.irq_keydesc[0].irqnum, &imx6uirq);



	//删除设备

	cdev_del(&imx6uirq.cdev);

	//注销设备号

	unregister_chrdev_region(imx6uirq.devid,  IMX6UIRQ_CNT);

	//删除设备的类

	device_destroy(imx6uirq.class,  imx6uirq.devid);

	//删除类

	class_destroy(imx6uirq.class);

		

	printk(" dev exit\n");

}



module_init( imx6uirq_init);

module_exit( imx6uirq_exit);



MODULE_LICENSE("GPL");

MODULE_AUTHOR("ZK");



 看下cpu利用率:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值