More handy embedded software tips
This is the latest in my occasional series of posts where I outline a bunch of tips that I hope will be useful to embedded software developers. Today I am looking at optimizing C code, accessing hardware ports in C/C++, writing readable code, learning a language and debugging …
Always make a C function static, if you only make a single call to it, as this aids compiler optimization
If you write a C function, which is only called from one place and that call is in the same module [file], make it static. This tells the compiler that there are no other calls from outside the module, so no external linkage is accommodated. It also opens up the possibility for automatic inlining of the function.
To access a specific memory location as a variable, cast to a pointer and dereference
It used to be assumed that all embedded software developers need to have the skills to do the “low level stuff” – i.e. program device registers etc. This is no longer the case, as many developers work at a higher level, specializing in the application, with little awareness of the underlying hardware. However, for those that do get close to the “metal”, it is necessary to know how to access memory locations [device registers] from C. This is quite straightforward, even though the syntax is a little arcane. Just cast a constant [the address] to be a pointer to volatile unsigned [say] and dereference, thus:
#define DEVICE (*((volatile unsigned *)0x8000000))
You can then use DEVICE as if it were an unsigned variable. Some caution is needed, however, as device registers do not always behave like regular variables. For example, a device may allow to write some data, but not read it back again. This can, of course, be accommodated, but that is a bigger story.
Long C functions are hard to read. Always consider breaking them down to screen-sized chunks
Years ago, it was received wisdom that a chunk of code [a subroutine, function, whatever] should always fit on a single page of line printer paper [which had, if I recall correctly, 66 lines]. This was perceived as an amount of code that could be assimilated by someone performing a review. Nowadays, a screen-full would probably serve the same function. Of course, there are plenty other things that can be done to maximize the readability of code.
Learning C puts you on track to learn C++, Java, Python, Lua …
Currently, the most widely used language for embedded software development is C. Although C++ has long been promoted as its replacement, C’s proportion of the market is actually increasing. So, if you are starting out on embedded programming, you should become proficient in C programming. Even if you never do a lot of work in the language, you have taken the first step in learning some of the other widely-used languages: C++, Java, python, Lua …
The first debugging tool you should use is your brain
It is very easy to crank out some code, let the compiler sift out the syntax errors and then throw it at a debugger to get it working. There is a better way:
- write the code
- read through the code carefully to find syntax errors
- walk through the code to follow the logic; if it is hard to follow, rewrite it
- fix any errors [they are errors – avoid the euphemism “bug”]
- build the code [then fix any syntax errors you missed]
- use the debugger to verify the code’s operation