【Linux驱动】Linux中断(二)—— 按键中断驱动

前一篇已经在设备树的 gpio-led 节点中引入了中断信息,接下来将通过API来获取设备树中的中断信息。gpio-led 节点具体内容如下:

gpio-key0 {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_gpio_keys>;            // pinctrl子系统配置电气属性
	key-gpio = <&gpio1 18 GPIO_ACTIVE_HIGH>;     // gpio子系统进行引脚初始化
	interrupt-parent = <&gpio1>;                 // 中断类型为 gpio1
	interrupts = <18 IRQ_TYPE_EDGE_FALLING>;     // 中断引脚为 GPIO1_IO18,触发方式为下降沿
	status = "okay";
};

一、中断 API

1、获取中断信息

获取设备树中 interrupts 属性的信息有两种方式,一种是针对 gpio 的方式,另一种是比较通用的方式。最终获取到的都是中断号,这里的中断号和裸机开发时的中断号不一样,裸机开发我们是根据参考文档来获取中断号

而下面通过 API 获取到的中断号是经过映射的,类似于虚拟内存和物理内存的映射,这样做的目的是保护原中断号。这也是为什么后续获取到的中断号会与裸机开发时使用的中断号不一致。

gpio_to_irq

gpio_to_irq 是仅用于获取 gpio 中断相关信息,要求对应节点的父类中断控制器为 gpio,即 interrupt-parent 属性引用的是 gpio 控制器。该接口的声明在 <asm/gpio.h>,接口原型如下: 

#define gpio_to_irq	__gpio_to_irq
/**
 * @ param gpio  表示根据gpio设备树节点获取到的 gpio 编号
 * @ return      成功返回中断号,失败返回负值
 */
int __gpio_to_irq(unsigned gpio);

irq_of_parse_and_map

irq_of_parse_and_map 是比较通用的中断信息获取方式,不仅仅适用于 gpio,也适用于其他外设中断。该接口的声明在 <linux/of_irq.h>,接口原型如下:

/**
 * @ param dev   设备树节点
 * @ param index interrupts属性索引
 * @ return      成功返回中断号,失败返回负值
 */
unsigned int irq_of_parse_and_map(struct device_node *dev,
						          int index);

注意:interrupts属性中可以包含多个中断信息,需要index来获取当前驱动所需的中断信息

2、注册中断

注册中断时主要告诉内核以下内容:

  • 中断号: 映射后的中断号
  • 触发方式:如何触发中断
  • 中断服务函数:中断触发后如何处理
  • 中断服务函数参数

注册中断使用的 API 为 request_irq,函数原型的声明在 <linux/interrupt.h>

/**
 * @param irq       映射后的中断号
 * @param handler   中断服务函数
 * @param flags     触发方式
 * @param name      中断名
 * @param dev       给中断服务函数传递的参数
 * @return          成功返回0,失败返回负值
 */
int request_irq(unsigned int irq, 
                irq_handler_t handler, 
                unsigned long flags,
	            const char *name, 
                void *dev);

中断服务函数声明:

typedef irqreturn_t (*irq_handler_t)(int, void *);

触发方式 flags(linux/irq.h)

中断名 name:

        设置以后可以在/proc/interrupts 文件中看到对应的中断名字,以此来判断中断是否注册成功

3、释放中断

注册中断后,如果模块被卸载,需要释放中断,释放中断 free_irq 的接口原型声明在 <linux/interrupt.h>

/**
 * @param irq       映射后的中断号
 * @param dev       给中断服务函数传递的参数
 */
void free_irq(unsigned int irq, void * dev);

二、驱动完善

1、驱动入口函数

驱动入口函数主要是获取中断号,并申请中断,其他的诸如申请设备号、自动创建驱动节点等操作将不再赘述。下面使用变量 status 来代表某个外设的状态,按键按下时中断触发,此时反转设备状态。

struct chrdev_t 
{
    // ...

