• 【正点原子I.MX6U-MINI应用篇】4、嵌入式Linux关于GPIO的一些操作


    一、应用层如何操作GPIO

    先看看板子的文件系统目录。与LED设备一样,GPIO同样也是通过sysfs方式进行操控,进入到/sys/class/gpio目录下,如下所示:

    可以看到该目录下包含两个文件exportunexport以及 5 个gpiochipX(X 等于 0、32、64、96、128)命名的文件夹。

    • gpiochipX: 当前 SoC 所包含的 GPIO 控制器,我们知道I.MX6UL/I.MX6ULL一共包含了 5 个GPIO控制器,分别为 GPIO1、GPIO2、GPIO3、GPIO4、GPIO5,在这里分别对应 gpiochip0、gpiochip32、gpiochip64、gpiochip96、gpiochip128 这 5 个文件夹,每一个gpiochipX文件夹用来管理一组GPIO。随便进入到其中某个目录下,可以看到这些目录下包含了如下文件:

    在这个目录我们主要关注的是 base、label、ngpio 这三个属性文件,这三个属性文件均是只读、不可写。

    • base :与gpiochipX中的 X 相同,表示该控制器所管理的这组 GPIO 引脚中最小的编号。每一个 GPIO引脚都会有一个对应的编号,Linux下通过这个编号来操控对应的GPIO引脚。

    • label :该组 GPIO 对应的标签,也就是名字。

    • ngpio :该控制器所管理的GPIO引脚的数量(所以引脚编号范围是:base ~ base+ngpio-1)。

    对于给定的一个GPIO引脚,如何计算它在sysfs中对应的编号呢?其实非常简单,譬如给定一个 GPIO引脚为GPIO4_IO16,那它对应的编号是多少呢?首先我们要确定GPIO4对应于gpiochip96,该组GPIO引脚的最小编号是 96(对应于 GPIO4_IO0),所以 GPIO4_IO16 对应的编号自然是96 + 16 = 112;同理GPIO3_IO20对应的编号是64 + 20 = 84。

    • export :用于将指定编号的GPIO引脚导出。在使用GPIO引脚之前,需要将其导出,导出成功之后才能使用它。注意export文件是只写文件,不能读取,将一个指定的编号写入到export文件中即可将对应的GPIO引脚导出,譬如:
    echo 0 > export  #导出编号为0的GPIO引脚(对于I.MX6UL/I.MX6ULL来说,也就是GPIO1_IO0)
    
    • 1

    导出成功之后会发现在/sys/class/gpio目录下生成了一个名为gpio0的文件夹(gpioX,X 表示对应的编号),如图所示。这个文件夹就是导出来的GPIO引脚对应的文件夹,用于管理、控制该GPIO引脚。

    • unexport :将导出的 GPIO 引脚删除。当使用完 GPIO 引脚之后,我们需要将导出的引脚删除,同样该文件也是只写文件、不可读,譬如:
    echo 0 > unexport #删除导出的编号为0的GPIO引脚
    
    • 1

    删除成功之后,之前生成的 gpio0 文件夹就会消失!

    以上就给大家介绍了/sys/class/gpio目录下的所有文件和文件夹,控制GPIO引脚主要是通过export导出之后所生成的gpioX(X 表示对应的编号)文件夹,在该文件夹目录下存在一些属性文件可用于控制GPIO引脚的输入、输出以及输出的电平状态等。

    Tips:需要注意的是,并不是所有GPIO引脚都可以成功导出,如果对应的GPIO已经在内核中被使用了,那便无法成功导出,打印如下信息:那也就是意味着该引脚已经被内核使用了,譬如某个驱动使用了该引脚,那么将无法导出成功!

    • gpioX将指定的编号写入到export文件中,可以导出指定编号的GPIO引脚,导出成功之后会在/sys/class/gpio目录下生成对应的gpioX(X表示GPIO的编号)文件夹,以前面所生成的gpio0为例,进入到gpio0目录,该目录下的文件如下所示:

    我们主要关心的文件是active_low、direction、edge以及value这四个属性文件,接下来分别介绍这四个属性文件的作用:

    • direction :配置 GPIO 引脚为输入或输出模式。该文件可读、可写,读表示查看 GPIO 当前是输入还是输出模式,写表示将GPIO配置为输入或输出模式;读取或写入操作可取的值为"out"(输出模式)和"in"(输入模式),如下所示:

    • value :在 GPIO 配置为输出模式下,向value文件写入"0"控制GPIO引脚输出低电平,写入"1"则控制GPIO引脚输出高电平。在输入模式下,读取value文件获取GPIO引脚当前的输入电平状态。
      譬如:
    # 获取 GPIO 引脚的输入电平状态
    echo "in" > direction
    cat value
    # 控制 GPIO 引脚输出高电平
    echo "out" > direction
    echo "1" > value
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • active_low :这个属性文件用于控制极性,可读可写,默认情况下为 0,譬如:
    # active_low 等于 0
    echo "0" > active_low
    echo "out" > direction
    echo "1" > value  #输出高
    echo "0" > value  #输出低
    # active_low 等于 1
    $ echo "1" > active_low
    $ echo "out" > direction
    $ echo "1" > value #输出低
    $ echo "0" > value #输出高
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    由此看出,active_low 的作用已经非常明显了,对于输入模式来说也同样适用。

    • edge :控制中断的触发模式,该文件可读可写。在配置 GPIO 引脚的中断触发模式之前,需将其设置为输入模式:
    非中断引脚:echo "none" > edge
    上升沿触发:echo "rising" > edge
    下降沿触发:echo "falling" > edge
    边沿触发:echo "both" > edge
    
    • 1
    • 2
    • 3
    • 4

    当引脚被配置为中断后可以使用poll()函数监听引脚的电平状态变化,在后面的示例中将向大家介绍。

    二、GPIO应用编程之输出

    1.1 程序源码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    static char gpio_path[100];
    
    static int gpio_config(const char *attr, const char *val)
    {
        char file_path[100];
        int len;
        int fd;
    
        sprintf(file_path, "%s/%s", gpio_path, attr);
        if (0 > (fd = open(file_path, O_WRONLY))) {
            perror("open error");
            return fd;
        }
    
        len = strlen(val);
        if (len != write(fd, val, len)) {
            perror("write error");
            close(fd);
            return -1;
        }
    
        close(fd);  //关闭文件
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        /* 校验传参 */
        if (3 != argc) {
            fprintf(stderr, "usage: %s  \n", argv[0]);
            exit(-1);
        }
    
        /* 判断指定编号的GPIO是否导出 */
        sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);
    
        if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出
    
            int fd;
            int len;
    
            if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) {
                perror("open error");
                exit(-1);
            }
    
            len = strlen(argv[1]);
            if (len != write(fd, argv[1], len)) {//导出gpio
                perror("write error");
                close(fd);
                exit(-1);
            }
    
            close(fd);  //关闭文件
        }
    
        /* 配置为输出模式 */
        if (gpio_config("direction", "out"))
            exit(-1);
    
        /* 极性设置 */
        if (gpio_config("active_low", "0"))
            exit(-1);
    
        /* 控制GPIO输出高低电平 */
        if (gpio_config("value", argv[2]))
            exit(-1);
    
        /* 退出程序 */
        exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    执行程序时需要传入两个参数,argv[1]指定GPIO的编号、argv[2]指定输出电平状态(0 表示低电平、1 表示高电平)。

    上述代码中首先使用access()函数判断指定编号的GPIO引脚是否已经导出,也就是判断相应的gpioX目录是否存在,如果不存在则表示未导出,则通过/sys/class/gpio/export文件将其导出;导出之后先配置了GPIO引脚为输出模式,也就是向direction文件中写入out;接着再配置极性,通过向active_low 文件中写入0(不用配置也可以);最后再控制GPIO引脚输出相应的电平状态,通过对value属性文件写入10来使其输出高电平或低电平。

    2.2 编译程序

    我们要想给ARM板编译出程序,需要使用交叉编译工具链,交叉编译的工具链我们已经安装过了,详细请看【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.x.pdf 的第4.3小节。我是用的是arm-linux-gnueabihf交叉编译工具链。使用arm-linux-gnueabihf-gcc -v可以查看交叉编译工具链的版本号。

    然后就可以使用下面命令编译出可以在ARM板子上运行的可执行文件了。

    arm-linux-gnueabihf-gcc -o gpio_out gpio_out .c
    
    • 1
    • 1、arm表示这是编译arm架构代码的编译器。
    • 2、linux表示运行在linux环境下。
    • 3、gnueabihf表示嵌入式二进制接口。
    • 4、gcc表示是gcc工具。

    这样编译出来的 led程序才可以在ARM板子上运行。执行file gpio_out 命令就可以看出hello是32位LSB的ELF格式文件,目标机架构为ARM,说明这个交叉编译正常,可执行文件可以在ARM板上执行。

    2.3 上传程序到开发板执行

    开发板启动后通过nfs挂载Ubuntu目录的方式,将相应的文件拷贝到开发板上。简单来说,就是通过NFS在开发板上通过网络直接访问ubuntu虚拟机上的文件,并且就相当于自己本地的文件一样。

    开发板想访问/home/zhiguoxin/myproject/alientek_app_development_source这个目录中的文件,就要把/home/zhiguoxin/myproject/alientek_app_development_source挂载到开发板的mnt目录,这样就可以通过nfs来访问/home/zhiguoxin/myproject/alientek_app_development_source了。

    因为我的代码都放在/home/zhiguoxin/myproject/alientek_app_development_source这个目录下,所以我们将这个目录作为NFS共享文件夹。设置方法参考移植SQLite3、OpenCV到RV1126开发板上开发人脸识别项目第一章。

    Ubuntu IP为192.168.10.100,然后一般都是挂载在开发板的mnt目录下,这个目录是专门用来给我们作为临时挂载的目录。

    文件系统目录简介

    然后使用MobaXterm软件通过SSH访问开发板。

    ubuntu ip:192.168.10.100
    windows ip:192.168.10.200
    开发板ip:192.168.10.50
    
    • 1
    • 2
    • 3

    在开发板上执行以下命令:

    mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_app_development_source /mnt
    
    • 1

    就将开饭的mnt目录挂载在ubuntu的/home/zhiguoxin/myproject/alientek_app_development_source目录下了。这样我们就可以在Ubuntu下修改文件,然后可以直接在开发板上执行可执行文件了。当然我这里的/home/zhiguoxin/myproject/windows之间是一个共享目录,我也可以直接在windows上面修改文件,然后ubuntu和开发板直接进行文件同步了。

    这里以板子上的 GPIO1_IO01 引脚为例,该引脚在底板上已经引出

    然后编译,可以编译成功。

    ./gpio_out 1 1 #控制GPIO1_IO01输出高电平
    ./gpio_out 1 0 #控制GPIO1_IO01输出高电平
    
    • 1
    • 2

    执行相应的命令后,可以使用万用表或者连接一个LED小灯进行检验,以验证实验结果!

    三、GPIO应用编程之输入

    3.1 程序源码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    static char gpio_path[100];
    
    static int gpio_config(const char *attr, const char *val)
    {
        char file_path[100];
        int len;
        int fd;
    
        sprintf(file_path, "%s/%s", gpio_path, attr);
        if (0 > (fd = open(file_path, O_WRONLY))) {
            perror("open error");
            return fd;
        }
    
        len = strlen(val);
        if (len != write(fd, val, len)) {
            perror("write error");
            close(fd);
            return -1;
        }
    
        close(fd);  //关闭文件
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        char file_path[100];
        char val;
        int fd;
    
        /* 校验传参 */
        if (2 != argc) {
            fprintf(stderr, "usage: %s \n", argv[0]);
            exit(-1);
        }
    
        /* 判断指定编号的GPIO是否导出 */
        sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);
    
        if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出
    
            int len;
    
            if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) {
                perror("open error");
                exit(-1);
            }
    
            len = strlen(argv[1]);
            if (len != write(fd, argv[1], len)) {//导出gpio
                perror("write error");
                close(fd);
                exit(-1);
            }
    
            close(fd);  //关闭文件
        }
    
        /* 配置为输入模式 */
        if (gpio_config("direction", "in"))
            exit(-1);
    
        /* 极性设置 */
        if (gpio_config("active_low", "0"))
            exit(-1);
    
        /* 配置为非中断方式 */
        if (gpio_config("edge", "none"))
            exit(-1);
    
        /* 读取GPIO电平状态 */
        sprintf(file_path, "%s/%s", gpio_path, "value");
    
        if (0 > (fd = open(file_path, O_RDONLY))) {
            perror("open error");
            exit(-1);
        }
    
        if (0 > read(fd, &val, 1)) {
            perror("read error");
            close(fd);
            exit(-1);
        }
    
        printf("value: %c\n", val);
    
        /* 退出程序 */
        close(fd);
        exit(0);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    执行程序时需要传入一个参数,argv[1]指定要读取电平状态的GPIO对应的编号。上述代码中首先使用access()函数判断指定编号的GPIO引脚是否已经导出,若未导出,则通过/sys/class/gpio/export文件将其导出;导出之后先配置了GPIO引脚为输入模式,也就是向direction文件中写入in;接着再配置极性、设置 GPIO 引脚为非中断模式(向 edge 属性文件中写入none)。最后打开value属性文件,读取GPIO的电平状态并将其打印出来。

    3.2 编译程序

    我们要想给ARM板编译出程序,需要使用交叉编译工具链,交叉编译的工具链我们已经安装过了,详细请看【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.x.pdf 的第4.3小节。我是用的是arm-linux-gnueabihf交叉编译工具链。使用arm-linux-gnueabihf-gcc -v可以查看交叉编译工具链的版本号。

    然后就可以使用下面命令编译出可以在ARM板子上运行的可执行文件了。

    arm-linux-gnueabihf-gcc -o gpio_in gpio_in .c
    
    • 1
    • 1、arm表示这是编译arm架构代码的编译器。
    • 2、linux表示运行在linux环境下。
    • 3、gnueabihf表示嵌入式二进制接口。
    • 4、gcc表示是gcc工具。

    这样编译出来的 led程序才可以在ARM板子上运行。执行file gpio_in 命令就可以看出hello是32位LSB的ELF格式文件,目标机架构为ARM,说明这个交叉编译正常,可执行文件可以在ARM板上执行。

    2.3 上传程序到开发板执行

    开发板启动后通过nfs挂载Ubuntu目录的方式,将相应的文件拷贝到开发板上。简单来说,就是通过NFS在开发板上通过网络直接访问ubuntu虚拟机上的文件,并且就相当于自己本地的文件一样。

    开发板想访问/home/zhiguoxin/myproject/alientek_app_development_source这个目录中的文件,就要把/home/zhiguoxin/myproject/alientek_app_development_source挂载到开发板的mnt目录,这样就可以通过nfs来访问/home/zhiguoxin/myproject/alientek_app_development_source了。

    因为我的代码都放在/home/zhiguoxin/myproject/alientek_app_development_source这个目录下,所以我们将这个目录作为NFS共享文件夹。设置方法参考移植SQLite3、OpenCV到RV1126开发板上开发人脸识别项目第一章。

    Ubuntu IP为192.168.10.100,然后一般都是挂载在开发板的mnt目录下,这个目录是专门用来给我们作为临时挂载的目录。

    文件系统目录简介

    然后使用MobaXterm软件通过SSH访问开发板。

    ubuntu ip:192.168.10.100
    windows ip:192.168.10.200
    开发板ip:192.168.10.50
    
    • 1
    • 2
    • 3

    在开发板上执行以下命令:

    mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_app_development_source /mnt
    
    • 1

    就将开饭的mnt目录挂载在ubuntu的/home/zhiguoxin/myproject/alientek_app_development_source目录下了。这样我们就可以在Ubuntu下修改文件,然后可以直接在开发板上执行可执行文件了。当然我这里的/home/zhiguoxin/myproject/windows之间是一个共享目录,我也可以直接在windows上面修改文件,然后ubuntu和开发板直接进行文件同步了。

    这里以板子上的GPIO1_IO01引脚为例,该引脚在底板上已经引出:

    首先通过杜邦线将GPIO1_IO01引脚连接到板子上的3.3V电源引脚上,接着执行命令读取GPIO电平状态:

    ./gpio_in 1
    
    • 1

    打印出的value等于1,表示读取到GPIO的电平确实是高电平;接着将GPIO1_IO01引脚连接到板子上的GND引脚上,执行命令:

    ./gpio_in 1 
    
    • 1

    打印出的value等于0,表示读取到GPIO的电平确实是低电平;测试结果与实际相符合!

    四、GPIO应用编程之中断

    4.1 程序源码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    static char gpio_path[100];
    
    static int gpio_config(const char *attr, const char *val)
    {
        char file_path[100];
        int len;
        int fd;
    
        sprintf(file_path, "%s/%s", gpio_path, attr);
        if (0 > (fd = open(file_path, O_WRONLY))) {
            perror("open error");
            return fd;
        }
    
        len = strlen(val);
        if (len != write(fd, val, len)) {
            perror("write error");
            return -1;
        }
    
        close(fd);  //关闭文件
        return 0;
    }
    
    int main(int argc, char *argv[])
    {
        struct pollfd pfd;
        char file_path[100];
        int ret;
        char val;
    
        /* 校验传参 */
        if (2 != argc) {
            fprintf(stderr, "usage: %s \n", argv[0]);
            exit(-1);
        }
    
        /* 判断指定编号的GPIO是否导出 */
        sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);
    
        if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出
    
            int len;
            int fd;
    
            if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) {
                perror("open error");
                exit(-1);
            }
    
            len = strlen(argv[1]);
            if (len != write(fd, argv[1], len)) {//导出gpio
                perror("write error");
                exit(-1);
            }
    
            close(fd);  //关闭文件
        }
    
        /* 配置为输入模式 */
        if (gpio_config("direction", "in"))
            exit(-1);
    
        /* 极性设置 */
        if (gpio_config("active_low", "0"))
            exit(-1);
    
        /* 配置中断触发方式: 上升沿和下降沿 */
        if (gpio_config("edge", "both"))
            exit(-1);
    
        /* 打开value属性文件 */
        sprintf(file_path, "%s/%s", gpio_path, "value");
    
        if (0 > (pfd.fd = open(file_path, O_RDONLY))) {
            perror("open error");
            exit(-1);
        }
    
        /* 调用poll */
        pfd.events = POLLPRI; //只关心高优先级数据可读(中断)
    
        read(pfd.fd, &val, 1);//先读取一次清除状态
        for ( ; ; ) {
    
            ret = poll(&pfd, 1, -1);    //调用poll
            if (0 > ret) {
                perror("poll error");
                exit(-1);
            }
            else if (0 == ret) {
                fprintf(stderr, "poll timeout.\n");
                continue;
            }
    
            /* 校验高优先级数据是否可读 */
            if(pfd.revents & POLLPRI) {
                if (0 > lseek(pfd.fd, 0, SEEK_SET)) {//将读位置移动到头部
                    perror("lseek error");
                    exit(-1);
                }
    
                if (0 > read(pfd.fd, &val, 1)) {
                    perror("read error");
                    exit(-1);
                }
    
                printf("GPIO中断触发\n", val);
            }
        }
    
        /* 退出程序 */
        exit(0);
    }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126

    执行程序时需要传入一个参数,argv[1]指定要读取电平状态的GPIO对应的编号。

    上述代码中首先使用access()函数判断指定编号的GPIO引脚是否已经导出,若未导出,则通过/sys/class/gpio/export文件将其导出。对GPIO进行配置:配置为输入模式、配置极性、将触发方式配置为边沿触发。打开value属性文件,获取到文件描述符,接着使用poll()函数对value 的文件描述符进行监视,这里为什么要使用poll()监视、而不是直接对文件描述符进行读取操作?这里简单的描述一下。

    poll()函数可以监视一个或多个文件描述符上的I/O状态变化,譬如 POLLIN、POLLOUT、POLLERR、POLLPRI 等,其中POLLIN和 POLLOUT表示普通优先级数据可读、可写,而POLLPRI表示有高优先级数据可读取,中断就是一种高优先级事件,当中断触发时表示有高优先级数据可被读取。

    4.2 编译程序

    我们要想给ARM板编译出程序,需要使用交叉编译工具链,交叉编译的工具链我们已经安装过了,详细请看【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.x.pdf 的第4.3小节。我是用的是arm-linux-gnueabihf交叉编译工具链。使用arm-linux-gnueabihf-gcc -v可以查看交叉编译工具链的版本号。

    然后就可以使用下面命令编译出可以在ARM板子上运行的可执行文件了。

    arm-linux-gnueabihf-gcc -o gpio_intr gpio_intr.c
    
    • 1
    • 1、arm表示这是编译arm架构代码的编译器。
    • 2、linux表示运行在linux环境下。
    • 3、gnueabihf表示嵌入式二进制接口。
    • 4、gcc表示是gcc工具。

    这样编译出来的 led程序才可以在ARM板子上运行。执行file gpio_intr命令就可以看出hello是32位LSB的ELF格式文件,目标机架构为ARM,说明这个交叉编译正常,可执行文件可以在ARM板上执行。

    4.3 上传程序到开发板执行

    开发板启动后通过nfs挂载Ubuntu目录的方式,将相应的文件拷贝到开发板上。简单来说,就是通过NFS在开发板上通过网络直接访问ubuntu虚拟机上的文件,并且就相当于自己本地的文件一样。

    开发板想访问/home/zhiguoxin/myproject/alientek_app_development_source这个目录中的文件,就要把/home/zhiguoxin/myproject/alientek_app_development_source挂载到开发板的mnt目录,这样就可以通过nfs来访问/home/zhiguoxin/myproject/alientek_app_development_source了。

    因为我的代码都放在/home/zhiguoxin/myproject/alientek_app_development_source这个目录下,所以我们将这个目录作为NFS共享文件夹。设置方法参考移植SQLite3、OpenCV到RV1126开发板上开发人脸识别项目第一章。

    Ubuntu IP为192.168.10.100,然后一般都是挂载在开发板的mnt目录下,这个目录是专门用来给我们作为临时挂载的目录。

    文件系统目录简介

    然后使用MobaXterm软件通过SSH访问开发板。

    ubuntu ip:192.168.10.100
    windows ip:192.168.10.200
    开发板ip:192.168.10.50
    
    • 1
    • 2
    • 3

    在开发板上执行以下命令:

    mount -t nfs -o nolock,vers=3 192.168.10.100:/home/zhiguoxin/myproject/alientek_app_development_source /mnt
    
    • 1

    就将开饭的mnt目录挂载在ubuntu的/home/zhiguoxin/myproject/alientek_app_development_source目录下了。这样我们就可以在Ubuntu下修改文件,然后可以直接在开发板上执行可执行文件了。当然我这里的/home/zhiguoxin/myproject/windows之间是一个共享目录,我也可以直接在windows上面修改文件,然后ubuntu和开发板直接进行文件同步了。

    执行应用程序监测 GPIO1_IO01 引脚的中断触发情况,如下所示:

    ./gpio_intr 1  # 监测GPIO1_IO01引脚中断触发
    
    • 1

    当执行命令之后,我们可以使用杜邦线将 GPIO1_IO01 引脚连接到 GND 或 3.3V电源引脚上,来回切换,使得GPIO1_IO01引脚的电平状态发生由高到低或由低到高的状态变化,以验证 GPIO 中断的边沿触发情况;当发生中断时,终端将会打印相应的信息,如图所示。测试完成后按Ctrl+C退出程序!

  • 相关阅读:
    深入了解JavaScript中的AJAX和HTTP请求
    APK漏洞扫描工具
    【Leetcode】 707. 设计链表
    洛谷P1227 完美的对称
    redis集群
    教你从零开始画echarts地图
    crontab 定时任务
    Element - el-tree 树形结构拖拽以及增删改查
    Win11如何增强麦克风?Win11增强麦克风的设置
    代码注释有感
  • 原文地址:https://blog.csdn.net/qq_39400113/article/details/127446205