Linux GPIO子系统

Linux GPIO子系统是内核提供的一套标准化接口,用于统一管理GPIO(通用输入输出)引脚。相比直接操作物理内存(如/dev/mem),它更安全、可移植且支持高级功能(如中断、去抖动)。以下是详细说明:


一、核心概念

  1. GPIO控制器驱动
    • 内核通过GPIO控制器驱动管理硬件,如pinctrl子系统负责引脚复用和配置。
    • 控制器驱动需实现gpiolib接口,暴露GPIO编号和操作函数。
  2. GPIO编号
    • 逻辑编号(如GPIO17)由控制器驱动分配,可能与物理引脚号不同。
    • 使用gpiochip前缀表示控制器(如gpiochip0)。

二、用户空间操作

1. 通过Sysfs接口(传统方法)

# 导出GPIO到用户空间
echo 17 > /sys/class/gpio/export

# 设置方向(in/out)
echo out > /sys/class/gpio/gpio17/direction

# 读写值(0/1)
echo 1 > /sys/class/gpio/gpio17/value
cat /sys/class/gpio/gpio17/value

# 取消导出
echo 17 > /sys/class/gpio/unexport
2. 使用libgpiod(推荐)
  • 安装库
    sudo apt-get install libgpiod-dev
  • C语言示例

    #include <gpiod.h>
    
    int main() {
        struct gpiod_chip *chip = gpiod_chip_open_by_name("gpiochip0");
        struct gpiod_line *line = gpiod_chip_get_line(chip, 17);
        gpiod_line_request_output(line, "my_gpio", 0);  // 初始输出0
    
        // 设置为高电平
        gpiod_line_set_value(line, 1);
    
        gpiod_line_release(line);
        gpiod_chip_close(chip);
        return 0;
    }

三、内核空间操作

1. 内核模块示例

#include <linux/gpio.h>
#include <linux/module.h>

static int gpio_num = 17;
module_param(gpio_num, int, 0644);

static int __init my_gpio_init(void) {
    int ret;

    // 申请GPIO
    ret = gpio_request(gpio_num, "my_gpio");
    if (ret) {
        printk(KERN_ERR "Failed to request GPIO%d\n", gpio_num);
        return ret;
    }

    // 设置为输出模式
    ret = gpio_direction_output(gpio_num, 0);
    if (ret) {
        printk(KERN_ERR "Failed to set direction\n");
        gpio_free(gpio_num);
        return ret;
    }

    // 输出高电平
    gpio_set_value(gpio_num, 1);

    return 0;
}

static void __exit my_gpio_exit(void) {
    gpio_set_value(gpio_num, 0);  // 设置为低电平
    gpio_free(gpio_num);          // 释放GPIO
}

module_init(my_gpio_init);
module_exit(my_gpio_exit);
MODULE_LICENSE("GPL");
2. 中断处理

static irqreturn_t gpio_isr(int irq, void *dev_id) {
    printk(KERN_INFO "GPIO%d interrupt triggered\n", gpio_num);
    return IRQ_HANDLED;
}

// 在初始化函数中:
ret = gpio_to_irq(gpio_num);  // 获取中断号
ret = request_irq(ret, gpio_isr, IRQF_TRIGGER_RISING, "my_gpio_irq", NULL);

四、关键API

函数/宏作用
gpio_request()申请GPIO
gpio_free()释放GPIO
gpio_direction_input()设置为输入模式
gpio_direction_output()设置为输出模式
gpio_get_value()读取输入值
gpio_set_value()写入输出值
gpio_to_irq()获取GPIO对应的中断号

五、优势与注意事项

  1. 优势
    • 安全性:避免直接操作物理地址导致的系统崩溃。
    • 可移植性:逻辑GPIO编号由控制器驱动抽象,代码无需修改即可适配不同硬件。
    • 高级功能:支持中断、去抖动、驱动强度配置等。
  2. 注意事项
    • 资源释放:必须调用gpio_free()释放GPIO,避免泄漏。
    • 并发访问:在中断上下文中避免使用可能睡眠的函数(如printk)。
    • 权限管理:用户空间操作需root权限或配置udev规则。

六、调试工具

  • gpiodetect:列出所有GPIO控制器。
  • gpioinfo:查看GPIO详细信息(方向、值、中断等)。
  • gpioset:快速设置GPIO值(需libgpiod工具)。

通过Linux GPIO子系统,开发者可以更高效、安全地操作GPIO,同时利用内核的抽象层实现跨平台兼容性。对于新项目,建议优先使用libgpiod库进行用户空间开发,内核模块则使用gpiolib API。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值