Linux第84步_了解Linux中断及其函数

news2024/9/23 21:22:49

1、中断号

中断号又称中断线,每个中断都有一个中断号,通过中断号即可区分不同的中断。

2、Linux中断API函数

需要包含头文件“#include <linux/interrupt.h>

1)、在使用某个中断功能的时候,需要执行“申请中断”

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

irq:要申请中断的中断号;

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

fags:中断标志;可以在文件“include/linux/interrupt.h”里面查看所有的中断标志;

name:中断名字,设置以后可以在“/proc/interrupts”文件中看到对应的中断名字;

dev:如果将flags设置为IRQF_SHARED的话,dev用来区分不同的中断

一般情况下将dev设置为“设备结构体”,dev会传递给中断处理函数irg_handler_t的第二个参数。

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

注意:

在“中断服务程序”中,不能使用request_irq()函数;

在“禁止睡眠的代码段”中,不能使用request_irq()函数;

执行request_irq()函数可能会导致睡眠;

request_irq()函数会使能“中断”,不需要我们手动去使能中断。

2)、常用的中断标志

#define IRQF_SHARED     0x00000080

/*

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

*/

#define IRQF_TRIGGER_NONE 0x00000000  //无触发

#define IRQF_ONESHOT 0x00002000 //单次中断,中断执行一次就结束

#define IRQF_TRIGGER_RISING 0x00000001  //上升沿触发

#define IRQF_TRIGGER_FALLING 0x00000002  //下降沿触发

#define IRQF_TRIGGER_HIGH     0x00000004  //高电平触发

#define IRQF_TRIGGER_LOW     0x00000008  //低电平触发

3)、在不需要使用某个中断功能的时候,需要执行“释放中断”

void free_irq(unsigned int irq, void *dev)

irq:要释放的中断号。

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

返回值:无。

4)、中断处理函数

irqreturn_t (*irq_handler_t) (int, void *)

int”:第1个参数中断号,和request_irq()函数的irq参数保持一致。

void *”:第2个参数是指向void型的指针,和request_irq()函数的dev参数保持一致。至于用于区分“共享中断”的不同设备,dev也可以是指向设备数据结构。

返回值:为irqreturn_t类型;

enum irqreturn {

IRQ_NONE     = (0 << 0),  /*中断没有被这个设备处理*/

IRQ_HANDLED  = (1 << 0),  /*中断被这个设备处理*/

IRQ_WAKE_THREAD   = (1 << 1),  /*处理程序请求唤醒处理程序线程*/

};

typedef enum irqreturn irqreturn_t;//将irqreturn起个别名叫irqreturn_t

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

#define IRQ_RETVAL(x) ((x) ? IRQ_HANDLED : IRQ_NONE)

一般中断服务函数返回值使用如下形式:

return IRQ_RETVAL(IRQ_HANDLED);

5)、中断使能

void enable_irq(unsigned int irq)

irq:要使能的中断号;

需要包含头文件“interrupt.h

6)、中断禁止

void disable_irq(unsigned int irq)

irq:要禁止的中断号;

注意:

要等到当前正在执行的中断处理函数执行完才返回,因此使用者需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。

void disable_irq_nosync(unsigned int irq)

irq:要禁止的中断号;

注意:

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

7)、使能总中断和关闭总中断

local_irq_enable()  //打开全局中断,需要包含头文件“interrupt.h

local_irq_disable() //关闭全局中断,需要包含头文件“interrupt.h

local_irq_save(flags) //用于禁止中断,并且将中断状态保存在flags中;

local_irq_restore(flags) //用于恢复中断,将中断到flags状态

3、上半部和下半部

上半部:上半部就是“中断处理函数”,那些“处理过程较快,占用时间较短的中断程序”由上半部完成。

下半部:那些“处理过程比较耗时的中断服务程序”,放到“下半部”去执行。

4、根据实际情况将中断服务程序的代码放到上半部或下半部,通常依据如下:

1)、如果要处理的内容不希望被其他中断打断,那么可以放到上半部。2)、如果要处理的内容对时间敏感,可以放到上半部。3)、如果要处理的内容与硬件有关,可以放到上半部

4)、如果要处理的内容比较耗时的中断服务程序”,则放到“下半部”去执行。

Linux内核提供了多种下半部机制:软中断,tasklet,工作队列。2.5版本的以前Linux内核是使用“bottom half”机制(BH机制)来实现“下半部”,了解一下。

Linux内核将中断程序分为“上半部和下半部”的目的就是实现中断处理函数的快进快出。

5、下半部机制

1)、软中断

软中断是一种下半部机制,要求推后的工作不能睡眠,需要包含头文件“#include <linux/interrupt.h>

注意:

软中断必须在编译的时候静态注册,使用softirq_init()初始化软中断。

①、softirq_action结构体如下:

struct softirq_action

