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

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十七章 Linux中断实验

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263(加群获取驱动文档+例程)


第五十七章 Linux中断实验

本章导读

在 Linux 下的驱动实验中,中断是频繁使用的功能,Linux 内核提供了完善的中断框架,我们只需要使

用内核提供的函数,便可以方便的使用中断功能。本章我们就来学习一下如何在 Linux 中使用中断。

57.1章节讲解了Linux中断基础理论知识

57.2章节在iTOP-IMX8MM开发板上以按键中断为例,进行实验。

本章内容对应视频讲解链接(在线观看):

中断基础概念  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=34

设备树中的中断节点以及相关函数  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=35

按键中断实验  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=36

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\014-Linux中断实验”路径下。

57.1 Linux中断简介

57.1.1 Linux中断介绍

中断是指 CPU 在执行程序的过程中,出现了某些突发事件急待处理,CPU 必须暂停当前程序的执行,

转去处理突发事件,处理完毕后又返回原程序被中断的位置继续执行。由于中断的存在极大的提高了 CPU的运行效率,但是设备的中断会打断内核进程中的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽量短小精悍。

举例来说,我现在正在厨房做饭,突然电话响了,然后我关火去接电话,接完电话在回去开火继续做饭,这个过程就是中断的一个过程。在这个看似简单的过程中,却涉及到了中断的几个过程,我们一起来看一下:

  • 电话铃声响了:中断请求
  • 我要去接电话:中断响应
  • 我关掉火:保护现场
  • 我接电话的过程:中断处理
  • 接完电话回到厨房开火:恢复现场
  • 继续做饭:中断返回
  • 如果我不接电话:中断屏蔽

为保证系统实时性,中断服务程序必须足够简短,但实际应用中某些时候发生中断时必须处理大量的

事物,这时候如果都在中断服务程序中完成,则会严重降低中断的实时性,基于这个原因,linux 系统提出了一个概念:把中断服务程序分为两部分:顶半部-底半部 。

顶半部(中断上文):完成尽可能少的比较急的功能,它往往只是简单的读取寄存器的中断状态,并清除中断标志后就进行“中断标记”(也就是把底半部处理程序挂到设备的底半部执行队列中)的工作。 顶半部的特点就是响应速度快。

底半部(中断下文):处理中断的剩余大部分任务,可以被新的中断打断。

57.1.2 中断相关函数

linux 中断有专门的中断子系统,其实现原理很复杂,但是驱动开发者不需要知道其实现的具体细节,

只需要知道如何应用该子系统提供的 API 函数来编写中断相关驱动代码即可。

1 获取中断号相关函数

编写驱动的时候需要用到中断号,每一个中断都有中断号,我们用到中断号,中断信息已经写到了设备树里面,因此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号,函数原型如下表所示:

函数

unsigned int irq_of_parse_and_map(struct device_node *dev,int index)

dev

设备节点

index

索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。

返回值

中断号

功能

通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号

如下表所示:

如果使用 GPIO 的话,可以使用 gpio_to_irq 函数来获取 gpio 对应的中断号,函数原型如下表所 

函数

int gpio_to_irq(unsigned int gpio)

gpio

要获取的 GPIO 编号

返回值

GPIO 对应的中断号

功能

获取GPIO对应的中断号

2 申请中断函数

同GPIO一样,在Linux内核里面,如果我们要使用某个中断也是需要申请的,申请中断我们使用的函数是 request_irq。函数原型如下表所示:

函数

int request_irq( unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev)

irq

要申请中断的中断号

handler

中断处理函数,当中断发生以后就会执行此中断处理函数。

flags

中断标志

name

中断名字,设置以后可以在开发板/proc/interrupts 文件中看到对应的中断名字

dev

如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将 dev 设置为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个参数。

返回值

中断申请成功返回0,其他负值则中断申请失败,如果返回-EBUSY 的话表示中断已经被申请了。

中断标识可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,这里我们介绍几个常用的中断标志,如下图所示: 

标志

功能

IRQF_SHARED

多个设备共享一个中断线,共享的所有中断都必须指定此标志。如果使用共享中断的话,request_irq 函数的 dev 参数就是唯一区分他们的标志。

IRQF_ONESHOT

单次中断,中断执行一次就结束。

IRQF_TRIGGER_NONE

无触发。

IRQF_TRIGGER_RISING

上升沿触发。

IRQF_TRIGGER_FALLING

下降沿触发。

IRQF_TRIGGER_HIGH

高电平触发。

IRQF_TRIGGER_LOW

低电平触发。

3 、free_irq 函数

中断使用完成以后就要通过 free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会

删除中断处理函数并且禁止中断。free_irq 函数原型如下所示

函数

void free_irq(unsigned int irq,void *dev)

irq

要释放的中断

dev

如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。

返回值

功能

释放掉相应的中断

4、中断处理函数

使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数函数如下表所示:

函数

irqreturn_t (*irq_handler_t) (int, void *)

第一个参数

要中断处理函数要相应的中断号

第二个参数

是一个指向 void 的指针,也就是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备,dev 也可以指向设备数据结构。

返回值

中断处理函数的返回值为 irqreturn_t 类型

irqreturn_t 类型定义如下所示:

 enum irqreturn {

     IRQ_NONE = (0 << 0),

     IRQ_HANDLED = (1 << 0),

     IRQ_WAKE_THREAD = (1 << 1),

     };

 typedef enum irqreturn irqreturn_t;

