Ardunio Programming: C++ and Embedded Systems

Ideally we would use assembly language to wring the last drop of performance from small microcontrollers, and at one time that really was the only way to do it. But assembly language programming is tedious and error-prone, and if I never have to wrestle with another assembly language program that would be fine with me.

With the advent of C, things got a lot easier in the embedded systems world. As its creators stated, C is essentially a close relative of an assembler, rather like a macro assembler (there’s a good Google/Wikipedia topic, if you don’t know what a macro assembler is). A C program can be compiled into very tight and efficient code, with an almost one-to-one correspondence to the underlying assembly language that the compiler generates.

But times change, and things are extended, improved, and expanded, and thus C++ arose from C. Over time C++ has become one of the dominant languages in programming, but there are challenges when attempting to use it with a microcontroller.

One thing I want to get out of the way first off: There is no such thing as the Arduino Language. That is BS, pure and simple, and I get weary of seeing it pop up again and again. The Arduino IDE uses C++, the GNU tool chain (g++ and so on), and the avr-libc run-time, to build executable program images for the AVR microcontroller. The IDE just supplies the main() function with a run-forever loop that calls your program (i.e. sketch) over and over again until you disconnect the power. Granted, what avr-libc and the tool chain supports is only a sub-set of C++, but it’s still C++, and you can write and compile programs without the IDE if you want to.

C++ is a dynamic language, with class objects created and destroyed on-the-fly, and memory allocated and released as necessary for lists, vectors, and queues, among other things. Embedded systems are, by their very nature, constrained environments. So how is it possible to use C++ with something like an AVR microcontroller that has only a small amount of RAM to work with and with the program stored in read-only flash memory?

The key is how an embedded system is used. An embedded device based on a small microcontroller is not a general-purpose computer. It doesn’t have disk drives or lots of memory, and often these little devices aren’t very fast, either (20 MHz as opposed to 2+ GHz for a PC). They also don’t have large inherent data sizes (the AVR devices used in most Arduino boards are 8-bit microcontrollers). It is designed to do one thing, and one thing only, for the entire time it is active, and that one thing is loaded into the microcontroller as firmware in flash memory, always ready to go when power is applied.

The 8-bit AVR MCUs, like those found on Arduino boards, are Harvard architecture processors, and they will not execute code from SRAM, only from flash memory space, and there is only a small amount of SRAM available for the stack and variables. Consequently, using malloc() and free() (or new and delete) in an embedded system like these is generally considered to be a bad idea. It also means that C++ STL things like vectors, queues, and lists are often not implemented by the run-time environment for an embedded microcontroller.

So when a class object is created (or instantiated, if you prefer) it must be done at compile-time and put into the flash program memory space. It also means that many data objects that would normally be dynamically created and destroyed at run-time should be permanent global objects so they can be dealt with during compilation.

In the case of an AVR microcontroller, the run-time library, avr-libc, supports malloc() and free(), but it does not support new and delete. However, the good folks at Arduino decided to incorporate new and delete, and they did it by tricking the compiler into creating the class objects in flash memory space at compile-time. When a class object is created in flash the new operator simply returns a pointer to the already instantiated object when it is called.

You can find the Arduino definitions of new and delete in /usr/share/arduino/hardware/arduino/cores/arduino/new.h on a Linux system, and in C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino on a Windows system. You can read more about this, and how different instantiation techniques use different amounts of memory, in my book “Arduino: A Technical Reference“.

avr-libc is the preferred open source solution for the run-time library part of the GNU tool chain for the AVR family of microcontrollers. It has some limitations, however. From the FAQ:

…there’s currently no support for libstdc++, the standard support library needed for a complete C++ implementation. This imposes a number of restrictions on the C++ programs that can be compiled. Among them are:

Obviously, none of the C++ related standard functions, classes, and template classes are available.

The operators new and delete are not implemented, attempting to use them will cause the linker to complain about undefined external references. (This could perhaps be fixed.)

Some of the supplied include files are not C++ safe, i. e. they need to be wrapped into

extern “C” { . . . }

(This could certainly be fixed, too.)

Exceptions are not supported. Since exceptions are enabled by default in the C++ frontend, they explicitly need to be turned off using -fno-exceptions in the compiler options. Failing this, the linker will complain about an undefined external reference to __gxx_personality_sj0.

Constructors and destructors are supported though, including global ones.

When programming C++ in space- and runtime-sensitive environments like microcontrollers, extra care should be taken to avoid unwanted side effects of the C++ calling conventions like implied copy constructors that could be called upon function invocation etc. These things could easily add up into a considerable amount of time and program memory wasted. Thus, casual inspection of the generated assembler code (using the -S compiler option) seems to be warranted.

Here are some guidelines that I use when creating software for an Arduino (or similar) platform with the GNU tool chain:

  • Make as many variables as possible global, and put them in their own code module. Reference them using an include file with extern type declarations. Use global variables for things like status, error codes, and string buffers. If more than one function needs to use the data, make it global.
  • Do not pass arguments by value between functions, and avoid passing anything at all if possible.
  • Avoid the use of automatic variables within a function as much as possible. Exceptions to this are small integer types used in loops and as counters.
  • Keep integer data types as small as possible and explicitly define them using the standard Unix types found in stdint.h (uint8_t, int16_t, etc.).
  • Avoid using complex math operations and functions (sin, cos, pow, etc.). These must be simulated, and they are slow and use resources. Use fixed-point math as much as possible if the program needs to do math.
  • Keep interrupt routines as short as possible. Put the primary functionality into the main loop, and use a conditional to handle it once an interrupt routine sets a flag and returns.

If you need consistent main loop timing, then consider setting an initial loop delay time value at the front of the loop, and subtract from it each time a function in the loop executes. If there is anything left over at the end of the loop, that will be the delay time to use. If the remaining delay time is zero, then your loop has exceeded the time budget.

Because most of the software created for an Arduino platform is single-threaded there isn’t much need to worry about collisions between threads attempting to access the same global variable. Nonetheless, it is still a good idea to define how a global variable can be manipulated. As a general rule I try to use a write-by-one, read-by-many paradigm. That can still lead to strange and annoying forms of control and data coupling, but it’s a lot easier to manage than a situation where any function can modify a global variable when it is executing.

If you want to dig deeper into the subject of programming a small microcontroller, then I would suggest taking a look at how the the Arduino team did it. The Arduino IDE and libraries source code are installed when you install the IDE, so poke around and see what’s there. But don’t take it as the absolute last word on the subject. I don’t agree with all of their design decisions, and I probably wouldn’t have done it the way they did, but for the most part it works. A search across the WWW will reveal multiple opinions on different ways to build that particular wheel.

Here are a few books on embedded microcontroller programming that I am aware of:

Programming Embedded Systems in C and C++, Michael Barr, 1999, O’Reilly, ISBN 978-1565923546.

The Art of Programming Embedded Systems, Jack Ganssle, 1991, Academic Press, ISBN 978-0122748806

An Embedded Software Primer, David E. Simon, 1999, Addison-Wesley, ISBN 978-0201615692

I’m currently in the outline and planning stages of a book on microcontroller software design, with a special emphasis on things like foreground-background, cyclic executives, and minimal real-time message-passing operating systems. I’ll post updates on the progress from time to time.

Advertisements

0 Responses to “Ardunio Programming: C++ and Embedded Systems”



  1. Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




Follow Crankycode on WordPress.com

Little Buddy

An awesome little friend

Jordi the Sheltie passed away in 2008 at the ripe old age of 14. He was the most awesome dog I've ever known.


%d bloggers like this: