当前位置: 首页 > article >正文

8.Linux按键驱动-中断下半部

在这里插入图片描述

1.编程思路
1.1在gpio结构体中添加tasklet_struct结构体
在这里插入图片描述
1.2在probe函数中初始化tasklet结构体
在这里插入图片描述
1.3在中断服务程序中调度tasklet
在这里插入图片描述
1.4在这个函数中执行其它任务
在这里插入图片描述
2.代码:
应用程序和Makefile和上节一致

https://blog.csdn.net/weixin_40933496/article/details/143263990?sharetype=blogdetail&sharerId=143263990&sharerefer=PC&sharesource=weixin_40933496&spm=1011.2480.3001.8118

驱动

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>


/* 定义结构体来描述gpio */
struct gpio_key{
	int gpio;
	struct gpio_desc* gpiod;
	int flag;
	int irq;
	struct timer_list key_timer;
	struct tasklet_struct tasklet;
};

/* 定义全局变量来存储设备树中的所有gpio节点信息 */
static struct gpio_key* gpio_keys_100ask;

/* 字符设备的主设备号 */
static unsigned int major = 0;
static struct class *gpio_class;
//static int g_key = 0;

/* 定义等待队列 */
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
struct fasync_struct * button_fasync;




/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;

#define NEXT_POS(x) ((x+1) % BUF_LEN)

int is_key_buf_empty(void)
{
	return (r == w);
}

int is_key_buf_full(void)
{
	return (r == NEXT_POS(w));
}

void put_key(int key)
{
	if (!is_key_buf_full())
	{
		g_keys[w] = key;
		w = NEXT_POS(w);
	}
}

int get_key(void)
{
	int key = 0;
	if (!is_key_buf_empty())
	{
		key = g_keys[r];
		r = NEXT_POS(r);
	}
	return key;
}

static ssize_t gpio_read(struct file *fp, char __user *buf, size_t size, loff_t * offset)
{
	int err;
	int key;
	if(is_key_buf_empty() && (fp->f_flags & O_NONBLOCK))
	{
		return -EAGAIN;
	}
	wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
	key = get_key();
	//err = copy_to_user(buf, &g_key, 4);
	err = copy_to_user(buf, &key, 4);
	//g_key = 0;
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 4;
}

static unsigned int gpio_poll(struct file *fp, struct poll_table_struct *wait)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	poll_wait(fp, &gpio_key_wait, wait);
	return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}

static int gpio_fasync(int fd , struct file *file, int on)
{
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	if (fasync_helper(fd, file, on, &button_fasync) >= 0)
	{
		return 0;
	}
	else
	{
		return -EIO;
	}
}




static const struct file_operations gpio_fops = {
	.owner = THIS_MODULE,
	.read  = gpio_read,
	.poll  = gpio_poll,
	.fasync = gpio_fasync,
};

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
	struct gpio_key* gpio_key = dev_id;
	//int val;
	//int key;
	//val = gpio_get_value(gpio_key->gpio);
	//printk("key %d %d\n", gpio_key->gpio, val);
	//g_key = (gpio_key->gpio << 8) | val;
	//key = (gpio_key->gpio << 8) | val;
	
	//put_key(key);
	//wake_up_interruptible(&gpio_key_wait);

	/* 发信号 */
	//kill_fasync(&button_fasync, SIGIO, POLL_IN);
	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 调度tasklet函数 */
	tasklet_schedule(&gpio_key->tasklet);

	/* 修改定时器超时时间 */
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	mod_timer(&gpio_key->key_timer, jiffies + HZ / 5);
	return IRQ_HANDLED;
}

/* 定时器超时函数 */
static void key_timer_expire(unsigned long arg)
{
	struct gpio_key* gpio_key = arg;
	int val;
	int key;
	val = gpio_get_value(gpio_key->gpio);
	//printk("key %d %d\n", gpio_key->gpio, val);
	//g_key = (gpio_key->gpio << 8) | val;
	key = (gpio_key->gpio << 8) | val;
	
	put_key(key);
	wake_up_interruptible(&gpio_key_wait);

	/* 发信号 */
	kill_fasync(&button_fasync, SIGIO, POLL_IN);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 修改定时器超时时间 */
	//mod_timer(&gpio_key->key_timer, jiffies + HZ / 5);
}


/* tasklet处理函数 */
static void key_tasklet_func(unsigned long data)
{
	struct gpio_key* key = data;
	int val;
	val = gpio_get_value(key->gpio);
	printk("进入了tasklet函数\n");
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	printk("key %d value is %d\n", key->gpio, val);
}