可以看出 irqreturn_t 是个枚举类型,一共有三种返回值。一般中断服务函数返回值使用如下形式

return IRQ_RETVAL(IRQ_HANDLED)

5、中断使能和禁止函数

 常用的中断使用和禁止函数如下所示:

void enable_irq(unsigned int irq)

void disable_irq(unsigned int irq)

enable_irq 和 disable_irq 用于使能和禁止指定的中断,irq 就是要禁止的中断号。disable_irq 函数要

等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有

已经开始执行的中断处理程序已经全部退出。在这种情况下,可以使用另外一个中断禁止函数:

void disable_irq_nosync(unsigned int irq)

disable_irq_nosync 函数调用以后立即返回,不会等待当前中断处理程序执行完毕。

57.1.3 中断上文和中断下文

中断的存在可以极大的提高CPU的运行效率,但是中断会打断内核进程中的正常调度和运行,所以为保证系统实时性,中断服务程序必须足够简短,但实际应用中某些时候发生中断时必须处理大量的事物,这时候如果都在中断服务程序中完成,则会严重降低中断的实时性,基于这个原因,linux 系统提出了一个概念:把中断服务程序分为两部分:中断上文和中断下文。

有些资料中也将顶半部和底半部称为上半部和下半部,都是一个意思。Linux 内核将中断分为顶半部和

底半部的主要目的就是实现中断处理函数的快进快出,那些对时间敏感、执行速度快的操作可以放到中断

处理函数中,也就是顶半部。剩下的所有工作都可以放到底半部去执行,至于哪些代码要在顶半部完成,哪些代码要在底半部完成,并没有严格的要求,要根据实际情况来判断,下面有一些参考点:

① 如果要处理的内容不希望被其他中断打断,那么可以放到上半部。

② 如果要处理的任务对时间敏感,可以放到上半部。

③ 如果要处理的任务与硬件有关,可以放到上半部

④ 除了上述三点以外的其他任务,优先考虑放到下半部。

中断上文:完成尽可能少却比较急的任务,中断上文的特点就是响应速度快。中断下文:处理中断剩余的大量比较耗时间的任务,而且可以被新的中断打断。

举例来说,我现在正在厨房做饭,突然电话响了,然后我关火去接电话,快递员打电话让我下楼去拿快递,接完电话叫我女朋友去下楼拿快递,然后我在回去开火继续做饭,这个过程就是中断上下文。

分析例子:快递员打电话让我下去拿快递,这个事情很紧急,所以要快速处理,这个就是要在中断上文中完成。但是下楼拿快递这个过程非常耗时间,所以叫女朋友去拿快递,这个就是中断下文。下楼拿快递很耗时间,如果我不叫女朋友去帮我拿而是自己拿,等我拿完饭回来我锅里的菜是不是就凉了呀,同理,如果你在中断里面做很耗时间的时间,系统就会崩溃。如果女朋友在去拿快递的过程中,突然口渴了,要去超市买水,所以,中断下半部分是可以被中断打断的。

总之,中断上文越快越好,中断下文可以做比较耗时间的事情,但是不能死循环。Linux中断可以嵌套吗?以前是可以,现在不可以。

57.1.4 设备树中的中断节点

如果一个设备需要用到中断功能,开发人员就需要在设备树中配置好中断属性信息,因为设备树是用来描述硬件信息的,然后Linux内核通过设备树配置的中断属性来配置中断功能。对于中断控制器而言,打开/home/topeet/linux/linux-imx/arch/arm64/boot/dts/freescale/itop8mm-evk.dtsii文件,其中的 gic节点就是 IMX8MM的中断控制器节点,节点内容如下所示:

