GPIO中断线特定电平等待实现及引脚状态读取技术问询
配置为IRQ的GPIO状态读取与电平等待实现方案
一、读取IRQ模式GPIO当前状态:可行且可通过寄存器操作实现
针对全志H3平台,由于PIO控制器支持动态修改引脚功能配置,我们可以通过临时切换引脚到输入模式读取电平,再恢复IRQ功能的方式实现,步骤如下:
- 读取并保存当前PIO引脚的功能配置寄存器值(避免修改后无法恢复原IRQ配置)
- 修改PIO功能选择寄存器,将该引脚切换为通用输入模式(参考H3数据手册第322页的寄存器位定义)
- 读取GPIO输入状态寄存器对应位,获取当前电平值
- 写入之前保存的配置值,将引脚恢复为IRQ功能模式
注意:操作寄存器时必须使用ioremap映射后的内核虚拟地址,且用自旋锁保护寄存器操作的原子性,防止并发修改引发异常。
二、阻塞等待特定电平的逻辑实现
结合Linux内核的中断+等待队列机制,可以优雅实现你提出的等待逻辑,无需频繁申请/释放中断,具体实现如下:
核心代码实现
#include <linux/module.h> #include <linux/interrupt.h> #include <linux/gpio.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/spinlock.h> #define TARGET_GPIO_PIN 10 // 替换为实际使用的GPIO引脚号 static int irq_num; static wait_queue_head_t wait_q; static bool target_level_reached = false; static DEFINE_SPINLOCK(gpio_lock); static bool wait_for_low = false; // 可根据用户请求动态设置等待高/低电平 // 中断处理函数 static irqreturn_t gpio_level_irq_handler(int irq, void *dev_id) { unsigned long flags; int current_level = gpio_get_value(TARGET_GPIO_PIN); spin_lock_irqsave(&gpio_lock, flags); // 判断当前电平是否符合等待条件 if ((wait_for_low && !current_level) || (!wait_for_low && current_level)) { target_level_reached = true; wake_up_interruptible(&wait_q); } spin_unlock_irqrestore(&gpio_lock, flags); return IRQ_HANDLED; } // 字符设备read接口实现 static ssize_t gpio_wait_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { unsigned long flags; int ret; int current_level; // 第一步:先检查当前电平是否已满足条件 spin_lock_irqsave(&gpio_lock, flags); current_level = gpio_get_value(TARGET_GPIO_PIN); if ((wait_for_low && !current_level) || (!wait_for_low && current_level)) { target_level_reached = true; spin_unlock_irqrestore(&gpio_lock, flags); goto output_result; } target_level_reached = false; spin_unlock_irqrestore(&gpio_lock, flags); // 第二步:启用中断,再次检查电平(防止启用过程中状态变化) enable_irq(irq_num); spin_lock_irqsave(&gpio_lock, flags); current_level = gpio_get_value(TARGET_GPIO_PIN); if ((wait_for_low && !current_level) || (!wait_for_low && current_level)) { target_level_reached = true; spin_unlock_irqrestore(&gpio_lock, flags); disable_irq(irq_num); goto output_result; } spin_unlock_irqrestore(&gpio_lock, flags); // 第三步:阻塞等待中断触发 ret = wait_event_interruptible(wait_q, target_level_reached); if (ret == -ERESTARTSYS) { disable_irq(irq_num); return ret; } // 第四步:禁用中断 disable_irq(irq_num); output_result: // 将电平结果返回给用户空间('0'代表低电平,'1'代表高电平) char level_str = wait_for_low ? '0' : '1'; if (copy_to_user(buf, &level_str, 1)) { return -EFAULT; } return 1; } // 字符设备操作集 static const struct file_operations gpio_fops = { .owner = THIS_MODULE, .read = gpio_wait_read, // 可添加ioctl接口用于动态设置wait_for_low参数 }; static int __init gpio_wait_driver_init(void) { int ret; init_waitqueue_head(&wait_q); // 请求GPIO引脚 ret = gpio_request(TARGET_GPIO_PIN, "gpio-level-wait"); if (ret) { pr_err("GPIO %d request failed\n", TARGET_GPIO_PIN); return ret; } // 配置GPIO为输入模式(IRQ功能依赖此基础配置) gpio_direction_input(TARGET_GPIO_PIN); // 获取GPIO对应的IRQ号 irq_num = gpio_to_irq(TARGET_GPIO_PIN); if (irq_num < 0) { pr_err("GPIO %d to IRQ failed\n", TARGET_GPIO_PIN); gpio_free(TARGET_GPIO_PIN); return irq_num; } // 申请中断,初始状态禁用(按需启用) ret = request_irq(irq_num, gpio_level_irq_handler, IRQF_TRIGGER_LOW | IRQF_TRIGGER_HIGH, "gpio-level-wait", NULL); if (ret) { pr_err("IRQ %d request failed\n", irq_num); gpio_free(TARGET_GPIO_PIN); return ret; } disable_irq(irq_num); // 注册字符设备(省略设备号分配、class创建等通用步骤,可根据需求补充) // ... pr_info("GPIO level wait driver loaded\n"); return 0; } static void __exit gpio_wait_driver_exit(void) { free_irq(irq_num, NULL); gpio_free(TARGET_GPIO_PIN); // 注销字符设备等清理操作 // ... pr_info("GPIO level wait driver unloaded\n"); } module_init(gpio_wait_driver_init); module_exit(gpio_wait_driver_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Full H3 GPIO IRQ Level Wait Driver");
关键细节说明
- 中断触发类型:由于IRQ_TYPE标志无法动态修改,这里直接申请了高低电平触发的中断,在中断处理函数内判断是否符合当前等待条件;也可以操作全志H3的PIO中断配置寄存器,动态修改触发类型,减少不必要的中断触发。
- 竞态保护:用自旋锁保护
target_level_reached变量和GPIO电平读取操作,确保多线程/中断上下文访问的原子性。 - 动态等待条件:可通过添加
ioctl接口,让用户空间动态设置wait_for_low参数,实现灵活切换等待高/低电平的需求。
内容的提问来源于stack exchange,提问作者Anonymous
相关产品推荐
相关产品推荐