	struct device_node* gpioNode;			/* 设备树节点 */
	uint32_t 			gpioNum;			/* gpio 引脚编号 */
	uint32_t 			irqNum;				/* 中断号 */

	uint32_t            status;				/* 设备状态 */
};
static struct chrdev_t chrdev;

/* 驱动入口函数 */
static int __init kerneltimer_init(void)
{
	uint32_t ret = 0;

	/* 获取key0设备树节点 */
	chrdev.gpioNode = of_find_node_by_path("/gpio-key0");
	if(chrdev.gpioNode == NULL)
	{	
		printk("node cannot be found!\n");
		return -1;
	}
    // 获取 gpio 编号
	chrdev.gpioNum = of_get_named_gpio(chrdev.gpioNode, "key-gpio", 0);
	if (chrdev.gpioNum < 0)
	{
		printk("gpio property fetch failed!\n");
		return -1;
	}
    // 配置 gpio 为输入
	ret = gpio_direction_input(chrdev.gpioNum);
	if (ret < 0)
	{
		printk("gpio set failed!\n");
		return -1;
	}

#if 1
	// 根据gpio编号获取中断信息
	chrdev.irqNum = gpio_to_irq(chrdev.gpioNum);
	if (chrdev.irqNum < 0)
	{
		printk("irq number fetch failed!\n");
		return -1;
	}
#else
	// 根据节点获取中断号
	chrdev.irqNum = irq_of_parse_and_map(chrdev.gpioNode, 0);
	if (chrdev.irqNum < 0)
	{
		printk("irq number fetch failed!\n");
		return -1;
	}
#endif
	printk("中断号: %u\n", chrdev.irqNum);
    // 设备初始状态为 0
	chrdev.status = 0;
	// 注册中断
	ret = request_irq(chrdev.irqNum, key0_handler, IRQ_TYPE_EDGE_FALLING, "key0-int", &chrdev);
	if (ret < 0)
	{
		printk("irq subscribe failed!\n");
		return -1;
	}

    // ... 
}

2、中断服务函数

上面在介绍注册中断 API 时已经提及了中断服务函数的声明,第一个参数为 中断号,第二个参数为注册时传递给中断服务函数的参数

static irqreturn_t key0_handler(int irq, void * dev)
{
	struct chrdev_t* pdev = (struct chrdev_t*)dev;

	// 状态反转
	pdev->status = !pdev->status;
	return IRQ_RETVAL(IRQ_HANDLED);
}

3、read 操作函数

static ssize_t chrdev_read(struct file *pfile, char __user * pbuf, size_t size, loff_t * poff)
{
    // 在 open 函数中 pfile->private_data = &chrdev;
	struct chrdev_t* pdev = pfile->private_data;
	unsigned long ret = 0;
	
    // 将设备状态返回给应用层
	ret = copy_to_user(pbuf, &pdev->status, sizeof(pdev->status));
	if (ret != 0)
	{
		printk("kernel send data failed!\n");
		return -1;
	}
	
	return sizeof(pdev->status);
}

4、驱动退出函数

驱动退出函数需要释放中断

static void __exit kerneltimer_exit(void)
{
    // ... 

	/* 注销中断 */
	free_irq(chrdev.irqNum, &chrdev);
}

三、测试

在应用程序中每隔 1s 调用 read 函数来获取设备状态

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#define delayms(x)        usleep(x * 1000)

void printHelp()
{
    printf("usage: ./xxxApp <driver_path>\n");
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        printHelp();
        return -1;
    }
    
    char* driver_path = argv[1];       // 位置0 保存的是 ./chrdevbaseApp
    int state = 0;
    int ret = 0;
    int fd = 0;

    fd = open(driver_path, O_RDONLY);
    if (fd < 0)
    {
        perror("open file failed");
        return -2;
    }

    while (1)
    {
        ret = read(fd, &state, sizeof(state));
        if (ret < 0)
        {
            printf("read data error\n");
            break;
        }

        printf("中断触发状态:%d\n", state);
        delayms(1000);
    }
    
    close(fd);
    return 0;
}

