因特尔Edison第2站--mraa下gpio

Edison上软件的开发,使用的是mraa库,可在这个网址(https://github.com/intel-iot-devkit/mraa)下载到mraa的源码。

下载后解压源码,其目录结构如下:

因特尔Edison第2站--mraa下gpio_第1张图片

这里我最关注的是apisrcexampleinclude文件夹。

api里面是mraa的头文件,里面包含了各个函数的说明和声明,教我们怎么使用mraa的函数,这个库安装到Edisonapi里的内容就对应Edison/usr/include/mraa 和 /usr/include/mraa.h 和 /usr/include/mraa.hpp

src里面是api中各个函数的实现源码,对我们去了解底层很有帮助。

example里面是关于mraa的一些例子,有了这些例子我们就可以依葫芦画瓢,编写自己的应用代码了,这个很重要。

include里面是几个头文件,是关于mraa初始化的头文件。简单说就是让mraa知道自己工作的平台,大的分为arm架构和x86架构,arm架构下目前只有一个树莓派B版本,而在x86架构下则有因特尔的Edisongalileo平台。

今天,首先了解一下mraagpio的操作。

根据官网,我们先看一下mraagpio写了多少函数和这些函数的用法。在api/mraa文件夹里有一个gpio.h,这里面声明了关于gpio操作的函数。

这里首先定义了一个_gpio型的结构体指针

typedef struct _gpio* mraa_gpio_context;

那么这个_gpio的结构体包含了什么内容呢,使用sourceInsight这个强大的工具很容易就找到了,_gpio结构体位于/include/mraa_internal_types.h文件里。下面看一下这个结构体包含了什么内容:

/**

 * Opaque pointer definition to the internal struct _gpio

*/

struct _gpio {

    /*@{*/

    int pin; /**< the pin number, as known to the os. */

    int phy_pin; /**< pin passed to clean init. -1 none and raw*/

    int value_fp; /**< the file pointer to the value of the gpio */

    void (* isr)(void *); /**< the interupt service request */

    void *isr_args; /**< args return when interupt service request triggered */

    pthread_t thread_id; /**< the isr handler thread id */

    int isr_value_fp; /**< the isr file pointer on the value */

    mraa_boolean_t owner; /**< If this context originally exported the pin */

    mraa_result_t (*mmap_write) (mraa_gpio_context dev, int value);

    int (*mmap_read) (mraa_gpio_context dev);

    /*@}*/

};

根据上面一行注释,这个结构体代表一个gpio引脚的结构。这里面包括了:

pingpio引脚号

phy_pin:我觉得是内存释放的时候使用(有待查阅)

value_fp:这个是给引脚写入值的文件指针,Linux下是一切皆文件的。对应到linux系统上,位置是在/sys/class/gpio/gpio%d/value

(* isr)(void *):我觉得是中断函数服务程序指针(有待查阅)

*isr_args:中断处理参数

 thread_id:中断处理线程ID

isr_value_fp:中断文件指针(具体怎么用,还不清楚)

owner:是否对该pin有所有权

(*mmap_write) (mraa_gpio_context dev, int value):写返回,其返回的对应在Types.h里面的mraa_result_t结构体下面。

 (*mmap_read) (mraa_gpio_context dev):读返回,其返回的对应在Types.h里面的mraa_result_t结构体下面。

紧接着,下面定义了3个枚举变量。

/**

 * Gpio Output modes

 */

typedef enum {

    MRAA_GPIO_STRONG     = 0, /**< Default. Strong high and low */

    MRAA_GPIO_PULLUP     = 1, /**< Resistive High */

    MRAA_GPIO_PULLDOWN   = 2, /**< Resistive Low */

    MRAA_GPIO_HIZ        = 3  /**< High Z State */

} gpio_mode_t;

引脚输出模式选择,

0:默认输出模式。

1:上拉输出

2:下拉输出

3:高阻状态

/**

 * Gpio Direction options

 */

typedef enum {

    MRAA_GPIO_OUT    = 0, /**< Output. A Mode can also be set */

    MRAA_GPIO_IN     = 1,  /**< Input */

    MRAA_GPIO_OUT_HIGH = 2, /**< Output. Init High */

    MRAA_GPIO_OUT_LOW = 3 /**< Output. Init Low */

} gpio_dir_t;

引脚方向设置:

0:引脚输出

1:引脚输入

2:引脚输出高

3:引脚输出低

/**

 * Gpio Edge types for interupts

 */

typedef enum {

    MRAA_GPIO_EDGE_NONE    = 0, /**< No interrupt on Gpio */

    MRAA_GPIO_EDGE_BOTH    = 1, /**< Interupt on rising & falling */

    MRAA_GPIO_EDGE_RISING  = 2, /**< Interupt on rising only */

    MRAA_GPIO_EDGE_FALLING = 3  /**< Interupt on falling only */

} gpio_edge_t;

中断触发沿设置:

0:无触发

1:上升沿河下降沿均可触发

2:上升沿触发

3:下降沿触发

下面就是函数的声明了:

mraa_gpio_context mraa_gpio_init(int pin);gpio的初始化,这里的参数pin值得就是板子上的io编号。返回类型是一个结构体,不成功返回NULL

mraa_gpio_context mraa_gpio_init_raw(int gpiopin);这个也是gpio初始化,不过和上一个不一样,这里的形参gpiopin对应的是系统列表的gpio而不是板子上的io口。

mraa_result_t mraa_gpio_edge_mode(mraa_gpio_context dev, gpio_edge_t mode);引脚的触发沿模式设置,参数dev就是函数mraa_gpio_context mraa_gpio_init(int pin)的返回值,mode是枚举变量gpio_edge_t里的值。

mraa_result_t mraa_gpio_isr(mraa_gpio_context dev, gpio_edge_t edge, void (*fptr)(void *), void * args);中断设置函数,第一个参数是io引脚,第二个参数是边沿触发模式,第三个参数是中断处理函数,第四个参数中断函数的参数。

mraa_result_t mraa_gpio_isr_exit(mraa_gpio_context dev);中断退出时的处理函数,查看其实现代码可以发现,包括结束线程和文件关闭。

mraa_result_t mraa_gpio_mode(mraa_gpio_context dev, gpio_mode_t mode);gpio引脚的输出模式设置,第二个参数是结构体gpio_mode_t里的值。

mraa_result_t mraa_gpio_dir(mraa_gpio_context dev, gpio_dir_t dir);gpio引脚方向设置,第二个参数是结构体gpio_dir_t里的值。

mraa_result_t mraa_gpio_close(mraa_gpio_context dev);gpio引脚关闭(既是disabledlinux里一切皆文件可以说关闭)。通过实现代码可以发现,主要处理的是解除引脚占用(unexport)和内存释(free)

int mraa_gpio_read(mraa_gpio_context dev);读引脚电平(首先引脚必须是输入模式)

mraa_result_t mraa_gpio_write(mraa_gpio_context dev, int value);写引脚电平(首先引脚必须是输出状态)。第二个参数是你要写入的值01

mraa_result_t mraa_gpio_owner(mraa_gpio_context dev, mraa_boolean_t owner);改变引脚所有权

mraa_result_t mraa_gpio_use_mmaped(mraa_gpio_context dev, mraa_boolean_t mmap);使用内存io映射,而不是文件io(不是很明白)

int mraa_gpio_get_pin(mraa_gpio_context dev);通过mraa_gpio_context获得板子对应的引脚号。

int mraa_gpio_get_pin_raw(mraa_gpio_context dev);通过mraa_gpio_context获得文件系统对应的gpio编号。

上面就是gpio的基本函数。

下面拿两个例子来说一下,一个是读io,一个是写io。都是example下的例子,gpio_read6.c是读arduino上的6引脚。

#include "stdio.h"

#include "unistd.h"

#include "mraa.h"

int

main(int argc, char **argv)

{

    mraa_init();//mraa的初始化,src/mraa.c函数里面

    fprintf(stdout, "MRAA Version: %s\nStarting Read on IO6\n",

            mraa_get_version());//mraa的版本号定向输出到标准输出

//! [Interesting]

    mraa_gpio_context gpio;

    gpio = mraa_gpio_init(6);//mraagpio初始化,此处是6

    mraa_gpio_dir(gpio, MRAA_GPIO_IN);//gpio方向的设置,此处为输入

    for (;;) {

        fprintf(stdout, "Gpio is %d\n", mraa_gpio_read(gpio));//把读到的值输出到标准输出

        sleep(1);

    }

    mraa_gpio_close(gpio);//关闭gpio

//! [Interesting]

    return 0;

}

我是把例子程序移动到其他地方进行编译的,需要加上-lmraa进行库链接,然后进行编译,gcc -o gpio_read6 gpio_read6.c -lmraa

因特尔Edison第2站--mraa下gpio_第2张图片


然后运行程序,./gpio_read6

io6接地的时候,读出的数据如下:

因特尔Edison第2站--mraa下gpio_第3张图片

当io6接到3.3v的时候,独处数据如下:


io6什么都不接的时候,读出来的数据有时候是1有时候是0

因特尔Edison第2站--mraa下gpio_第4张图片


下面是一个写IO的程序:把led接到io5引脚上实现闪烁。

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include "mraa/gpio.h"

int

main(int argc, char **argv)

{

    mraa_platform_t platform = mraa_get_platform_type();//获取平台信息,下面有说明。

    mraa_gpio_context gpio;

    char board_name[] = "Some weird devboard that isn't recognised...";

    int ledstate = 0;

    switch (platform) {

        case MRAA_INTEL_GALILEO_GEN1:

            strcpy(board_name, "Intel Galileo Gen1");

            gpio = mraa_gpio_init_raw(3);

            break;

        case MRAA_INTEL_GALILEO_GEN2:

            strcpy(board_name, "Intel Galileo Gen2");

        default:

            gpio = mraa_gpio_init(5);//io5进行初始化

    }

    fprintf(stdout, "Welcome to libmraa\n Version: %s\n Running on %s\n",

        mraa_get_version(), board_name);

    mraa_gpio_dir(gpio, MRAA_GPIO_OUT);//设置io方向

    for (;;) {

        ledstate = !ledstate;

        mraa_gpio_write(gpio, ledstate);//io5写值

        sleep(1);

    }

    return 0;

}

 mraa_get_platform_type()//这个函数是获取平台的信息,也就是让mraa知道工作在什么平台下是,edison,伽利略,还是树莓派等。这个函数实现在mraa.c函数里面,这个函数里面只有一个语句,就是返回了return platform_type;那么这个变量platform_type怎么来的呢,同样在mraa.c文件里面有一个mraa_init函数,里面有一句

platform_type = mraa_x86_platform();那么继续往下,这个mraa_x86_platform()函数是怎么回事,这个函数是在src/x86/x86.c文件里面,通过读取函数实现,我们可以发现这里是通过读取板子上/sys/devices/virtual/dmi/id/board_name来获取平台的,如果是Edison,这个文件里面的内容是BODEGA BAY然后 platform_type = MRAA_INTEL_EDISON_FAB_C;这样就用MRAA_INTEL_EDISON_FAB_C来代替Edison板子,MRAA_INTEL_EDISON_FAB_CTypes.h里面的一个枚举变量的一项,看这个枚举类型:

/**

 * MRAA supported platform types

 */

typedef enum {

    MRAA_INTEL_GALILEO_GEN1 = 0, /**< The Generation 1 Galileo platform (RevD) */

    MRAA_INTEL_GALILEO_GEN2 = 1, /**< The Generation 2 Galileo platform (RevG/H) */

    MRAA_INTEL_EDISON_FAB_C = 2, /**< The Intel Edison (FAB C) */

    MRAA_INTEL_DE3815 = 3, /**< The Intel DE3815 Baytrail NUC */

    MRAA_INTEL_MINNOWBOARD_MAX = 4, /**< The Intel Minnow Board Max */

    MRAA_RASPBERRY_PI_B = 5, /**< The Raspberry PI Model B */

    MRAA_UNKNOWN_PLATFORM = 99 /**< An unknown platform type, typically will load INTEL_GALILEO_GEN1 */

} mraa_platform_t;

这里面列出了mraa支持的板子类型。

说明:在gpio.c里面还有其他的函数是gpio.h里没有声明的,io的操作到底部是对/sys/class/gpio下各个文件的操作,像directionvalue。而mode的设置/sys/kernel/debug/gpio_debug/下面的文件里面,比如/sys/kernel/debug/gpio_debug/gpio28/current_pinmux

这里面当然还有一些不明白的地方:

1、比如是如何判断属于x86还是arm

2、一个板子上的io设置牵涉到好几个系统上gpio的设置,这几个系统上的gpio的设置在哪里?

3、由于知识有限,还有一些地方没有说明白,已经标注了,希望知道的朋友给以之指点。

你可能感兴趣的:(因特尔Edison第2站--mraa下gpio)