Get access to the new Intel® IoT Developer Kit, a complete hardware and software solution that allows developers to create exciting new solutions with the Intel® Galileo and Intel® Edison boards. Visit the Intel® Developer Zone for IoT.
The Intel® Edison board contains an internal microcontroller unit (MCU) in addition to the CPU. In this article, I explain the benefits of using an internal MCU for both
- Real-time
- Power efficiency
MCU is important in several applications. For example, Linux* on the Intel® Edison board does not provide a real-time response when working with sensors. MCU support has been available on the Intel® Edison® Board Firmware Software Release since version 2.1.
Let’s look at the system-on-chip (SoC) used in the Intel® Edison compute module:
The SoC used in the Intel® Edison compute module includes two CPUs:
- Dual-core Intel® Atom™ processor running at 500 MHz. Labeled as Host CPU.
- MCU with Minute Intel® architecture running at 100 MHz. Labeled as MCU.
The Minute Intel architecture core is energy-efficient architecture, based on the Intel® 486 with added instructions for compatibility with the Intel® Pentium® processor. The MCU contains a rich I/O subsystem (GPIO, I2C, high-speed UART, DMA) and SRAM, and has access to all GPIO pins in the Intel® Edison compute module. The total SRAM size for code and data is 192 kb. The MCU is running with the real-time Viper* OS from WindRiver.
The MCU program works on the Viper core and controls the peripherals connected to the MCU, independently of the Intel Atom processor. For example, it can control the GPIO pins, communicate with sensors with I2C and UART protocols, and communicate with the Intel Atom processor.
Reasons to use the MCU on the Intel® Edison
Using the MCU is beneficial for two reasons: providing real-time microsecond delays and power efficiency.
The Intel Atom processor and Yocto Project* standard Linux* distribution do not support real-time applications “out of the box.”
The Linux application could be preempted by the scheduler, which leads to unacceptable and unpredictable delays, so it’s not possible to provide a real-time response.
The MCU runs a single application and a real-time OS, which makes it possible to provide a real-time response.
A real-time response is required for many sensors where the communication protocol depends on the strict compliance of short timings.
To connect these sensors to the Intel® Edison board without an internal MCU, you will need to use an external MCU. All the sensor communication is implemented in an external MCU in this case. For example, SparkFun Block* for Intel® Edison board – Arduino extension board provides external MCU functionality. However, the use of an external MCU increases BOM costs and the complexity of the solution.
An MCU could improve the power efficiency in applications where the CPU is set to sleep state, and the MCU is waiting for an external event (for example, when the sensor value is rises above the threshold).
When the external event occurs, the MCU wakes up the CPU. Implementation examples are provided in the article Using the MCU SDK and API: Code examples.
To illustrate how to use the internal MCU, we will connect Ultrasonic Ranging Sensor HC-SR04 to the Intel® Edison board. We will output the measured distance to the Grove* LCD RGB Backlight symbolic display.
Ultrasonic Ranging Sensor HC-SR04
The sensor has four pins:
- Vcc: 5V
- Trig: Trigger signal to sensor. The MCU sends a 10 us pulse to the sensor. Sensors initiate a single measurement.
- Echo: Echo signal from sensor to MCU. Pulse width is proportional to the measured distance.
- Gnd: Ground
The picture demonstrates the protocol on the oscilloscope screen:
- 1 channel: Trig
- 2 channel: Echo
The MCU sends a pulse to the Trig pin. After that, the sensor responds with a pulse on the Echo pin.
The pulse duration is proportional to the measured distance.
The distance is calculated using the formula (from the sensor datasheet):
Distance (cm) = Echo pulse duration (us) / 58
The sensor could measure the distance from 2 cm to 400 cm as mentioned in datasheet.
It’s not possible to measure the duration of such a short pulse with estimated accuracy without microsecond real-time delays. For example, the scheduler could preempt the measurement process and the measurement result will become invalid.
Connecting the HC-SR04 sensor to the MCU on the Intel® Edison board
Components:
- Intel® Edison compute module
- Intel® Edison board for Arduino
- Grove Basic Shield
- Grove LCD RGB Backlight
- Ultrasonic Range Sensor HC-SR04
- Breadboard
First, assemble the Intel® Edison compute module and the Intel® Edison board for Arduino. Next, connect the Grove Base Shield extension board to Intel® Edison board for Arduino. Connect the Grove LCD RGB Backlight to any I2C port on the Grove Basic Shield.
Connect the Ultrasonic Range Sensor HC-SR04 to the Grove Basic Shield as follows:
- Vcc to +5V
- Trig to Pin 3
- Echo to Pin 4
- Gnd to Gnd
Pins 3 and 4 are selected randomly. You can use any GPIO pin for this purpose.
Updating the Intel® Edison board firmware
MCU support was added to the Intel® Edison Board Firmware Software Release in version 2.1. If you have older firmware, you will need to update it.
To get the current firmware version, use the following command:
# configure_edison –version
This example is based on firmware version 146.
Firmware update instructions are provided in the article Flashing Intel® Edison. I prefer to use the alternate flashing method described in the article.
Please, read the instructions carefully before flashing.
Connecting the Intel® Edison board using Ethernet over USB
You must configure the network connection to connect with the Intel® Edison board from the MCU SDK.
To do that, connect the USB cable to the top micro-USB port and set the micro-switch to the bottom position (toward the micro-USB port).
To configure the network on Linux:
# ifconfig usb0 192.168.2.2
Intel® Edison board IP address: 192.168.2.15
For more information, please refer to the article Connecting to your Intel® Edison board using Ethernet over USB.
MCU SDK
The developer needs the MCU SDK to create applications for the internal MCU. The MCU SDK is a cross-platform IDE based on Eclipse*. The installation process is explained in the article Installing the MCU SDK.
The MCU SDK provides functionality to create, compile, upload to the board, and debug applications for MCU.
Communicating with the MCU
Several interfaces are available to communicate with the MCU at the Linux level:
/dev/ttymcu0
— Interface to send/receive data to/from the MCU. It is possible to operate using standard read
and write
file operations from Linux. The MCU program can use host_send
and host_receive
functions.
/dev/ttymcu1
— Interface to send debug messages from the MCU using debug_print function.
/sys/devices/platform/intel_mcu/log_level
— Interface to set log level for debug messages (fatal, error, warning, info, debug).
Working with the Intel® Edison board for Arduino pins from the MCU
MCU is integrated in the Intel® Edison compute module and can control GPIOs located on the 70-pin Hirose module connector.
To use the MCU with the Intel® Edison board for Arduino, you need to find a map between the Intel® Edison compute module GPIO pin and the Intel® Edison board for Arduino pin. Configure the multiplexors and level shifters to route the GPIO from the Intel® Edison compute module to the Intel® Edison board for Arduino pin.
When working from the Linux level, these routines are handled in the MRAA library. In case of MCU, developer needs to handle these using the scripts (init_DIG.sh, init_i2c8.sh, init_mcu_PWM.sh, set_DIG.sh, read_DIG.sh, init_UART1.sh). More information is provided inTable 4 in Intel® Edison Kit for Arduino* Hardware Guide.
Script for Linux
The Python* script show_distance.py below fetches the data from the internal MCU and shows them on the Grove LCD display. We will use the Jhd1313m1 module from the UPM library to work with the Grove LCD display.
show_distance.py (Note: all the files/scripts are included as a .zip at the end of this article)
import time
import pyupm_i2clcd
RET_ERROR = -1
if __name__ == '__main__':
lcd = pyupm_i2clcd.Jhd1313m1(6, 0x3E, 0x62)
with open('/dev/ttymcu0', 'w+t') as f:
while True:
f.write('get_distance\n') # Send command to MCU
f.flush()
line = f.readline() # Read response from MCU, -1 = ERROR
value = int(line.strip('\n\r\t '))
lcd.clear()
if value == RET_ERROR:
lcd.setColor(255, 0, 0) # RED
lcd.write('ERROR')
else:
lcd.setColor(0, 255, 0) # GREEN
lcd.write('%d cm' % (value,))
time.sleep(1)
MCU program
The MCU program waits for the get_distance
command from the host CPU. If the program receives the get_distance
command, it measures the distance and sends the result to the host CPU (distance in cm or -1 in case of error).
Set up the pins on the Intel® Edison board for Arduino:
# ./init_DIG.sh -o 3 -d output
# ./init_DIG.sh -o 4 -d input
The MCU works with the GPIO pins on the Intel® Edison compute modules that have different numbers from the labels on the Intel® Edison board for Arduino. For more information, refer to the table in the article Blinking an LED using the MCU.
mcu.c (Note: all the files/scripts are included as a .zip at the end of this article)
#include "mcu_api.h"
#include "mcu_errno.h"
#define TRIG 12
#define ECHO 129
#define MIN_DISTANCE 2
#define MAX_DISTANCE 400
#define MAX_WAIT 10000
#define RET_ERROR -1
int get_distance() {
gpio_write(TRIG, 1);
mcu_delay(10);
gpio_write(TRIG, 0);
int i;
i = 0;
while ((gpio_read(ECHO) == 0) && (i < MAX_WAIT)) {
mcu_delay(1);
i++;
}
unsigned long t0 = time_us();
if (gpio_read(ECHO) == 0 || i == MAX_WAIT) {
return RET_ERROR;
}
i = 0;
while ((gpio_read(ECHO) == 1) && (i < MAX_WAIT)) {
mcu_delay(1);
i++;
}
unsigned long t1 = time_us();
if (gpio_read(ECHO) == 1 || i == MAX_WAIT) {
return RET_ERROR;
}
unsigned long distance = (t1 - t0) / 58;
if (MIN_DISTANCE < distance && distance < MAX_DISTANCE) {
return distance;
} else {
return RET_ERROR;
}
}
#define MAX_BUF 255
unsigned char buf[MAX_BUF];
void mcu_main() {
gpio_setup(TRIG, 1);
gpio_write(TRIG, 0);
gpio_setup(ECHO, 0);
while (1) {
unsigned int len;
len = host_receive(buf, MAX_BUF);
if ((len >= 12) && (strncmp(buf, "get_distance", 12) == 0)) {
unsigned int distance;
distance = get_distance();
len = mcu_snprintf(buf, MAX_BUF, "%d\n", distance);
host_send(buf, len);
}
}
}
Adding our script to auto start
To launch our script, first create a shell script:
File /home/root/startup.sh:
startup.sh (Note: all the files/scripts are included as a .zip at the end of this article)
#!/bin/bash
cd /home/root
# configure PIN3 as GPIO OUPUT (TRIG signal)
./init_DIG.sh -o 3 -d output
# configure PIN4 as GPIO INPUT (ECHO signal)
./init_DIG.sh -o 4 -d input
python show_distance.py
Mark scripts as executables:
# chmod a+x /home/root/startup.sh
# chmod a+x /home/root/init_DIG.sh
The Yocto Project Linux uses systemd, so we need to create “service” file to add the script to auto start.
Create the file/lib/systemd/system/startup-script.service:
startup-script.service (Note: all the files/scripts are included as a .zip at the end of this article)
[Unit]
Description=Startup User Script
After=syslog.target
[Service]
ExecStart=/home/root/startup.sh
[Install]
WantedBy=multi-user.target
Add the service to auto start:
# systemctl enable startup-script
After rebooting, symbol display shows the measured distance:
Source Code
Used resources