• 树莓派学习笔记——使用文件IO操作GPIO SysFs方式


    0 前言
        本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口。通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包含direction和value等,direction控制GPIO方向,而value可控制GPIO输出或获得GPIO输入。
        Linux学习可从应用出发,先不纠结Linux驱动编写,先把Linux给玩起来。

        【同样与不同】
        本文和【EasyARM i.mx28学习笔记——文件IO方式操作GPIO】内容类似,大部分代码同样。通过文件IO操作能够有效地避免平台差异,尽管EasyARM im287平台和树莓派全然不同,可是通过sysfs操作GPIO实现代码大致同样。
        和EasyARM im287不同,此处并没有使用交叉编译工具,有树莓派中的gcc工具链编译链接获得可运行文件,而EasyARM im287并不能这样操作。EasyARM im287採用busybox指令集,这也与树莓派中的debian指令集存在差异。

        【相关博文】

        【代码仓库】
        代码仓库位于bitbucket——rpi-gpio-sysfs ,请使用Hg克隆或者直接下载zip包。请不要使用不论什么版本号的IE浏览器訪问链接,除非你已经知道所使用的IE浏览器符合HTML5标准。推荐使用谷歌或者火狐浏览器訪问,若使用国产双核浏览器请切换到极速模式。
        【原理图示意】

    图1 连线示意图
    1 暴露GPIO操作接口
    static int gpio_export(int pin)
    {
        char buffer[BUFFER_MAX];
        int len;
        int fd;
    
        fd = open("/sys/class/gpio/export", O_WRONLY);
        if (fd < 0) {
            fprintf(stderr, "Failed to open export for writing!
    ");
            return(-1);
        }
    
        len = snprintf(buffer, BUFFER_MAX, "%d", pin);
        if (write(fd, buffer, len) < 0) {
            fprintf(stderr, "Fail to export gpio!");
            return -1;
        }
       
        close(fd);
        return 0;
    }

    2 隐藏GPIO操作接口
    static int gpio_unexport(int pin)
    {
        char buffer[BUFFER_MAX];
        int len;
        int fd;
    
        fd = open("/sys/class/gpio/unexport", O_WRONLY);
        if (fd < 0) {
            fprintf(stderr, "Failed to open unexport for writing!
    ");
            return -1;
        }
    
        len = snprintf(buffer, BUFFER_MAX, "%d", pin);
        if (write(fd, buffer, len) < 0) {
            fprintf(stderr, "Fail to unexport gpio!");
            return -1;
        }
       
        close(fd);
        return 0;
    }

    3 配置GPIO方向
    static int gpio_direction(int pin, int dir)
    {
        static const char dir_str[] = "inout";
        char path[DIRECTION_MAX];
        int fd;
    
        snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin);
        fd = open(path, O_WRONLY);
        if (fd < 0) {
            fprintf(stderr, "failed to open gpio direction for writing!
    ");
            return -1;
        }
    
        if (write(fd, &dir_str[dir == IN ? 0 : 3], dir == IN ? 2 : 3) < 0) {
            fprintf(stderr, "failed to set direction!
    ");
            return -1;
        }
    
        close(fd);
        return 0;
    }
        【简单说明】
        【1】dir_str[dir == IN ? 0 : 3], dir == IN ? 2 : 3 假设输入为常数宏IN, 取dir_str[0]=“in”;若输入常数宏为OUT,取dir_str[0]=“out”。此处巧妙的使用了在数组中的“”。

    4 控制GPIO输出
    static int gpio_write(int pin, int value)
    {
        static const char values_str[] = "01";
        char path[DIRECTION_MAX];
        int fd;
    
        snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/value", pin);
        fd = open(path, O_WRONLY);
        if (fd < 0) {
            fprintf(stderr, "failed to open gpio value for writing!
    ");
            return -1;
        }
    
        if (write(fd, &values_str[value == LOW ? 0 : 1], 1) < 0) {
            fprintf(stderr, "failed to write value!
    ");
            return -1;
        }
    
        close(fd);
        return 0;
    }

    5 获得GPIO输入
    static int gpio_read(int pin)
    {
        char path[DIRECTION_MAX];
        char value_str[3];
        int fd;
    
        snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/value", pin);
        fd = open(path, O_RDONLY);
        if (fd < 0) {
            fprintf(stderr, "failed to open gpio value for reading!
    ");
            return -1;
        }
    
        if (read(fd, value_str, 3) < 0) {
            fprintf(stderr, "failed to read value!
    ");
            return -1;
        }
    
        close(fd);
        return (atoi(value_str));
    }

    6 GPIO翻转操作
        【main函数】
    int main(int argc, char *argv[])
    {
        int i = 0;
        
        GPIOExport(POUT);
        GPIODirection(POUT, OUT);
        
        for (i = 0; i < 20; i++) {
            GPIOWrite(POUT, i % 2);
            usleep(500 * 1000);
        }
    
        GPIOUnexport(POUT);
        return(0);
    }
        【makefile】——此处的代码tab显示可能存在问题,请以代码仓库为主。
    # 可运行文件
    TARGET=test
    # 依赖目标
    SRCS=gpio-sysfs.c
    
    # 目标文件
    OBJS = $(SRCS:.c=.o)
    
    # 指令编译器和选项
    CC=gcc
    CFLAGS=-Wall -std=gnu99
    
    $(TARGET):$(OBJS)
     $(CC) -o $@ $^
    
    clean:
     rm -rf $(TARGET) $(OBJS)
    
    # 连续动作,先清除再编译链接,最后运行
    exec:clean $(TARGET)
     @echo 開始运行
     sudo ./$(TARGET)
     @echo 运行结束
    
    # 编译规则 $@代表目标文件 $< 代表第一个依赖文件
    %.o:%.c
     $(CC) $(CFLAGS) -o $@ -c $<

        【上传树莓派中 编译链接并运行】
        make exec
        makefile中exec目标包含下面一个过程,先清除目标文件和可运行文件,然后进行交叉编译,最后使用超级权限运行可运行文件。
        makefile的使用详见【 Linux学习笔记——例说makefile 索引博文

    7 总结
    【1】树莓派和其它嵌入式Linux开发板存在区别和联系,树莓派同样能够使用sysfs控制GPIO。
    【2】树莓派即在其它助剂中交叉编译,也可在平台直接编译。

    8 參考资料
  • 相关阅读:
    数组操作方法和迭代方法
    三元运算符
    数组求和/去重
    javascript保留字
    window.onload和document.ready区别
    alert()和consloe.log()区别
    Eventutil函数封装
    前端中的事件流
    react的生命周期
    小程序初体验
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4198672.html
Copyright © 2020-2023  润新知