{

void (*action)(struct softirq_action *);

};

enum {

HI_SOFTIRQ=0,    /* 高优先级软中断 */

TIMER_SOFTIRQ,   /* 定时器软中断 */

NET_TX_SOFTIRQ,  /* 网络数据发送软中断 */

NET_RX_SOFTIRQ,  /* 网络数据接收软中断 */

BLOCK_SOFTIRQ,

IRQ_POLL_SOFTIRQ,

TASKLET_SOFTIRQ, /* tasklet软中断 */

SCHED_SOFTIRQ,   /* 调度软中断 */

HRTIMER_SOFTIRQ, /* 高精度定时器软中断 */

RCU_SOFTIRQ,     /* RCU软中断 */

NR_SOFTIRQS      /*NR_SOFTIRQS的值是10*/

};

const char * const softirq_to_name[NR_SOFTIRQS];

②、注册软中断处理函数

void open_softirq(int nr, void (*action)(struct softirq_action *))

nr:要开启的软中断。

action:软中断对应的处理函数

注意:需要包含头文件“interrupt.h

③、触发软中断

void raise_softirq(unsigned int nr)

nr:要触发的软中断。

注意:需要包含头文件“interrupt.h

④、软中断初始化函数

void softirq_init(void)

注意:需要包含头文件“interrupt.h

2)、tasklet

tasklet是一种下半部机制,要求推后的工作不能睡眠。在介于软中断和tasklet之间,建议大家使用tasklet,需要包含头文件“#include <linux/interrupt.h>

①、tasklet_struct结构体如下:

struct tasklet_struct

{

struct tasklet_struct *next;

unsigned long state;

atomic_t count;

void (*func)(unsigned long);

unsigned long data;

};

②、初始化tasklet

void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data);

t:要初始化的tasklet;

func:tasklet的处理函数;

data:要传递给func函数的参数;

注意:需要包含头文件“interrupt.h

DECLARE_TASKLET(name, func, data)

name:为要定义的tasklet名字,其实就是tasklet_struct类型的变量名;

func:就是tasklet的处理函数;

data:是传递给fnc函数的参数;、

注意:需要包含头文件“interrupt.h

tasklet调度函数:

void tasklet_schedule(struct tasklet_struct *t)

t:要调度的tasklet,也就是DECLARE_TASKLET(name, func, data)里面的name;

注意:需要包含头文件“interrupt.h

注意:

在上半部,也就是“中断处理函数”中调用tasklet_schedule()函数就能使 tasklet在合适的时间运行;

④、举例:

struct tasklet_struct testtasklet; /* 定义taselet */

/* tasklet处理函数 */

void testtasklet_func(unsigned long data)

{

/* tasklet具体处理内容 */

}

/* 中断处理函数 */

irqreturn_t test_handler(int irq, void *dev_id)

{

......

/* 调度tasklet */

tasklet_schedule(&testtasklet);

/*

在上半部,也就是中断处理函数中调用tasklet_schedule()函数使 tasklet在合适的时间运行;

*/

......

}

/* 驱动入口函数 */

static int __init xxxx_init(void)

{

......

tasklet_init(&testtasklet, testtasklet_func, data);

/* 初始化tasklet*/

t=&testtasklet,要初始化的tasklet;

func=testtasklet_func,tasklet的处理函数testtasklet_func();

data:要传递给func函数的参数;

request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);

irq=xxx_irq:要申请中断的中断号;

handler=test_handler:中断处理函数test_handler(),当中断发生以后就会执行此中断处理函数;

fags=0:中断标志;

name="xxx":中断名字,设置以后可以在“/proc/interrupts”文件中看到对应的中断名字;

dev=&xxx_dev:将dev设置为“设备结构体”,dev会传递给中断处理函数irg_handler_t的第二个参数。

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

/* 注册中断处理函数 */

......

}

3)、工作队列

工作队列是一种下半部机制,它工作在进程的上下文处,将要推后的工作交给一个内核线程去执行,因此,允许工作队列进入睡眠或被重新调度

work_struct结构体表示一个“工作”,需要包含头文件“workqueue.h”,如下;

struct work_struct {

  atomic_long_t data;

  struct list_head entry;

  work_func_t func; /* 工作队列处理函数 */

};

workqueue_struct结构体表示“工作队列”,需要包含头文件“workqueue.h”,如下:

struct workqueue_struct {

  struct list_head pwqs;

  struct list_head list;

  struct mutex mutex;

  int work_color;

  int flush_color;

  atomic_t nr_pwqs_to_flush;

  struct wq_flusher *first_flusher;

  struct list_head flusher_queue;

  struct list_head flusher_overflow;

  struct list_head maydays;

  struct worker *rescuer;

  int nr_drainers;

  int saved_max_active;

  struct workqueue_attrs *unbound_attrs;

