imx6ull/linux应用编程学习(2)GPIO编程(上)

0. 前言

     这是基于正点imx6ull开发板的linux应用编程开发,正如所言,其中开发板的驱动编程已经被编写好了。

     那么应用编程和驱动编程之间的关系和驱动是什么呢?对于一个系统,普通用户的程序肯定是不愿意随便一个程序直接影响到硬件的,其需要一个中介来联系应用层和硬件,而操作系统便是这个中介。而驱动程序根据硬件编写好了各种设备驱动文件,充当了硬件与应用软件中间的桥梁。而应用程序呢,则是调用已经编译好的设备驱动文件,去实现一个控制。应用编程学习主要是学习arm的linux应用层的应用开发。

1. imx6ull的GPIO

连接开发板后(步骤可以看我的上一篇文章),进入/sys/class/gpio文件夹

可以看到,其拥有以上文件夹。关于gpiochip0~gpiochip128这中间五个文件夹(后面统称为gpiochioX,X为该控制器最小引脚编号)分别代表了imx6ull的gpio1~gpio5五个控制器。注意,写引脚编号是连续的,每个gpio控制器均有32个引脚,GPIO1为0-31,GPIO2为32-63,GPIO3为64-95,GPIO4为96-127,GPIO128-159。

进入gpiochip0这个文件夹,可得知其有以下驱动文件:

其中的含义为:

1.base:与 gpiochipX 中的 X 相同,表示该控制器所管理的这组 GPIO 引脚中最小的编号

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

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

可以用cat命令看到这些信息。

回到gpio文件夹,其中还有export和unexport未进行解释

 1.export:用于将指定编号的GPIO引脚导出。需要对其导出,才能成功使用GPIO引脚。例:

echo 0 > export # 导出编号为 0 的 GPIO 引脚(对于 I.MX6UL/I.MX6ULL 来说,也就是
GPIO1_IO0)

因为编号是0,所以就是GPIO1_IO0。可以思考一下,如果是echo 34 >export呢?其导出哪个引脚?

答,34大于32小于64,显然是GPIO2,GPIO2是32到63,所以是GPIO2_IO2。

2.unexport:将导出的GPIO删除,当使用完GPIO引脚后,需要将导出的引脚删除

echo 0 > unexport # 删除导出的编号为 0 的 GPIO 引脚

删除后,export语句生成的gpio0文件就会消失。

3.gpioX:这是第一条语句生成的文件,第一条生成的应该是gpio0。将指定的编号写入到 export 文件中,可以导出指定编号的 GPIO 引脚,导出成功之后会在/sys/class/gpio目录下生成对应的 gpioX(X 表示 GPIO 的编号)文件夹。

我们利用在终端执行第一条语句,可以发现生成了gpio0文件,打开gpio0,可以发现有以下文件,我们来依次解读。

1.direction: 配置 GPIO 引脚为输入或输出模式,in为输入,out为输出,设置为输出的语句为:

ech0 "out" > direction

2.value:

       若 GPIO 配置为输出,,向 value 文件写入"0"控制 GPIO 引脚输出低电平;写入"1"则控制 GPIO 引脚输出高电平。

      在输入模式下,读取 value 文件获取 GPIO 引脚当前的输入电平状态。

echo "1" > value

3.active_low: 这个属性文件用于控制极性, 可读可写,默认情况下为 0,控制极性是什么意思呢?在正常情况下,默认为0,那就是正常的,value输入0的时候就是控制低电平,但是如果将极性取反,active_low输入1,那么value的输入会取反,value输入1就是控制高电平了。

# 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 #输出高

4.edge: 控制中断的触发模式,在配置 GPIO 引脚的中断触发模式之前,需将其设置为输入模式

非中断引脚: echo "none" > edge
上升沿触发: echo "rising" > edge
下降沿触发: echo "falling" > edge
边沿触发: echo "both" > edg

学习了这些概念,之后便开始代码测试部分。

