本文记录了如何使用Linux上经典的sysfs接口控制GPIO。不同于灵眸官方文档介绍的较新的libgpiod接口,sysfs接口可以在shell环境下进行控制,非常方便进行测试和演示。本文会首先介绍一些背景知识,然后在shell中交互式演示,最后通过编写C语言程序读写sysfs进行GPIO控制。
本篇的硬件包含EASY EAI Nano开发板和三色LED灯,EASY EAI Nano开发板的40pin GPIO扩展接口参考官方文档。
EASY EAI Nano的GPIO硬件资源以及复用关系如下图所示。

EASY EAI Nano默认开启了下方三个GPIO引脚资源,对应的接口描述情况如下所示。

三色LED灯,外观长这样:

两者直接连接关系如下:

具体连接关系为:
首先需要介绍sysfs,其次是GPIO的sysfs接口。sysfs 是由 Linux 内核提供的伪文件系统(并不是在磁盘上真实存在的文件),它通过虚拟文件在用户空间中提供了各种内核子系统、硬件设备和设备驱动程序的信息。GPIO 设备通常也通过 sysfs 提供了一些接口。
和之前的实验类似,给EASY EAI Nano开发板接上电源并通过USB线连接到PC后,再将设备连接到VMWare Workstation Player的虚拟机中,我们就可以通过 adb shell登录到设备的shell会话中了:

首先执行如下命令:
ls /sys/class/gpio/
可以看到类似如下输出:

接下来我们将看看如何使用这个接口。注意,以“gpiochip”开头的设备名称是 GPIO 控制器,我们不会直接使用它们。
从 sysfs 接口使用 GPIO 引脚的基本步骤如下:
要导出引脚,我们需要将引脚编号写入伪文件 /sys/class/gpio/export。相应的取消导出引脚,也是将引脚编号写入到 /sys/class/gpio/unexport 文件。所以,开始之前,需要先了解一下引脚编号规则。
灵眸官网文档中已经提供了GPIO引脚编号的规则,具体如下图所示

在使用某一引脚前,需要先导出该引脚资源:
# 导出 GPIO3_B2 :
echo 106 > /sys/class/gpio/export
接着设置该引脚的方向,输入或者输出:
# 设置 GPIO3_B2 为输入:
echo in > /sys/class/gpio/gpio106/direction
# 设置 GPIO3_B2 为输出:
echo out > /sys/class/gpio/gpio106/direction
根据引脚的工作模式,做相应的控制,写入电平或读取电平:
# 输出高电平,此时三色灯应该会发出红色光
echo 1 > /sys/class/gpio/gpio106/value
# 输出低电平
echo 0 > /sys/class/gpio/gpio106/value
# 读取输入(如果为输入模式,则可以读取到引脚实际的电平状态):
# cat /sys/class/gpio/gpio106/value
引脚使用完毕后,需要手动向gpio管理器申请释放该引脚资源:
# 释放引脚
echo 106 > /sys/class/gpio/unexport
有了以上介绍后,我们就可以使用C语言编写一个控制三色LED灯闪烁的程序了:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define GPIO_PIN(m, p, n) ((m)*32 + (p)*8 + n)
#define DIR_IN "in"
#define DIR_OUT "out"
#define VAL_LOW "0"
#define VAL_HIGH "1"
bool file_write(const char *path, const char *text)
{
    FILE *fptr = NULL;
    if (!path || !text)
    {
        printf("%s: invalid argument!", __FUNCTION__);
        return false;
    }
    fptr = fopen(path, "w");
    if (!fptr)
    {
        printf("fopen %s failed!\n", path);
        return false;
    }
    if (fputs(text, fptr) < 0)
    {
        printf("write %s failed: %s!\n", path, strerror(ferror(fptr)));
        fclose(fptr);
    }
    fclose(fptr);
    return true;
}
void msleep(int ms)
{
    usleep(ms * 1000);
}
bool gpio_export(int gpio_pin)
{
    char number[PATH_MAX];
    snprintf(number, sizeof(number), "%d", gpio_pin);
    return file_write("/sys/class/gpio/export", number);
}
bool gpio_unexport(int gpio_pin)
{
    char number[PATH_MAX];
    snprintf(number, sizeof(number), "%d", gpio_pin);
    return file_write("/sys/class/gpio/unexport", number);
}
bool gpio_set_dir(int gpio_pin, const char *value)
{
    char path[PATH_MAX] = {0};
    snprintf(path, sizeof(path),
             "/sys/class/gpio/gpio%d/direction", gpio_pin);
    return file_write(path, value);
}
bool gpio_set_value(int gpio_pin, const char *value)
{
    char path[PATH_MAX] = {0};
    snprintf(path, sizeof(path),
             "/sys/class/gpio/gpio%d/value", gpio_pin);
    return file_write(path, value);
}
enum
{
    A = 0,
    B,
    C,
    D,
};
int led_pins[] = {
    GPIO_PIN(3, B, 2), // GPIO3_B2
    GPIO_PIN(3, B, 3), // GPIO3_B3
    GPIO_PIN(3, C, 4), // GPIO3_C4
};
int main(int argc, char *argv[])
{
    int i = 0;
    int loops = argc > 1 ? atoi(argv[1]) : 10;
    for (i = 0; i < ARRAY_SIZE(led_pins); i++)
    {
        gpio_export(led_pins[i]);
        gpio_set_dir(led_pins[i], DIR_OUT);
    }
    while (loops--)
    {
        printf("loops: %d ...\n", loops);
        for (i = 0; i < ARRAY_SIZE(led_pins); i++)
        {
            gpio_set_value(led_pins[i], VAL_HIGH);
            msleep(250);
            gpio_set_value(led_pins[i], VAL_LOW);
        }
        msleep(250);
    }
    for (i = 0; i < ARRAY_SIZE(led_pins); i++)
    {
        gpio_unexport(led_pins[i]);
    }
    return 0;
}
将其保存到~/workspace/blink目录,命名为blink.c文件。
接下来,编译该文件为blink可执行程序:
arm-linux-gnueabihf-gcc -Wall -o blink blink.c
编译成功后,将会生成blink文件。
接着,将blink文件推送到开发板的/home目录中:
adb push blink /home
推送到开发板上之后,我们就可以运行该程序了。
使用如下命令,运行开发板上的blink程序:
adb shell /home/blink
此时,应该可以看到LED灯开始交替闪烁三种颜色。
更多回帖