  struct pool_workqueue *dfl_pwq;

  char name[WQ_NAME_LEN];

  struct rcu_head rcu;

  unsigned int flags ____cacheline_aligned;

  struct pool_workqueue __percpu *cpu_pwqs;

  struct pool_workqueue __rcu *numa_pwq_tbl[];

};

worker结构体表示“工作者线程”,需要包含头文件“workqueue_internal.h” ,worker结构体内容如下:

struct worker {

  union {    struct list_head   entry;    struct hlist_node  hentry;  };  struct work_struct  *current_work;  work_func_t  current_func;  struct pool_workqueue  *current_pwq;  struct list_head  scheduled;  struct task_struct  *task;  struct worker_pool  *pool;  struct list_head  node;  unsigned long last_active;  unsigned int flags;  int id;  int sleeping;  char desc[WORKER_DESC_LEN];  struct workqueue_struct *rescue_wq;  work_func_t last_func;};

Linux内核使用“工作者线程(worker thread)”来处理工作队列中的各个工作,每个“工作者线程(worker)”都有一个“工作队列(workqueue_struct)”,它会处理属于自己工作队列中的所有“工作(work_struct)”。

在实际驱动开发中,我们只需要定义“工作(work_struct)”即可,关于工作队列和工作者线程我们基本不用去管。

创建“工作(work_struct)”很简单,直接定义一个work_struct结构体变量即可,然后使用INIT_WORK宏来初始化“工作(work_struct)”,也可以使用 DECLARE_WORK宏来一次性完成“工作(work_struct)”的创建和初始化。

INIT_WORK 宏定义,需要包含头文件“workqueue.h”,如下:

#define INIT_WORK(_work, _func)

_work:表示要初始化的工作;

_func:是工作对应的处理函数;

DECLARE_WORK宏定义,需要包含头文件“workqueue.h”,如下:

#define DECLARE_WORK(n, f)

n:表示定义的“工作(work_struct)

f:表示工作对应的处理函数

工作调度函数,需要包含头文件“workqueue.h”:

bool schedule_work(struct work_struct *work)

work:要调度的工作;返回值:0 成功,其他值 失败;

注意:

在“上半部”,也就是“中断处理函数”中调用schedule_work()函数就能使 “工作队列(workqueue_struct)”在合适的时间运行;

struct work_struct testwork;  /* 定义工作(work) */

/* work处理函数 */void testwork_func_t(struct work_struct *work){

  /* work具体处理内容 */}

/* 中断处理函数 */irqreturn_t test_handler(int irq, void *dev_id){

  .....

  schedule_work(&testwork);

  /* 调度work */

  //work=&testwork:要调度的工作;  //返回值:0 成功,其他值 失败;

  ......}

/* 驱动入口函数 */static int __init xxxx_init(void){

  ......

  INIT_WORK(&testwork, testwork_func_t);

  /* 初始化work */

  //_work=&testwork:表示要初始化的工作;

  //_func= testwork_func_t:是工作对应的处理函数;

  request_irq(xxx_irq,test_handler,0,"xxx",&xxx_dev);

  /* 注册中断处理函数 */

  //irq= xxx_irq:要申请中断的中断号;

  //Handler= test_handler:中断处理函数,当中断发生以后就会执行此中断处理函数;

  //fags=0:中断标志;

  //name="xxx":中断名字;

  //dev=&xxx_dev:将dev设置为“设备结构体”,dev会传递给中断处理函数irg_handler_t的第二个参数。

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

  ......

}

6、GIC中断控制器

GIC是ARM公司给Cortex-A/R内核提供的一个中断控制器,类似 Cortex-M 内核中的NVIC;

7、GIC中断源分类:

1)、Shared Peripheral Interrupt,简写SPI,称为“共享中断”,即所有Core共享的中断。比如:GPIO中断、串口中断等,这些中断所有的Core都可以处理,不限定特定 Core;

2)、Private Peripheral Interrupt,简写PPI,称为“私有中断”。 GIC支持多核,每个核有自己专用的中断,且由指定的核心处理,这些中断就叫做私有中断;

3)、Software-generated Interrupt,简写SGI,称为“软件中断”,由软件触发引起的中断,通过向寄存器GICD_SGIR写入数据来触发,系统会使用SGI中断来完成多核之间的通信;

8、中断ID

每个中断源都有一个唯一的中断ID,每个CPU最多支持1020个中断ID,中断ID号为ID0~ID1019。其中IQ0~ID15分配给“软件中断SGI”,IQ16~ID31分配给“私有中断PPI”,ID32~ID1019分配给“共享中断SPI”。

9、外部中断和事件控制器EXTI

Extended interrupt and event controller简写EXTI,它是ST公司设计的,用来辅助GIC管理STM32MP1的相应中断。

1)、EXTI特性:

支持76个输入事件;

