This article describes the MRAA library, which can greatly simplify working with various types of devices that use:
- analog input;
- digital input and output;
- pulse width modulation (PWM);
- two-wire bus I2C;
- Universal Asynchronous Receiver-Transmitter (UART);
- interrupts when input level changes.

Instead of examples with complete programs, we will consider small fragments in the C language, which will show the basic principles of the MRAA API. To get more information from this article, you should know the following things:
- easy to master Linux and C;
- have basic knowledge of digital electronics and the use of GPIO (general-purpose input / output - general purpose inputs and outputs);
- have documentation on the devices you use.
This article does not explain how to compile, build a program, and install applications on this platform.
')
1. Overview MRAA
MRAA (pronounced "em-ra") is a low-level library written in C. It is designed to abstract from the details associated with accessing and controlling I / O on a platform such as Intel Galileo or Intel Edison, by introducing one small API .
- MRAA works as a transition layer for basic input / output port (GPIO) capabilities in Linux. Despite the fact that Linux offers a fairly rich infrastructure for managing GPIO, and typical commands for working with GPIO are standard, it can still be difficult to use them.
- All platforms are different. Each has different features, pure pins and GPIO types. For example, a pin may not support the same features for two different platforms. Pin may not exist on this platform. Also, the way it is configured may depend on many factors. For example, using a pin in one mode may interfere with the work of another pin in another mode or prohibit its use altogether. And since the MRAA library allows you to create platform-independent code, it makes development less complicated.
- It must be remembered that although MRAA can be used to write platform-independent code, the developer is still responsible for ensuring that the code is resilient to the limitations of all platforms on which it can be run.
1.1 Downloading the MRAA Library and API Documentation
The MRAA package is already installed on Intel Galileo and Intel Edison and can be connected to your code as shown below. You can also download the latest source code from the Intel repository.
API documentation is available at
http://iotdk.intel.com/docs/master/mraa/1.2 GPIO pin names
There are various “pins” in this article. The hardware pins are usually referred to by number. The pin number can also begin with the letter “D” for the digital type and the letter “A” for the analog type. For example, “D0” will point to digital pin # 0, and “A3” on analog input pin # 3. Pin can also be specified as GPIO6, i.e. This is GPIO-pin number 6, without specifying its type “D” or “A”, i.e. It can be both digital and analog.
1.3 Library Linking
When compiling, link the library with the command –lmraa, for example:
gcc -g blink.c -o blink -lmraa
1.4 Using MRAA
Before writing code, remember the following.
1. At the beginning of the program, the MRAA should be initialized as follows:
mraa_result_t rv; rv = mraa_init(); if (rv != MRAA_SUCCESS) . . .
2. Many MRAA functions return a result of type mraa_result_t. It is very important to make sure that the function call occurred without errors.
3. The examples in this article do not perform error checking, but it is still highly recommended to do this.
4. After initialization, you must specify the MRAA, which way you want to use the pin (input / output, digital / analog / PWM / AURT). Below will be shown how to do it.
5. When the work is completed (for example, at the end of the program), MRAA must be released to release the pins occupied so that it resets all internal states and settings. Below will be shown how to do it.
1.5 Connecting MRAA Header Files
The main MRAA include file is mraa.h. It connects with other hardware-dependent header files. For example:
The code for an analog device should include:
#include <mraa.h> #include <mraa/aio.h>
The code for a digital device should include:
#include <mraa.h> #include <mraa/gpio.h>
2. Using MRAA with analog devices
An analog device is a device that receives data by measuring the pin voltage value, varying from 0 to the maximum supported. This maximum voltage is called the reference voltage (AREF - Analog Reference Voltage). For example, an analog pressure sensor can provide a voltage value starting at 0 (corresponding to no pressure), and increasing with increasing pressure. This voltage on the sensor is converted into a number by a device called an ADC (Analog-to-digital converter, ADC - analog-to-digital converter). The program that works with the sensor reads this number, issued from the ADC.
2.1 Reference Voltage
The reference voltage is usually 3.3 V or 5.0 V DC. However, the reference voltage may differ, as some platforms, such as Intel Edison, allow you to set a different reference value instead of using the built-in value. Therefore, you need to know the exact value of the reference voltage before you receive data from the device.
2.2 bit ADC
The ADC resolution is very important, as it determines the accuracy of your measurements. All ADCs used on the Intel platform are 10 bits wide (1024 values). And at least in the case of Intel Edison 12 bits (4096 values).
You can determine the approximate step of voltage measurement accuracy by dividing the value of the reference voltage by the number of available values. This value can then be used in the application. For example, with a reference voltage of 5 V and a bit width of 10 bits, we find that the ADC step is approximately 5 mV. Since 5.0 / 1024 = 0.00488 V
2.3 Interpretation of data
Using the previously described information, you can determine the approximate voltage that is on the analog pin. With a higher ADC resolution, it is possible to measure voltage with greater accuracy.
2.4 Analog Example
Grove's humidity sensor is an example of a simple analog device. It is simply a resistor that changes the voltage level at the analog input according to which moisture level was determined. The following example shows how the sensor works when it is connected to pin A0. The program shows how to initialize MRAA and pin A0, read the value, print it and then release the pin.
int main() { mraa_init(); mraa_aio_context m_aio; m_aio = mraa_aio_init(0); int value; value = mraa_aio_read(m_aio); printf(“The value returned was: %d\n”, value); mraa_aio_close(m_aio); return(0) ; }
With an ADC of 10 bits, the return value will be from 0 to 1023. How to interpret the value depends on the sensor device. For the Grove sensor and the ADC bit depth of 10 bits, the documentation gives the following ranges for the state of dry, wet and damp.
- 0-299 = dry
- 300-699 = wet
- 700-1023 = wet
It is important to remember that all sensors are different, and their values ​​can be difficult to decode. Consider the following:
1. Some sensors have jitter (jitter). In this case, you need to obtain several consecutive values ​​from the sensor and find the average value.
2. If you are writing a MRAA driver for use on different platforms, it is important that you correctly specify the reference voltage and resolution of the ADC that will be used in the calculations. Otherwise, the resulting data may be useless. In the previous example, we did not need to know the reference voltage, but this does not apply to other more complex analog devices. On some devices, the exact value of the reference voltage and the resolution of the ADC are required to determine the value received from the sensor.
3. The value on the sensor is not always linearly related to the measured parameter. In this case, you need to look in the documentation for the formulas for the translation of values ​​or use additional libraries, for example, UPM.
3. Using MRAA with digital devices
Digital devices deal with high and low signal values. Only these two values ​​are used. For example, in a system operating at a voltage of 5 V, a low level may correspond to 0 V, and a high level of 5 V. Usually a high level is indicated by 1 and a low level by 0.
Digital devices can be configured for input and output. To be used as an input device, you need to use MRAA to read from a digital pin and return a value indicating whether the voltage was high or low.
MRAA provides an API for reading and writing the status of a digital pin. Additionally, you can connect an interrupt handler to a digital input. To use the digital input and output capabilities in MRAA, you need to include a header file.
#include <mraa/gpio.h>
3.1 Example of using a digital input
For example, take a simple digital device, a button. When the button is not pressed, it has a high voltage, when pressed it is low.
int main() { mraa_init(); mraa_ai o_context m_aio; m_gpio = mraa_gpio_init(2); mraa_gpio_dir(m_gpio, MRAA_GPIO_IN); int value = mraa_gpio_read(m_gpio); if (value != 0) printf(“The button is being pushed\n”); else printf(“The button is not being pushed\n”); mraa_gpio_close(m_gpio); return(0); }
As you can see, everything is quite simple. Notice how we tell MRAA to set the pin to enter using the mraa_gpio_dir () function. Digital pins can be used for both input and output, as opposed to analog pins, which can only work on input.
3.2. Interrupt handlers
Sometimes you do not want to repeat reading the value on the pin to determine the change in its state. Take for example the sensor that is connected to the motor to count the revolutions per minute. In this case, it would be unwise to constantly keep reading the state of a pin to determine changes.
MRAA offers the possibility to create an interrupt handler and connect it with a pin. In this case, the MRAA guarantees that your function will be called whenever a specified state change occurs (from 0 to 1 or from 1 to 0).
Using this feature, it is easy to write the counting function, and tell MRAA to call it when the state changes. Below is a simple example of counting the number of transitions from a high signal state to a low one.
volatile int counter = 0; void intrHandler(void *arg) { counter++; } int main() { mraa_init(); mraa_aio_context m_aio; m_gpio = mraa_gpio_init(2); mraa_gpio_dir(m_gpio, MRAA_GPIO_IN); */ mraa_gpio_isr(m_gpio, MRAA_GPIO_EDGE_FALLING, intrHandler, NULL); sleep(5); printf(“Counter = %d\n”, counter); mraa_gpio_isr_exit(m_gpio); mraa_gpio_close(m_gpio); return(0); }
Note the following in this example:
- The counter variable is defined with the volatile keyword. It is very important. The volatile keyword tells the C compiler that the variable can be changed without his knowledge and prohibits it from doing any optimizations with it. Without volatile, the compiler may decide that the variable does not change at all, and will always output "0". Any variable that is handled inside the interrupt handler and simultaneously outside of it must be marked as volatile.
- In addition to MRAA_GPIO_EDGE_FALLING, the mraa_gpio_isr () function also supports MRAA_GPIO_EDGE_RISING (to define a transition from LOW to HIGH), and MRAA_GPIO_EDGE_BOTH (to define both transitions). Please note that not all types of transitions are supported by all platforms. The easiest way to determine what is supported is to check the error code returned by the mraa_gpio_isr () function.
- The last argument passed to mraa_gpio_isr () is a pointer. This argument will be passed to the intrHandler () interrupt handler. It is optional and can be used to pass additional parameters to the handler function. In our example, we do not need such features, and we simply pass NULL.
- Calling mraa_gpio_isr_exit () disables the interrupt handler, and it is always recommended to call it in order to avoid any unexpected surprises.
- Only one interrupt handler can be connected to a specific pin. The total number of pins that can control interrupts at one time depends on the platform, as well as the type of interrupts that can be used (MRAA_GPIO_EDGE_RISING, MRAA_GPIO_EDGE_FALLING or MRAA_GPIO_EDGE_BOTH). This is another reason to check the error code when calling MRAA functions.
3.3 Example of digital output
Using digital output is easy. The following example changes the signal from high (1) to low (0) on a digital output with a period of 1 second. Something similar can be used to blink an LED connected to a pin.
int main() { mraa_init(); mraa_aio_context m_aio; m_gpio = mraa_gpio_init(13); mraa_gpio_dir(m_gpio, MRAA_GPIO_OUT); int i; for (i=0; i<10; i++) { mraa_gpio_write(m_gpio, 1); sleep(1); mraa_gpio_write(m_gpio, 0); sleep(1); } mraa_gpio_close(m_gpio); return(0); }
As you can see, with MRAA it is very simple to use digital I / O. Interrupt handling is a bit more complicated, but the main thing here is to carefully use the volatile keyword for variables that you work with outside of the interrupt.
4. Using MRAA for Pulse Width Modulation (PWM)
Pulse width modulation (PWM, PWM - Pulse-width modulation) is a type of digital output. PWM is defined by two parameters, the period and the fill factor:
- the period specifies how often the impulse must be generated;
- the fill factor indicates which part of the period should be in a high state.
For example, if you set the period to 2 ms, and the fill factor is 50%, then you will receive repeated changes: 1 ms the signal will be high and 1 ms low. Changing the fill factor can be used for various functions, such as controlling the brightness of the LED or the speed of rotation of the motor.
4.1 Basic Terms of Use
- MRAA offers the ability to configure a digital output for use in PWM mode. It is important to check your platform to find out which pin can work in this mode. This may differ on different platforms.
- Platforms may vary in the length of the period that is available. Therefore, it is important to check for errors in calling MRAA functions.
- Some devices have requirements for the length of the period that can be used. For example, a servomotor usually operates with a period of 20 ms.
- Header file that must be connected to work with PWM:
#include <mraa/pwm.h>
4.2. An example of working with PWM
In the following example, we will change the brightness of the LED. We will do this by setting a period of 10 ms and will increase and decrease the fill factor every 100 ms.
int main() { mraa_init(); mraa_pwm_context m_pwm; m_pwm = mraa_gpio_init(3); mraa_pwm_period_ms(m_pwm, 10); mraa_pwm_write(m_pwm, 0.0); mraa_pwm_enable(m_pwm, 1); int i; float duty = 0.0; for (i=0; i<10; i++) { for (duty= 0.0; duty < 1.0; duty+=0.1) { mraa_pwm_write(m_pwm, duty); usleep(100000); } sleep(1); for (duty= 1.0; duty > 0.0; duty-=0.1) { mraa_pwm_write(m_pwm, duty); usleep(100000); } sleep(1); } mraa_pwm_enable(m_pwm, 0); mraa_pwm_close(m_pwm); return(0); }
Note the following in this example:
- We used the mraa_pwm_write () function which takes a floating-point value with a value of 0.0 (turned off, or a fill factor of 0%) and 1.0 (turned on, or a fill factor of 100%).
- MRAA also provides a set of functions that allow you to directly set the duration of the fill factor, and not as percentages. This can be useful for devices that have specific requirements for the period and the amount of time that the signal is in the HIGH state, for example, a servomotor.
- In MRAA, there are functions for specifying a period in seconds, milliseconds (as in the example above), and microseconds. MRAA also has a function that allows you to specify a period and a fill factor with one call.
- The main problem is to find out if your platform supports the required period. MRAA will return the corresponding error if this period is not supported.
5. Using MRAA with Inter-Integrated Circuits (I2C)
Using I2C, remember the following:
- I2C is a two-wire bidirectional bus. It can operate at a frequency of 100 kHz, 400 kHz, 3.4 MHz.
- I2C consists of two signal lines - SCL (clocking) and SDA (data).
- I2C devices have an address that must be unique on the I2C bus. Multiple devices can be connected to one bus, but each must have a unique address, and only one of them can be connected at a time.
- In addition to the address, I2C devices can have a set of registers (sometimes called commands) into which you can write or read. Writing and reading these registers enables communication and control.
- Header file for working with I2C
#include <mraa/i2c.h>
5.1 Example of working with I2C
In the following example, we will work with I2C real time clock (DS1307) and read the value from the register of seconds. We set the I2C MRAA context on the I2C-bus 0, using the address 0x68, and read the second register. The resulting value will be printed every 10 seconds.
It is important to remember that many I2C devices have different requirements, for example, how data should be written or read. Therefore, the details of the work check in the documentation
int main() { mraa_init(); mraa_i2c_context m_i2c; m_i2c = mraa_i2c_init(0); int i; for (i=0; i<10; i++) { char buf; mraa_i2c_address(m_i2c, 0x68); mraa_i2c_read(m_i2c, &buf, 1); printf(“The seconds returned was: %d\n”, buf); sleep(1); } mraa_i2c_stop(m_pwm); return(0); }
See the example above:
- We used the mraa_i2c_read () function, which starts reading from register 0x00. For more flexible use, MRAA offers several more functions for reading and writing to registers. It is also possible to write and read data width of 16 bits. The method to use depends on your device and software requirements.
- When you read a 16-bit word, you may have to change the byte order if the device has such a requirement.
- Some access methods work better on some devices. For example, the function mraa_i2c_read () functioned incorrectly on one of our proven devices, and it was necessary to use mraa_i2c_read_byte_data (). Therefore, in the design may require additional experiments.
6. Using MRAA with Universal Asynchronous Receivers / Transmitters (UART)
- A UART based device is essentially a regular serial device. Like the devices we connected to the COM port in the past. This is usually a full duplex connection that operates at a certain speed, measured in baud. Standard speed for many sensors is 9600 baud (or 9600 bps).
- In fact, you have two wires. TX for transmission and RX for reception. It should be borne in mind that in the case of most sensors, you will use different voltage levels than those used on a traditional COM port that is compatible with the RS232 standard. Usually these voltages are 5 V or 3.3 V, while the RS232 COM port uses -15V ... 15V. Do not connect such a low voltage device directly to the COM port unless it is specifically supported by the manufacturer of the sensor. So how can you mess up your device.
- MRAA offers a mechanism when two pins D0 and D1 (Intel Galileo and Intel Edison) can implement a hardware UART, allowing the program to simply read and write data on a device with UART access. When MRAA is configured with the correct pins, you can communicate with the device via / dev / ttyX using standard Linux read () and write () functions. Consider that MRAA only configures the correct routing of the pins for connecting the UART, and your program should open and properly configure the TTY device and start working with it.
- Header file for using UART:
#include <mraa/uart.h>
6.1 UART example
In the following example, we use a fictional UART-based sensor connected to D0 and D1. It will be denoted by UART 0.
It is important that after opening the device the settings that were made automatically by the Linux kernel are correctly changed. We added a setupTTY () function that performs these actions after opening a TTY device.
int setupTTY(int fd, speed_t baud) { if (fd < 0) return 0; struct termios termio; tcgetattr(fd, &termio); cfmakeraw(&termio); // set our baud rates cfsetispeed(&termio, baud); cfsetospeed(&termio, baud); // make it so if (tcsetattr(fd, TCSAFLUSH, &termio) < 0) { fprintf(stderr, “%s\n”, “tcsetattr failed”); return 0; } return 1; } int main() { mraa_init(); mraa_uart_context m_uart; m_uart = mraa_uart_init(0); char *devPath = mraa_uart_get_dev_path(m_uart); if (!devPath) { fprintf(stderr, “%s\n”, “Could not get device path”); return 0; } int fd; if ((fd = open(devPath, O_RDWR)) == -1) { fprintf(stderr, “%s\n”, “Could not open device path”); return 0; } if (!setupTTY(fd, B9600)) { fprintf(stderr, “%s\n”, “Could not setup TTY port”); return 0; } close(fd); return(0); }
:
- UART-, , MRAA , Unix , read() write().
- read() write() , . , , , . select(). UPM wt5001 . dataAvailable().
- , 9600 . B9600 Linux'. . , .
- setupTTY(), , . , .
7.
MRAA - Intel Galileo Intel Edison. , MRAA , , , I2C UART . .