【EASY EAI Nano人工智能开发套件试用体验】GPIO点灯——使用sysfs接口 - RISC-V MCU技术社区 - 电子技术论坛 - 广受欢迎的专业电子论坛
分享 收藏 返回

【EASY EAI Nano人工智能开发套件试用体验】GPIO点灯——使用sysfs接口

本文记录了如何使用Linux上经典的sysfs接口控制GPIO。不同于灵眸官方文档介绍的较新的libgpiod接口,sysfs接口可以在shell环境下进行控制,非常方便进行测试和演示。本文会首先介绍一些背景知识,然后在shell中交互式演示,最后通过编写C语言程序读写sysfs进行GPIO控制。

一、硬件部分

本篇的硬件包含EASY EAI Nano开发板和三色LED灯,EASY EAI Nano开发板的40pin GPIO扩展接口参考官方文档。

1.1 开发板的GPIO扩展接口

EASY EAI Nano的GPIO硬件资源以及复用关系如下图所示。

mvx1p2bzibovbejrwogm.png

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

kezgb577jnqtvj2522w8.png

1.2 GRB三色LED灯

三色LED灯,外观长这样:

Untitled

1.3 硬件连接

两者直接连接关系如下:

Untitled

具体连接关系为:

  • R —— GPIO3_B2
  • G —— GPIO3_B3
  • B —— GPIO3_C4
  • GND —— GND(39)

二、软件部分

2.1 背景知识

首先需要介绍sysfs,其次是GPIO的sysfs接口。sysfs 是由 Linux 内核提供的伪文件系统(并不是在磁盘上真实存在的文件),它通过虚拟文件在用户空间中提供了各种内核子系统、硬件设备和设备驱动程序的信息。GPIO 设备通常也通过 sysfs 提供了一些接口。

2.2 shell中读写sysfs控制GPIO

和之前的实验类似,给EASY EAI Nano开发板接上电源并通过USB线连接到PC后,再将设备连接到VMWare Workstation Player的虚拟机中,我们就可以通过 adb shell登录到设备的shell会话中了:

Untitled

首先执行如下命令:

ls /sys/class/gpio/

可以看到类似如下输出:

Untitled

接下来我们将看看如何使用这个接口。注意,以“gpiochip”开头的设备名称是 GPIO 控制器,我们不会直接使用它们。

从 sysfs 接口使用 GPIO 引脚的基本步骤如下:

  1. 导出引脚;
  2. 设置引脚方向(输入或输出);
  3. 如果是输出,设置引脚输出电平为高或者低;
  4. 如果是输入,则读取引脚的电平(为高或者低);
  5. 完成后,取消导出引脚;

要导出引脚,我们需要将引脚编号写入伪文件 /sys/class/gpio/export。相应的取消导出引脚,也是将引脚编号写入到 /sys/class/gpio/unexport 文件。所以,开始之前,需要先了解一下引脚编号规则。

灵眸官网文档中已经提供了GPIO引脚编号的规则,具体如下图所示

Untitled

在使用某一引脚前,需要先导出该引脚资源:

# 导出 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

2.3 C语言读写sysfs控制GPIO

有了以上介绍后,我们就可以使用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文件。

2.4 编译、运行C语言控制三色灯程序

接下来,编译该文件为blink可执行程序:

arm-linux-gnueabihf-gcc -Wall -o blink blink.c

编译成功后,将会生成blink文件。

接着,将blink文件推送到开发板的/home目录中:

adb push blink /home

推送到开发板上之后,我们就可以运行该程序了。

使用如下命令,运行开发板上的blink程序:

adb shell /home/blink

此时,应该可以看到LED灯开始交替闪烁三种颜色。

三、参考链接

  1. 【灵眸官方文档】“EASY-EAI-Toolkit|硬件外设组件|GPIO”篇: https://www.easy-eai.com/document_details/3/142
  2. 【Linux内核文档】GPIO sysfs接口文档: https://www.kernel.org/doc/html/latest/admin-guide/gpio/sysfs.html
  3. 【关于树莓派的一篇文章】GPIO编程——使用sysfs接口: https://www.ics.com/blog/gpio-programming-using-sysfs-interface

VID_20230507_232827_hor

更多回帖

×
发帖