通过sysfs方式访问gpio,在命令行界面进行操作。

Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。这里对于Sysfs不展开,有需要详细查看Sysfs文件系统的,可以参考我的博文“”。

=========================================================================

(1). 在应用层通过Sysfs访问Gpio子系统,首先需要操作接口,没有操作接口一切都无从谈起。

先进入/sys/class/目录,看看系统中有没有“/sys/class/gpio”这个文件夹。

如果没有请在 make ARCH=arm menuconfig 中打开(以ARM平台为例):

Device Drivers ->  GPIO Support ->  /sys/class/gpio/… (sysfs interface)

这是系统提供的gpio的驱动,保存退出后重新编译内核,将新内核烧写到目标板子中。

(2). 然后就是计算引脚值。

  • 一般芯片引脚的表达为:GPIOx_yZ
  • 引脚值的计算公式为: num = x*32 + (y - a) * 8 + Z
  • 其中x代表0,1,2,3.....n; y代表a,b,c...;Z代表0,1,2,3.....n
  • 以GPIO 0_A7 为例(此处A与a不区分大小写):num = 0*32 + (a - a) * 8  + 7 = 7
  • 因此该引脚值num为:7

-------------------------------------------------------------------------------

  • 针对芯片引脚的表达为:Py_Z
  • 引脚值的计算公式为: num = (y-a)*32  + Z
  • 其中y代表a,b,c...;Z代表0,1,2,3.....n
  • 以PH_6为例(此处H与h不区分大小写):num =  (h - a) * 32  + 6 = 7 * 32 + 6 = 230
  • 一般内核中,PA定义为0,PB定义为1,PC定义为2,以此类推,则PH定义为7
  • 因此该引脚值num为:230

(3). 接口和引脚值提供好了,剩下的就是操作,有6种操作方式。

                    先 cd 进入 /sys/class/gpio 目录,以230引脚为例

  • 导出230引脚:        echo 230> export

                     导出成功后会有引脚目录出现,如下:

  • 设置方向:

                  cd 进入gpio230目录, ls 查看一下,会出现几个操作符,如下:

                其中: direction 代表引脚方向设置,value 代表设置或获取的引脚高低电平值。

                通过字符串 "in" 或 "out" 可以设置引脚输入输出方向

                echo "in" > direction     -----------> 输入方向

                echo "out" > direction    -----------> 输出方向

  • 查看引脚方向:        cat direction

  • 设置输出值

                        在引脚为输出状态下,通过数0或1,可以设置引脚输出高低电平。

                        echo 1 > value  -------> 输出高电平

                        echo 0 > value  -------> 输出低电平

  • 查看输出值:        cat value

                        在引脚为输入状态下,可以通过数0或1,判断引脚的高低电平状态。

                        0 ----------引脚电平为低

                        1 ----------引脚电平为高

  • 取消导出:        echo 230> unexport

               取消导出后,该gpio230目录消失

(4). 另外就是作为中断引脚使用

作为中断引脚使用时,需要先看这个引脚是否为外部GPIO的中断引脚源,一般几组引脚中只有一到两组引脚是具有GPIO外部中断功能。

以引脚编号230为例子:进入进入gpio230目录

如果目录中存在 “edge” 变量,则代表该引脚可以作为中断引脚来使用。

edge 表示中断的触发方式,edge文件有如下四个值:"none", "rising", "falling","both"

none:表示引脚为输入,不是中断引脚

rising:表示引脚为中断输入,上升沿触发

falling:表示引脚为中断输入,下降沿触发

both:表示引脚为中断输入,边沿触发

这个文件节点(即该 “edge” 变量)只有在引脚被配置为输入引脚的时候才存在。

当值是none时可以通过如下方法将变为中断引脚

echo "both" > /sys/class/gpio/gpioN/edge

对于是both,falling还是rising依赖具体硬件的中断的触发方式。