两个CPU内核都支持;

所有事件输入均可让CPU唤醒;

2)、EXTI异步输入事件:

①、可配置事件,其特性如下:

可选择的有效触发边沿;

中断挂起状态寄存器位;

单独的中断和事件生成屏蔽;

支持软件触发;

②、直接事件,其特性如下:

固定上升沿有效触发;

EXTI中无中断挂起状态寄存器位(中断挂起状态由生成事件的外设提供);

单独的中断和事件生成屏蔽;

不支持软件触发;

3)、STM32MP1的中断处理方式:

外设直接产生中断到“GIC中断控制器”,然后由“GIC中断控制器”通知“CPU内核”;GPIO或外设产生中断到“外部中断和事件控制器EXTI”,然后将信号提交给“GIC中断控制器”,再由“GIC中断控制器”通知“CPU内核”;GPIO或外设产生中断到“外部中断和事件控制器EXTI”,然后直接将中断信号提交给“CPU内核”;

4)、GPIO中断

GPIO中断是我们最常用的功能。STM32MP1的所有GPIO都有中断功能,每一组GPIO最多有16个IO,比如:PA0~PA15,因此、每组GPIO就有16个中断,这16个GPIO事件输入对应EXTI0~15,其中 PA0、PB0 等都对应 EXTI0;

5)、设备树绑定信息参考文档

①、“GIC中断控制器”的设备树绑定信息参考文档:

Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml

②、EXTI控制器的设备树绑定信息参考文档:

Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt

10、GIC控制器节点

Table 9. Register boundary addresses

在“stm32mp151.dtsi”文件中,“intc节点”就是“ GIC控制器节点”;

intc: interrupt-controller@a0021000 {

compatible = "arm,cortex-a7-gic";

#interrupt-cells = <3>;

/*使用GIC中断控制器需要用3个cells来描述一个中断*/

interrupt-controller;/*这是一个中断控制器*/

reg = <0xa0021000 0x1000>,

      <0xa0022000 0x2000>;

/*表示address=0xa0021000,length=0x1000,占4096个字节*/

/*GICD的起始地址为0xa0021000,结束地址为0xA0021FFF,合计4KB*/

/*表示address=0xa0022000,length=0x2000,占8192个字节*/

/*GICC的起始地址为0xa0022000,结束地址为0xA0023FFF,合计8KB*/

};

#interrupt-cells = <3>;

/*使用GIC中断控制器需要用3个cells来描述一个中断*/

第1个cells:中断类型,0表示“共享中断SPI”,1表示“私有中断PPI”。第2个cells:中断号

对于“共享中断SPI”来说中断号的范围为32~287(256 个);

对于“私有中断PPI”来说中断号的范围为 16~31,但是该cell描述的中断号是从 0开始。第3个cells:标志,bit[3:0]表示中断触发类型

bit[3:0]=1表示上升沿触发;

bit[3:0]=2表示下降沿触发;

bit[3:0]=4表示高电平触发;

bit[3:0]=8表示低电平触发;

bit[15:8]为“私有中断PPI”的CPU掩码;

11、SPI6节点

SPI6中断号和中断ID,见下表:

第1列的“Num”就是SPI6的中断号:86

第2列“ID”为118,ID = Num + 32

SPI6地址范围,见下表:

Table 117. STM32MP157 interrupt mapping for Cortex®-A7 GIC

SPI6的起始地址为0x5C001000,结束地址为0x5C0013FF,合计1KB

打开stm32mp151.dtsi,找到SPI6节点内容,如下:

spi6: spi@5c001000 {

#address-cells = <1>;

/*定义子节点的reg和ranges的addres长度为32个位*/

#size-cells = <0>;

/*表示子节点的reg和ranges的length占0个位,即没有length*/

compatible = "st,stm32h7-spi";

reg = <0x5c001000 0x400>;

/*表示address=0x5c001000,length=0x400,占1024个字节*/

/*SPI6的起始地址为0x5C001000,结束地址为0x5C0013FF,合计1KB*/

interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;

/*GIC_SPI表示共享中断SPI,86为中断号*/

/*中断触发类型IRQ_TYPE_LEVEL_HIGH*/

clocks = <&scmi0_clk CK_SCMI0_SPI6>;

resets = <&scmi0_reset RST_SCMI0_SPI6>;

dmas = <&mdma1 34 0x0 0x40008 0x0 0x0 0x0>,

       <&mdma1 35 0x0 0x40002 0x0 0x0 0x0>;

dma-names = "rx", "tx";

power-domains = <&pd_core>;

status = "disabled";

};

12、EXTI控制器节点