裸机开发时,GPIO1_IO18 对应的中断号为99,现在因为经过一层映射,屏蔽了真正的中断号,使用了虚拟中断号,所以这里的中断号为 47 

应用程序的测试结果如下,按下按键时触发中断,此时状态反转。 

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

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

相关文章

计算化学顶刊封面!玻色量子联合上海交大张健课题组发表量子计算重要成果

​2023年12月13日&#xff0c;北京玻色量子科技有限公司&#xff08;以下简称“玻色量子”&#xff09;联合上海交通大学在中科院分区1区、计算化学领域Top刊物JCTC&#xff08;Journal of Chemical Theory and Computation&#xff09;内刊的封面上发表了以“Encoding Molecul…

【Spring】AOP原来如此

AOP概述 什么是AOP的技术&#xff1f; 在软件业&#xff0c;AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程AOP是一种编程范式&#xff0c;隶属于软工范畴&#xff0c;指导开发者如何组织程序结构AOP最早由AOP联盟的组织提出的,制定了一套…

vercel部署Gemini pro

一、注册一个vercel账号&#xff08;这个东西类似于第三方的github pages&#xff0c;能部署github中的项目&#xff09; 二、注册结束后&#xff0c;填写github的账号&#xff08;需要事先在该github账号中fork一个gemini的repository&#xff09; 三、babaohuang/GeminiPro…

Koordinator 助力云原生应用性能提升:小红书混部技术实践

作者&#xff1a;宋泽辉&#xff08;小红书&#xff09;、张佐玮&#xff08;阿里云&#xff09; 编者按&#xff1a; Koordinator 是一个开源项目&#xff0c;是基于阿里巴巴内部多年容器调度、混部实践经验孵化诞生&#xff0c;是行业首个生产可用、面向大规模场景的开源混…

layuiadmin新建tabs标签页,点击保存,打开新的标签页并刷新

用的layuiamin前端框架 需求&#xff1a;新增的页面为一个标签页&#xff0c;保存后&#xff0c;需要刷新列表 1、新建customMethod.js文件&#xff0c;自定义自己的方法 layui.define(function (exports) {var $ layui.$var customMethod {// 表单点击保存后&#xff0c;…

解决docker容器内无法连接宿主redis

背景 小程序的发短信服务挂了&#xff0c;随查看日志&#xff0c;该报错日志如下 Error 111 connecting to 127.0.0.1:6379. Connection refused. 6379是监听redis服务的端口&#xff0c;那大概是redis出错了。 首先查看了redis是否正常启动&#xff0c;检查出服务正常。 由于小…

Docker-Compose部署Redis(v7.2)主从模式

文章目录 一、前提准备1. redis配置文件2. 下载redis镜像3. 文件夹结构 二、docker-compose三、主从配置1.主节点配置文件 环境 docker desktop for windows 4.23.0redis 7.2 一、前提准备 1. redis配置文件 因为Redis 7.2 docker镜像里面没有配置文件&#xff0c;所以需要…

推荐Linux和Ubuntu系统中特别有用的几个指令

常用推荐指令 1.在Ubuntu中好多文件或文件夹是不能使用右键删除的&#xff0c;因此知道删除文件或文件夹的rm命令显得尤为重要。 &#xff08;1&#xff09;删除文件夹的内容包括文件夹: # 以最高权限删除 sudo rm -rf 文件夹的名字 #&#xff08;-r 是循环的意思&…

Fast R-CNN

Fast R-CNN算法流程 对比与R-CNN其在第二步时并没有将所有的候选区域进行逐个的CNN特征提取&#xff0c;而是直接将整个图片进行一次CNN特征提取&#xff0c;让后再将候选区映射到feature map上。可想而知速度得到了提升。这里的ROI pooling层缩放到7x7就是将候选区域对应的特征…

MySQL Enterprise版本各系统安装包下载