此方法即用户态gpio转换为中断引脚的方式:

先将GPIO配置为输入,然后使用poll()来阻塞程序直到GPIO的输入电平发生改变,关键是使用POLLPRI而不是POLLIN来侦听事件;或者使用select()。

=========================================================================

同样,也可以在应用程序中,通过系统调用进行操作,但是要注意所要操作节点的权限问题。

如下:

        // 导出打开
        int export_fd = open("/sys/class/gpio/export", O_WRONLY);
        
        // 导出关闭
        close(export_fd);
            
        // 方向设置打开
        int direction_fd = open("/sys/class/gpio/gpio7/direction", O_WRONLY);
        
        // 写方向    
        write(direction_fd, "in", sizeof("in"))
        write(direction_fd, "out", sizeof("out"))
        
        // 方向设置关闭
        close(direction_fd);
        
        // 引脚值设置/读取打开
        int gpiovalue_fd = open("/sys/class/gpio/gpio7/value", O_RDONLY);
        
        // 设置引脚值
        write(gpiovalue_fd, "1", sizeof("1"))
        write(gpiovalue_fd, "0", sizeof("0"))    

         // 引脚值设置关闭
        close(gpiovalue_fd);    


        // 读取引脚值
        read(gpiovalue_fd, &value, sizeof(value))

       注意:如果在循环中,不重新open和close情况下,循环 读写 某个IO口的状态,每次 读写 

                   需要用lseek将指针重置回文件起始处,因为本质上IO口的状态值是保存在文件中。 

if(lseek(gpio_fd,0,SEEK_SET) < 0){
    return;
} else {
  .....
}

       

例子:用户态使用gpio控制LED

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>


// Led pin number is 242
int main(int argc,char **argv)
{
    int gpio_fd = -1;
    int ret;
    char gpio[]="242";
    char dir[]="out";
	
	// 导出引脚
    gpio_fd = open("/sys/class/gpio/export",O_WRONLY);
    if(gpio_fd < 0){
        printf("open gpio/export failed\n");
        return -1;
    }
    ret = write(gpio_fd,gpio,strlen(gpio));
    if(ret < 0){
        printf("write to gpio/export failed\n");
        return -1;
    }
    close(gpio_fd);
	
	// 配置引脚方向为输出
    gpio_fd = open("/sys/class/gpio/gpio242/direction",O_RDWR);
    if(gpio_fd < 0){
        printf("open gpio242/direction failed\n");
        return -1;
    }

    ret = write(gpio_fd,dir,strlen(dir));
    if(ret < 0){
        printf("write to gpio242/direction failed\n");
        return -1;
    }
    close(gpio_fd);
	
	// 配置引脚输出电平
    gpio_fd = open("/sys/class/gpio/gpio242/value",O_RDWR);
    if(gpio_fd < 0){
        printf("open gpio242/value failed\n");
        return -1;
    }

	// 配置引脚依次输出高低电平
    char off[] = "1";
    char on[] = "0";
    for(int i=0;i < 10;i++){
        printf("led off\n");
        ret = write(gpio_fd,off,strlen(off));
        if(ret < 0){
            printf("write to gpio242/value failed\n");
            return -1;
        }
        sleep(1);
        printf("led on\n");
        ret = write(gpio_fd,on,strlen(on));
        if(ret < 0){
            printf("write to gpio242/value failed\n");
            return -1;
        }
        sleep(1);
    }
    close(gpio_fd);
	
	// 关闭引脚
    gpio_fd = open("/sys/class/gpio/unexport",O_WRONLY);
    if(gpio_fd < 0){
        printf("open gpio/unexport failed\n");
        return -1;
    }
    ret = write(gpio_fd,gpio,strlen(gpio));
    if(ret < 0){
        printf("write to gpio/unexport failed\n");
        return -1;
    }
    close(gpio_fd);

	// 结束
    printf("test gpio led ok\n");

    return 0;
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