《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记
文章目录
- 中断基础
- 中断
- 中断上下文
- 中断号
- 中断源类型
- 内核中断 API
- 获取中断号
- 中断申请
- 释放中断
- 禁止中断
- 使能中断
- 实验程序
中断基础
中断
CPU 在正常运行期间,由外部或者内部引起的事件,让 CPU 停下当前正在运行的程序,转而去执行触发他中断的对应程序,处理完中断对应程序后再回来继续运行。这就是中断。
中断上下文
虽然中断可以提高 CPU 的运行效率,但中断会打断内核进程的正常调度和运行,所以为了保证系统的实时性,中断服务程序必须足够短。但实际应用中,我们可能需要在中断后执行一些比较耗时的任务,为此,内核提出了中断上下文的概念,即把中断服务程序分为两部分,中断上文和中断下文。
中断上文用来完成比较紧急且耗时短的任务,中断下文用来处理比较耗时的任务。
中断号
中断号(IRQ number)也叫软中断号,在 Linux 系统中是唯一的,也是我们在中断编程中需要用到的中断号。
中断源类型
- SGI(Software-generated interrupt):范围0 - 15,软件触发的中断,一般用于核间通讯
- PPI(Private peripheral interrupt ): 范围16 - 31,私有外设中断,只对指定的core有效
- SPI(Shared peripheral interrupt):范围32 - 1019,共享中断,不限定特定的core
内核中断 API
获取中断号
操作内核中断,我们需要使用到中断号,gpio_to_irq() 可以将 GPIO 编号转换为中断号,该函数原型如下:
int gpio_to_irq(unsigned gpio);
中断申请
内核中 request_irq() 函数被用来申请中断,该函数原型如下:
#include <linux/interrupt.h>
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
函数功能:向内核注册一个中断服务函数(当 irq 对应的中断发生时,会执行 handler 指向的中断服务函数)。
中断服务函数原型为:typedef irqreturn_t (*irq_handler_t)(int, void *)
函数参数
- irq 中断号(在内核中是唯一的)
- handler 中断服务函数指针
- flags 中断标志,具体内容由中断源决定,如果中断源为外部中断,则存在上升沿和下降沿两种标志
- name 中断名(会在 /proc/interrupts 下体现)
- dev 中断服务函数的参数
flags 可取值可以在 linux/interrupt.h 头文件查看,本文将用到标志有 IRQF_TRIGGER_RISING 和 IRQF_TRIGGER_FALLING。
当 flags 指定为共享中断(IRQF_SHARED)时,dev 参数不能为 NULL,并且不同的共享中断 dev 参数不能相同,因为共享中断可以注册多个中断函数,中断号相同,所以需要根据 dev 来区分不同的中断函数。
返回值
返回 0 表示申请成功,失败返回负值。
释放中断
free_irq() 用来注销中断,函数原型为 :
void free_irq(unsigned int irq, void *dev_id);
参数与 request_irq() 的部分参数意义相同,这里不再赘述。
【注意】在调用 free_irq() 时,为了防止此时中断被触发,需要先禁止中断。
禁止中断
禁止内核中断的函数包括:
void disable_irq_nosync(unsigned int irq);
void disable_irq(unsigned int irq);
disable_irq() 在调用后不会立刻返回,需要等待中断程序执行完才返回,所以在中断服务函数中不能调用该函数。
disable_irq_nosync() 则会在调用后立刻返回。
使能中断
函数原型:
void enable_irq(unsigned int irq);
实验程序
迅为教学视频里用触摸屏所用的中断 IO 作为外部中断实验的 IO,在实验之前,需要将触摸屏驱动关闭。具体操作步骤为:进入内核根目录,输入 make ARCH=arm64 menucofig
,关闭 Device Driver -> Input device support -> Touchscreens 下的 FT5x06 触摸屏驱动:
修改完成后,用 .config 替换平台默认的配置文件(参考 rk3568 相关文档),然后编译内核,最后将内核烧录到开发板上。
我们要操作触摸屏中断脚,首先要知道中断脚的 IO,根据原理图得知其 IO 为 GPIO3_A5,
在驱动中,gpio 都是通过编号表示的,那么 GPIO3_A5 的编号是多少呢?这个问题在之前的点灯实验中已经提及:
到网上找资料发现,RK3568 的 GPIO 编号很好计算,以 GPIO0_B7 为例,其 bank 为 0,group 为 1(A 为 0,以此类推),X 为 7,那么它的 pin 编号为 bank * 32 + group * 8 + X = 15,直白点讲,GPIO 的编号就是它在所有 IO 中所属序号
根据上面的公式, GPIO3_A5 的编号为 3 * 32 + 0 * 8 + 5 = 101
实验源码
下面是一个简单的中断驱动代码(参考原文/视频):
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
int irq;
//中断服务函数
irqreturn_t my_interrupt(int irq, void *args)
{
printk("my interrupt handler.\n");
return IRQ_RETVAL(IRQ_HANDLED);
}
static int interrupt_irq_init(void)
{
int ret = 0;
// 获取中断号
irq = gpio_to_irq(101);
printk("irq is %d\n", irq);
// 申请中断
ret = request_irq(irq, my_interrupt, IRQF_TRIGGER_RISING, "inttrupt_test", NULL);
if(ret < 0)
{
printk("request irq error.\n");
return 0;
}
return 0;
}
static void interrupt_irq_exit(void)
{
printk("interrupt irq exit.\n");
// 注销中断
free_irq(irq, NULL);
}
module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaohui");
Makefile 文件:
#目标文件,与驱动源文件同名,编译成模块
obj-m := interrupt.o
#架构平台选择
export ARCH=arm64
#编译器选择
export CROSS_COMPILE=aarch64-linux-gnu-
#内核目录
KDIR := /home/topeet/Linux/rk356x_linux/kernel/
#编译模块
all:
make -C $(KDIR) M=$(shell pwd) modules
#清除编译文件
clean:
make -C $(KDIR) M=$(shell pwd) clean
实验结果
触碰触摸屏时,中断触发,运行中断服务函数(打印数据到串口终端)。