Page 1 of 3
You can write real time programs using standard Linux as long as you know how to control scheduling. In fact it turns out to be relatively easy and it enables the Edison to do things you might not think it capable of.
This is a chapter from our ebook on the Intel Edison. The full contents can be seen below. Notice this is a first draft and a work in progress. Use the comments or email email@example.com with your queries or suggestions.
Now On Sale!
You can now buy a print edition of Exploring Intel Edison.
You can buy it from:
USA and World Amazon.com
In this chapter we consider the Edison's pros and cons and get an overview of its structure and the ways in which you can make use of it. If you have ever wondered if you need an Edison or an Arduino or even a Raspberry Pi then this is the place to start.
- First Contact
When you are prototyping with the Edison you are going to need to use one of the two main breakout boards - the Arduino or the mini. This chapter explains how to set up the Edison for both configurations.
- In C
- Mraa GPIO
Using the mraa library is the direct way to work with the GPIO lines and you have to master it. Output is easy but you do need to be aware of how long everything takes. Input is also easy but using it can be more difficult. You can use polling or the Edison interrupt system which might not work exactly as you would expect.
- Fast Memory Mapped I/O
There is a faster way to work with GPIO lines - memory mapped I/O. Using this it is possible to generate pulses as short at 0.25 microsecond and read pulse widths of 5 microseconds. However getting things right can be tricky. We look at how to generate fast accurate pulses of a given width and how to measure pulse widths.
- Near Realtime Linux
You need to be aware how running your programs under a non-realtime operating system like Yocto Linux effects timings and how accurately you can create pulse trains and react to the outside world. In this chapter we look the realtime facilities in every version of Linux.
- Sophisticated GPIO - Pulse Width Modulation
Using the PWM mode of the GPIO lines is often the best way of solving control problems. PWM means you can dim an LED or position a servo and all using mraa.
- Sophisticated GPIO - I2C
I2C is a simple communications bus that allows you to connect any of a very large range of sensors.
- I2C - Measuring Temperature
After looking at the theory of using I2C here is a complete case study using the SparkFun HTU21D hardware and software.
- Life At 1.8V
How to convert a 1.8V input or output to work with 5V or 3.3V including how to deal with bidirectional pull-up buses.
- Using the DHT11/22 Temperature Humidity Sensor at 1.8V
In this chapter we make use of all of the ideas introduced in earlier chapters to create a raw interface with the low cost DHT11/22 temperature and humidity sensor. It is an exercise in interfacing two logic families and implementing a protocol directly in C.
The DS18B20 1-Wire Temperature
The Edison doesn't have built in support for the Maxim 1-Wire bus and this means you can't use the very popular DS18B20 temperature sensor. However with a little careful planning you can and you can do it from user rather than kernel space.
- Using the SPI Bus
The SPI bus can be something of a problem because it doesn't have a well defined standard that every device conforms to. Even so, if you only want to work with one specific device it is usually easy to find a configuration that works - as long as you understand what the possibilities are.
- SPI in Practice The MCP3008 AtoD
The SPI bus can be difficult to make work at first, but once you know what to look for about how the slave claims to work it gets easier. To demonstrate how its done let's add eight channels of 12-bit AtoD using the MCP3008.
- Beyond mraa - Controlling the features mraa doesn't.
There is a Linux-based approach to working with GPIO lines and serial buses that is worth knowing about because it provides an alternative to using the mraa library. Sometimes you need this because you are working in a language for which mraa isn't available. It also lets you access features that mraa doesn't make available.
If you are writing a real time system there are two things that should concern you - how fast the system can act and how poor this response can be in the worst case.
After learning how to generate accurate and fast pulses, see Fast Memory Mapped I/O, we now have the ability to work with I/O down in the 10 microsecond region, but we still have the problem that our program can be interrupted at any time by the operating system. This means that our outputs and inputs can go drastically wrong.
For example, if you generate a fast pulse train in the 10 microsecond range using a standard GPIO line and set a logic analyser to trigger on a long pulse, you will eventually find one or more very long pulses - typically in the millisecond range. This problem becomes worse the more the CPU is loaded as the operating system switches between tasks to make sure that everything has an opportunity to progress.
If you are familiar with microcontrollers such as the PIC, AMTEL or any dedicated mcu then this idea that there could be something getting between you and the hardware will be new. The majority of simple mcus do nothing but run the program you download. Any talk of an "operating system" generally refers to code that does the downloading or minimal system preparation. When you write a control loop then you can safely assume that the loop will run as you wrote it and without interruption - unless of course you have coded an interrupt handler.
The point is that in many situations your program is the only program running and you are in complete charge of the processor.
In the case of running a program on the Edison's dual core Atom the situation is very different. Your program is just one of a number of programs running at any given time. The Edison has two cores and this means that at most two programs can be running at any given time. The operating system is responsible for starting and stopping programs so that each and every program has a turn to run.
This is called scheduling and it is a problem if you are trying to write a real time system.
The problem is that you might write a program that toggles a GPIO line between high and low with a given timing, but whether this timing is honored depends on not just your program but on the operating system as well. You can't even be sure how the operating system will treat your program because it depends in a fairly complex way on what else is running on the system and exactly what the other programs are doing.
Sometime this is expressed as your program execution being non-deterministic whereas in a simple mcu it is deterministic. This means that if you run the same program twice on on the Edison you probably don't get exactly the same result but on an mcu this is a reasonable expectation.
The whole subject of multi-tasking operating systems and scheduling in particular is a large one and it is usually taught as part of a computer science degree - but generally not as it applies to real time programming. What this means is that there is often a lot of guess work involved in getting programs with real time demands to work properly under general operating systems such as Linux. In fact it is often state that you can't do real time processing under Linux because you cannot even place a bound, an upper limit, on how long your program might be suspended by the OS. This isn't true and real time processing on standard Linux is possible - as long as you are able to live within the constraints.
As an alternative you could opt to run a specially designed real time OS that does provide guarantees on how quickly a request will be serviced. For example the Quark mcu running in the Edison runs the Viper RTOS. There are also specifically real time versions of Linux that you can install, but since version 2.6 the Linux Kernel has had sufficient real time facilities for many applications so you don't need to move to anything different to the standard Yocto Linux that comes with the Edison.
Before we continue it is important to realise that there is no way that a real time operating system can increase the speed of operation of the processor - the maximum speed of operation cannot be improved upon. In the case of the Edison this means that you can achieve around the 10 microsecond pulse times if you are careful and no amount of real time programming is going to improve on this.
What real time provides is higher consistency of that response time. It isn't perfect, however, and after we have used all of the features of real time Linux there will still be small periods of time when your program isn't operating and there is little to be done about this.
Every Linux thread is assigned a scheduling policy and a static priority.
The normal scheduling algorithm, SCHED_OTHER, that Linux uses applies to all threads with static priority zero. If you are not using real time scheduling then all the threads run at priority zero. In place of a static priority each thread is assigned a dynamic priority, which increases each time it is passed over for execution by the scheduler. The scheduler gives the thread with the highest dynamic priority an opportunity to run for one quantum of time or for one time slice. A thread can be suspended before its time slice is up because it has to wait for I/O or because it is blocked in some other way. Any time a thread makes system call it is also a candidate to be suspended in favour of another thread.
You have only a little control over the computation of the dynamic priority. All you can do is set its initial value using the nice command or setpriority.
The normal scheduling algorithm doesn't provide much control over what runs. It is "fair" in the sense that all threads get a turn at running, but it isn't possible to set a thread to have a high priority so that it runs in preference to all others.
To do this we need to look at the real time scheduling options.
The most important for us is SCHED_FIFO and sometimes the closely related SCHED_RR.
These apply to threads, real time threads, with static priorities 1 to 99(high).
The first thing to note is that a thread with priority greater than zero will always run in preference to a thread with priority zero and so all real time threads will run before a thread using the normal scheduling algorithm.
What happens in FIFO is that the system maintains queues of threads that are ready to run at each priority. It then looks for the list with the highest priority with threads ready to run and it starts the thread at the head of the list.
When a thread is started it is added to the back of its priority queue.
Once a FIFO thread gets to run it can be preempted by a thread with a higher static priority that is ready to run.
If a FIFO thread is suspended because of a higher priority thread it goes back at the head of the queue. This makes it the next thread to resume. This is the sense in which the schedule is First In First Out FIFO - if a thread is suspended by another thread of higher priority that becomes runnable then it is restarted as soon as that thread that replaced it is suspended or stops running.
Finally if a thread explicitly yields (by calling yield) it goes to the end of its priority queue.