static int gpio_probe(struct platform_device *pdev)
{
	int count, i;
	struct device_node *node;
	int err;

	node = pdev->dev.of_node;
	count = of_gpio_count(node);
	if (!count)
	{
		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}
	
	/* 申请资源 */
	gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
	if (!gpio_keys_100ask)
	{
		printk("kzalloc error\n");
		return -1;
	}

	/* 获得资源 */
	for (i = 0; i < count; i++)
	{
		gpio_keys_100ask[i].gpio = of_get_gpio(node, i);
		if (gpio_keys_100ask[i].gpio < 0)
		{
			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
			return -1;
		}
		gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio);
		gpio_keys_100ask[i].irq   = gpio_to_irq(gpio_keys_100ask[i].gpio);

		/* 申请中断 */
		err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "test1_gpio_keys_100ask", &gpio_keys_100ask[i]);
		if (err) 
		{
			printk("request_irq err\n");
		}

		/* 设置定时器 */
		setup_timer(&gpio_keys_100ask[i].key_timer, key_timer_expire, &gpio_keys_100ask[i]);
		//设置定时器超时时间为无穷
		gpio_keys_100ask[i].key_timer.expires = ~0;
		/* 添加定时器 */
		add_timer(&gpio_keys_100ask[i].key_timer);

		/* 初始化tasklet */
		tasklet_init(&gpio_keys_100ask[i].tasklet, key_tasklet_func, &gpio_keys_100ask[i]);
		
	}

	/* 注册字符设备 */
	major = register_chrdev(major, "100ask_key", &gpio_fops);
	if(major < 0)
	{
		printk("register_chrdev err'\n");
		return -1;
	}

	/* 注册类 */
	gpio_class = class_create(THIS_MODULE, "100ask_key_class");

	/* 注册设备 */
	device_create(gpio_class, NULL, MKDEV(major,0), NULL, "100ask_key_button");
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int gpio_remove(struct platform_device *pdev)
{
	int count, i;
	struct device_node *node;

	node = pdev->dev.of_node;
	count = of_gpio_count(node);
	device_destroy(gpio_class, MKDEV(major,0));
	class_destroy(gpio_class);
	unregister_chrdev(major, "100ask_key");

	for (i = 0; i < count; i++)
	{
		free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
	}
	kfree(gpio_keys_100ask);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


/*
* 在设备树中添加的节点的compatible属性为:"test1_100ask,test1_gpio_key"
*/
static const struct of_device_id gpio_key_of_match[] = {
	{.compatible = "test1_100ask,test1_gpio_key"},
	{/* 这里必须要有一个空项,表示结束 */}
};

static struct platform_driver gpio_driver = {
	.driver = {
		.name	= "test1_gpio_keys_100ask",
		.of_match_table = gpio_key_of_match,
	},
	.probe	= gpio_probe,
	.remove = gpio_remove,
};

/* 基于platform总线来实现这个程序 */
static int gpio_init(void)  
{
	int ret;

	ret = platform_driver_register(&gpio_driver);
	if (ret != 0)
	{
		printk("platform_driver_register err\n");
		return -1;
	}
	else
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}
	return ret;
}

static void gpio_exit(void)
{
	platform_driver_unregister(&gpio_driver);
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
}

module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");

3.运行结果
在这里插入图片描述
由运行结果可知,在驱动中检测到两次按键按压事件(tasklet函数被调用两次),由于上节中的定时器消抖,导致key_timer_expire函数只被调用了一次,故read函数只执行了一次,应用程序只能获取后一个按键值。
4.代码路径

/home/book/nfs_rootfs/CSDN/01_gpio_irq/08_read_key_irq_poll_fasync_block_timer_tasklet

http://www.kler.cn/a/369002.html

相关文章:

  • vue通过JSON文件生成WPML文件源码
  • Python条形图 | 指标(特征)重要性图的绘制
  • 面向对象进阶(下)(JAVA笔记第二十五期)
  • OpenStack将运行的系统导出 QCOW2 镜像并导入阿里云
  • 逆向破解真随机数系统的思路
  • Oracle CONNECT BY、PRIOR和START WITH关键字详解
  • MySQL 指定字段排序二
  • 华为原生鸿蒙操作系统:我国移动操作系统的新篇章
  • XCode16.0 Command PhaseScriptExecution failed with a nonzero exit code 的错误
  • 学生宿舍管理信息化:Spring Boot系统研究
  • 【go】仅设想,能不能通过pure go编写页面。
  • springboot095学生宿舍信息的系统--论文pf(论文+源码)_kaic
  • 训练集alpaca、sharegpt格式
  • 【STM32】STM32系统时钟RCC
  • 小知识(9) MySQL慢查询sql分析与优化之explain解析
  • 在Postgresql中对空间数据进行表分区的实践
  • 《证据规定》之关于鉴定人出庭的操作性规定
  • 搜维尔科技:将外骨骼触觉力反馈手套Cyber​​Glove与机械手集成
  • 小电机两端并联104电容的作用
  • 算法Day-9
  • 66.基于DDR3 SDRAM的HDMI图像显示
  • 第1次CCF CSP认证真题解
  • 华为配置BFD状态与接口状态联动实验
  • 【C++】构造函数冒号后面的初始化列表使用小括号( )和大括号{ }的区别(回子的四种写法)
  • 高效思维管理:2024年必备在线思维导图软件!
  • C++初阶学习第八弹--深入解析vector的使用