DragonBoard 410c手把手控制Linux的GPIO

上一篇文章中,我们通过操作LED文件位置的brightness文件,控制了LED的亮与灭,并且在DragonBoard 410c上实现了LED的闪烁功能。 同时相信各位也对DragonBoard 410c上的硬件开发有了深入的了解。接下来我们将深入研究其他硬件,并一个一个的手把手进行操作与解释。

背景知识

无论是输出方波信号或者是处理复杂的通讯协议,我们都要直接或者间接的用到GPIO进行输出与输入。 GPIO是芯片与外界进行通讯最常用的资源之一,其重要性不言而喻。同时它也是Linux中最简单的驱动程序之一。 大部分程序都可以使用提供的接口对GPIO进行控制。

DragonBoard 410c上充满了各种形式的GPIO,如高速口或低速口,还有可以复用成通讯口的GPIO。 于是,Linux中使用0~MAX_INT之间的整数标识来明确指定需要调用的GPIO, 而且标识必须是正整数。 Linux中提供了统一的接口来操作GPIO, 再实际控制GPIO前先向各位介绍GPIO的使用方法。

需要使用的函数

首先我们可以检测GPIO端口是否可用(合法):

int gpio_is_valid(int number);

其次我们需要将GPIO端口导出到用户空间,这样我们才可以在shell中进行直接控制:

Int gpio_export(unsigned gpio, bool direction_may_change);

参数direction_may_change表示用户程序是否允许修改gpio的方向,假如可以,则参数direction_may_change为真此外,gpio_export的反函数就是gpio_unexport(), 该函数可以将GPIO结点从用户空间移除。

需要调用的文件

用户空间如何访问gpio呢:一般方法是通过sysfs接口访问gpio,也就是通过操作文件系统中的文件。下面是/sys/class/gpio目录下的三种文件,这三个目录下的文件包含了能够操作GPIO的所有接口:

  • export/unexport文件
  • gpioN:指代具体的gpio引脚
  • gpio_chipN:指代gpio控制器

在进行操作前,开发者必须提前预知以上接口没有device文件与它们连接,接下来对每个文件进行解释:

  • export/unexport文件接口:该接口只能写不能读
    用户程序通过写入gpio的编号来向内核申请将某个gpio的控制权,并导出到用户空间。当然前提是没有内核代码申请这个gpio端口,比如
echo 19 > export 

上述操作会为19号gpio创建一个节点gpio19,此时/sys/class/gpio目录下边生成一个gpio19的目录。/sys/class/gpio/unexport和导出的效果相反,比如:

echo 19 > unexport

上述操作将会移除gpio19这个节点。

  • /sys/class/gpio/gpioN:指代某个具体的gpio端口
    里边有如下属性文件:
     direction 表示gpio端口的方向,读取结果是in或out。该文件也可以写,写入out 时该gpio设为输出同时电平默认为低。写入low或high则不仅可以设置为输出 还可以设置输出的电平。 当然如果内核不支持或者内核代码不愿意,将不会存在这个属性,比如内核调用了gpio_export(N,0)就表示内核不愿意修改gpio端口方向属性
     value表示gpio引脚的电平,0(低电平)1(高电平),如果gpio被配置为输出,这个值是可写的,记住任何非零的值都将输出高电平, 如果某个引脚能并且已经被配置为中断,则可以调用poll(2)函数监听该中断,中断触发后poll(2)函数就会返回。
     edge表示中断的触发方式,edge文件有如下四个值:”none”, “rising”, “falling”,”both”。
    • none表示引脚为输入,不是中断引脚
    • rising表示引脚为中断输入,上升沿触发
    • falling表示引脚为中断输入,下降沿触发
    • both表示引脚为中断输入,边沿触发

这个文件节点只有在引脚被配置为输入引脚的时候才存在。 当值是none时可以通过如下方法将变为中断引脚:

echo "both" > edge;

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

DragonBoard 410c的LED控制程序编写

头文件

/* ============================================================================
Name : TogglyGPIO.c
Author : ZhouJunyu
Version : 0.0.1
Copyright : Your copyright notice
Description : Basic Hardware access, Ansi-style ============================================================================
*/ #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h>

定义max_buf以及GPIO的标识符

#define MAX_BUF 10
#define GPIO_A 36
#define GPIO_B 12

定义GPIO的写函数与读函数以及导入/导出函数

int Export_GPIO(int gpio);
int UnExport_GPIO(int gpio);
int Write_GPIO(int gpio, int value);
int Read_GPIO(int gpio, int *value);

