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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902701.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通过call指令来学习指令摘要表的细节

E8 cw cw 表示E8后面跟随2 字节 (什么数不知道) rel16 指在与指令同一代码段内的相对地址偏移 D ,指向Instruction Operand Encoding 表中的D列, 他告诉我们 操作数1 是一个0FFSET N.S. 在64位模式下&#xff0c;某些指令需要使用“地址覆盖前缀”&#xff08;address over…

RL学习笔记-马尔可夫过程

参考资料&#xff1a;蘑菇书、周博磊老师课程 在强化学习中&#xff0c;智能体与环境交互是通过马尔可夫决策过程来表示的&#xff0c;因此马尔可夫决策过程是强化学习的基本框架。 马尔可夫性质 指一个随机过程在给定现在状态及所有过去状态情况下&#xff0c;其未来状态的条件…

Golang | Leetcode Golang题解之第506题相对名次

题目&#xff1a; 题解&#xff1a; var desc [3]string{"Gold Medal", "Silver Medal", "Bronze Medal"}func findRelativeRanks(score []int) []string {n : len(score)type pair struct{ score, idx int }arr : make([]pair, n)for i, s : …

BERT语言模型详解【Encoder-Only】

NLP-大语言模型学习系列目录 一、注意力机制基础——RNN,Seq2Seq等基础知识 二、注意力机制【Self-Attention,自注意力模型】 三、Transformer图文详解【Attention is all you need】 四、大语言模型的Scaling Law【Power Low】 五、大语言模型微调方法详解【全量微调、PEFT、…

Android Studio 导入/删除/新建库的模块(第三方项目) - Module

文章目录 一、导入module项目 Module空项目如何导入Project工程项目二、删除module项目三、新建module项目(不常用) 一、导入module项目 首先&#xff0c;你必须要有一个工程(Project),才可以打开项目(Module) 第一步骤&#xff1a;右键项目依次点击 New -> Module 1、工…

LLM | 论文精读 | 基于大型语言模型的自主代理综述

论文标题&#xff1a;A Survey on Large Language Model based Autonomous Agents 作者&#xff1a;Lei Wang, Chen Ma, Xueyang Feng, 等 期刊&#xff1a;Frontiers of Computer Science, 2024 DOI&#xff1a;10.1007/s11704-024-40231-1 一、引言 自主代理&#xff08;…

AI 提示词(Prompt)入门 :ChatGPT 4.0 高级功能指南

这段时间 GPT4 多了很多功能&#xff0c;今天主要是增加了 GPTs Store 的介绍和 创建 GPTs 的简单方法&#xff0c;那么我们开始吧&#xff0c;文末有彩蛋。 这里主要讲解如下几个点&#xff1a; 1&#xff1a; ChatGPT 4.0 插件的使用 2&#xff1a;ChatGPT 4.0 高级数据分…

【已解决】【hadoop】【hive】启动不成功 报错 无法与MySQL服务器建立连接 Hive连接到MetaStore失败 无法进入交互式执行环境

启动hive显示什么才是成功 当你成功启动Hive时&#xff0c;通常会看到一系列的日志信息输出到控制台&#xff0c;这些信息包括了Hive服务初始化的过程以及它与Metastore服务连接的情况等。一旦Hive完成启动并准备就绪&#xff0c;你将看到提示符&#xff08;如 hive> &#…

大数据Azkaban(二):Azkaban简单介绍

文章目录 Azkaban简单介绍 一、Azkaban特点 二、Azkaban组成结构 三、Azkaban部署模式 1、solo-server ode&#xff08;独立服务器模式&#xff09; 2、two server mode&#xff08;双服务器模式&#xff09; 3、distributed multiple-executor mode&#xff08;分布式多…

FPGA第 13 篇,使用 Xilinx Vivado 创建项目,点亮 LED 灯,Vivado 的基本使用(点亮ZYNQ-7010开发板的LED灯)

前言 在FPGA设计中&#xff0c;Xilinx Vivado软件是一款功能强大的设计工具&#xff0c;它不仅支持硬件描述语言&#xff08;HDL&#xff09;的开发&#xff0c;还提供了丰富的图形化设计界面&#xff0c;方便用户进行硬件设计、调试和测试。这里我们将详细介绍&#xff0c;如…

RabbitMQ 高级特性——事务

文章目录 前言事务配置事务管理器加上Transactional注解 前言 前面我们学习了 RabbitMQ 的延迟队列&#xff0c;通过延迟队列可以实现生产者生产的消息不是立即被消费者消费。那么这篇文章我们将来学习 RabbitMQ 的事务。 事务 RabbitMQ 是基于 AMQP 协议实现的&#xff0c;…

Gstreamer的webrtcbin插件

1、输入参数 static GOptionEntry entries[] {{"peer-id", 0, 0, G_OPTION_ARG_STRING, &peer_id, "String ID of the peer to connect to", "ID"},{"server", 0, 0, G_OPTION_ARG_STRING, &server_url, "Signalling se…

unity项目导出安卓工程后,在AndroidStudio打包报错:unityLibrary:BuildIl2CppTask‘.

下面这个是我在unity开发者社区提问后&#xff0c;他们回答得&#xff1a; 解决方案&#xff1a;我这边按照这几个方案检查了下&#xff0c;NDK和JDK都没问题&#xff0c;最后重启电脑才解决的&#xff0c;应该是文件被锁定了&#xff0c;我用的windows系统的。 验证&#xff…

一篇文章快速认识YOLO11 | 旋转目标检测 | 原理分析 | 模型训练 | 模型推理

本文分享YOLO11的旋转目标检测任务&#xff0c;在原始目标检测中&#xff0c;添加多一个角度预测&#xff0c;实现定向边界框检测。 其中旋转角度算法设计中&#xff0c;通过回归预测实现的。 目录 1、旋转目标检测概述 2、YOLO11中的OBB数据格式 3、分析模型配置参数 4、…

dmsql日志分析工具部署与使用DM8/DM7

dmsql日志分析工具部署与使用DM8/DM7 1 环境介绍2 JAVA 环境变量配置2.1 Os Kylin 10 JAVA 环境变量配置2.2 Windos7 JAVA环境变量配置 3 数据库配置3.1 数据库初始化参数3.2 数据库创建表 4 配置DMLOG日志分析工具4.1 Kylin v10 配置DMLOG日志分析工具4.2 执行日志分析4.3 Win…

Node-RED的面板的认识及操作

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 文章引言 &#x1f4df; 面板…

jvm虚拟机介绍

Java虚拟机&#xff08;JVM&#xff09;是Java语言的运行环境&#xff0c;它基于栈式架构&#xff0c;通过加载、验证、准备、解析、初始化等类加载过程&#xff0c;将Java类文件转换成平台无关的字节码&#xff0c;并在运行时动态地将其翻译成特定平台的机器码执行。 JVM的核心…

如何尽早地发现并抵御 DDoS 攻击?

近半年&#xff0c;随着软硬件服务的廉价化、规模化&#xff0c;国内外云厂商频繁遭受不明原因的大规模网络攻击&#xff0c;给很多网站带来了不良的影响。其实&#xff0c;DDoS 攻击这把「达摩斯之剑」一直高悬在各家互联网公司的头顶&#xff0c;虽然很多互联网企业对 DDoS 攻…

「C/C++」C++ STL容器库 之 std::list 双向链表容器

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

使用飞桨AI Studio平台训练数据,并进行图像识别分析得牡丹花测试

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…