linux中断驱动
‘壹’ ZYNQ XC7Z020的PL PS中断驱动程序编写测试(linux4.14版本下)
设计目的旨在实现ARM与FPGA的高效交互,特别聚焦于PL与PS间的中断驱动通信。通过使用BRAM存储数据并触发外部PL端中断,通知PS端进行数据读写操作,本文将详细阐述这一过程。程序设计逻辑围绕按键触发中断,通过按键直接连接至PL端,驱动产生异步通知。应用开始向BRAM写入数据,随后阻塞等待读取数据,读取后进行比较。
Vivado平台中,增加BRAM控制器与BRAM配置,以及中断机制的集成。本文重点介绍了在中断系统中使用第11个中断,连接至IRQ_F2P接口。此外,需在Linux设备树中添加相应的中断配置,明确中断号为86,通过计算得出实际中断号为54,并设置为上升沿触发模式。
中断程序设计包含驱动注册、字符驱动注册以及中断注册,考虑到数据交换量可能较大,本文提出使用mmap函数优化数据读写效率。中断处理函数中,首先产生异步通知信号,随后清除阻塞等待队列信号。
应用程序测试阶段,接收中断触发信号后,通过mmap功能将数据写入BRAM,接着执行阻塞等待读取数据,直至唤醒后读取并打印数据。通过 printk 功能,直观展示了中断触发的数据处理流程,具体步骤如下:
中断处理函数 plps_handler 触发。
先前阻塞状态的 plpsirq_read 函数被激活,用于处理异步通知信号。
应用层的异步通知函数 my_signal_fun 被调用。
最终执行完成阻塞读取操作,read(fd, str, 10)完成数据读取。
综上,本文详细介绍了ZYNQ XC7Z020芯片下中断驱动程序的编写与测试过程,特别关注了Linux环境下的设备树配置、中断机制实现,以及程序的高效数据交换逻辑。通过实证分析,验证了设计方法的有效性,为后续FPGA与ARM交互应用提供了参考依据。
‘贰’ 《Linux设备驱动程序》(十六)-中断处理
设备与处理器之间的工作通常来说是异步,设备数据要传递给处理器通常来说有以下几种方法:轮询、等待和中断。
让CPU进行轮询等待总是不能让人满意,所以通常都采用中断的形式,让设备来通知CPU读取数据。
2.6内核的函数参数与现在的参数有所区别,这里都主要介绍概念,具体实现方法需要结合具体的内核版本。
request_irq函数申请中断,返回0表示申请成功,其他返回值表示申请失败,其具体参数解释如下:
flags 掩码可以使用以下几个:
快速和慢速处理例程 :现代内核中基本没有这两个概念了,使用SA_INTERRUPT位后,当中断被执行时,当前处理器的其他中断都将被禁止。通常不要使用SA_INTERRUPT标志位,除非自己明确知道会发生什么。
共享中断 :使用共享中断时,一方面要使用SA_SHIRQ位,另一个是request_irq中的dev_id必须是唯一的,不能为NULL。这个限制的原因是:内核为每个中断维护了一个共享处理例程的列表,例程中的dev_id各不相同,就像设备签名。如果dev_id相同,在卸载的时候引起混淆(卸载了另一个中断),当中断到达时会产生内核OOP消息。
共享中断需要满足以下一个条件才能申请成功:
当不需要使用该中断时,需要使用free_irq释放中断。
通常我们会在模块加载的时候申请安装中断处理例程,但书中建议:在设备第一次打开的时候安装,在设备最后一次关闭的时候卸载。
如果要查看中断触发的次数,可以查看 /proc/interrupts 和 /proc/stat。
书中讲述了如何自动检测中断号,在嵌入式开发中通常都是查看原理图和datasheet来直接确定。
自动检测的原理如下:驱动程序通知设备产生中断,然后查看哪些中断信号线被触发了。Linux提供了以下方法来进行探测:
探测工作耗时较长,建议在模块加载的时候做。
中断处理函数和普通函数其实差不多,唯一的区别是其运行的中断上下文中,在这个上下文中有以下注意事项:
中断处理函数典型用法如下:
中断处理函数的参数和返回值含义如下:
返回值主要有两个:IRQ_NONE和IRQ_HANDLED。
对于中断我们是可以进行开启和关闭的,Linux中提供了以下函数操作单个中断的开关:
该方法可以在所有处理器上禁止或启用中断。
需要注意的是:
如果要关闭当前处理器上所有的中断,则可以调用以下方法:
local_irq_save 会将中断状态保持到flags中,然后禁用处理器上的中断;如果明确知道中断没有在其他地方被禁用,则可以使用local_irq_disable,否则请使用local_irq_save。
locat_irq_restore 会根据上面获取到flags来恢复中断;local_irq_enable 会无条件打开所有中断。
在中断中需要做一些工作,如果工作内容太多,必然导致中断处理所需的时间过长;而中断处理又要求能够尽快完成,这样才不会影响正常的系统调度,这两个之间就产生了矛盾。
现在很多操作系统将中断分为两个部分来处理上面的矛盾:顶半部和底半部。
顶半部就是我们用request_irq来注册的中断处理函数,这个函数要求能够尽快结束,同时在其中调度底半部,让底半部在之后来进行后续的耗时工作。
顶半部就不再说明了,就是上面的中断处理函数,只是要求能够尽快处理完成并返回,不要处理耗时工作。
底半部通常使用tasklet或者工作队列来实现。
tasklet的特点和注意事项:
工作队列的特点和注意事项:
‘叁’ Linux内核:中断、软中断、tasklet
在Linux驱动程序开发中,中断机制是关键组件之一,广泛应用于各类硬件驱动,例如CC1100高频驱动和倒车雷达驱动。通常,我们主要使用中断的顶半部功能,即通过注册中断处理函数和请求中断,以便在中断触发时自动执行特定的代码段。虽然中断的底半部技术同样存在,但因其复杂性和性能考虑,我们通常在驱动程序中较少使用。本文将深入探讨中断的底半部、软中断以及tasklet微线程,以提供更直观的理解。
中断的底半部与顶半部存在显着差异。在顶半部中,当中断触发时,处理器会暂停当前执行的进程,并执行相应的中断处理程序。而底半部在中断处理过程中可能涉及耗时操作,这可能会打断其他中断处理或进程执行,对系统响应造成影响。为避免这种情况,底半部功能被设计为延迟执行,即在中断处理程序的最后将耗时任务放入特定队列中,由专门的线程或机制在适当时间执行。此设计允许系统在中断处理过程中保持高效响应,同时确保耗时操作不会影响整体性能。
软中断是底半部实现的一种重要方式,它允许内核在中断上下文外执行延迟任务。软中断通过do_softirq函数进行调度,允许多个CPU同时执行,从而提高系统处理效率。通过注册特定类型的软中断和相应的处理函数,开发者可以将任务放入软中断队列中,由系统在合适时机执行。软中断机制简化了在中断上下文外执行任务的流程,但要求任务实现遵循线程安全和可重入原则,以避免数据竞争。
tasklet微线程进一步优化了软中断机制,针对特定场景提供更灵活的线程管理。与软中断类似,tasklet允许在中断处理之外执行延迟任务,但其通过为每个CPU提供独立的tasklet队列,以及在队列内部执行任务,进一步减少了任务执行过程中的互斥需求。这不仅降低了对任务执行函数的并发要求,还提高了整体系统性能。开发者通过调用tasklet_schele函数将任务加入队列,并通过tasklet_init函数创建tasklet结构体对象,以实现在驱动程序中的高效、并发执行。
综合而言,中断的底半部、软中断和tasklet微线程为Linux内核提供了多种实现延迟任务执行的机制。这些技术通过优化任务执行方式,显着提高了系统在处理中断和复杂任务时的响应能力和性能,是驱动程序开发中不可或缺的工具。理解并合理运用这些机制,能够有效提升驱动程序的可靠性和效率。
‘肆’ linux中,中断注册和中断服务程序只能写在驱动层还是能写在应用层
我也不完全理解,但是比你知道的多点。
Linux中,分内核态和用户态。
你写的所有的驱动,都是出于内核态->可以直接使用内核相关资源;
应用层,都是用户态->无法直接操作底层的东西 -> 想要操作,比如获得权限,切换到内核态,然后才能操作。
你这里的需求,我的理解是:
对应你这句
“在中断服务程序中操作另一个外设”
不知道你的目的和打算用的手段是啥
一般的,ISR中,操作别的设备,常见的是:
设置对应的(比如该硬件本身,或者别的设备B的)寄存器的对应的位,以便通知其某种事情发送或状态变化了。
然后设备B会:
要么是由于(被修改了寄存器而)发生了中断,然后可以接着处理其所要做的事情;
要么是一直轮训,检测对应的某种资源释放变化,比如上面被改的寄存器的对应的位,发现变化了,再去调用你的函数,做对应的处理。
注意:
中断,不论是哪个设备的中断,都不应该占用(CPU)太长时间
-> 导致别的中断或服务无法及时运行
仅供参考。
‘伍’ 怎样看linux串口驱动中断 dma
查询就是一直在查看标志位,是不是被置1了,如果是就去读或者其他操作
中断就是平时不用管,一单有东西来就会进入中断服务程序,你再去操作
DMA是你初始化的时候把串口地址和需要传输的地址写上,来东西他就自己把数据存到你初始化的地址上