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

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.rhkb.cn/news/291920.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

左手医生:医疗 AI 企业的云原生提效降本之路

相信这样的经历对很多人来说并不陌生&#xff1a;为了能到更好的医院治病&#xff0c;不惜路途遥远奔波到大城市&#xff1b;或者只是看个小病&#xff0c;也得排上半天长队。这些由于医疗资源分配不均导致的就医问题已是老生长谈。 云计算、人工智能、大数据等技术的发展和融…

阿里云安全产品简介,Web应用防火墙与云防火墙产品各自作用介绍

在阿里云的安全类云产品中&#xff0c;Web应用防火墙与云防火墙是用户比较关注的安全类云产品&#xff0c;二则在作用上并不是完全一样的&#xff0c;Web应用防火墙是一款网站Web应用安全的防护产品&#xff0c;云防火墙是一款公共云环境下的SaaS化防火墙&#xff0c;本文为大家…

海量数据处理项目-账号微服务和流量包数据库表+索引规范(下)

海量数据处理项目-账号微服务和流量包数据库表索引规范&#xff08;下&#xff09; 第2集 账号微服务和流量包数据库表索引规范讲解《下》 简介&#xff1a;账号微服务和流量包数据库表索引规范讲解 账号和流量包的关系&#xff1a;一对多traffic流量包表思考点 海量数据下每…

Spring依赖注入思想分析

Spring 依赖注入思想分析 文章目录 Spring 依赖注入思想分析一、前言二、控制反转&#xff08;Inversion of Control&#xff09;1. 代码依赖初始化问题2. 匿名内部类解决方案3. 创建接口实现类方案4. 问题深入5. 定义父类解决问题1方案6. 控制反转解决问题2方案 三、依赖注入&…

const在指针中的作用以及*p在各种写法中分别代表什么含义

const在指针中起固定的作用&#xff0c;在不同的写法中其效果也有所区别&#xff0c;具体如下&#xff1a; 1、int* const p固定的是指针p指向的地址。 2、int const *p固定的是指针p指向地址中储存的内容。 例&#xff1a; 以上操作在编译器中执行不了&#xff0c;会报错。…

武汉星起航:助力跨境电商新手,打造高质量亚马逊产品评价新策略

在今日全球化与数字化浪潮的推动下&#xff0c;跨境电商已成为推动国际贸易发展的新动力。然而&#xff0c;随着市场竞争的日益激烈&#xff0c;如何让自己的产品在亚马逊平台上脱颖而出&#xff0c;成为了众多跨境电商新手面临的重要问题。武汉星起航电子商务有限公司&#xf…

【AI系列】Python NLTK 库和停用词处理的应用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Codeforces Round 934 (Div. 2) D. Non-Palindromic Substring

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e9, maxm 4e4 5; co…

【ERP原理与应用】用友U8实验

实验一、系统管理与基础设置 实验内容&#xff1a; 一、核算体系的建立 好友软件公司是一家软件制造和系统集成企业&#xff0c;其产品面向国内外市场&#xff0c;自 2019 年 3 月公司开始使用 ERP 软件管理业务。软件操作员有三位&#xff0c;黄红是账套 主管&#xff0c;张…

【C++】string类(常用接口)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;http://t.csdnimg.cn/eCa5z 目录 修改操作 push_back append operator assign insert erase replace c_str find string类非成…

量化交易入门(二十五)什么是RSI,原理和炒股实操

前面我们了解了KDJ&#xff0c;MACD&#xff0c;MTM三个技术指标&#xff0c;也进行了回测&#xff0c;结果有好有坏&#xff0c;今天我们来学习第四个指标RSI。RSI指标全称是相对强弱指标(Relative Strength Index),是通过比较一段时期内的平均收盘涨数和平均收盘跌数来分析市…

论文研读:Transformers Make Strong Encoders for Medical Image Segmentation

论文&#xff1a;TransUNet&#xff1a;Transformers Make Strong Encoders for Medical Image Segmentation 目录 Abstract Introduction Related Works 各种研究试图将自注意机制集成到CNN中。 Transformer Method Transformer as Encoder 图像序列化 Patch Embed…

Net8 ABP VNext完美集成FreeSql、SqlSugar,实现聚合根增删改查,完全去掉EFCore

没有基础的&#xff0c;请参考上一篇 彩蛋到最后一张图里找 参考链接 结果直接上图&#xff0c;没有任何业务代码 启动后&#xff0c;已经有了基本的CRUD功能&#xff0c;还扩展了批量删除&#xff0c;与动态查询 动态查询截图&#xff0c;支持分页&#xff0c;排序 实现原理…

消息队列经典应用场景

笔者心中,消息队列,缓存,分库分表是高并发解决方案三剑客。 在职业生涯中,笔者曾经使用过 ActiveMQ 、RabbitMQ 、Kafka 、RocketMQ 这些知名的消息队列 。 这篇文章,笔者结合自己的真实经历,和大家分享消息队列的七种经典应用场景。 1 异步&解耦 笔者曾经负责某电…

Charles抓包配置代理手机连接

Charles下载地址&#xff1a; Charles_100519.zip官方版下载丨最新版下载丨绿色版下载丨APP下载-123云盘123云盘为您提供Charles_100519.zip最新版正式版官方版绿色版下载,Charles_100519.zip安卓版手机版apk免费下载安装到手机,支持电脑端一键快捷安装https://www.123pan.com…

Scala介绍与环境搭建

Scala环境搭建与介绍 一、Scala环境搭建 1、环境准备与下载 2、验证Scala 3、IDEA新建项目&#xff0c;配置Scala&#xff0c;运行Hello world 二、Scala介绍 1、Scala 简介 2、Scala 概述 一、Scala环境搭建 1、环境准备与下载 JDK1.8 Java Downloads | Oracle 下载需求版本…

酒店能源监测管理系统:实现节能减排与提升管理效率的利器

随着全球能源问题的日益突出和可持续发展理念的深入人心&#xff0c;酒店业也在积极探索节能减排的途径。在这一背景下&#xff0c;酒店能源监测管理系统应运而生&#xff0c;成为了酒店行业提升管理效率、降低能源消耗的重要工具。本文将从多个角度介绍酒店能源监测管理系统的…

Elastic 8.13:Elastic AI 助手中 Amazon Bedrock 的正式发布 (GA) 用于可观测性

作者&#xff1a;来自 Elastic Brian Bergholm 今天&#xff0c;我们很高兴地宣布 Elastic 8.13 的正式发布。 有什么新特性&#xff1f; 8.13 版本的三个最重要的组件包括 Elastic AI 助手中 Amazon Bedrock 支持的正式发布 (general availability - GA)&#xff0c;新的向量…

360奇酷刷机 360刷机助手 QGDP360手机QGDP刷机

360奇酷刷机 360刷机助手 QGDP破解版360手机QGDP刷机 360手机刷机资源下载链接&#xff1a;360rom.github.io 参考&#xff1a;360手机-360刷机360刷机包twrp、root 360奇酷刷机&#xff1a;360高通驱动安装 360手机刷机驱动&#xff1b;手机内置&#xff0c;可通过USB文件传输…

前端学习<二>CSS基础——11-CSS3属性详解(一)

前言 我们在上一篇文章中学习了CSS3的选择器&#xff0c;本文来学一下CSS3的一些属性。 本文主要内容&#xff1a; 文本 盒模型中的 box-sizing 属性 处理兼容性问题&#xff1a;私有前缀 边框 背景属性 渐变 文本 text-shadow&#xff1a;设置文本的阴影 格式举例&…