Exploring Edison - Mraa GPIO
Written by Harry Fairhead   
Monday, 13 July 2015
Article Index
Exploring Edison - Mraa GPIO
Phased pulses
Interrupts
Program and Summary

Output

The mraa functions that you need to make use of a pin in output mode are very simple. 

You need

mraa_gpio_init (mraa_pin)
mraa_gpio_init_raw (SYSFS_gpiopin)

To set the pin up using either mraa number or SYSFS number. 

You also need

mraa_gpio_dir (pin,dir)

to see the pin to output. 

At its simplest direction is 0 for output and 1 for input but there is an enumeration you can use:

MRAA_GPIO_OUT = 0 
MRAA_GPIO_IN = 1
MRAA_GPIO_OUT_HIGH = 2
MRAA_GPIO_OUT_LOW = 3

The last two, dir=2 and dir=3 set the line to output and initially high or low use if you need the line to be in a given state from the start. 

The only other function that is specifically concerned with output is:

mraa_gpio_mode (pin, mode)

The output mode can be any of:

MRAA_GPIO_STRONG = 0
MRAA_GPIO_PULLUP = 1
MRAA_GPIO_PULLDOWN = 2
MRAA_GPIO_HIZ = 3 

This works with the Arduino breakout board but not with the mini breakout.  

In this case the default is to use a 50K pull up resistor. 

The pullup mode is useful when you are working with devices that might also want to control the line. For example if you select pullup mode then the Edison only drives the line low. When the line is set to high the drive is switched off and the line floats up to the high voltage (1.8V) via the resistor. If the device connected to the line pulls the line down in this mode it will go to 0V even though you have set the output to a high state.

This is, of course, how simple serial busses share a single data line. 

You can gain direct control over the Edison's output mode and select strong, pullup, pulldown and you select the strenght of the pullup or down i.e. you select the resistor value used. However to do this you have to work outside of mraa and so this isn't covered here. 

Finally we have:

mraa_gpio_write (pin, value)

which sets the line to high or low 

Note that all of these functions make use of the SYSFS method of working with the GPIO. That is they are not interfacing directly with the GPIO driver. 

Phased Pulses

As a simple example of using the mraa output functions lets try to write a short program that pulses two lines - one high and one low and then one low and one high i.e. two pulse trains out of phase by 180 degrees.

The simplest program to do this job is

 

#include "mraa.h"
#include <stdio.h>
#include <unistd.h>
int main()
{
 mraa_init();
 mraa_gpio_context pin15 = mraa_gpio_init(15);
 mraa_gpio_dir(pin15, MRAA_GPIO_OUT_HIGH);
 mraa_gpio_context pin31 = mraa_gpio_init(31);
 mraa_gpio_dir(pin31, MRAA_GPIO_OUT_LOW);
 for (;;) {
  mraa_gpio_write(pin15, 0);
  mraa_gpio_write(pin31, 1);
  mraa_gpio_write(pin15, 1);
  mraa_gpio_write(pin31, 0);
 }
 return MRAA_SUCCESS;
}

Notice that there is no delay in the loop so the pulses are produced at the fastest possible speed. 

Mraa pin 15 is J18-2 and 31 is J19-4. Ground is J19-3. 

Using a logic analyzer reveals that the result isn't what you might expect:

phase

 

You can see that the pulse trains are not 180 degrees out of phase. The top train switches on and the bottom train takes about half a pulse before it switches off - the intent is for both actions to occur at the same time. The point is that it does take quite a long time to access and change the state of an output line. 

Of course if we include a delay to increase the pulse width then the delay caused by accessing the pin via SYSFS is a smaller proportion of the total and the lag isn't so important:

phase2

In this case the loop now has usleep(100) delays i.e. 100 microsecond pauses.

 for (;;) {
  mraa_gpio_write(pin15, 0);
  mraa_gpio_write(pin31, 1);
  usleep(100);

  mraa_gpio_write(pin15, 1);
  mraa_gpio_write(pin31, 0);
  usleep(100);

 }

You will notice that the pulses are now nearly 500 microseconds wide and they are changing at what looks like nearer to being the same time.

The point is that when you call usleep(100) you yield the thread to the operating system which might well schedule another thread to run - so you usually get a delay that is longer than 100 microsecond. More about how to get round this sort of problem in the next chapter. 

There is still a lag, but in many applications it might not be important. In other applications it could be crucial.

For example, if the two pulse trains were driving different halves of a motor controller bridge there would be a significant time when both were high - so shorting the power supply. It might only be for 10 microseconds but over time it could well damage the power supply.  Of course, any sensible, cautious, engineer wouldn't feed a motor control bridge from two independently generated pulse trains unless they were guaranteed not to switch both sides of the bridge on at the same time. 

There are ways of improving on this situation because most of the delays are due to mraa having to make use of SYSFS which is slow - see the next chapter. 

Basic Input

Reading the state of an input line 0 or 1.8V is easy. You set the direction to input and then make use of:

mraa_gpio_read (pin)

which returns a 0 if the input is 0 and 1 if it is 1.8V.

Simple but in most cases input isn't that easy for reasons that have to do with timing. 

The most common use case for input is just reading the state of a switch - open or closed. In other words the input line is either high or low according to the relatively slow change of state of the switch. You can read the state of such a slow changing line using polling - i.e. a loop that reads the input over and over:


#include "mraa.h"
#include <stdio.h>
#include <unistd.h>
int main()
{
 mraa_init();
 mraa_gpio_context pin31 = mraa_gpio_init(31);
 mraa_gpio_dir(pin31, MRAA_GPIO_IN);
 int in;
 for (;;) {
  in=mraa_gpio_read(pin31);
  printf("switch %d \n",in);
  usleep(1000*1000);
 }
 return MRAA_SUCCESS;
}

This reads the state of the line every second, usleep(n) pauses for n microseconds, and prints the result to the Eclipse console. If you connect the input line J19-4 to 1.8V J19-2 or to 0V J19-3 then you wil see the output change to 1 and back to 0. If you want to use a switch to do the job then you need a circuit something like:

 

switch

Notice that if you speed up the polling loop to take readings more often then there will come a point where the printf is the limiting factor.

Polling for input is a problem in that it often means that you program is tied up doing nothing but polling. This doesn't mean that the operating system won't suspend your program, run something else and then restart your program. Even with polling you cannot guarantee to respond to a change in an input line within a given time. 

The big problem with polling is that your program can't get on with doing anything else. Sometimes this doesn't matter because you need input as fast as possible for a short time. Sometimes it does matter and when it does the solution is to use an interrupt. 



Last Updated ( Thursday, 26 November 2015 )