gic: interrupt-controller@38800000 {compatible = "arm,gic-v3";reg = <0x0 0x38800000 0 0x10000>, /* GIC Dist */<0x0 0x38880000 0 0xC0000>; /* GICR (RD_base + SGI_base) */#interrupt-cells = <3>;interrupt-controller;interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;interrupt-parent = <&gic>;};

简单说明:

IMX8MM使用中断控制器是 gic-v3。

gic-v3 是 ARM Generic Interrupt Controller, version 3 的缩写,是一款 ARM 出品的通用中断控制器。

AArch64 SMP 内核通常与 GICv3 搭配使用,GICv3 提供了专用外设中断(PPI),共享外设中断(SPI),软件生成的中断(SGI)和特定于区域的外设中断(LPI)。

#interrupt-cells = <3> 表明 Interrupt client devices 需要用 3个单元才能确定引用的中断,例如 interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;。interrupt-controller 节点为空,表示当前节点是中断控制器。

对于 gpio 来说,gpio 节点也可以作为中断控制器,比如 imx6ull.dtsi 文件中的 gpio5 节点内容如下所示:

1 gpio5 : gpio @020ac000{
2     compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
3     reg = <0x020ac000 0x4000>;
4     interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
5     gpio-controller;
6     #gpio-cells = <2>;
7     interrupt-controller;
8     #interrupt-cells = <2>;
9    };

第 4 行,interrupts 描述中断源信息,对于 gpio5 来说一共有两条信息,中断类型都是 SPI,触发电

平都是 IRQ_TYPE_LEVEL_HIGH。不同之处在于中断源,一个是 74,一个是 75,可以打开参考手册的“Chapter 3 Interrupts and DMA Events”章节,找到表 3-1,有如图所示的内容:

从上图可以看出,GPIO5 一共用了 2 个中断号,一个是 74,一个是 75。其中 74 对应 GPIO5_IO00~GPIO5_IO15 这低 16 个 IO,75 对应 GPIO5_IO16~GPIOI5_IO31 这高 16 位 IO。

第 7 行,interrupt-controller 表明了 gpio5 节点也是个中断控制器,用于控制 gpio5 所有 IO的中断。

第 8 行,将#interrupt-cells 修改为 2。

简单总结下与中断有关的设备树属性信息

  • #interrupt-cells,指定中断源的信息 cells 个数。 
  • interrupt-controller,表示当前节点为中断控制器 
  • interrupts,指定中断号,触发方式等。 
  • interrupt-parent,指定父中断,也就是中断控制器 

中断实际上是非常复杂的,但是作为开发人员,我们只需要关心怎么在设备树中指定中断,怎么在代码中获得中断就可以。其他的事情,比如设备树中的中断控制器,这些都是由原厂的BSP工程师帮我们写好了,我们不需要来修改他。除非将来你有机会去原厂工作,否则我们不会从头开始写一个设备树文件的,分工是非常明确的。我们需要关注的点是怎么在设备树里面描述一个外设的中断节点,我们来看一个例子。

ft5x06_ts@38 {compatible = "edt,edt-ft5x06";reg = <0x38>;pinctrl-names = "defaults";pinctrl-0 = <&pinctrl_ft5x06_int>;interrupt-parent = <&gpio1>;interrupts = <15 2>;status = "okay";
};
pinctrl_ft5x06_int: ft5x06_int {fsl,pins = </*MX8MM_IOMUXC_GPIO1_IO09_GPIO1_IO9               0x159*/MX8MM_IOMUXC_GPIO1_IO15_GPIO1_IO15              0x159MX8MM_IOMUXC_SAI5_RXD2_GPIO3_IO23               0x41>;};

在这个例子中,我们先使用pinctrl和gpio子系统把这个引脚设置为了gpio功能,因为我们在使用中断的时候需要把引脚设置成输入。然后使用interrupt-parent和interrupts属性来描述中断。interrupt-parent的属性值是gpio1,也就是他的要使用gpio1这个中断控制器,为什么是gpio1呢,因为我们的引脚使用的是gpio1里面的io15,所以我们使用的是gpio1这个中断控制器。interrupts属性设置的是中断源,为什么里面是两个cells呢,因为我们在gpio1这个中断控制器里面#interrupt-cells的值为2,如下图所示:

例子中的第一个 cells 的 15表示 GPIO1 组的 15号 IO。2表示下降沿有效。

IRQ_TYPE_EDGE_BOTH 定义在文件 include/linux/irq.h 中,定义如下 

所以我们在设备树里面配置中断的时候只需要两个步骤即可,第一个步骤是把管脚设置为gpio功能。第二个步骤是使用interrupt-parent和interrupts属性来描述中断。

57.2 实验程序编写

我们以iMX8MM开发板为例,,写一个按键中断,按一下按键就让在终端上打印一句话,这个程序是非常简单的,因为他没有涉及到中断下文的编写,只有中断上文。

57.2.1 修改设备树文件

我们修改设备树文件itop8mm-evk.dtsi,路径在源码目录/home/topeet/linux/linux-imx/arch/arm64/boot/dts/freescale/目录下。我们要使用开发板底板上的音量+键进行试验,首先将原来的节点注释掉,如下图所示:

 然后在根节点下,添加以下内容。

注释掉以前修改的内容,如下图所示

然后编译源码,编译源码请参考IMX8MM开发板使用手册。然后重新烧写Linux镜像,接下来我们来检查你编译的设备树文件有没有被加载到系统里面,也就是说查看你添加的节点有没有,如下图所示: 

 从上图可以看到我们添加的节点,我们查看下节点“test”,如下图所示:

从上图可以看出“compatible”属性值是keys。

57.2.2 编写驱动代码

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\014-Linux中断实验\001”路径下。

我们在Ubuntu的/home/topeet/imx8mm/14/001目录下新建driver.c,将上次编译driver.c的Makefile文件和build.sh拷贝到新建的driver.c同级目录下,修改Makefile为:

obj-m += driver.o
KDIR:=/home/topeet/linux/linux-imx
PWD?=$(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules ARCH=arm64
clean:make -C $(KDIR) M=$(PWD) clean

完整的驱动代码如下面所示:

/** @Author:topeet* @Description: 使用irq_of_parse_and_map函数来获取中断号*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
//定义结构体表示我们的节点
struct device_node *test_device_node;
struct property *test_node_property;
//要申请的中断号
int irq;
// GPIO 编号
int gpio_nu;/*** @description: 中断处理函数test_key* @param {int} irq :要申请的中断号* @param {void} *args :* @return {*}IRQ_HANDLED*/
irqreturn_t test_key(int irq, void *args)
{printk("test_key \n");return IRQ_RETVAL(IRQ_HANDLED);
}
/***************************************************************************************** @brief led_probe : 与设备信息层(设备树)匹配成功后自动执行此函数,* @param inode : 文件索引* @param file  : 文件* @return 成功返回 0           ****************************************************************************************/
int led_probe(struct platform_device *pdev)
{int ret = 0;// 打印匹配成功进入probe函数printk("led_probe\n");test_device_node = of_find_node_by_path("/test");if (test_device_node == NULL){//查找节点失败则打印信息printk("of_find_node_by_path is error \n");return -1;}gpio_nu = of_get_named_gpio(test_device_node, "gpios", 0);if (gpio_nu < 0){printk("of_get_namd_gpio is error \n");return -1;}//设置GPIO为输入模式gpio_direction_input(gpio_nu);//获取GPIO对应的中断号//irq = gpio_to_irq(gpio_nu);irq = irq_of_parse_and_map(test_device_node, 0);printk("irq is %d \n", irq);/*申请中断,irq:中断号名字  test_key:中断处理函数IRQF_TRIGGER_RISING:中断标志,意为上升沿触发"test_key":中断的名字*/ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);if (ret < 0){printk("request_irq is error \n");return -1;}return 0;
}int led_remove(struct platform_device *pdev)
{printk("led_remove\n");return 0;
}
const struct platform_device_id led_idtable = {.name = "keys",
};
const struct of_device_id of_match_table_test[] = {{.compatible = "keys"},{},
};
struct platform_driver led_driver = {//3. 在led_driver结构体中完成了led_probe和led_remove.probe = led_probe,.remove = led_remove,.driver = {.owner = THIS_MODULE,.name = "led_test",.of_match_table = of_match_table_test},//4 .id_table的优先级要比driver.name的优先级要高,优先与.id_table进行匹配.id_table = &led_idtable};/*** @description: 模块初始化函数* @param {*}* @return {*}*/
static int led_driver_init(void)
{//1.我们看驱动文件要从init函数开始看int ret = 0;//2.在init函数里面注册了platform_driverret = platform_driver_register(&led_driver);if (ret < 0){printk("platform_driver_register error \n");}printk("platform_driver_register ok \n");return 0;
}/*** @description: 模块卸载函数* @param {*}* @return {*}*/
static void led_driver_exit(void)
{free_irq(irq, NULL);platform_driver_unregister(&led_driver);printk("goodbye! \n");
}
module_init(led_driver_init);
module_exit(led_driver_exit);MODULE_LICENSE("GPL");

57.2.3 运行测试

编译驱动代码为驱动模块,编译成功如下图所示:

我们进入共享文件夹,加载驱动模块,如下图所示:

那么我们怎么查看是否申请中断成功了呢?我们可以进入到开发板的/proc/interrupts目录查看,如下图所示:

cat /proc/interrupts | grep test_key

我们按一下底板上的音量+(VOL+),可以看到控制台打印信息,如下图所示: 

我们可以看中断发生了几次,我们可以进入到开发板的/proc/irq/196/spurious 目录下,如下图所示:

cat /proc/irq/196/spurious

 

从上图可知,中断发生了15次,我们可以数一下test_key打印的次数是十五次,说明发生一次中断,会打印一次test_key。

57.2.4 优化方案

上面的例子我们是使用函数gpio_to_irq来获取中断号的,接下来我们通过在设备树文件里面使用属性“interrupt-parent”和“interrupts”来获取中断号,我们修改设备树文件如下图所示:

 

重新编译源码,然后烧写设备树镜像,启动开发板后如下图所示: 

我们检查一下有没有我们添加的节点,如下图所示,出现添加的设备节点test。 

接下来我们改一下驱动driver.c,完整代码如下所示:

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\014-Linux中断实验\002”路径下。

/** @Author:topeet* @Description: 使用gpio_to_irq函数来获取中断号*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
//定义结构体表示我们的节点
struct device_node *test_device_node;
struct property *test_node_property;
//要申请的中断号
int irq;
// GPIO 编号
int gpio_nu;/*** @description: 中断处理函数test_key* @param {int} irq :要申请的中断号* @param {void} *args :* @return {*}IRQ_HANDLED*/
irqreturn_t test_key(int irq, void *args)
{printk("test_key \n");return IRQ_HANDLED;
}
/***************************************************************************************** @brief led_probe : 与设备信息层(设备树)匹配成功后自动执行此函数,* @param inode : 文件索引* @param file  : 文件* @return 成功返回 0           ****************************************************************************************/
int led_probe(struct platform_device *pdev)
{int ret = 0;// 打印匹配成功进入probe函数printk("led_probe\n");test_device_node = of_find_node_by_path("/test");if (test_device_node == NULL){//查找节点失败则打印信息printk("of_find_node_by_path is error \n");return -1;}gpio_nu = of_get_named_gpio(test_device_node, "gpios", 0);if (gpio_nu < 0){printk("of_get_namd_gpio is error \n");return -1;}//设置GPIO为输入模式gpio_direction_input(gpio_nu);//获取GPIO对应的中断号// irq = gpio_to_irq(gpio_nu);irq =irq_of_parse_and_map(test_device_node,0);printk("irq is %d \n", irq);/*申请中断,irq:中断号名字  test_key:中断处理函数IRQF_TRIGGER_RISING:中断标志,意为上升沿触发"test_key":中断的名字*/ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);if (ret < 0){printk("request_irq is error \n");return -1;}return 0;
}int led_remove(struct platform_device *pdev)
{printk("led_remove\n");return 0;
}
const struct platform_device_id led_idtable = {.name = "keys",
};
const struct of_device_id of_match_table_test[] = {{.compatible = "keys"},{},
};
struct platform_driver led_driver = {//3. 在led_driver结构体中完成了led_probe和led_remove.probe = led_probe,.remove = led_remove,.driver = {.owner = THIS_MODULE,.name = "led_test",.of_match_table = of_match_table_test},//4 .id_table的优先级要比driver.name的优先级要高,优先与.id_table进行匹配.id_table = &led_idtable};/*** @description: 模块初始化函数* @param {*}* @return {*}*/
static int led_driver_init(void)
{//1.我们看驱动文件要从init函数开始看int ret = 0;//2.在init函数里面注册了platform_driverret = platform_driver_register(&led_driver);if (ret < 0){printk("platform_driver_register error \n");}printk("platform_driver_register ok \n");return 0;
}/*** @description: 模块卸载函数* @param {*}* @return {*}*/
static void led_driver_exit(void)
{free_irq(irq, NULL);platform_driver_unregister(&led_driver);printk("gooodbye! \n");
}
module_init(led_driver_init);
module_exit(led_driver_exit);MODULE_LICENSE("GPL");

 编译驱动代码为驱动模块,如下图所示:

编译成功加载驱动,可以成功获得irq号,如下图所示: 

 我们按一下底板上的音量+(VOL+),可以看到控制台打印信息,如下图所示:

 

我们可以看中断发生了几次,我们可以进入到开发板的/proc/irq/196/spurious 目录下,如下图所示:

cat /proc/irq/196/spurious

 

从上图可知,中断发生了9次,我们可以数一下test_key打印的次数是9次,说明发生一次中断,会打印一次test_key。 

相关文章:

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十七章 Linux中断实验

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...

每日一题~961div2A+B+C(阅读题,思维,数学log)

A 题意&#xff1a;给你 n*n 的表格和k 个筹码。每个格子上至多放一个 问至少占据多少对角线。 显然&#xff0c;要先 格数的多的格子去放。 n n-1 n-2 …1 只有n 的是一个&#xff08;主对角线&#xff09;&#xff0c;其他的是两个。 #include <bits/stdc.h> using na…...

Fireflyrk3288 ubuntu18.04添加Qt开发环境、安装mysql-server

1、创建一台同版本的ubuntu18.04的虚拟机 2、下载rk3288_ubuntu_18.04_armhf_ext4_v2.04_20201125-1538_DESKTOP.img 3、创建空img镜像容器 dd if/dev/zero ofubuntu_rootfs.img bs1M count102404、将该容器格式化成ext4文件系统 mkfs.ext4 ubuntu_rootfs.img5、将该镜像文件…...

简化mybatis @Select IN条件的编写

最近从JPA切换到Mybatis&#xff0c;使用无XML配置&#xff0c;Select注解直接写到interface上&#xff0c;发现IN条件的编写相当麻烦。 一般得写成这样&#xff1a; Select({"<script>","SELECT *", "FROM blog","WHERE id IN&quo…...

Windows图形界面(GUI)-MFC-C/C++ - Control

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 Control 资源编辑器 添加控件 设置控件属性 添加控件变量 添加消息处理 处理控件事件 控件焦点顺序 Control 资源编辑器 资源编辑器&#xff1a;用于可视化地编辑对话框和控件。…...

SQL Server数据库安全:策略制定与实践指南

SQL Server数据库安全&#xff1a;策略制定与实践指南 在当今数字化时代&#xff0c;数据安全是每个组织的核心关注点。SQL Server作为广泛使用的关系型数据库管理系统&#xff0c;提供了一套强大的安全特性来保护存储的数据。制定有效的数据库安全策略是确保数据完整性、可用…...

Spring Boot入门指南:留言板

一.留言板 1.输⼊留⾔信息,点击提交.后端把数据存储起来. 2.⻚⾯展⽰输⼊的表⽩墙的信息 规范&#xff1a; 1.写一个类MessageInfo对象&#xff0c;添加构造方法 虽然有快捷键&#xff0c;但是还是不够偷懒 项目添加Lombok。 Lombok是⼀个Java⼯具库&#xff0c;通过添加注…...

Docker 中安装和配置带用户名和密码保护的 Elasticsearch

在 Docker 中安装和配置带用户名和密码保护的 Elasticsearch 需要以下步骤。Elasticsearch 的安全功能&#xff08;包括基本身份验证&#xff09;在默认情况下是启用的&#xff0c;但在某些版本中可能需要手动配置。以下是详细步骤&#xff0c;包括如何设置用户名和密码。 1. …...

面试官:说说JVM内存调优及内存结构

1. JVM简介 JVM&#xff08;Java虚拟机&#xff09;是运行Java程序的平台&#xff0c;它使得Java能够跨平台运行。JVM负责内存的自动分配和回收&#xff0c;减轻了程序员的负担。 2. JVM内存结构 运行时数据区是JVM中最重要的部分&#xff0c;包含多个内存区域&#xff1a; …...

Ansible的脚本-----playbook剧本【下】

目录 实战演练六&#xff1a;tags 模块 实战演练七&#xff1a;Templates 模块 实战演练六&#xff1a;tags 模块 可以在一个playbook中为某个或某些任务定义“标签”&#xff0c;在执行此playbook时通过ansible-playbook命令使用--tags选项能实现仅运行指定的tasks。 playboo…...

Mysql开启远程控制简化版,亲测有效

首先关闭防火墙 改表法 打开上图的CMD&#xff0c;输入密码进入&#xff0c;然后输入一下指令 1.use mysql; 2.update user set host % where user root;//更新root用户的权限&#xff0c;允许任何主机连接 3.FLUSH PRIVILEGES;//刷新权限&#xff0c;使更改生效 具体参考…...

【MQTT协议与IoT通信】MQTT协议的使用和管理

MQTT协议与IoT通信&#xff1a;MQTT协议的使用和管理 目录 引言MQTT协议概述 什么是MQTTMQTT的工作原理 MQTT协议的关键特性 轻量级与高效性发布/订阅模式质量服务等级(QoS)持久会话安全性 MQTT协议的使用方法 设置MQTT Broker连接MQTT Client发布消息订阅主题断开连接 MQTT协…...

根据题意写出完整的css,html和js代码【购物车模块页面及功能实现】

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…...

AWS免费层之后:了解和管理您的云服务成本

Amazon Web Services (AWS) 为新用户提供了12个月的免费层服务&#xff0c;这是许多人开始使用云服务的绝佳方式。但是&#xff0c;当这一年结束后&#xff0c;您的AWS使用会如何变化&#xff1f;我们九河云通过本文将探讨免费层结束后的AWS成本情况&#xff0c;以及如何有效管…...

Linux定时同步系统时间到硬件时间

Linux定时同步系统时间到硬件时间 1. 系统时间、软件时间 系统时间 &#xff08;System Time&#xff09;&#xff1a; 一般说来就是我们执行 date命令看到的时间&#xff0c;linux系统下所有的时间调 用&#xff08;除了直接访问硬件时间的命令&#xff09;都是使用的这个时…...

网络编程——wireshark抓包、tcp粘包

目录 一、前言 1.1 什么是粘包 1.2 为什么UDP不会粘包 二、编写程序 文件树 客户端程序 服务器程序 tcp程序 头文件 makefile 三、 实验现象 四、改进实验 五、小作业 一、前言 最近在做网络芯片的驱动&#xff0c;验证功能的时候需要借助wireshark这个工具&…...

el-table合计行更新问题

说明&#xff1a;在使用el-table自带的底部合计功能时&#xff0c;初始界面不会显示合计内容 解决方案&#xff1a;使用 doLayout()方法 updated() {this.$nextTick(() > {this.$refs[inventorySumTable].doLayout();});},完整代码&#xff1a; // show-summary&#xff1a…...

ChatGPT:数据库不符合第二范式示例

ChatGPT&#xff1a;数据库不符合第二范式示例 这张图片为什么不符合数据库第二范式 这个表格不符合数据库第二范式&#xff08;2NF&#xff09;的原因如下&#xff1a; 1. 数据库第二范式&#xff08;2NF&#xff09;定义 第二范式要求一个数据库表格在满足第一范式&#xf…...

27、美国国家冰雪中心(NSIDC)海冰密集度月数据下载与处理

文章目录 一、前言二、数据下载三、使用Ponply查看数据结构四、代码一、前言 处理美国国家冰雪中心(NSIDC)的海冰密集度月度数据时,坐标转换是一个重要的步骤。NSIDC提供的数据通常采用极地球面坐标系,需要将其转换为常用的地理坐标系(如经纬度)以便进行分析和可视化。 坐…...

vite环境下使用bootstrap

环境 nodejs 18 pnpm 初始化 pnpm init pnpm add -D vite --registry http://registry.npm.taobao.org pnpm add bootstrap popperjs/core --registry http://registry.npm.taobao.org pnpm add -D sass --registry http://registry.npm.taobao.org新建vite.config.js cons…...

Laravel视图渲染封装

第一种 app/Helpers/ViewHelper.php 创建一个辅助函数&#xff0c;用于动态确定视图路径&#xff1a; <?php if (!function_exists(fetchView)) {function fetchView($data []){$currentAction \Route::currentRouteAction();list($controller, $method) explode(, $c…...

C++学习补充2:MySQL select 查询

MySQL select 查询 MySQL 查询 select时&#xff0c; 不区分大小写的。 MySQL 在默认情况下是区分大小写的&#xff0c;但是它的行为可能因配置和使用的字符集而有所不同。以下是一些可能导致查询在 SELECT 语句中不区分大小写的原因&#xff1a; 字符集设置&#xff1a;如果…...

uni-app声生命周期

应用的生命周期函数在App.vue页面 onLaunch:当uni-app初始化完成时触发&#xff08;全局触发一次&#xff09; onShow:当uni-app启动&#xff0c;或从后台进入前台时显示 onHide:当uni-app从前台进入后台 onError:当uni-app报错时触发,异常信息为err 页面的生命周期 onLoad…...

排序算法--堆排序

基本思想 堆排序的基本思想是&#xff0c;将待排序的元素构建成一个最大堆或最小堆。对于最大堆来说&#xff0c;堆顶是整个堆中的最大元素&#xff1b;对于最小堆来说&#xff0c;堆顶是整个堆中的最小元素。然后&#xff0c;将堆顶元素与堆中最后一个元素交换&#xff0c;并…...

iPhone 在 App Store 中推出的 PC 模拟器 UTM SE

PC 模拟器是什么&#xff1f;PC 模拟器是一种软件工具&#xff0c;它模拟不同硬件或操作系统环境&#xff0c;使得用户可以在一台 PC 上运行其他平台的应用程序或操作系统。通过 PC 模拟器&#xff0c;用户可以在 Windows 电脑上体验 Android 应用、在 Mac 电脑上运行 Windows …...

FastAPI删除mongodb重复数据(数据清洗)

在 FastAPI 中删除 MongoDB 重复数据&#xff0c;你需要结合使用 MongoDB 查询和 FastAPI 的路由功能。以下是一个通用的例子&#xff0c;演示如何删除特定字段上的重复数据&#xff1a; 1. 定义数据模型: from pydantic import BaseModel, Field from bson import ObjectId …...

移动UI:排行榜单页面如何设计,从这五点入手,附示例。

移动UI的排行榜单页面设计需要考虑以下几个方面&#xff1a; 1. 页面布局&#xff1a; 排行榜单页面的布局应该清晰明了&#xff0c;可以采用列表的形式展示排行榜内容&#xff0c;同时考虑到移动设备的屏幕大小&#xff0c;应该设计合理的滚动和分页机制&#xff0c;确保用户…...

如何解决 uni-app 项目中 “文件查找失败:‘crypto-js‘“ 的问题

在开发使用 uni-app 框架的项目时&#xff0c;遇到依赖问题是常见的。本文将介绍如何解决编译过程中出现的 “文件查找失败&#xff1a;‘crypto-js’” 错误&#xff0c;并说明这种错误为什么会发生以及如何避免。 问题背景 在对 uni-app 项目进行编译时&#xff0c;我们可能…...

Apache DolphinScheduler 3.2.2 版本正式发布!

Apache DolphinScheduler 3.2.2 版本正式发布&#xff01; 近日&#xff0c;Apache DolphinScheduler 发布了 3.2.2 版本。此版本主要基于 3.2.1 版本进行了 bug 修复&#xff0c;新增若干特性&#xff0c;并进行了众多改进和 Bug 修复&#xff0c;以及文档修复等。 &#x1…...

汇川CodeSysPLC教程03-2-6 ModBus TCP

什么是ModBus TCP&#xff1f; ModBus TCP是一种基于TCP/IP协议的工业网络通信协议&#xff0c;常用于工业自动化和控制系统。它是ModBus协议的一个变种&#xff0c;ModBus协议最初由Modicon&#xff08;现在是施耐德电气的一部分&#xff09;在1979年开发。 以下是ModBus TC…...

【Python机器学习】决策树的构造——划分数据集

分类算法除了需要测量信息熵&#xff0c;还需要划分数据集&#xff0c;度量划分数据集的熵&#xff0c;以便判断当前是否正确划分了数据集。 我们将对每个特征划分数据集的结果计算一次信息熵&#xff0c;然后判断按照哪个特征划分数据集是最好的划分方式。 想象一个分部在二…...

Pip换源使用帮助

PyPI 镜像使用帮助 PyPI 镜像帮助提高包安装的速度&#xff0c;特别是当默认源访问较慢时。镜像每次同步成功后&#xff0c;每隔 5 分钟进行更新&#xff0c;确保镜像内容尽量与官方源保持一致。 pip 临时使用 如果您只想在一次安装中使用镜像&#xff0c;可以使用以下命令&…...

力扣1089复写0

1089. 复写零 - 力扣&#xff08;LeetCode&#xff09; 我们的思路是利用类似双指针的方式去解答&#xff0c;来看下代码 class Solution { public:void duplicateZeros(vector<int>& arr){int cur 0, dest -1, n arr.size();while (cur < n){if (arr[cur])d…...

10 VUE Element

文章目录 VUE1、概述2、快速入门3、Vue 指令4、生命周期5、案例 Elemant1、快速入门2、Element 布局3、常用组件-案例 VUE 1、概述 Vue 是一套前端框架&#xff0c;免除原生JavaScript中的DOM操作&#xff0c;简化书写基于MVVM(Model-View-ViewModel)思想&#xff0c;实现数据…...

独立游戏《星尘异变》UE5 C++程序开发日志8——实现敏感词过滤功能(AC自动机)

在游戏中经常会有需要玩家输入一些内容的功能&#xff0c;例如聊天&#xff0c;命名等&#xff0c;这款游戏只有在存档时辉用到命名功能&#xff0c;所以这个过滤也只是一个实验性的功能&#xff0c;我们将使用AC自动机来实现&#xff0c;这是在我们把“csdn”这个词设置为屏蔽…...

使用 Swagger 在 Golang 中进行 API 文档生成

Swagger 是一款强大的 API 文档生成工具&#xff0c;可以帮助开发者轻松创建、管理和展示 RESTful API 文档。在本文中&#xff0c;我们将介绍如何在 Golang 项目中使用 Swagger 来生成 API 文档。 官网地址 &#xff1a; gin-swagger 前提条件 Golang 开发环境&#xff08;…...

Pip换源实战指南:加速你的Python开发

1. Pip换源的重要性 在使用Python进行软件开发或数据分析时&#xff0c;pip 是Python的包管理工具&#xff0c;用于安装和管理第三方库。然而&#xff0c;由于网络环境的差异&#xff0c;特别是在某些国家&#xff0c;访问默认的PyPI&#xff08;Python Package Index&#xff…...

【数据结构】常用数据结构的介绍:理解与应用

文章目录 前言一、介绍二、使用场景三、总结 前言 在计算机科学中&#xff0c;数据结构是我们组织和存储数据的方式&#xff0c;它可以帮助我们高效地执行各种操作&#xff0c;如搜索、插入和删除。从数组和链表&#xff0c;到树和图&#xff0c;不同的数据结构有着不同的优点…...

【优秀python系统毕设】基于Python flask的气象数据可视化系统设计与实现,有LSTM算法预测气温

第一章 绪论 1.1 研究背景 在当今信息爆炸的时代&#xff0c;气象数据作为重要的环境信息资源&#xff0c;扮演着关键的角色。然而&#xff0c;传统的气象数据呈现方式存在信息量庞大、难以理解的问题&#xff0c;限制了用户对气象信息的深入理解和利用。因此&#xff0c;基…...

【康复学习--LeetCode每日一题】2951. 找出峰值

题目&#xff1a; 给你一个下标从 0 开始的数组 mountain 。你的任务是找出数组 mountain 中的所有 峰值。 以数组形式返回给定数组中 峰值 的下标&#xff0c;顺序不限 。 注意&#xff1a; 峰值 是指一个严格大于其相邻元素的元素。 数组的第一个和最后一个元素 不 是峰值。…...

PYTHON学习笔记(八、字符串及的使用)

目录 1、字符串 1.1、字符串的常用操作 1.2、格式化字符串 1.2.1、占位符格式化字符串 1.2.2、f-string格式化字符串 1.2.3、str.format( )格式化字符串 1.3、数据的验证 1.4、正则表达式 1.5.1元字符 1.5.2限定符 1.5.3其他字符 1.5.4re模块 1、字符串 1.1、字符…...

文件共享功能无法使用提示错误代码0x80004005【笔记】

环境情况&#xff1a; 其他电脑可以正常访问共享端&#xff0c;但有一台电脑访问提示错误代码0x80004005。 处理检查&#xff1a; 搜索里输入“启用或关闭Windows功能”按回车键&#xff0c;在“启用或关闭Windows功能”里将“SMB 1.0/CIFS文件共享支持”勾选后&#xff08;故…...

FTP(File Transfer Protocal,文件传输协议)

文章目录 引言FTP管理工具FTP客户端FTP连接模式控制连接数据连接FTP命令/响应FTP命令FTP响应FTPSSFTP引言 FTP(File Transfer Protocal,文件传输协议)用于建立两台主机间的数据文件传输下载。使用客户/服务器(Client/Server)架构,基于TCP协议,服务端口为21。 FTP链接…...

DevEco Studio中使用Qt,编写HarmonyOS程序

文章目录 1.操作2.注意事项2.1.adapter_ts2.1.手机插到电脑后&#xff0c;DevEco无法识别 1.操作 最近需要尝试把之前在Windwos下用Qt实现的程序移植到鸿蒙&#xff08;HarmonyOS&#xff09;系统上。 我使用的DevEco版本是5.03.501 找了一下资料&#xff0c;官方&#xff0…...

基于单文档的MFC图像增强

目录 function.h ColorEnhanceDib.h ColorEnhanceDib.cpp Dib.h Dib.cpp FrequencyFilterDib.h FrequencyFilterDib.cpp GrayTransformDib.h GrayTransformDib.cpp HistogramDib.h HistogramDib.cpp SharpenProcessDib.h SharpenProcessDib.cpp SmoothProcessDib.h Sm…...

云计算实训13——DNS域名解析、ntp时间服务器配置、主从DNS配置、多区域DNS搭建

一、DNS域名解析 1.正向解析 将域名解析为IP地址 DNS正向解析核心配置 (1)安装bind [rootdns ~]# yum -y install bind (2)编辑配置文件 编辑named.conf文件&#xff0c;限定访问权限 [rootdns ~]# vim /etc/named.conf 编辑named.rfc文件&#xff0c;指定要访问的域名 [ro…...

【C#】Visual Studio2022打包依赖第三方库的winForm程序为exe

0.简介 IDE&#xff1a;VS2022 平台&#xff1a;C# .NetFramework4.7.2 WinForm界面 有GDAL、EEplus第三方库的依赖&#xff0c;所以在其他未安装环境的电脑中功能无法使用。 1. 安装 1.1 运行文件输出 在VS扩展中选择管理扩展&#xff0c;安装&#xff1a;Microsoft Visua…...

《算法笔记》总结No.11——数字处理(上)欧拉筛选

机试中存在部分涉及到较复杂数字的问题&#xff0c;这是编码的基本功&#xff0c;各位一定要得心应手。 目录 一.最大公约数和最小公倍数 1.最大公约数 2.最小公倍数 二.素数 1.判断指定数 2.输出所有素数 3.精进不休——埃拉托斯特尼筛法 4.达到更优&#xff01;——…...

DP学习——享元模式

学而时习之&#xff0c;温故而知新。 享元模式 名词解析 有必要解释下“享元”两字&#xff0c;英文原文是flyweight pattern——轻量级模式&#xff0c;但是翻译过来的“享元”两字太牛逼了——褒贬不一&#xff0c;翻译的他妈都不认识。 享元的高雅在于: 享:共享/共用 元:…...

无人机10公里WiFi图传摄像模组,飞睿智能超清远距离无线监控,智能安防新潮流

在这个科技日新月异的时代&#xff0c;我们对影像的捕捉和传播有了更高的要求。从传统的有线传输到无线WiFi图传&#xff0c;每一次技术的飞跃都为我们带来了全新的视觉体验。今天&#xff0c;我们要探讨的&#xff0c;正是一款具有划时代意义的科技产品——飞睿智能10公里WiFi…...