在Linux中使用中断没什么要特别注意的,但最近调一个中断很苦恼,特总结一下。程序上将中断配置好了,打印信息上也没有报错,程序上将该引脚配置成双边沿触发中断,通过cat /proc/interrupts一直没有看到中断触发。拿示波器测量该引脚电平,一是低电平,发现硬件上拉不高该引脚电平,断开io引脚与外部电路的连接,测量外部电路的电压正常,怀疑是gpio内部的设置导致的,该引脚本来的功能是uim2卡的引脚(qcom不建议使用该引脚,默认modem端控制),怀疑是该引脚的配置问题,按照qcom提供的方法,将uim2关掉就可以将该引脚当做普通io使用。配置后,还是出现同样的情况。于是,将该引脚强制接到1.8v上,检测到了中断。这样很明显就是硬件的问题了。后来,硬件那边通过修改电阻,该问题得以解决。
可通过如下方法查看中断发生的次数
adb shell cat /proc/interrupts,如
这里的cpu是4核的,但这里只显示了其中的3个核的中断数,这是因为函数的实现是只显示在线的cpu的中断。
int show_interrupts(struct seq_file *p, void *v)
{
static int prec;
unsigned long flags, any_count = 0;
int i = *(loff_t *) v, j;
struct irqaction *action;
struct irq_desc *desc;
if (i > ACTUAL_NR_IRQS)
return 0;
if (i == ACTUAL_NR_IRQS)
return arch_show_interrupts(p, prec);
/* print header and calculate the width of the first column */
if (i == 0) {
for (prec = 3, j = 1000; prec < 10 && j <= nr_irqs; ++prec)
j *= 10;
seq_printf(p, "%*s", prec + 8, "");
for_each_online_cpu(j)
seq_printf(p, "CPU%-8d", j);
seq_putc(p, '\n');
}
irq_lock_sparse();
desc = irq_to_desc(i);
if (!desc)
goto outsparse;
raw_spin_lock_irqsave(&desc->lock, flags);
for_each_online_cpu(j)
any_count |= kstat_irqs_cpu(i, j);
action = desc->action;
if (!action && !any_count)
goto out;
seq_printf(p, "%*d: ", prec, i);
for_each_online_cpu(j)
seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
if (desc->irq_data.chip) {
if (desc->irq_data.chip->irq_print_chip)
desc->irq_data.chip->irq_print_chip(&desc->irq_data, p);
else if (desc->irq_data.chip->name)
seq_printf(p, " %8s", desc->irq_data.chip->name);
else
seq_printf(p, " %8s", "-");
} else {
seq_printf(p, " %8s", "None");
}
if (desc->irq_data.domain)
seq_printf(p, " %*d", prec, (int) desc->irq_data.hwirq);
#ifdef CONFIG_GENERIC_IRQ_SHOW_LEVEL
seq_printf(p, " %-8s", irqd_is_level_type(&desc->irq_data) ? "Level" : "Edge");
#endif
if (desc->name)
seq_printf(p, "-%-8s", desc->name);
if (action) {
seq_printf(p, " %s", action->name);
while ((action = action->next) != NULL)
seq_printf(p, ", %s", action->name);
}
seq_putc(p, '\n');
out:
raw_spin_unlock_irqrestore(&desc->lock, flags);
outsparse:
irq_unlock_sparse();
return 0;
}
假设这里的中断号是157,只需
root@www:/home/w/桌面# adb shell cat /proc/irq/157/spurious
count 65
unhandled 0
last_unhandled 0 ms
可以看出该中断一共发生了65次。
当然,也可以通过脚本自动打印中断的次数(每隔1s打印一次)
test.sh
while true;echo -e "\n";do adb shell cat /proc/irq/中断号/spurious;sleep 1;done
中断的使用方法
#include <linux/irq.h>
#include <linux/interrupt.h>
#define PIN_NAME "pin"
#define PIN_IRQ_NAME "pin_irq"
static irqreturn_t pin_interrupt_hander(int irq, void *dev_id)
{
return IRQ_HANDLED;
}
void request_pin_irq(void)
{
int ret;
int pin=106;
int irq;
ret = gpio_request(pin, PIN_NAME);
if (ret) {
pr_err("Requesting GPIO %d failed!\n",pin);
goto err;
}
irq = gpio_to_irq(pin);
if(!irq)
goto err2;
ret = request_irq(irq, pin_interrupt_hander,flags,PIN_IRQ_NAME, NULL);
if(ret<0)
goto err2;
pr_err("Requesting IRQ %d,IRQ %d succeed!\n",pin,irq);
return;
err2:
gpio_free(pin);
err:
return;
}
flags可选值
/*
* These correspond to the IORESOURCE_IRQ_* defines in
* linux/ioport.h to select the interrupt line behaviour. When
* requesting an interrupt without specifying a IRQF_TRIGGER, the
* setting should be assumed to be "as already configured", which
* may be as per machine or firmware initialisation.
*/
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
* registered first in an shared interrupt is considered for
* performance reasons)
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
* IRQF_NO_SUSPEND - Do not disable this IRQ during suspend. Does not guarantee
* that this interrupt will wake the system from a suspended
* state. See Documentation/power/suspend-and-interrupts.txt
* IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
* IRQF_NO_THREAD - Interrupt cannot be threaded
* IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
* resume time.
* IRQF_COND_SUSPEND - If the IRQ is shared with a NO_SUSPEND user, execute this
* interrupt handler after suspending interrupts. For system
* wakeup devices users need to implement wakeup detection in
* their interrupt handlers.
*/
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define __IRQF_TIMER 0x00000200
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
#define IRQF_NO_SUSPEND 0x00004000
#define IRQF_FORCE_RESUME 0x00008000
#define IRQF_NO_THREAD 0x00010000
#define IRQF_EARLY_RESUME 0x00020000
#define IRQF_COND_SUSPEND 0x00040000
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)
gpio_to_irq函数在不同上会有差异,有些平台上没有实现。需要在dts添加节点信息,通过
irq_of_parse_and_map函数将gpio号转换成虚拟中断号。
总结如下
1.无法申请中断 gpio占用/申请的触发方式不支持
2.休眠后中断无法使用 参考https://blog.csdn.net/mike8825/article/details/98473014
3.无法检测到中断(cat /proc/interrupts看次数) gpio配置/硬件连接等