学习笔记之Linux的GPIO控制

本文介绍了如何在rk3566主控芯片上通过GPIOsysfs接口和libgpiod库在Linux系统中控制GPIO,包括GPIO命名规则、导出与方向设置、输出控制以及使用libgpiod的高级功能和命令行操作示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. GPIO命名规则

2. GPIO的控制方式

2.1 使用GPIO sysfs接口控制IO

2.2 使用libgpiod控制IO 


      笔者在学习到点灯实验时,想到直接进行小灯的参数修改比较简单,于是想通过直接对GPIO进行操作从而达到相同的效果(这里可能需要修改设备树,这块笔者也不是很懂不敢乱说,于是借用另一个32开发板的小灯进行测试)。泰山派所采用的主控芯片是rk3566,包含40pinGPIO口,具体接口信息如下图所示:

1. GPIO命名规则

        在rk3566中,GPIO口的引脚按照“控制器+端口+索引序号”的形式进行命名,例如GPIO1_A4表示第一组控制器中A端口的4号引脚。在硬件设计中共有5个固定端口A,B,C和D(对应0,1,2,3),各端口有8个索引序号(0~7)。

2. GPIO的控制方式

2.1 使用GPIO sysfs接口控制IO

        在Linux中,最常见的读写GPIO方式就是用GPIO sysfs interface, 是通过操作 /sys/class/gpio 目录下的 export 、 unexport 、gpio{N}/directiongpio{N} /value (用实际引脚号替代{N})等文件实现的,经常出现shell脚本里面。这种方法不需要任何特殊的内核模块或驱动,只需通过用户空间的程序就可以访问和控制GPIO。

        以下是一个基本的步骤指南,用于通过sysfs接口控制GPIO:

1.导出GPIO

        首先,需要将GPIO从内核导出到用户空间。这可以通过向/sys/class/gpio/export文件写入GPIO编号来实现。例如,我们需要控制GPIO3_B4  (32*3+1*8+4 = 108,详细规则可以看:2. GPIO控制 — 快速使用手册—基于LubanCat-RK356x系列板卡 文档)的GPIO,可以这样做:

echo 108 > /sys/class/gpio/export

2.设置GPIO方向

        接下来,需要设置GPIO的方向(输入或输出)。这可以通过修改/sys/class/gpio/gpioX/direction文件来实现,其中X是你之前导出的GPIO的编号。

  • 对于输入GPIO:
echo in > /sys/class/gpio/gpio108/direction

        这里也可以先查看一下此时的IO口状态:

cat /sys/class/gpio/gpio108/value

        可以看到此时是低电平,且小灯是亮着的 

  • 对于输出GPIO:
echo out > /sys/class/gpio/gpio108/direction

3.控制输出GPIO

        如果你设置了GPIO为输出,你可以通过修改/sys/class/gpio/gpioX/value文件来控制其电平。写入1将设置GPIO为高电平,写入0将设置GPIO为低电平。 这里写入高电平,可以看到此时的小灯已经熄灭,证明我们实验成功了。

echo 1 > /sys/class/gpio/gpio108/value

4.复位GPIO

当你不再需要控制某个GPIO时,可以将其从用户空间取消导出。这可以通过向/sys/class/gpio/unexport文件写入GPIO编号来实现。

echo 108 > /sys/class/gpio/unexpor

5.程序编写

    首先介绍两个常用的函数:access和open。

access 函数:

//用于检查进程是否有权限读取、写入或执行某个文件
#include <unistd.h>  
  
int access(const char *pathname, int mode);

        其中:

  • pathname 是你想要检查的文件的路径。
  • mode 是一个标志,用于指定你想要检查的访问权限。它可以是以下值之一或它们的组合:
    • R_OK:测试读权限
    • W_OK:测试写权限
    • X_OK:测试执行权限
    • F_OK:测试文件是否存在

        如果调用成功,access 返回0;如果失败,则返回-1,并设置errno以指示错误。

open函数:

        open函数有两个或三个参数,具体形式如下:

//用于打开或创建文件
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

        其中,pathname参数表示要打开或创建的文件名(含路径,缺省为当前路径)。flags参数用于指定打开文件的方式和权限,常用的标志位有:

  • O_RDONLY:只读方式打开文件。
  • O_WRONLY:只写方式打开文件。
  • O_RDWR:读写方式打开文件。
  • O_CREAT:如果文件不存在,则创建文件。
  • O_EXCL:与O_CREAT一起使用,如果文件已存在,则返回错误。
  • O_TRUNC:如果文件已存在,将其截断为0字节。
  • O_APPEND:在文件末尾追加写入。

        当使用两个参数的open函数时,仅通过flags参数指定打开文件的方式和权限。而使用三个参数的open函数时,还需要通过mode参数指定新文件的存取许可权限。

        open函数的返回值是一个文件描述符(file descriptor),它是一个非负整数,用于在后续的文件操作中标识该文件。如果操作成功,open函数将返回一个有效的文件描述符;如果操作失败,将返回-1。 

    在认识以上两个函数之后,我们就可以正式来写IO口的驱动函数了,这个过程执行的原理非常简单,这里以野火的程序为例 :

  • 把GPIO的编号写入到export文件,导出GPIO设备。

  • 修改GPIO设备属性direction文件值为out,把GPIO设置为输出方向。

  • 修改GPIO设备属性文件value的值为1或0,控制GPIO高电平或低电平。

#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

#define GPIO_INDEX "42"
static char gpio_path[75];
int gpio_init(char *name)
{
    int fd;

    sprintf(gpio_path, "/sys/class/gpio/gpio%s", name);
    //查询文件是否存在
    if (access("gpio_path", F_OK)){
        //只读方式打开
        fd = open("/sys/class/gpio/export", O_WRONLY);
        //打开文件失败
        if(fd < 0)
            return 1 ;
    
        write(fd, name, strlen(name));
        close(fd);
    
        //初始化gpio使用的引脚为输出模式
        sprintf(gpio_path, "/sys/class/gpio/gpio%s/direction", name);
        fd = open(gpio_path, O_WRONLY);
        if(fd < 0)
            return 2;
    
        write(fd, "out", strlen("out"));
        close(fd);
    }

    return 0;
}
//向unexport文件写入编号,取消导出
int gpio_deinit(char *name)
{
    int fd;
    fd = open("/sys/class/gpio/unexport", O_WRONLY);
    if(fd < 0)
        return 1;

    write(fd, name, strlen(name));
    close(fd);

    return 0;
}

//输出高电平
int gpio_high(char *name)
{
    int fd;
    sprintf(gpio_path, "/sys/class/gpio/gpio%s/value", name);
    fd = open(gpio_path, O_WRONLY);
    if(fd < 0){
        printf("open gpio%s wrong\n",name);
        return -1;
    }
        
    if(2 != write(fd, "1", sizeof("1")))
        printf("wrong set \n");
    close(fd);
    return 0;
}

//输出低电平
int gpio_low(char *name)
{
    int fd;
    sprintf(gpio_path, "/sys/class/gpio/gpio%s/value", name);
    fd = open(gpio_path, O_WRONLY);
    if(fd < 0){
        printf("open gpio%s wrong\n",name);
        return -1;
    }
        
    if(2 != write(fd, "0", sizeof("0")))
        printf("wrong set \n");
    close(fd);
    return 0;
}

int main(int argc, char *argv[])
{
    char buf[10];
    int res;

    /* 校验传参 */
    if (2 != argc) {
        printf( "usage: %s <PinNum>\n",argv[0]);
        return -1;
    }
    res = gpio_init(argv[1]);
    if(res){
        printf("gpio init error,code = %d",res);
        return 0;
    }

    while(1){
        printf("Please input the value : 0--low 1--high q--exit\n");
        scanf("%10s", buf);
 
        switch (buf[0]){
            case '0':
                gpio_low(argv[1]);
                break;
 
            case '1':
                gpio_high(argv[1]);
                break;
 
            case 'q':
                gpio_deinit(argv[1]);
                printf("Exit\n");
                return 0;
 
            default:
                break;
       }
    }
    return 0;
}

2.2 使用libgpiod控制IO 

        4.8版本的Kernel加入了libgpiod的支持,这里笔者的kernel版本不支持就不继续编译了,可以简单了解一下,详细可以访问野火的:2. GPIO控制 — 快速使用手册—基于LubanCat-RK356x系列板卡 文档

        先输入进行插件安装:

sudo apt install gpiod

        下面是一些简单的常用命令: 

命令

作用

使用举例

说明

gpiodetect

列出所有的GPIO控制器

gpiodetect(无参数)

列出所有的GPIO控制器

gpioinfo

列出gpio控制器的引脚情况

gpioinfo 1

列出第一组控制器引脚组情况

gpioset

设置gpio

gpioset 1 20=0

设置第一组控制器编号4引脚为低电平

gpioget

获取gpio引脚状态

gpioget 1 20

获取第一组控制器编号4的引脚状态

gpiomon

监控gpio的状态

gpiomon 1 20

监控第一组控制器编号4的引脚状态

免责声明:本文所引用的各种资料均用于自己学习使用,这里感谢立创和正点原子官方的资料以及各位优秀的创作者。  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值