网站建设过程中的网站设计怎么做,求百度关键词搜索网站,网站后台登陆素材,名师工作室网站建设 意义1、中断号
中断号又称中断线#xff0c;每个中断都有一个中断号#xff0c;通过中断号即可区分不同的中断。 2、Linux中断API函数
需要包含头文件“#include linux/interrupt.h” 1)、在使用某个中断功能的时候#xff0c;需要执行“申请中断”
int request_irq(…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_SOFTIRQ0, /* 高优先级软中断 */
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
functasklet的处理函数
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*/
ttesttasklet要初始化的tasklet
functesttasklet_functasklet的处理函数testtasklet_func()
data要传递给func函数的参数 request_irq(xxx_irq, test_handler, 0, xxx, xxx_dev);
irqxxx_irq要申请中断的中断号
handlertest_handler中断处理函数test_handler()当中断发生以后就会执行此中断处理函数
fags0中断标志
namexxx中断名字设置以后可以在“/proc/interrupts”文件中看到对应的中断名字
devxxx_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 */ //worktestwork:要调度的工作 //返回值:0 成功其他值 失败 ......}/* 驱动入口函数 */static int __init xxxx_init(void){ ...... INIT_WORK(testwork, testwork_func_t); /* 初始化work */ //_worktestwork表示要初始化的工作 //_func testwork_func_t是工作对应的处理函数 request_irq(xxx_irq,test_handler,0,xxx,xxx_dev); /* 注册中断处理函数 */ //irq xxx_irq要申请中断的中断号 //Handler test_handler中断处理函数当中断发生以后就会执行此中断处理函数 //fags0中断标志 //namexxx中断名字 //devxxx_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-controllera0021000 {
compatible arm,cortex-a7-gic;
#interrupt-cells 3;
/*使用GIC中断控制器需要用3个cells来描述一个中断*/
interrupt-controller;/*这是一个中断控制器*/
reg 0xa0021000 0x1000, 0xa0022000 0x2000;
/*表示address0xa0021000length0x1000,占4096个字节*/
/*GICD的起始地址为0xa0021000结束地址为0xA0021FFF合计4KB*/
/*表示address0xa0022000length0x2000,占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”为118ID Num 32 SPI6地址范围见下表
Table 117. STM32MP157 interrupt mapping for Cortex®-A7 GIC SPI6的起始地址为0x5C001000结束地址为0x5C0013FF合计1KB 打开stm32mp151.dtsi找到SPI6节点内容如下
spi6: spi5c001000 {
#address-cells 1;
/*定义子节点的reg和ranges的addres长度为32个位*/
#size-cells 0;
/*表示子节点的reg和ranges的length占0个位,即没有length*/
compatible st,stm32h7-spi;
reg 0x5c001000 0x400;
/*表示address0x5c001000length0x400,占1024个字节*/
/*SPI6的起始地址为0x5C001000结束地址为0x5C0013FF合计1KB*/
interrupts GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH;
/*GIC_SPI表示共享中断SPI86为中断号*/
/*中断触发类型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-controller5000d000 {
compatible st,stm32mp1-exti, syscon;
interrupt-controller;
#interrupt-cells 2;
/*使用EXTI中断控制器需要用2个cells来描述一个中断*/
/*第1个cells中断号第2个cells中断标志位*/
reg 0x5000d000 0x400;
/*表示address0x5000d000length0x400,占1024个字节*/
/*EXTI的起始地址为0x5000d000结束地址为0x5000D3FF合计1KB*/
hwlocks hsem 1 1; /* exti_pwr is an extra interrupt controller used for * EXTI 55 to 60. Its 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-controller50002000 {
#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: gpio50002000 {
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;
/*表示address0length0x400,占1024个字节*/
/*GPIOA的起始地址为(0x500020000)结束地址为(0x5000200000x400-1) */
/*GPIOA的起始地址为0x50002000结束地址为0x500023FF */
clocks rcc GPIOA;
st,bank-name GPIOA;
status disabled;
}; gpiob: gpio50003000 {
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;
/*表示address0x1000length0x400,占1024个字节*/
/*GPIOB的起始地址为(0x500020000x1000)*/
/*GPIOB的结束地址为(0x500020000x10000x400-1) */
/*GPIOB的起始地址为0x50003000结束地址为0x500033FF */
clocks rcc GPIOB;
st,bank-name GPIOB;
status disabled;
}; gpioc: gpio50004000 {
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;
/*表示address0x2000length0x400,占1024个字节*/
/*GPIOC的起始地址为(0x500020000x2000)*/
/*GPIOC的结束地址为(0x500020000x20000x400-1) */
/*GPIOC的起始地址为0x50004000结束地址为0x500043FF */ clocks rcc GPIOC;
st,bank-name GPIOC;
status disabled;
}; gpiod: gpio50005000 {
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;
/*表示address0x3000length0x400,占1024个字节*/
/*GPIOD的起始地址为(0x500020000x3000)*/
/*GPIOD的结束地址为(0x500020000x30000x400-1) */
/*GPIOD的起始地址为0x50005000结束地址为0x500053FF */ clocks rcc GPIOD;
st,bank-name GPIOD;
status disabled;
}; gpioe: gpio50006000 {
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: gpio50007000 {
gpio-controller;
#gpio-cells 2;
interrupt-controller;
#interrupt-cells 2;
reg 0x5000 0x400;
clocks rcc GPIOF;
st,bank-name GPIOF;
status disabled;
}; gpiog: gpio50008000 {
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: gpio50009000 {
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: gpio5000a000 {
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: gpio5000b000 {
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: gpio5000c000 {
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-z54004000 {
#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: gpio54004000 {
gpio-controller;
/*指定gpioz节点是一个GPIO控制器*/
#gpio-cells 2;
interrupt-controller;
#interrupt-cells 2;
/*interrupts属性第1个cell为某个IO在所处组的编号*/
/*第2个cell表示中断触发方式*/
reg 0 0x400;
/*表示address0length0x400,占1024个字节*/
/*GPIOZ的起始地址为(0x500040000)*/
/*GPIOZ的结束地址为(0x5000400000x400-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; button1 {
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对应的中断号