共计 3168 个字符,预计需要花费 8 分钟才能阅读完成。
0. 导语
学习进入到了下一个阶段,还是以 AD9833 为例,这次学习是向设备申请中断,实现触发,在未来很多场景,比如做用户级的 SPI 传输完毕数据之后,怎么知道从设备要发数据呢,则需要一个 IO 信号通知主设备来读从设备的数据,那么就需要一个外部的 IO 中断信号,所以呢,对于中断的处理十分重要 ,本 demo 实现这样的一个功能 增加一个 GPIO 口,这个 GPIO 口接的是一个按键,通过触发信号,进入中断服务函数,在中断服务函数内改变 AD9833 的波形。以此达到学习实验目的。
之前的代码都是一样的,在这里尽量剥离 AD9833 驱动和 Linux 的代码模板,只写中断相关相关程序。
效果演示视频:https://v.youku.com/v_show/id…
1. 前篇导读:
- 基于 OMAPL138 的字符驱动_GPIO 驱动 AD9833(一)之 ioctl
- 基于 OMAPL138 的 Linux 字符驱动_GPIO 驱动 AD9833(二)之 cdev 与 read、write
- Linux GPIO 键盘驱动开发记录_OMAPL138
原理图:
2. 申请中断准备
- 首先需要两个头文件:
#include <linux/interrupt.h>
#include <linux/irq.h>
- IO 口配置准备
在此次使用中断中,主要用的是 GPIO 口,我们使用电平跳变使之进入到中断处理程序中,所以作为 IO 口,需要配置 IO 口的方向为输入方向。我的 OMAPL138 中给的 IO 口操作使用 GPIO_TO_PIN 这个宏函数进行,在 IO 口操作上每个平台都会给定自己的寻 IO 口的方法,然后使用 linux 通用 gpio_direction_output 进行设定该 GPIO 口为输入方向,如上面的原理图,本 demo 的驱动使用的 GPIO6[1],所以 as follow:gpio_direction_output(GPIO_TO_PIN(6, 1) , 0 );
- 硬件中断号 IRQ
我参考了很多文献,也找了很多书籍,对于硬件中断号码从哪里得到一笔带过,也没有具体说明,不过,经过一下午的努力,我找到了查找中断号码的方法。使用 gpio_to_irq
这个函数方法可以得到 irq。我之前找到手册,看到了手册里面说 GPIO6 BANK 的 IRQ 为 48 号,我尝试加载到内核里面,每次初始化的时候都告诉我中断申请失败,这个号看来不是 datasheet 给定的 48 号,在 Linux 内核里面对于硬件 IRQ 又进行了重新映射。
- 中断事件
在内核中断申请的时候,需要指定中断事件是什么,边沿信号,高电平触发,低电平触发,在 irq.h 里面定义了:
IRQ_TYPE_NONE = 0x00000000,
IRQ_TYPE_EDGE_RISING = 0x00000001,
IRQ_TYPE_EDGE_FALLING = 0x00000002,
IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
IRQ_TYPE_LEVEL_HIGH = 0x00000004,
IRQ_TYPE_LEVEL_LOW = 0x00000008,
IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
IRQ_TYPE_SENSE_MASK = 0x0000000f
我们需要指定这个事件。
- 中断的名字
这个使用#cat /proc/interrupts
查看当前内核中断资源的时候就可以看到你指定注册的名字。
- 中断服务程序
这个我们自己定一个函数就可以,然后一会儿使用中断申请的时候将数据传输进去就好。我们在中断服务函数里面进行按键进行波形切换:
static int key_count = 0;
static irqreturn_t ad9833_press_intHandle(int irq, void *dev_id)
{printk( DRV_NAME "\t press trigger!\n");
if(key_count == 0) {ad9833->set_wave_type( ad9833, SIN);
printk(DRV_NAME "\tSet wave is SIN.\n");
}else if(key_count == 2) {ad9833->set_wave_type( ad9833, TRI);
printk(DRV_NAME "\tSet wave is TRI.\n");
}else if(key_count == 4) {ad9833->set_wave_type( ad9833, SQU);
printk(DRV_NAME "\tSet wave is SQU.\n");
}
key_count ++;
if(key_count >= 5)
key_count = 0;
return IRQ_RETVAL(IRQ_HANDLED);
}
3. 申请中断准备
使用 request_irq 函数就好,我们在初始化函数里面,申请 irq。在申请 irq 前,为了更好的管理中断函数,我们定义了一个结构体,专门进行 irq 配置。
struct gpio_irq_desc {
int irq;
unsigned long flags;
char *name;
} press_dev_desc = {
0,
IRQ_TYPE_EDGE_FALLING,
"sw6_push_button"
};
第一个是 irq,我们在向内核申请中断的时候会使用 gpio_to_irq 进行 irq 的赋值,flags 就是中断事件的触发条件,这里是下降边沿触发,最后一个 name 就是上面注册号中断分配的名字,初始化程序如下:
/*
* interrupt apply
* */
press_dev_desc.irq = gpio_to_irq(ad9833_gpios[3]);
ret = request_irq(press_dev_desc.irq , &ad9833_press_intHandle, press_dev_desc.flags, press_dev_desc.name, (void*)0 );
if(ret) {printk( DRV_NAME "\terror %d: IRQ = %d number failed!\n",ret,gpio_to_irq(ad9833_gpios[3]) );
ret = -EBUSY;
unregister_chrdev_region(devno,1);
for(i = 0; i < ARRAY_SIZE(ad9833_gpios); i++)
gpio_free(ad9833_gpios[i] );
kfree(ad9833);
return ret;
}
printk(DRV_NAME "\tiqr apply ok!!\n");
到此我们就完成了中断配置。
4. 中断释放
使用 freqq_irq 进行释放。这个函数应该放在 exit 驱动的函数里面。free_irq(press_dev_desc.irq, (void*)0 );
源代码下载
链接: https://pan.baidu.com/s/1JgPg… 密码: 5×84
参考文献
[1] 创龙电子科技, [OMAPL138 的 GPIO 输出输入
](https://wenku.baidu.com/view/… 百度文库, 2014 年 5 月 8 日
[2] wh_19910525, [Linux 的 标准 GPIO 及中断 API 函数
](https://blog.csdn.net/wh_1991… CSDN 博客, 2013 年 12 月 25 日
[3] wangcong02345, [Linux 内核 —44. 关于中断号与中断引脚
](https://blog.csdn.net/wangcon… CSDN 博客, 2016 年 7 月 9 日
[4] GreenHand#, [Linux 设备驱动中断机制
](https://www.cnblogs.com/chen-… CNBLOGS, 2016 年 12 月 27 日