一、官方下载地址 oracle下载地址 https://edelivery.oracle.com/osdc/faces/SoftwareDelivery 使用oracle账号登录进去 Category选择Download Package(下载安装包)&#xff0c;搜索栏输入mysql Enterprise关键字点search进行搜索。选项结果第一个MySQL Enterprise Edition&a…

buuctf-Misc 题目解答分解106-108

106.[DDCTF2018]流量分析 提示了私钥 &#xff0c;无厘头&#xff0c;先不管了&#xff0c;应该是流量加密了&#xff0c;用wireshark 打开 看看&#xff0c;真个数据流量&#xff0c;没有http 直接找到TCP 协议的包追踪一下TCP 找到TCP 不是红色的包追踪&#xff0c;大量的数…

有人说品酒品的是文化,品红酒的文化是什么?

喝茶有茶文化&#xff0c;品酒有酒文化&#xff0c;云仓酒庄的品牌雷盛红酒LEESON分享那么品红酒的文化是什么呢&#xff1f;一千个人可能喝出一千种不同的文化。但核心只有一个&#xff0c;那就是在葡萄酒里不单单是品出酒味&#xff0c;而要品出品味、品出深度&#xff0c;品…

windows-Qt 获取设备PCIE通道宽度

pcie通道信息获取似乎一般都是在linux环境下&#xff0c;windows方法较少。本次是调用第三方命令行工具&#xff0c;通过windows版的lspci.exe去获取。 lspci.exe资源可从这里下载&#xff1a; https://download.csdn.net/download/bangtanhui/88701726 程序主要需要用到以下这…

MySQL之CRUD、常见函数及union查询

目录 一. CRUD 1.1 什么是crud 1.2 SELECT(查询) 1.3 INSERT(新增) 1.4 UPDATE(修改) 1.5 DELETE(删除) 二. 函数 2.1 常见函数 2.2 流程控制函数 2.3 聚合函数 三. union与union all 3.1 union 3.2 union all 3.3 具体不同 3.4 结论 四. 思维导图 一. CRUD 1.1 什么是crud…

Leetcode算法系列| 11. 盛最多水的容器

目录 1.题目2.题解C# 解法一&#xff1a;暴力C# 解法二&#xff1a;双指针&#xff08;左指针大于右指针&#xff0c;left&#xff09;C# 解法三&#xff1a;双指针优化&#xff08;左指针小于等于最小高度&#xff0c;left&#xff09;Java 解法一&#xff1a;双指针Python3 解…

【KingbaseES】实现MySql函数Median

本方法只支持在聚合函数窗口中调用 不支持在GROUP BY中使用&#xff0c;使用plsql写的玩意新能都会稍微差一些 建议使用原生方法修改 CREATE OR REPLACE FUNCTION _final_median(numeric[])RETURNS numeric AS $$SELECT AVG(val)FROM (SELECT valFROM unnest($1) valORDER BY …

wy的leetcode刷题记录_Day72

wy的leetcode刷题记录_Day72 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间&#xff1a; 前言 目录 wy的leetcode刷题记录_Day72声明前言2397. 被列覆盖的最多行数题目介绍思路代码收获 1137. 第 N 个泰波那契数题目介绍思路代码收获 2397. 被列覆…

氢燃料电池——产品标准规范汇总和梳理

文章目录 氢燃料电池模块 氢燃料电池发动机 氢燃料电池汽车 加氢系统 总结 氢燃料电池模块 GB/T 33978-2017 道路车辆用质子交换膜燃料电池模块 GB/T 43361-2023 气体分析 道路车辆用质子交换膜燃料电池氢燃料分析方法的确认 GB/T 29729-2022 氢系统安全的基本要求 GB/T 4…

拿到年终奖后马上辞职,厚道吗?

拿到年终奖后马上辞职&#xff0c;厚道吗&#xff1f; 作为一个人&#xff0c;你首先要对自己负责&#xff0c;其次是对自己身边的人&#xff08;妻儿&#xff0c;家人&#xff0c;朋友&#xff09;负责。 你明明可以跳槽到有更好的职业发展你不去&#xff0c;是为不智&#…