Increasing Complexity: BB-8 Patrols
While state machines and interrupts are useful for user interfaces, not all of interrupts interact with a human. Many parts of BB-8 interact only with itself and the world it encounters. BB-8 has a patrol mode that lets it wander around on its own, sending gobs of data back to the control device, usually a tablet or smartphone. There are different ways to see this output, one way is the smartphone display shown in Figure 4-7.
With karaoke’s play mode state machine and the quadcopter power up sequence, I spent a lot of time considering “what happens next?” to identify their state machines. For BB-8, that line of thinking only goes so far. Instead I started wondering “how does it do that?”
Consider the different parts of Figure 4-8: we know BB-8 has LEDs, motors, battery and some sort of wireless communication method. I snuck in the inertial sensor on the block diagram because it so neat it will be in a chapter of its own soon. The inertial sensor helps BB-8 keep its balance, detect collisions, and output data to the application in patrol mode.
While it is acting autonomously in patrol mode, BB-8 has to communicate its status to the user, drive the motors, make a map of the room so it can try to avoid obstacles, and generally act like it is intelligent. To do this, it has to run a state machine for each one of these things (and probably a few others), all at the same time. I could try to show the state machines for each of BB-8’s tasks but those specifics are less interesting than what all this complexity implies. BB-8 probably manages to do multiple tasks like that by running a real-time operating system (RTOS).
What an RTOS does
An RTOS (“ar-toss”) is a lot like any other operating system on a computer: Windows, MacOS, Linux, Android, iOS, and so on. The real-time part of it means that the operating system is deterministic, that it responds to the application predictably. RTOSs always respond to interrupts within a certain amount of time, called interrupt latency. Different RTOSs have different latencies.
Toys need this consistency and fast response time. What if karaoke didn’t play the next chunk of audio quickly enough because it was putting words on a screen? What if BB-8’s motors kept going even after it collided with a chair because it was updating its map of the room? What if the quadcopter did not receive a frantic throttle down command as it headed toward a tree because it was too busy maintaining level flight?
Even without the need for fast response for a user, BB-8 is juggling too many balls to be able to do everything on its own. A system that handles multiple tasks is called multitasking. The trick to doing more than one thing at a time is a scheduler. Like a kindergarten teacher with a single bathroom pass, a scheduler doles out time to whoever needs it most.
Many systems run bare metal, without an operating system. If you have tried out an Arduino UNO, then you’ve run bare metal. It can be faster and more power-efficient. But the kindergarten analogy is important: why can’t the schoolkids self-organize? If they cooperate, they don’t need a teacher at all. That works for one or two, maybe even three, tasks/kids before they start to fight. After that, everyone is happier if someone is in charge.
The RTOS also facilitates communication between the tasks. This lets the state machines and other tasks (like power monitoring) be focused and only get information from other tasks that are important to their mission. RTOSs also offer safe ways for the tasks to share the system’s resources without stomping on each other.
In toys, and in all embedded systems, designers have to balance between a cheap processor with no overhead available for an RTOS and a more expensive processor that allows for easier development.
Comparing Processors
Our three toys have different processors. Comparing processors is difficult as there are many parameters that can describe a processor. A few of the most important include:
- core: main part of the chip, some cores are good at math, audio, or motors; some are good at lots of things.
- architecture size: Usually 8-bit, 16-bit, or 32-bit, and often a feature of the core, larger sizes usually mean you can accomplish more in less time.
- maximum operating frequency: How fast can it run? Many processors don’t run this fast (especially if they are battery powered) but knowing the maximum is a good way to get a sense of the part.
- code space: How big can your program be? A complex program on a PC can be hundreds of megabytes (MB). The Arduino has only 32 kilobytes (kB) of code space. Code space is usually FLASH (meaning the program can change) or ROM (the program cannot be updated).
- RAM: How much memory does the program get to work with?
- peripheral support: Can the processor talk to only digital IOs like buttons and LEDs? Does it have an analog to digital converter? What else can it talk to?
- power consumption: For battery operated devices, a low power processor is a must.
- cost: While the price doesn’t necessarily indicate a processor’s capability, it gives a valuable hint.
The quadcopter and BB-8 have similar processors. The table in Figure 4-9 shows some of these parameters for each toy. Unfortunately, I couldn’t find out very much about the karaoke’s main processor. It is typically used in CD and DVD players but the vendor doesn’t provide datasheets to people who aren’t buying hundreds of thousands of parts.
For comparison, I included the Arduino UNO and a BeagleBone Black single board computer (which is similar to the Raspberry Pi). Note that the Arduino is much smaller than anything else listed. And the BeagleBone Black is huge compared to the processors in these toys (it runs Linux which is a much larger operating system than most RTOSs found in small devices).
Toy |
Primary processor |
Core |
Arch. size (bits) |
RAM (kB) |
Code space (kB) |
Max clock (MHz) |
Approximate cost |
Karaoke |
Sunplus SPHE8104GW |
custom |
? |
? |
? |
? |
$1 |
BB8 |
STM32 F373CCT6 |
Cortex-M4 + FPU |
32 |
32 |
256 |
72 |
$2.50 |
Quadcopter |
STM32 F031K4 |
Cortex-M0 |
32 |
4 |
32 |
48 |
$0.80 |
Arduino UNO |
ATmega328P |
RISC |
8 |
2 |
32 |
20 |
$1.25 |
BeagleBone Black |
AM335x |
Cortex-A8 |
32 |
524288 |
2097152 |
1000 |
$11.15 |
As a system design gets more complicated, it needs more RAM, more code space, and a faster clock. On Arduino, you can make a system that controls lots of lights in response to button presses, you can even drive a robot around a maze but you probably can’t do both at the same time. The quadcopter’s processor has a lot more capability so it could do both. And since those are separate state machines, likely only loosely coupled, the designer might want to put a small RTOS on the system.
As the complexity continues to increase to, say, a robot searching around a maze and communicating with the outside world, the system needs a more robust RTOS. Eventually you get back to small computers like the BeagleBone Black that could run any of several operating systems, both real-time and normal. A BeagleBone Black can be run bare metal but it is so complex by itself (before you add your own software and goals) that an operating system makes it easier to develop on.
What about programming languages?
Most software for devices is written in C or C++. These are great languages but sometimes difficult to learn thoroughly. Failures in understanding the language can lead catastrophic bugs that make the system crash or be unsecure. Developers keep using them because C/C++ runs quickly on the processor (which means they can use a smaller/cheaper/slower processor in the system). Some parts may even be written in assembly when they need to be especially fast. To understand why that is, you have to understand what happens to the code after you write it.
A source file gets translated into assembly by a compiler. Each processor core has its own special assembly, though when you see something like “ARM” or “Cortex” there is a good chance the assembly will be very similar to other related processors. Assembly code does one thing at the time, exactly as written. For example, take a look at this C code which sets x to one and then increments it:
int x = 1;
x = x + 1;
This may look trivial if you know how to program but the processor has to put a constant into a register, a special place the processor holds data when it is working on it. The compiler outputs this assembly code (R0 is the register, STR means store to a memory location, LDR means to load from a memory location, and [SP, #+0] is the memory location):
53 int x = 1;
(1) \ MOVS R0,#+1
(2) \ STR R0,[SP, #+0]
54 x = x + 1;
(3) \ LDR R0,[SP, #+0]
(4) \ ADDS R0,R0,#+1
(5) \ STR R0,[SP, #+0]
Writing assembly means that you have to worry about each little step. On the other hand, you can optimize out steps you don’t need (ones that the compiler may not be sure you need so it puts them in to be safe). In the above example, I could optimize out lines (2) and (3): I don’t need to store and load the register to memory as I’m about to use the same register again. Many compilers would remove those automatically; the one I used was set to avoid all optimization to help with this example.
The written or compiler-generated assembly code gets linked together (so that functions can be linked to their function calls) and translated into an executable binary. If you were running on your computer, you could stop there, and run the program.
To develop code for a toy, the compiler has to be special: a cross-compiler generates code for a processor different than the one it is running on. The cross-compiler is needed because the toys’ processors don’t have enough RAM and code space to run a compiler and the code.
Note: After the executable binary is generated, it must be loaded to the system, usually through a programmer device that uses a standard like JTAG or SWD.
What about other languages? There are lots of other languages. Some of them are easier to learn, others are safer to use. Python is one of my favorites. It is fast to develop in, especially if I know the related library. However, Python isn’t normally compiled, it is interpreted. As I type in commands or run a script, there is an interpreter (actually a state machine) looking at what the line says, then compiling, linking, then running it, all before the interpreter puts up the next prompt. Such a device has to have a processor big enough to run both the interpreter and the code; it is a lot of overhead for the smaller processors. Javascript is also usually interpreted. Both Python and Javascript can be compiled but there are fewer cross-compilers for those languages so they are not as prevalent in embedded software development.
I like C. It makes sense to me. I like C++, it has some nice additions but isn’t that different from C. I’ve been using both so much they feel very native to me. Please, use what feels best to you. The language doesn’t matter as much as the practice of doing some development.
Finally, Processing (Arduino’s language) is simply a dialect of C++. So if you’ve been playing along with an Arduino since the beginning of the book, you are way ahead.
Going Further
Identifying a toy’s state machine is useful as it gives hints about how the system is put together, even before you crack open the box. State machines are most obvious in games as the states and transition represent rules. Personally, I get much better at video games once I understand the state machine. State machines are everywhere; traffic lights, furnace thermostats, microwave ovens. Look around you and see if you can find some state machines.
You can try out building a state machine using any number of online interpreters. If you’ve never tried programming, I think Scratch (scratch.mit.edu) is fun because you can animate characters and make your own simple game. Starting with a flowchart or state table really helps me stay organized but the online tutorial will give you the help you need to make your idea work.
Of course, that is only software. You could figure out a flowchart for a stop light, even build a miniature version: four RGB LEDs, four buttons to indicate a car is waiting at a red light (one in each of the cardinal directions), and a processor to control them all and run your state machine.
Given what you know about inputs and outputs and state machines, what can you say about the electronics around you? Can you find an example of a bug due to an unhandled user event? Can you identify systems that are complicated enough to have a RTOS?
This is a series. If you’d like to read them in order, check out the Taking Apart Toys index.
This post provided a very brief introduction to assembly language. If you are interested in finding out more, check out Embedded Systems Engineering 101, especially the Microcontroller Basics sections (in posts 2, 3, 4, 5).
This post also blitzed through compilers. To understand how to compile for devices, check out Embedded Wednesday’s Tools of the Trade.