You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:34:51