Edison上软件的开发,使用的是mraa库,可在这个网址(https://github.com/intel-iot-devkit/mraa)下载到mraa的源码。
下载后解压源码,其目录结构如下:
这里我最关注的是api,src,example和include文件夹。
api里面是mraa的头文件,里面包含了各个函数的说明和声明,教我们怎么使用mraa的函数,这个库安装到Edison上api里的内容就对应Edison上/usr/include/mraa 和 /usr/include/mraa.h 和 /usr/include/mraa.hpp
src里面是api中各个函数的实现源码,对我们去了解底层很有帮助。
example里面是关于mraa的一些例子,有了这些例子我们就可以依葫芦画瓢,编写自己的应用代码了,这个很重要。
include里面是几个头文件,是关于mraa初始化的头文件。简单说就是让mraa知道自己工作的平台,大的分为arm架构和x86架构,arm架构下目前只有一个树莓派B版本,而在x86架构下则有因特尔的Edison和galileo平台。
今天,首先了解一下mraa下gpio的操作。
根据官网,我们先看一下mraa给gpio写了多少函数和这些函数的用法。在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引脚的结构。这里面包括了:
pin:gpio引脚号
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引脚关闭(既是disabled,linux里一切皆文件可以说关闭)。通过实现代码可以发现,主要处理的是解除引脚占用(unexport)和内存释(free)。
int mraa_gpio_read(mraa_gpio_context dev);读引脚电平(首先引脚必须是输入模式)。
mraa_result_t mraa_gpio_write(mraa_gpio_context dev, int value);写引脚电平(首先引脚必须是输出状态)。第二个参数是你要写入的值0或1
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);//mraa的gpio初始化,此处是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
然后运行程序,./gpio_read6
当io6接地的时候,读出的数据如下:
当io6接到3.3v的时候,独处数据如下:
当io6什么都不接的时候,读出来的数据有时候是1有时候是0:
下面是一个写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_C是Types.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下各个文件的操作,像direction,value。而mode的设置/sys/kernel/debug/gpio_debug/下面的文件里面,比如/sys/kernel/debug/gpio_debug/gpio28/current_pinmux
这里面当然还有一些不明白的地方:
1、比如是如何判断属于x86还是arm?
2、一个板子上的io设置牵涉到好几个系统上gpio的设置,这几个系统上的gpio的设置在哪里?
3、由于知识有限,还有一些地方没有说明白,已经标注了,希望知道的朋友给以之指点。