江门骏科网站建设,怎样找到免费的黄页网站,建设网站审批手续,珠海市网站建设制作设计平台文章目录 一、异常与中断的概念及处理流程1-1 中断的引入1-2 栈(1) CPU实现a ab的过程(2) 进程与线程 1-3 Linux系统对中断处理的演进1-4 Linux 中断系统中的重要数据结构(1) irq_desc 结构体(2) irqaction 结构体(3) irq_data 结构体(4) irq_domain 结构体(5) irq_domain 结构… 文章目录 一、异常与中断的概念及处理流程1-1 中断的引入1-2 栈(1) CPU实现a ab的过程(2) 进程与线程 1-3 Linux系统对中断处理的演进1-4 Linux 中断系统中的重要数据结构(1) irq_desc 结构体(2) irqaction 结构体(3) irq_data 结构体(4) irq_domain 结构体(5) irq_domain 结构体 1-5 设备树中的中断(1) 设备树里的中断控制器和使用中断(2) 在代码中获得中断 1-6 编写使用中断的按键驱动程序 本人学习完韦老师的视频因此来复习巩固写以笔记记之。 韦老师的课比较难第一遍不知道在说什么但是坚持看完一遍再来复习基本上就水到渠成了。 看完视频复习的同学观看最佳 基于
IMX6ULL-PRO 参考视频
Linux快速入门到精通视频 参考资料01_嵌入式Linux应用开发完全手册V5.1_IMX6ULL_Pro开发板.pdf 一、异常与中断的概念及处理流程
1-1 中断的引入
首先中断属于一种异常 中断需要保存现场(各类寄存器)、处理异常、恢复现场。 1、初始化①设置中断源让它可以产生中断②设置中断控制器 (可以屏蔽某个中断优先级)③设置 CPU总开关 (使能中断 ) 2、执行其他程序 正常进行 3、产生中断 比如 按下按键 —中断控制器 —CPU 4、CPU 每执行完一条指令都会检查有无中断 /异常产生 5、CPU发现有中断 /异常产生开始处理。 对于不同的异常跳去不同的地址执行程序。这地址是异常向量表只是一条跳转指令跳去执行某个函数 “ldr pc,_irq”cpu首先跳转到0x18偏移地址然后将_irq的地址传入pc指针再跳转了_irq的地址去执行某个函数 6、某个函数做什么事情 ① 保护现场(各种寄存器的值) ② 处理异常(中断)判断中断源在调用对应的处理函数(如按键、网卡、USB等) ③ 恢复现场
1-2 栈
(1) CPU实现a ab的过程
ARM芯片属于精简指令集计算机 (RISC Reduced Instruction Set Computing)它所用的指令比较简单有如下特点 ① 对内存只有读、写指令 ② 对于数据的运算是在CPU内部实现 ③ 使用RISC指令的CPU复杂度小一点易于设计 实现a ab CPU 运行时先去取得指令再执行指令 ① 把内存a 的值读入CPU 寄存器R0 ② 把内存b 的值读入CPU 寄存器R1 ③ 把R0、R1 累加存入R0 ④ 把R0 的值写入内存a
(2) 进程与线程
在Linux 中资源分配的单位是进程调度的单位是线程。 进程
进程是程序的一次执行过程是操作系统进行资源分配和调度的基本单位。每个进程都拥有独立的内存空间包括代码、数据和堆栈等。每个进程都由操作系统分配唯一的进程标识符PID并且拥有自己的地址空间、文件描述符、系统资源等。进程之间通常是相互独立的彼此不会直接影响到对方的运行状态除非通过特定的IPC机制进行通信。操作系统通过调度算法来管理进程的执行顺序以及为每个进程分配处理器时间。
线程
线程是进程中的实际执行单位一个进程可以包含多个线程它们共享同一个进程的地址空间和系统资源但是每个线程有独立的栈空间。线程之间可以方便地共享数据因为它们处于同一个进程的上下文中。多线程程序可以充分利用多核处理器的并行性能提高程序的执行效率。线程的切换开销通常比进程小因为它们共享了很多资源如内存空间等。
多线程编程读取按键是一个线程播放音乐是另一个线程它们之间可以通过全局变量传递数据示意代码如下
int g_key;
void key_thread_fn()
{while(1){if(g_key ! -1){switch(g_key){case NEXT:select_next_music(); /*在GUI选中下一首歌*/break;}}}
}void music_fn()
{while(1){if(g_key STOP)stop_music();else{send_music();}}
}int main(int argc, char **argv)
{int key;create_threads(key_tehread_fn);create_threads(music_fn);while(1){sleep(10);}return 0;
}1-3 Linux系统对中断处理的演进
Linux系统中断 在Linux 中中断处理程序的执行也可能会影响进程的调度情况例如通过唤醒等待中的进程或者改变进程的优先级等。 中断耗时处理方法 1、分为上下半部分进行执行。上半部处理紧急事情无法被打断下半部可以被打断 2、用内核线程处理中断 系统中有硬件中断也有软件中断。对硬件中断的处理有 2 个原则不能嵌套越快越好。 硬件中断每个硬件中断都有对应的处理函数比如按键中断、网卡中断的处理函数肯定不一样。 简单的认为硬件中断的处理是用数组来实现的数组里存放的是函数指针。当发生A中断时对应的irq_function_A 函数被调用。硬件导致该函数被调用。 软件中断由软件决定对于X号软件中断只需要把它的flag 设置为1就表示发生了该中断。在处理完硬件中断后再去处理软件中断。 在Linux 系统中使用中断可以使用request_irq函数为某个中断irq注册中断处理函数handlerhandler运行中断的上半部分并且触发软中断或者把工作放入工作队列使用线程化来处理中断下半部分。
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
{return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}中断下半部的实现有很多种方法2种主要的tasklet、work queue(工作队列)。 当下半部比较耗时但是能忍受并且它的处理比较简单时可以用tasklet来处理下半部。tasklet是使用软件中断来实现。 在Linux中中断是不会被嵌套执行的但是中断处理程序中可能会触发延迟处理机制导致在下半部处理中处理其他中断请求。
enum
{HI_SOFTIRQ0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,IRQ_POLL_SOFTIRQ,TASKLET_SOFTIRQ, /*通过软中断机制实现的允许将一些需要延迟处理的任务放入任务队列中在适当的时机执行。*/SCHED_SOFTIRQ,HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on thenumbering. Sigh! */RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */NR_SOFTIRQS
};中断要做的事情实在太耗时应该用内核线程来做在中断上半部唤醒内核线程。 略 最新的中断处理方法
request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn,unsigned long flags, const char *name, void *dev);这个函数通常用于注册一个中断处理程序当中断发生时会调用指定的中断处理函数来处理中断而线程中断处理函数则会在一个独立的线程中运行以避免中断处理函数执行时间过长导致系统性能下降。 unsigned int irq: 要请求的中断号。 irq_handler_t handler: 中断处理函数当中断发生时会被调用。 irq_handler_t thread_fn: 线程中断处理函数会在一个独立的线程中执行。 unsigned long flags: 用于指定中断处理的标志比如中断类型等。 const char *name: 中断处理函数的名称。 void *dev: 传递给中断处理函数的设备数据指针。
1-4 Linux 中断系统中的重要数据结构
(1) irq_desc 结构体
在Linux 内核中有一个中断数组对于每一个硬件中断都有一个数组项这个数组就是irq_desc 数组。irq_desc 结构体在include/linux/irqdesc.h 中定义。 每一个irq_desc 数组项中都有一个函数handle_irq还有一个action链表。
(2) irqaction 结构体
irqaction 结构体在include/linux/interrupt.h中定义。 当调用request_irq 、request_threaded_irq 注册中断处理函数时内核就会构造一个 irqaction 结构体。在里面保存 name、dev_id 等最重要的是 handler 、thread_fn 、thread 。 handler是中断处理的上半部函数用来处理紧急的事情。 thread_fn对应一个内核线程thread 当handler 执行完毕Linux内核会唤醒对应的内核线程。在内核线程里会调用 thread_fn函数。 (3) irq_data 结构体
irq_desc的irq_data结构体在 include/linux/irq.h 中定义它就是个中转站里面有irq_chip 指针 irq_domain 指针都是指向别的结构体。
(4) irq_domain 结构体
irq为软件中断号hwirq为硬件中断号。 irq_domain会把本地的hwirq映射为全局的irq。 比如GPIO控制器里有第1号中断UART模块里也有第1号中断这两个第1号中断是不一样的它们属于不同的“域”── irq_domain 设备树的中断通过irq_domain结构体的irq_domain_ops 结构体的一系列函数转换为request_irq(irq, handler)中的irq。 irq_domain结构体在 include/linux/irqdomain.h 中定义
(5) irq_domain 结构体
irq_chip结构体在include/linux/irq.h 中定义 我们在request_irq 后并不需要手工去使能中断原因就是系统调用对应的irq_chip 里的函数帮我们使能了中断。 我们提供的中断处理函数中也不需要执行主芯片相关的清中断操作也是 系统帮我们调用irq_chip 中的相关函数。
1-5 设备树中的中断
(1) 设备树里的中断控制器和使用中断
在设备树中中断控制器节点中必须有一个属性interrupt-controller表明它是中断控制器。还必须有一个属性#interrupt-cells表明引用这个中断控制器的话需要多少个cell。 一个外设的设备树里需要有这连个属性interrupt parentXXXX你要用哪一个中断控制器里的中 断interrupts你要用哪一个中断
#interrupt cells2别的节点要使用这个中断控制器时需要一个cell来表明使用哪一个中断还需要另一个cell来描述中断一般是表明触发类型。 第 2个cell的 bits[3:0] 用来表示中断触发类型 trigger type and level flags) 1 low to high edge triggered 上升沿触发 2 high to low edge triggered 下降沿触发 4 active high level sensitive 高电平触发 8 active low level sensitive 低电平触发
一个interrupts extended 属性就可以既指定 interrupt parent也指定interrupt
interrupts extended intc1 5 1, intc2 1 0;(2) 在代码中获得中断
① platform_device 一个节点能被转换为platform_device 如果它的设备树里指定了中断属 性那么可以从 platform_device中获得中断资源可以使用下列函数获得 IORESOURCE_IRQ 资源即中断号。
/*** platform_get_resource - get a resource for a device* dev: platform device* type: resource type* num: resource index*/
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
{int i;for (i 0; i dev-num_resources; i) {struct resource *r dev-resource[i];if (type resource_type(r) num-- 0)return r;}return NULL;
}② 对于I2C设备、SPI设备 对于I2C 设备节点I2C总线驱动在处理设备树里的I2C子节点时也会处 理其中的中断信息。一个I2C设备会被转换为一个i2c_client 结构体中断号会保存在i2c_client的irq成员里。代码drivers/i2c/i2c core.c 对于SPI设备节点SPI总线驱动在处理设备树里的 SPI 子节点时也会处 理其中的中断信息。一个SPI设备会被转换为一个spi_device结构体中断号会保存在spi_device的的irq成员里代码如下drivers/spi/spi.c
③ 调用of_irq_get 获得中断号 在驱动程序中自行调用 o f_irq_get 函数去解析设备树得到中断号。 ④ 对于GPIO 参考drivers/input/keyboard/gpio_keys.c
gpio keys {compatible gpio keys;pinctrl names default;user {label User Button;gpios gpio5 1 GPIO_ACTIVE_HIGH;gpio-key,wakeup;linux,code KEY_1;};
}使用下面函数获取引脚和flag
button --gpio of_get_gpio_flags(pp, 0, flags);
bdata --gpiod gpio_to_desc(button-gpio);再使用gpiod_to_irq获取中断号
irq gpiod_to_irq(bdata-gpiod);1-6 编写使用中断的按键驱动程序
设备树编写 节点信息 pinctrl子系统(实验结果显示不添加也可以因为这两个GPIO引脚默认工作于GPIO模式)