exti: interrupt-controller@5000d000 {

compatible = "st,stm32mp1-exti", "syscon";

interrupt-controller;

#interrupt-cells = <2>;

/*使用EXTI中断控制器需要用2个cells来描述一个中断*/

/*第1个cells:中断号;第2个cells:中断标志位*/

reg = <0x5000d000 0x400>;

/*表示address=0x5000d000,length=0x400,占1024个字节*/

/*EXTI的起始地址为0x5000d000,结束地址为0x5000D3FF,合计1KB*/

hwlocks = <&hsem 1 1>;

/* exti_pwr is an extra interrupt controller used for

 * EXTI 55 to 60. It's mapped on pwr interrupt

 * controller.

 */

exti_pwr: exti-pwr {

interrupt-controller;

#interrupt-cells = <2>;

interrupt-parent = <&pwr_irq>;

/*指定exti_pwr所有子节点的中断父节点为pwr_irq*/

st,irq-number = <6>;

};

};

#interrupt-cells = <2>;

/*使用EXTI中断控制器需要用2个cells来描述一个中断*/

第1个cells:中断号;第2个cells:中断标志位,bit[3:0]表示中断触发类型

13、GPIOA~GPIOK寄存器地址

Table 9. Register boundary addresses (continued)

pinctrl: pin-controller@50002000 {

#address-cells = <1>;

/*定义子节点的reg和ranges的addres长度为32个位*/

#size-cells = <1>;

/*定义子节点的reg和ranges的length长度为32个位*/

compatible = "st,stm32mp157-pinctrl";

ranges = <0 0x50002000 0xa400>;

        /*子节点寄存器起始地址为0*/

            /*父节点寄存器起始地址为0x50002000*/

            /*寄存器最大偏移地址为0xa400*/

interrupt-parent = <&exti>;

/*指定pinctrl所有子节点的中断父节点为exti*/

/*这样GPIO的中断就和EXTI联系起来*/

st,syscfg = <&exti 0x60 0xff>;

hwlocks = <&hsem 0 1>;

pins-are-numbered;

gpioa: gpio@50002000 {

gpio-controller;

/*指定gpioa节点是一个GPIO控制器*/

#gpio-cells = <2>;

/*定义描述使用一个gpio口需要提供2个指定的参数*/

interrupt-controller;

/* 指定gpioa节点为中断控制器, 其父节点为pinctrl,其中断为exti*/

#interrupt-cells = <2>;

/*interrupts属性第1个cell为某个IO在所处组的编号*/

/*第2个cell表示中断触发方式*/

reg = <0x0 0x400>;

/*表示address=0,length=0x400,占1024个字节*/

/*GPIOA的起始地址为(0x50002000+0),结束地址为(0x50002000+0+0x400-1) */

/*GPIOA的起始地址为0x50002000,结束地址为0x500023FF */

clocks = <&rcc GPIOA>;

st,bank-name = "GPIOA";

status = "disabled";

};

gpiob: gpio@50003000 {

gpio-controller;

/*指定gpiob节点是一个GPIO控制器*/

#gpio-cells = <2>;

/*定义描述使用一个gpio口需要提供2个指定的参数*/

interrupt-controller;

/* 指定gpiob节点为中断控制器, 其父节点为pinctrl,其中断为exti*/

#interrupt-cells = <2>;

/*interrupts属性第1个cell为某个IO在所处组的编号*/

/*第2个cell表示中断触发方式*/

reg = <0x1000 0x400>;

/*表示address=0x1000,length=0x400,占1024个字节*/

/*GPIOB的起始地址为(0x50002000+0x1000)*/

/*GPIOB的结束地址为(0x50002000+0x1000+0x400-1) */

/*GPIOB的起始地址为0x50003000,结束地址为0x500033FF */

clocks = <&rcc GPIOB>;

st,bank-name = "GPIOB";

status = "disabled";

};

gpioc: gpio@50004000 {

gpio-controller;

/*指定gpioc节点是一个GPIO控制器*/

#gpio-cells = <2>;

/*定义描述使用一个gpio口需要提供2个指定的参数*/

interrupt-controller;

/* 指定gpioc节点为中断控制器, 其父节点为pinctrl,其中断为exti*/

#interrupt-cells = <2>;

/*interrupts属性第1个cell为某个IO在所处组的编号*/

/*第2个cell表示中断触发方式*/

reg = <0x2000 0x400>;

/*表示address=0x2000,length=0x400,占1024个字节*/

/*GPIOC的起始地址为(0x50002000+0x2000)*/

/*GPIOC的结束地址为(0x50002000+0x2000+0x400-1) */

/*GPIOC的起始地址为0x50004000,结束地址为0x500043FF */

clocks = <&rcc GPIOC>;

st,bank-name = "GPIOC";

status = "disabled";

};

gpiod: gpio@50005000 {

gpio-controller;

/*指定gpiod节点是一个GPIO控制器*/

#gpio-cells = <2>;

/*定义描述使用一个gpio口需要提供2个指定的参数*/

interrupt-controller;

/* 指定gpiod节点为中断控制器, 其父节点为pinctrl,其中断为exti*/

#interrupt-cells = <2>;

/*interrupts属性第1个cell为某个IO在所处组的编号*/

/*第2个cell表示中断触发方式*/

reg = <0x3000 0x400>;

/*表示address=0x3000,length=0x400,占1024个字节*/

/*GPIOD的起始地址为(0x50002000+0x3000)*/

/*GPIOD的结束地址为(0x50002000+0x3000+0x400-1) */

/*GPIOD的起始地址为0x50005000,结束地址为0x500053FF */

clocks = <&rcc GPIOD>;

st,bank-name = "GPIOD";

status = "disabled";

};

gpioe: gpio@50006000 {

gpio-controller;

/*指定gpioe节点是一个GPIO控制器*/

#gpio-cells = <2>;

interrupt-controller;

/* 指定gpioe节点为中断控制器, 其父节点为pinctrl,其中断为exti*/

#interrupt-cells = <2>;

reg = <0x4000 0x400>;

clocks = <&rcc GPIOE>;

st,bank-name = "GPIOE";

status = "disabled";

};

gpiof: gpio@50007000 {

gpio-controller;

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells = <2>;

reg = <0x5000 0x400>;

clocks = <&rcc GPIOF>;

st,bank-name = "GPIOF";

status = "disabled";

};

gpiog: gpio@50008000 {

gpio-controller;

/*指定gpiogg节点是一个GPIO控制器*/

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells = <2>;

reg = <0x6000 0x400>;

clocks = <&rcc GPIOG>;

st,bank-name = "GPIOG";

status = "disabled";

};

gpioh: gpio@50009000 {

gpio-controller;

/*指定gpioh节点是一个GPIO控制器*/

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells = <2>;

reg = <0x7000 0x400>;

clocks = <&rcc GPIOH>;

st,bank-name = "GPIOH";

status = "disabled";

};

gpioi: gpio@5000a000 {

gpio-controller;

/*指定gpioi节点是一个GPIO控制器*/

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells = <2>;

reg = <0x8000 0x400>;

clocks = <&rcc GPIOI>;

st,bank-name = "GPIOI";

status = "disabled";

};

gpioj: gpio@5000b000 {

gpio-controller;

/*指定gpioj节点是一个GPIO控制器*/

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells = <2>;

reg = <0x9000 0x400>;

clocks = <&rcc GPIOJ>;

st,bank-name = "GPIOJ";

status = "disabled";

};

gpiok: gpio@5000c000 {

gpio-controller;

/*指定gpiok节点是一个GPIO控制器*/

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells = <2>;

reg = <0xa000 0x400>;

clocks = <&rcc GPIOK>;

st,bank-name = "GPIOK";

status = "disabled";

};

};

