Contents
5.2. PWM Example
1. Abstract
This white paper explains the general usage of the MRAA APIs that can greatly simplify working with various types of devices, such as:
Instead of complete program examples, this document contains excerpts of C code that demonstrate the central principles of the MRAA APIs. To get the most out of the information in this document, you should know or have the following things in hand:
Note: This document does not explain compiling or linking code, or installing software on a given platform.
2. MRAA Overview
MRAA (pronounced em-rah) is a low-level library written in the C language. The purpose of MRAA is to abstract the details associated with accessing and manipulating the basic I/O capabilities of a platforms, such as the Intel® Galileo or Intel® Edison boards, into a single, concise API. See below for more details:
2.1. Obtaining MRAA APIs and API Documentation
The MRAA package is already installed on Intel Galileo and Edison hardware and can be linked into your code as described below. You can also obtain recent versions from the Intel source code repository.
API documentation is available at: http://iotdk.intel.com/docs/master/mraa/
2.2. GPIO Pin Names
This white paper refers to various “pins.” These hardware pins are typically referred to by a number. Pin numbers may also be prefixed by a character, such as “D” for digital or “A” for analog. For example, “D0” would refer to the digital pin 0, while “A3” would refer to the analog input pin 3. A pin may also be referenced as something like GPIO6, referring to GPIO pin 6, without the “D” or “A” reference to analog or digital.
2.3. General MRAA Usage
Before beginning to code, certain guidelines should be followed:
The MRAA library must be linked into your software. Typically, you must first initialize MRAA with a statement, such as:
1 mraa_result_t rv;
2 rv = mraa_init();
3 if (rv != MRAA_SUCCESS)
4 <report the error, insert your own code below>
5 . . .
2. Many MRAA functions return an mraa_result_t value. It is important for you to confirm that the specific function succeeded.
3. The examples in this document do not provide error-checking. It is highly recommended that you error-check everything.
4. After initialization, you should indicate to MRAA that you will use a specific pin for a given purpose. The code examples used later in this document demonstrate this action.
5. Once done (that is, before exiting the program), you should tell MRAA to release the pin(s) so that it cleans up any internal states. The code examples used later in this document demonstrate this action.
2.4. MRAA Include Files
The main Include file used for MRAA is mraa.h, along with any device-dependent Include files, for example:
#include <mraa.h>
#include <mraa/aio.h>
#include <mraa/gpio.h>
3. Using MRAA with Analog Devices
An analog device is one that provides data in the form of a changing voltage level from 0 to the highest voltage supported. This data is typically referred to as the Analog Reference Voltage (AREF). For example, an analog pressure sensor may provide data as a voltage starting at 0 (indicating no pressure) and then increasing as the pressure increases. This sensor’s voltages are then interpreted and converted to a digital number by a device called an Analog to Digital Converter (ADC). The software controlling the sensor then reads this number generated by the ADC.
The information below is important to have when interpreting data from an analog sensor. These items are discussed individually in the following subsections.
1. The voltage reference (AREF) value
2. The resolution of the ADC
3. How to interpret the data (depends on the type of sensor)
3.1. Voltage Reference
The voltage reference is typically 3.3 volts or 5.0 volts DC. However, the reference voltage may vary because some platforms, such as the Intel® Edison board, allow you to generate a custom AREF for the platform instead of using its own built-in AREF. Therefore, you need to know this AREF before you can obtain meaningful data from such devices.
3.2. Analog to Digital Converter (ADC) Resolution
The ADC resolution is very important, as it can determine the precision of your measurements. All ADCs in use on the Intel platforms support resolutions of 1024 (10 bits), and at least in the case of the Intel® Edison board, 4096 (12 bits).
You can determine the approximate voltage “steps” that can be represented by dividing the AREF value by the ADC resolution. This number is then used by your application. To determine the voltage steps, divide the AREF of 5.0 volts by the ADC resolution of 1024. The result is that each step returned from the ADC represents approximately 4 millivolts.
5.0 / 1024.0 = 0.00488 volts
3.3. Interpreting the Data
With the information above, you can determine the approximate voltage level present on the device’s analog input pin. The higher the ADC resolution, the more precise your voltage measurement will be.
3.4. Analog Example
The Grove Moisture sensor (http://www.seeedstudio.com/depot/Grove-Moisture-Sensor-p-955.html) is an example of a simple analog device. It is basically a resistor that changes the voltage level on the analog input pin based on how much moisture it detects. The example below demonstrates how the sensor works by connecting it to the A0 pin. The following code example shows how to initialize MRAA and the A0 pin, read and print its value, and then release the pin:
01 int main()
02 {
03 /* initialize MRAA */
04 mraa_init();
05 /* create an MRAA analog context */
06 mraa_aio_context m_aio;
07 /* initialize A0 for use as an analog input */
08 m_aio = mraa_aio_init(0);
09
10 /* read the value, an integer */
11 int value;
12 value = mraa_aio_read(m_aio);
13
14 /* print the value */
15 printf(“The value returned was: %d\n”, value);
16
17 /* now release (close) the pin and exit */
18 mraa_aio_close(m_aio);
19 return(0) ;
With an ADC resolution of 1024 (10 bits), the value returned will range anywhere from 0-1023. How you interpret this value is determined by the sensor’s design. In the Grove Moisture sensor example, assuming a 10-bit ADC resolution, the data sheet provides the following ranges for the dry, humid, and wet property values:
Important: Each sensor is different and not all sensors are as easy to decode. Be aware of the following items:
1. Some sensors are “jittery” (their output voltages fluctuate). If this is the case, it may be necessary to take several sample readings and then compute their average.
2. If you are writing an MRAA driver for use on different platforms, it is important that you specify the correct AREF voltage in your code, and possibly the ADC resolution as well, that will be used in any computations. Otherwise, the data returned may not be usable. In the above example, we did not need to know the AREF voltage, but that is not the case for other more complex analog devices. On some devices, the precise value of the AREF voltage and ADC resolution will be required in order to determine the sensor's output.
4. Using MRAA with Digital Devices
A digital device is one that deals in LOW and HIGH values, where LOW refers to a low voltage level and HIGH refers to a high voltage level. Only two states can be represented. For example, in a 5.0v system, LOW might refer to 0 volts and HIGH might refer to 5.0 volts. Typically, however, HIGH is represented by a 1, and LOW is represented by a 0.
20
}
Digital devices can be set up as an input or an output. For use as an input device, you would use MRAA to read the digital pin and return a value indicating whether the voltage was LOW or HIGH. Conversely, writing to a digital device causes the digital pin to be driven LOW or HIGH.
MRAA provides an API for reading and writing the states of a digital pin. In addition, it is possible to attach a user supplied interrupt handler to a digital input. Interrupt handlers are discussed on page 9. To use MRAA’s digital input and output facilities you need this header file:
#include <mraa/gpio.h>
4.1. Digital Input Example
As an example, take a simple digital input device such as a push button switch. When the switch is depressed, a HIGH voltage level is placed on the pin, otherwise a LOW level voltage is present.
01 int main()
02 {
03 /* initialize MRAA */
04 mraa_init();
05
06 /* create an MRAA digital context */
07 mraa_ai o_context m_aio;
08
09 /* initialize D2 for use as a digital pin */
10 m_gpio = mraa_gpio_init(2);
11
12 /* configure the digital pin as an input */
13 mraa_gpio_dir(m_gpio, MRAA_GPIO_IN);
14
15 /* read the value into an integer */
16 int value = mraa_gpio_read(m_gpio);
17
18 /* print the value */
19 if (value != 0)
20 printf(“The button is being pushed\n”);
21 else
22 printf(“The button is not being pushed\n”);
23
24 /* now release (close) the pin and exit */
25 mraa_gpio_close(m_gpio);
26 return(0);
27 }
As you can see, this is pretty straightforward. Notice also how we told MRAA to configure the pin as an input using mraa_gpio_dir(). Digital pins can be used for either input or output, unlike analog pins, which are always input only.
4.2. Interrupt Handlers
In some cases, you will not want to have to keep re-reading a digital pin to determine its status. Take for example a sensor that is attached to a motor for the purpose of counting revolutions per minute (RPM). In this case, it would be somewhat painful and error prone to keep re-reading the pin (the device) to detect changes constantly.
MRAA provides the ability to create an interrupt handler and attach it to a pin. In this case, MRAA will ensure that your interrupt handler will be called whenever a specified change of the pin's state/transition occurs (LOW to HIGH or HIGH to LOW).
With this capability, it is easy to write a simple counting function and tell MRAA to call this function whenever a specified transition occurs. Below is a simple example to count HIGH to LOW transitions and keep them in a counter.
01 /* first, create our counting variable */
02 volatile int counter = 0;
03
04 /* Now our simple counting function. */
05 /* This will be our interrupt handler. */
06 void intrHandler(void *arg)
07 {
08 counter++;
09 }
10
11 /* now in our main() function */
12
13 int main()
14 {
15
16 /* initialize MRAA */
17 mraa_init();
18
19 /* create an MRAA digital context */
20 mraa_aio_context m_aio;
21
22 /* initialize D2 for use as digital pin */
23 m_gpio = mraa_gpio_init(2);
24
25 /* configure the digital pin as an input */
26 mraa_gpio_dir(m_gpio, MRAA_GPIO_IN);
27
28 /* now, setup an interrupt handler. */
29 /* Our function (intrHandler()) above will */
30 /* be called whenever the pin goes from */
31 /* HIGH to LOW */
32 */
33 mraa_gpio_isr(m_gpio, MRAA_GPIO_EDGE_FALLING, intrHandler, NULL);
34
35 /* sleep for 5 seconds, and then print out the current */
36 /* value of counter */
37 sleep(5);
38
39 printf(“Counter = %d\n”, counter);
40
41 /* now, stop the interrupt handler and cleanup */
42 mraa_gpio_isr_exit(m_gpio);
43
44 /* now release (close) the pin and exit */
45 mraa_gpio_close(m_gpio);
46 return(0);
Observe the following in the example above:
4.3. Digital Output Example
As you can imagine, digital output is pretty straight forward. The example below causes a digital pin to be driven HIGH (1) and LOW (0) with a 1 second sleep in between. Something like this could be used to blink an LED connected to the pin.
01 int main()
02 {
03 /* initialize MRAA */
04 mraa_init();
05
06 /* create an MRAA digital context */
07 mraa_aio_context m_aio;
08
09 /* initialize D13 for use as a digital pin */
10 m_gpio = mraa_gpio_init(13);
11
12 /* configure the digital pin as an output */
13 mraa_gpio_dir(m_gpio, MRAA_GPIO_OUT);
14
15 /* now run in a loop 10 times, blinking the output each second */
16 int i;
17 for (i=0; i<10; i++)
18 {
19 /* turn output on (HIGH) */
20 mraa_gpio_write(m_gpio, 1);
21 sleep(1);
22 /* turn output off (LOW) */
23 mraa_gpio_write(m_gpio, 0);
24 sleep(1);
25 }
26
27
28 /* now release (close) the pin and exit */
29 mraa_gpio_close(m_gpio);
30 return(0);
31 }
As you can see, using digital I/O is pretty easy with MRAA. Interrupt handling is a little more complex, but as long as you are careful about the proper use of the volatile keyword for variables shared outside of the interrupt handler, it is simple and convenient to use for many applications.
5. Using MRAA with Pulse Width Modulation (PWM) and Digital Devices
Pulse Width Modulation (PWM) is a type of digital output. The output of a PWM pin is composed of two elements, the period and the duty cycle:
For example, if you have set a period of 2 milliseconds, and a duty cycle of 50%, then you will get a repeating pattern of 1ms HIGH and 1ms LOW, and then the period repeats. This capability can be used for a variety of functions, such as dimming an LED or controlling the speed of a motor by increasing or decreasing the duty cycle.
5.1. General Rules of Use
MRAA provides the ability to configure a digital output to act as a PWM output. It is important to check your platform to find out which GPIOs can be used as a PWM output. This will vary from platform to platform.
Platforms vary in the lengths of the periods allowed; therefore, it's important to error check your MRAA calls.
Some devices have requirements for the lengths of the periods that can be used with them. For example, a servo motor will typically require a period of 20ms.
The header needed for using MRAA's PWM facilities is:
#include <mraa/pwm.h>
5.2. PWM Example
In the example below, let’s pulse the brightness of an LED. We will do this by setting a period of 10 milliseconds and changing the duty cycle up and down every 100 milliseconds.
01 int main()
02 {
03 /* initialize MRAA */
04 mraa_init();
05
06 /* create an MRAA PWM context */
07 mraa_pwm_context m_pwm;
08
09 /* initialize D3 for use as a digital pin */
10 m_pwm = mraa_gpio_init(3);
11
12 /* set the period to 10ms */
13 mraa_pwm_period_ms(m_pwm, 10);
14
15 /* set the initial duty cycle to 0 */
16 mraa_pwm_write(m_pwm, 0.0);
17
18 /* enable PWM output */
19 mraa_pwm_enable(m_pwm, 1);
20 /* now run in a loop 10 times, dimming or brightening /*
21 /* the LED every 100ms */
22
int i;
23 float duty = 0.0;
24 for (i=0; i<10; i++)
25 {
26 /* first, start at 0% duty cycle and increase to 100% */
27 for (duty= 0.0; duty < 1.0; duty+=0.1)
28 {
29 mraa_pwm_write(m_pwm, duty);
30 usleep(100000);
31 }
32 sleep(1);
33 /* now decrease it to 0% */
34 for (duty= 1.0; duty > 0.0; duty-=0.1)
35 {
36 mraa_pwm_write(m_pwm, duty);
37 usleep(100000);
38 }
39 sleep(1);
40 }
41
42 /* disable PWM output and clean up */
43 mraa_pwm_enable(m_pwm, 0);
44
45 mraa_pwm_close(m_pwm);
46 return(0);
47 }
Observe the following in the example above:
6. Using MRAA with Inter-Integrated Circuits (I2C)
When using I2C, keep the following points in mind:
#include <mraa/i2c.h>
6.1. I2C Example
In the example below, we query a real time clock I2C device module (the DS1307) and read the seconds register (0x00). We setup an MRAA I2C context on I2C bus 0, using the I2C address 0x68, and then read in the seconds register and print it every second for 10 seconds.
Important: Many I2C devices have different requirements in terms of how data should be written to or read from the device. Refer to the device specifications for this information.
01 int main()
02 {
03 /* initialize MRAA */
04 mraa_init();
05
06 /* create an MRAA I2C context */
07 mraa_i2c_context m_i2c;
08
09 /* initialize I2C on bus 0 */
10 m_i2c = mraa_i2c_init(0);
11
12 /* now run in a loop 10 times, reading the seconds */
13 /* register and printing it.*/
14 int i;
15 for (i=0; i<10; i++)
16 {
17 char buf;
18
19 /* always specify the address */
20 mraa_i2c_address(m_i2c, 0x68);
21 /* read in 1 byte. mraa_i2c_read() always reads */
22 /* starting at register 0x00 */
23 mraa_i2c_read(m_i2c, &buf, 1);
24
25 printf(“The seconds returned was: %d\n”, buf);
26 sleep(1);
27 }
28 mraa_i2c_stop(m_pwm);
29 return(0);
30 }
7. Using MRAA with Universal Asynchronous Receivers/Transmitters (UART)
Note: MRAA will only arrange the proper routing of the pins to connect to a hardware UART; it is up to your software to open and setup the correct TTY device and begin communicating with it.
The header needed for using MRAA's UART facilities is:
#include <mraa/uart.h>
7.1. UART Example
In the example below, we use a mythical UART based sensor attached to pins D0 and D1. UARTs are numbered in MRAA, so this will correspond to UART 0.
It is important that, after opening the device, you properly enable and disable various line disciplines that are automatically applied to the serial device by the Linux kernel. We include a function called setupTTY()to do this, after we open the TTY device itself.
01 int setupTTY(int fd, speed_t baud)
02 {
03 if (fd < 0)
04 return 0;
05
06 struct termios termio;
07
08 /* get current modes */
09 tcgetattr(fd, &termio);
10
11 /* setup for a 'raw' mode. 8bit, 1 stop bit, no parity, */
12 /* no echo or special character handling, */
13 /* no flow control or line editing semantics. */
14
15 cfmakeraw(&termio);
16
17 // set our baud rates
18 cfsetispeed(&termio, baud);
19 cfsetospeed(&termio, baud);
20
21 // make it so
22 if (tcsetattr(fd, TCSAFLUSH, &termio) < 0)
23 {
24 fprintf(stderr, “%s\n”, “tcsetattr failed”);
25 return 0;
26 }
27
28 return 1;
29 }
30
31 /* now our main function */
32 ¬
33 int main()
34 {
35 /* initialize MRAA */
36 mraa_init();
37
38 /* create an MRAA UART context */
39 mraa_uart_context m_uart;
40
41 /* initialize UART 0 (pins D0 and D1 used for TX and RX) */
42 m_uart = mraa_uart_init(0);
43
44 /* now that we have our context, query MRAA */
45 /* to get the file name of the TTY device we need to open. */
46
47 char *devPath = mraa_uart_get_dev_path(m_uart);
48
49 /* if this fails, we can go no further */
50 if (!devPath)
51 {
52 fprintf(stderr, “%s\n”, “Could not get device path”);
53 return 0;
54 }
55
56 /* now that we have a device path, open it and set it up */
57 int fd;
58 if ((fd = open(devPath, O_RDWR)) == -1)
59 {
60 fprintf(stderr, “%s\n”, “Could not open device path”);
61 return 0;
62 }
63
64 /* now we are almost ready, call setupTTY() and from then on */
65 /* we can read/write to the device normally. */
66 /* We assume a baud rate of 9600/ */
67 if (!setupTTY(fd, B9600))
68 {
69 fprintf(stderr, “%s\n”, “Could not setup TTY port”);
70 return 0;
71 }
72
73 /* now we can use standard read and write calls */
74 /* read(fd, …) or write(fd, …) */
75
76 /* when we are done, close the device and exit */
77 close(fd);
78
79 return(0);
80 }
Observe the following in the example above:
8. Closing
MRAA simplifies that process of accessing and manipulating the basic I/O capabilities of a platform like Intel® Galileo or Intel® Edison boards. A powerful library, MRAA provides a consistent approach to using analog, digital, PWM, I2C, and UART devices with these and similar platforms. It is a key addition to anyone’s Internet of Things tool chest.
For more such intel IoT resources and tools from Intel, please visit the Intel® Developer Zone
Source: https://software.intel.com/en-us/articles/internet-of-things-using-mraa-to-abstract-platform-io-capabilities