2.代码

1.GPIO输出

在源码上,加入了些自己的注释,有错误欢迎指出~

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

// 全局变量用于存储GPIO路径
static char gpio_path[100];

// 配置GPIO属性的函数
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[])
{
    // 校验传参个数,必须是3个
    if (3 != argc) {
        fprintf(stderr, "usage: %s <gpio> <value>\n", argv[0]);  // 提示正确用法
        exit(-1);
    }

    // 生成GPIO路径
    sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);

    // 检查GPIO目录是否存在
    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]);  // 获取GPIO编号字符串的长度

        // 写入GPIO编号到导出文件
        if (len != write(fd, argv[1], len)) {  // 导出GPIO
            perror("write error");  // 写入出错时输出错误信息
            close(fd);  // 关闭文件
            exit(-1);
        }

        close(fd);  // 关闭文件
    }

    // 配置GPIO为输出模式
    if (gpio_config("direction", "out"))
        exit(-1);

    // 配置GPIO极性为默认(不反转)
    if (gpio_config("active_low", "0"))
        exit(-1);

    // 设置GPIO的值(高电平或低电平)
    if (gpio_config("value", argv[2]))
        exit(-1);

    // 正常退出程序
    exit(0);
}

关于这段程序:

1.sprintf(file_path, "%s/%s", gpio_path, attr);这句作用是构造属性文件的完整路径。‘/’ 是普通字符,用于拼接路径

  • gpio_path:第一个字符串参数,对应第一个 %s,表示GPIO的基路径,例如 /sys/class/gpio/gpio20
  • attr:第二个字符串参数,对应第二个 %s,表示GPIO的属性名,例如 direction

最后拼接路径为:/sys/class/gpio/gpio20/direction。说白了就是将gpio_path+attr写入file_path.

2.static int gpio_config(const char *attr, const char *val)是配置GPIO属性函数,具体怎么配置呢?

当你在括号中写入attr和val后,attr则是补全路径,val则是将值写入文件描述符fd指向的文件,写完后关闭描述符,方便下次使用。

3.sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);,输入的路径和/sys/class/gpio/gpio合并写入gpio_path,这个值和第一条解释对应。注意,这里有俩个gpio,所以终端输入数字就行,

例如./xxx 1 1。所以argv[1]是1,所以就是路径/sys/class/gpio/gpio1,同时后面 write(fd, argv[1], len),将argv[1]的值写入fd描述符指向的文件路径,就是/sys/class/gpio/export。就是设置了gpio_path路径的同时,将gpiox的引脚引出。

4.access(gpio_path, F_OK) 这行代码的作用是检查指定路径 gpio_path 是否存在。F_OK 是一个宏,用于测试文件是否存在。

5.

// 配置GPIO为输出模式
    if (gpio_config("direction", "out"))
        exit(-1);

可得在gpio_config()中的第一个参数attr为direction,所以file_path路径为/sys/class/gpio/gpio(*argv[1])/direction。argv[1]需要在终端输入。第二个参数val为out,赋入文件描述符fd,而fd指向direction。写入成功

之后进行上机测试:

ubuntu上命令 :${CC} -o gpio_out gpio_out.c。

如果${CC} 环境不会配置,可以看我另外一篇文章:

Linux环境变量 ${CC}的配置方法-CSDN博客

后连接开发板,在mobaxterm上对开发板输入命令。

利用scp指令将编译好的文件传入开发板:

scp -r book@192.168.5.12:/home/book/project/APP/app/16_gpio/gpio_out /home/root/app

scp -r 【用户名】@【ubuntu的ip地址】:【unbuntu上要传入文件的路径】  【开发板要存入的路径】

传入成功。

我这是mini板引脚图,可知gpio1对应开发板引脚的7,gpio2为9,gpio4为10

./gpio_out 【引脚编号】【高低电平】

所以我设置了1、4脚为高,2为低。

根据led,测试成功

  • 41
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值