14、GPIOZ寄存器:

Table 9. Register boundary addresses (continued)

pinctrl_z: pin-controller-z@54004000 {

#address-cells = <1>;

/*定义子节点的reg和ranges的addres长度为32个位*/

#size-cells = <1>;

/*定义子节点的reg和ranges的length长度为32个位*/

compatible = "st,stm32mp157-z-pinctrl";

ranges = <0 0x54004000 0x400>;

/*指定子节点寄存器起始地址为0*/

/*父节点寄存器起始地址为0x50004000*/

/*地址长度为0x400*/

pins-are-numbered;

interrupt-parent = <&exti>;

st,syscfg = <&exti 0x60 0xff>;

hwlocks = <&hsem 0 1>;

gpioz: gpio@54004000 {

gpio-controller;

/*指定gpioz节点是一个GPIO控制器*/

#gpio-cells = <2>;

interrupt-controller;

#interrupt-cells = <2>;

/*interrupts属性第1个cell为某个IO在所处组的编号*/

/*第2个cell表示中断触发方式*/

reg = <0 0x400>;

/*表示address=0,length=0x400,占1024个字节*/

/*GPIOZ的起始地址为(0x50004000+0)*/

/*GPIOZ的结束地址为(0x50004000+0+0x400-1) */

/*GPIOZ的起始地址为0x50004000,结束地址为0x500043FF */

clocks = <&scmi0_clk CK_SCMI0_GPIOZ>;

st,bank-name = "GPIOZ";

st,bank-ioport = <11>;

status = "disabled";

};

};

timer {

compatible = "arm,armv7-timer";

interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,

 <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,

 <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,

 <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;

interrupt-parent = <&intc>;

always-on;

}

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

①、#interrupt-cells,指定中断源的信息cells个数;

②、imnterrupt-controller,表示当前节点为中断控制器;

③、interrupts,指定中断号,触发方式等;

④、imnterrupt-parent,指定父中断,也就是中断控制器;

⑤、interrupts-extended,指定中断控制器、中断号、中断类型和触发方式;

