在前两章中实现了模块的驱动框架,实现了轮询监测模块的输出信号。实际应用中,需要同时执行多个任务,不可能让一个线程持续地占用CPU。这就需要使用中断模式,在没有有效信号时,让线程进入休眠,从而节省CPU。
下面就在前两节的基础上,添加中断的框架。
(1)在设备树中添加中断节点。
想使用中断的话,需要在设备树中添加对中断的硬件信息的描述,GPIO的中断属于SPI中断,也就是共享外设中断,如果CPU有多核的话,所有的核都可以接受到这种中断的信息。
中断节点信息分为两个部分,(1)父中断标号(2)自身信息。代码如下:
interrupt-parent=<&gpio4>;
interrupts=<18 IRQ_TYPE_EDGE_FALLING>;
信号引脚是GPIO4_19,使用18号中断。
(2)在驱动程序中添加中断框架。
驱动中的中断框架有三个部分。
第一,中断注册部分
在probe函数中添加中断信息的提取,并向系统注册中断。
//提取设备树的中断信息
irq=gpiod_to_irq(sr501_gpio);
//注册中断函数
request_irq(irq,sr501_irq,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,"sr501_irq",NULL);
加载模块以后,可以在/proc/interrupts中看到名为sr501_irq的节点
第二,中断函数部分
注册中断的时候,需要带入一个中断处理函数,在这个函数中,快速提取需要的硬件信息,比如,读取P口电平。
irqreturn_t sr501_irq(int irq, void *dev_id)
{
return IRQ_HANDLED;
}
第三,添加休眠唤醒机制。
在没有有效信息的时候,使用休眠机制可以让当前线程让出CPU的使用权。这个机制由两个部分组成:
(1)在read函数中,添加睡眠函数
static ssize_t sr501_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
int len = (size < 4)? size : 4;
//如果没有有效的数据,线程在此处休眠,知道sr501_flag不为零。
wait_event_interruptible(sr501_wait, sr501_flag);
copy_to_user(buf, &sr501_flag,len);
printk("%s %s line %d,val=%x\n", __FILE__, __FUNCTION__, __LINE__,sr501_flag);
sr501_flag=0;
return len;
}
(2)中断函数中添加睡眠唤醒
irqreturn_t sr501_irq(int irq, void *dev_id)
{
int val;
sr501_flag=0x80;
val=gpiod_get_value(sr501_gpio);
sr501_flag|=val;
//数据有效,唤醒睡眠的线程
wake_up(&sr501_wait);
printk("%s line %d,%d\n",__FUNCTION__, __LINE__,sr501_flag);
return IRQ_HANDLED;
}
模块编译完成,并加载以后,就会得到如下的LOG:
完整的驱动及测试代码链接如下,请自行下载。