linux 配置GPIO输出模式,输出电平自动修改为低电平0

问题描述:

~# cat /sys/class/gpio/gpio115/direction
out
~# cat /sys/class/gpio/gpio115/value
1
~# echo out > /sys/class/gpio/gpio115/direction
~# cat /sys/class/gpio/gpio115/value
0

原本GPIO为输出模式,且输出高电平1。

再次配置输出模式后,输出自动变成低电平0。
什么情况下这样使用,其实是测试应用程序,运行过程中重启,重复执行了初始化动作,导致此问题发生。

问题原因:

这是一个非常经典的问题,而且不是 bug,是 Linux GPIO sysfs 驱动的默认行为。你遇到的这个现象在几乎所有使用 sysfs 接口的平台上都会出现,并且有明确的底层原因。

根本原因:写 direction 时,驱动会同时复位输出电平

当你执行 echo out > /sys/class/gpio/gpio115/direction 时,内核 GPIO 驱动在将引脚切换为输出模式的同时,会将输出寄存器的值初始化为 0(低电平)​。这个行为是由 GPIO 驱动框架的通用代码决定的,而不是某个特定芯片的问题。

关键代码路径在 Linux 内核的 drivers/gpio/gpiolib.c 中:

int gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
    // ...
    // 很多驱动在 direction_output 回调中,会直接写芯片的 GPIO 输出寄存器
    // 而 value 参数在这里被传入了 0
    // ...
}

当用户空间通过 sysfs 写入 direction = "out" 时,内核 gpiolib 核心代码会调用芯片驱动的 .direction_output() 回调,但传入的默认初始值就是 0。看一下 sysfs 的处理逻辑:

// drivers/gpio/gpiolib-sysfs.c
static ssize_t direction_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t size)
{
    // ...
    if (sysfs_streq(buf, "out")) {
        // 默认调 direction_output(chip, offset, 0)
        status = gpiod_direction_output(gpio, 0);
    }
    // ...
}

关键行gpiod_direction_output(gpio, 0),第二个参数 0 就是初始输出值。所以每次你设 out,内核都会把输出寄存器清零。

为什么内核要这样设计?

这是因为 GPIO 硬件层面,方向寄存器和输出寄存器可能是两个独立的寄存器,也可能是同一个寄存器中的不同位域。但不管哪种情况:

  1. 在将引脚从输入(高阻/浮空)切换到输出时,输出寄存器里的值是不可预测的——可能是之前遗留的值,也可能是上电随机值
  2. 如果输出寄存器里恰好是 1,切换方向后引脚立即输出高电平,可能造成被控设备意外动作
  3. 所以内核选择了一种安全的保守策略:每次配置为输出时,先把输出寄存器清 0,确保不会意外驱动负载

如果你需要输出为 1,正确的做法是两步

# 方法一:先设方向,再设值
echo out > /sys/class/gpio/gpio115/direction   # 此时输出为 0
echo 1   > /sys/class/gpio/gpio115/value        # 再拉高

# 方法二:利用内核的 "low" / "high" 扩展语法(部分驱动支持)
echo high > /sys/class/gpio/gpio115/direction  # 以高电平输出模式导出

echo high > direction 这个用法在 sysfs 文档中有定义,但取决于具体驱动是否实现了。很多驱动只实现了 inout,没有处理 lowhigh。你可以试一下,如果不行就用方法一。

验证:恢复后续操作不受影响

一旦你把方向设好、值也写好了,之后只要不再次写 direction,只写 value 就不会被复位:

echo out > /sys/class/gpio/gpio115/direction   # 输出变成 0
echo 1   > /sys/class/gpio/gpio115/value        # 拉高,没问题
echo 0   > /sys/class/gpio/gpio115/value        # 拉低,也没问题
echo 1   > /sys/class/gpio/gpio115/value        # 再次拉高,仍然正常

如果确实需要在设方向的同时保持输出状态

如果你在代码中控制 GPIO(比如在 C 应用程序或内核驱动中),可以直接调用 gpiod_direction_output(gpio_desc, initial_value),其中 initial_value 可以是 1:

// 这样直接以高电平输出模式初始化
gpiod_direction_output(gpio_desc, 1);

但 sysfs 接口没有提供这个参数,所以只能分两步走。

总结

你的操作 驱动实际行为 原因
echo out > direction 内核调用 direction_output(..., 0) 默认初始值 0,安全策略
输出被清 0 输出寄存器被写 0 不是原有值被保留,而是被覆写
解决方法 再写一次 echo 1 > value 两步设成你需要的电平

结论:​ 这不是你的 ARM 平台特有的问题,而是 Linux GPIO sysfs 内核接口的通用行为。每次写 direction = out 都会把输出复位为 0,这是设计如此——为了确保引脚从输入切换为输出时不会意外输出高电平。你只需要在设完方向后,再单独写一次 value 文件来设置你需要的电平即可。

代码怎么处理在复位后保持原输出电平不变

echo out > /sys/class/gpio/gpio115/direction 
# 操作前,先检查direction的方向是否与待设置方向相同,相同则不再重复配置
cat /sys/class/gpio/gpio115/direction
posted @ 2026-08-26 17:27  aaron511  阅读(4)  评论(0)    收藏  举报