举例:

interrupt-parent = <&gpiog>;/*指定父中断器为&gpiog*/

interrupts = <3 IRQ_TYPE_EDGE_BOTH>;

/*指定中断号为3,中断类型和触发方式为边沿触发*/

使用interrupts-extended可以代替上面的两句:

interrupts-extended = <&gpiog 3 IRQ_TYPE_EDGE_FALLING>;

/*指定中断控制器&gpiog、中断号为3、中断类型和触发方式下降沿触发*/

16、触发类型,位于文件“irq.h”中:

IRQ_TYPE_NONE            - default, unspecified type

IRQ_TYPE_EDGE_RISING - rising edge triggered

IRQ_TYPE_EDGE_FALLING - falling edge triggered

IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered

IRQ_TYPE_LEVEL_HIGH - high level triggered

IRQ_TYPE_LEVEL_LOW - low level triggered

IRQ_LEVEL        - Interrupt is level type

17、打开stm32mpl57f-ev1-a7-examples.dts文件,里面有如下所示代码

test_keys {

compatible = "gpio-keys";

#address-cells = <1>;

/*定义子节点的reg和ranges的addres长度为32个位*/

#size-cells = <0>;

/*表示子节点的reg和ranges的length占0个位,即没有length*/

autorepeat;

status = "okay";

/* gpio needs vdd core in retention for wakeup */

power-domains = <&pd_core_ret>;

button@1 {

label = "PA13";

linux,code = <BTN_1>;

interrupts-extended = <&gpioa 13 IRQ_TYPE_EDGE_FALLING>;

/*指定中断控制器&gpioa、中断号为13、中断类型和触发方式IRQ_TYPE_EDGE_FALLING*/

status = "okay";

wakeup-source;

};

};

18、相关函数:

1)、获取中断号,需要包含文件“of_irq.h

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

dev:为设备节点,Linux内核使用device_node结构体来描述一个节点。

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

返回值:中断号;

获取GPIO中断号,需要包含文件“gpio.h

int gpio_to_irq(unsigned int gpio)

gpio:要获取的GPIO编号;返回值:GPIO对应的中断号

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VSCode 如何同步显示网页在手机或者平板上

首先要确保 ①电脑上安装了VsCode ②VsCode安装插件LiveServer 安装成功之后 连续按住 Alt L 、Alt O 会跳转到对应的html页面上 http://127.0.0.1:5500/....... 是这个开头的 然后打开网络 如果桌面有网上邻居的可以直接点桌面的网上邻居 进来找到WLAN这个…

2024年【道路运输企业安全生产管理人员】最新解析及道路运输企业安全生产管理人员证考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业安全生产管理人员最新解析参考答案及道路运输企业安全生产管理人员考试试题解析是安全生产模拟考试一点通题库老师及道路运输企业安全生产管理人员操作证已考过的学员汇总&#xff0c;相对有效帮助道路运…

InstanceID:zero-shot identity-preserving generation in sconds

1.introduction 个性化图像合成&#xff0c;挑战是生成能够准确保留人物的复杂身份细节的定制图像&#xff0c;这类任务通常称之为控ID型任务&#xff0c;在AI写真&#xff0c;虚拟试穿上都有应用&#xff0c;但是和虚拟试装还是有区别的&#xff0c;但技术路线上其实可以考虑复…

python多进程卡死问题排查

文章目录 背景开发环境启动链路 问题排查pdb调试给文件加共享锁查看进程fdstrace追踪堆栈<br />GDB调试python安装gdb和python-dbgpython-dbg和python版本编译python3.9的dbg文件gdb调试 pytorch多进程卡死问题多进程的fork和spawn模式 其他解决方式使用fastapi自带的bac…

康耐视visionpro-CogDataAnalyTool工具详细说明

CogDataAnalyTool功能: 数据分析工具,统计数据的平均值、标准差、最大值及最小值等。 CogDataAnalyTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogDataAnalyTool ②.添加通道:根据需要添加多个输入通道,可同时统计多个输入数据。 ③.打开结果栏,点击运行可获…

Linux的学习之路:3、基础指令(2)

一、echo指令 这个指令在上篇文章我也用了但是忘了说了&#xff0c;这个指令的大概用法就是把后面跟的文本等输出在显示器上&#xff0c;如下代码所示打印的“Hello Linux” [rootVM-24-9-centos ~]# echo "Hello Linux" Hello Linux二、输出重定向与输入重定向 着…

金融案例:构建高效统一的需求登记与管理方案

在金融行业数字化转型背景下&#xff0c;银行等金融机构面临着业务模式创新与数据应用的深度融合。业务上所需要的不再是单纯的数据&#xff0c;而是数据背后映射的业务趋势洞察&#xff0c;只有和业务相结合转化为业务度量指标&#xff0c;经过数据分析处理呈现为报表进行展示…