描述主函数功能

本部分首先将指定的GPIO进行export操作,从而在用户空间对GPIO进行控制。 之后通过写函数与读函数对GPIO进行操作。

int main(void)
{
    char c=' ';
    int ret;
    int out_value = 1;
    int in_value = 0;
    //
    //将制定的GPIO口export
    //
    ret = Export_GPIO(GPIO_A);
    if(ret != 0)
    printf("Error exporting GPIO_%d", GPIO_A);
    ret = Export_GPIO(GPIO_B);
    if(ret != 0)
        printf("Error exporting GPIO_%d", GPIO_B);
        //
        //向指定的GPIO口写值
        //
    printf("press any key to toggle GPIO_A or 'q' to quit:\n");
    while(c != 'q'){
        printf("Writing GPIO_%d: value=%d \n", GPIO_A, out_value);
        ret = Write_GPIO(GPIO_A, out_value);
        if (ret != 0)
            printf("Error writing GPIO_%d", GPIO_A);
        ret = Read_GPIO(GPIO_B, (int*) &in_value);
        if (ret != 0)
            printf("Error reading GPIO_%d", GPIO_B);
            printf("Reading GPIO_%d: value=%d \n", GPIO_B, in_value);
            out_value = !out_value;
        c= getchar();
}
    //
    //将指定的GPIO口进行unexport
    //
    ret = UnExport_GPIO(GPIO_A);
    if(ret != 0)
        printf("Error UnExporting GPIO_%d", GPIO_A);
    ret = UnExport_GPIO(GPIO_B);
    if(ret != 0)
        printf("Error UnExporting GPIO_%d", GPIO_B);
    return 0;
}

export函数

该函数将GPIO注册,使得该GPIO可以在用户空间进行操作

int Export_GPIO(int gpio){
    int fd;
    char buf[MAX_BUF];
    //将指定位置的GPIO进行export
    //在/sys/class/gpio目录下将生成一个可用的GPIO文件
    sprintf(buf, "%d", gpio);
    //打开export文件夹并对其进行操作
    fd = open("/sys/class/gpio/export", O_WRONLY);
    if(fd < 0)
        return -1;
    write(fd, buf, strlen(buf));
    close(fd);
    return 0;
}

unexport函数

该函数将GPIO移除出用户空间,使程序无法调用GPIO

int UnExport_GPIO(int gpio){
    int fd;
    char buf[MAX_BUF];
    sprintf(buf, "%d", gpio);
    fd = open("/sys/class/gpio/unexport", O_WRONLY);
    if(fd < 0)
        return -1;
    write(fd, buf, strlen(buf));
    close(fd);
    return 0;
}

写GPIO值函数

本函数根据输入的GPIO口标识符以及值,修改GPIO输出的电平

int Write_GPIO(int gpio, int value){
    int fd;
    char buf[MAX_BUF];
    //将GPIO方向设置为输出
    sprintf(buf, "/sys/class/gpio/gpio%d/direction", gpio);
    fd = open(buf, O_WRONLY);
    if(fd<0)
        return -1;
    write(fd, "out", 3);// Set out direction
    close(fd);
    sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
    fd = open(buf, O_WRONLY);
    if(fd<0)
        return -1;
    // 向GPIO口写值
    sprintf(buf, "%d", value);
    write(fd, buf, strlen(buf));
    close(fd);
    return 0;
}

读取GPIO的值函数

这个意思就不言自明了吧

int Read_GPIO(int gpio, int *value){
    int fd;
    char val;
    char buf[MAX_BUF];
    sprintf(buf, "/sys/class/gpio/gpio%d/value", gpio);
    fd = open(buf, O_RDONLY);
    if(fd<0)
        return -1;
    // 读取GPIO的值
    read(fd, &val, 1);
    *value = atoi(&val);
    close(fd);
    return 0;
}

总结

运行后,用户可以使用键盘控制GPIO的高低电平,按q键可以退出程序。
效果如下图所示:
DragonBoard 410c手把手控制Linux的GPIO_第1张图片

GPIO的控制相比起LED可能要复杂一些,但是通过本教程,希望各位能够基本了解如何在DragonBoard 410c的嵌入式Linux下的操作。 可能有人会觉得非常复杂而且难以记住,但是由于Linux系统庞大无比,需要处理无数的硬件与软件特性,自然没有简单的STM32等单片机那么轻松自如。 不过如果学会了这些技巧,那么在以后的开发中,将会如鱼得水。

你可能感兴趣的:(DragonBoard 410c手把手控制Linux的GPIO)