学习进入到了下一个阶段,仍是以AD9833为例,此次学习是向设备申请中断,实现触发,在将来不少场景,好比作用户级的SPI传输完毕数据以后,怎么知道从设备要发数据呢,则须要一个IO信号通知主设备来读从设备的数据,那么就须要一个外部的IO中断信号,因此呢,对于中断的处理十分重要,本demo实现这样的一个功能增长一个GPIO口,这个GPIO口接的是一个按键,经过触发信号,进入中断服务函数,在中断服务函数内改变AD9833的波形。以此达到学习实验目的。html
以前的代码都是同样的,在这里尽可能剥离AD9833驱动和Linux的代码模板,只写中断相关相关程序。linux
效果演示视频: https://v.youku.com/v_show/id...ios
原理图:app
#include <linux/interrupt.h>
#include <linux/irq.h>
在这次使用中断中,主要用的是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 );
函数
我参考了不少文献,也找了不少书籍,对于硬件中断号码从哪里获得一笔带过,也没有具体说明,不过,通过一下午的努力,我找到了查找中断号码的方法。使用gpio_to_irq
这个函数方法能够获得irq。我以前找到手册,看到了手册里面说GPIO6 BANK的IRQ为48号,我尝试加载到内核里面,每次初始化的时候都告诉我中断申请失败,这个号看来不是datasheet给定的48号,在Linux内核里面对于硬件IRQ又进行了从新映射。学习
在内核中断申请的时候,须要指定中断事件是什么,边沿信号,高电平触发,低电平触发,在irq.h里面定义了:spa
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
咱们须要指定这个事件。.net
这个使用#cat /proc/interrupts
查看当前内核中断资源的时候就能够看到你指定注册的名字。3d
这个咱们本身定一个函数就能够,而后一下子使用中断申请的时候将数据传输进去就好。咱们在中断服务函数里面进行按键进行波形切换:code
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 ); }
使用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" );
到此咱们就完成了中断配置。
使用freqq_irq进行释放。这个函数应该放在exit驱动的函数里面。free_irq( press_dev_desc.irq, (void*)0 );
连接: https://pan.baidu.com/s/1JgPg... 密码: 5x84
[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日