MybatisPlus学习总结

MybatisPlus.xmind 一、MybatisPlus快速入门 1.基本介绍 官网: 简介 | MyBatis-Plus MyBatis Plus是一个基于MyBatis的增强工具&#xff0c;它简化了MyBatis的使用&#xff0c;提供了一系列的增强功能&#xff0c;使开发更加方便快捷。 MyBatis Plus的主要特点包括&#xff…

SQL,group by分组后分别计算组内不同值的数量

SQL&#xff0c;group by分组后分别计算组内不同值的数量 如现有一张购物表shopping 先要求小明和小红分别买了多少笔和多少橡皮&#xff0c;形成以下格式 SELECT name,COUNT(*) FROM shopping GROUP BY name;SELECT name AS 姓名,SUM( CASE WHEN cargo 笔 THEN 1 ELSE 0 END)…

PyPy 通过采用即时编译技术,能够显著提升 Python 代码的执行效率。

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 提升 Python 代码性能至接近 C 语言速度&#xff0c;无需修改源代码。遵循 Python 之父吉多・范罗苏姆的建议&#xff1a;“如果你想让你的代码神奇地运行得更快&#xff0c;你应该试试用 PyPy。” Yo…

二维前缀和与二维差分的表示

前缀和&#xff1a; 上述图片是求范围内的总和的图和公式 上述图片是初始化前缀和数组的图和公式 差分&#xff1a; 上图是差分公式 #include<iostream> #include<climits> #include<algorithm> #include<cstring> #include<cstdio> #include&l…

基于视图能力的县域治理视频基座数字化、智慧化解决方案

一、方案背景 县域治理方案是我国地方治理体系的重要组成部分&#xff0c;对于促进县域经济社会发展、维护社会稳定、推进全面深化改革具有重要意义。随着科技的不断进步&#xff0c;视频监管已经成为了现代社会治理的重要手段之一。县域治理视频监管方案是通过视频监控、数据…

SpringBoot国际化配置流程(超详细)

前言 最新第一次在做SpringBoot的国际化&#xff0c;网上搜了很多相关的资料&#xff0c;都是一些简单的使用例子&#xff0c;达不到在实际项目中使用的要求&#xff0c;因此本次将结合查询的资料以及自己的实践整理出SpringBoot配置国际化的流程。 SpringBoot国际化 "i…

关于svn安装报错2503问题的解决方法

问题&#xff1a; SVN在安装时&#xff0c;一直报错&#xff0c;安装失败 The installer has encountered an unexpected error installing this package.The error code is 2503 权限问题&#xff0c;右键以管理员权限运行。如果你也是像我一样&#xff0c;右键没有以管理员…

springboot 使用@profiles.active@多配置文件切换

项目配置文件结构&#xff1a; 主配置文件内容&#xff1a; pom配置文件&#xff1a; <profiles><profile><id>dev</id><properties><profiles.active>dev</profiles.active></properties></profile><profile>…

43 带 fixed 列的 el-table 不兼容于 sortablejs

前言 这是一个基于 sortablejs 来实现的 el-table 的拖拽功能的基础实现 然后 这个过程中遇到的一个比较特殊的问题是, 关于 el-table-column 的 fixed 的属性, 对于 sortablejs 这边来定位目标选择列 影响的一个问题 在基础的用例中, 使用 “.el-table__body-wrapper tbo…

数组的常见算法

数组的常见算法 数值型数组特征值统计 这里的特征值涉及到&#xff1a;平均值、最大值、最小值、总和等 举例1&#xff1a;数组统计&#xff1a;求总和、均值 public class TestArrayElementSum {public static void main(String[] args) {int[] arr {4,5,6,1,9};//求总和、…

污水处理迈入3D可视化新时代:智慧环保触手可及

在科技日新月异的今天&#xff0c;环保事业也迎来了前所未有的发展机遇。污水处理作为环保领域的重要组成部分&#xff0c;其技术的革新与进步&#xff0c;对于保护水资源、维护生态平衡具有重要意义。 传统的污水处理机组往往存在着操作复杂、监控困难等问题&#xff0c;使得污…

2024年【熔化焊接与热切割】报名考试及熔化焊接与热切割模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 熔化焊接与热切割报名考试考前必练&#xff01;安全生产模拟考试一点通每个月更新熔化焊接与热切割模拟试题题目及答案&#xff01;多做几遍&#xff0c;其实通过熔化焊接与热切割作业考试题库很简单。 1、【单选题】…

ORA-04031 错误分析及处理方法

一、问题描述 使用普通用户登录数据库报ORA-04031错误 $ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.1.0 Production on Mon Mar 25 09:14:59 2024Copyright (c) 1982, 2009, Oracle. All rights reserved.Connected to: Oracle Database 11g Enterprise Edition Releas…