C tips for embedded programming

Some very useful resource in writing this post were: an article on  Embedded.com , Nigel Jones’ “C Test” and C coding tips published under cprogramming.com website.

The driving idea was to share my knowledge with other “embedded programmers” but also to debate about several aspects which inherently occur when writing C code for microcontrollers. Those are some general guidelines which I usually try to follow in order to write more efficient code.

It is not very easy to find a starting point in this discussion, but I will “dump” things which I have into my mind along with those found on various web resources. I will struggle to cover embedded-specific topics as interrupts, I/O, timers, variable length, compiler particularities, C standard specifics. Unfortunately not all of them will be deeply explained in this post so I will come back later.

When you writing code for a certain microcontroller/microprocessor type first thing to take into consideration, in my opinion, is that the data type size should be appropriate for CPU used. For an 8-bit processor is more effective to work with 8-bit variables, same for 32-bit CPU and so on.

Another “rule of thumb” in programming (this time also in desktop programming) is that each line of code must have a corresponding line of comment or in any case, there should not be a big discrepancy in between “real code quantity” and its associated number of comment lines.

Be careful of some “delays” that you want to introduce in your embedded C code. Many compilers consider the “empty delay loops” as not useful code, so they are removed causing “unexpected behavior”. For example:


void delay(unsigned int);
//highly time dependent code
sent_data (0xAA);
delay(100);
sent_data (0x55);
delay(100);
void delay(unsigned int time)
{
for (i=0;i<10;i++); //considered to be not useful code
}

delay function can be transformed in

void (unsigned int time)
{
}

It is better and safer to use nop() instructions in case if corresponding microprocessors’ instruction set supports them.

Volatile meaning is…

…often a cumbersome problem in embedded programming. First answer would be the following: something that can change unexpectedly, usually some piece of hardware does this, it worth being prefixed with volatile. Somehow by prefixing a variable with volatile you tell the compiler: “I warn you – this can change unpredictably, do something“.

Concerning this point I would like to extend a little bit the discussion and to say some words about using registers vs using memory for data storage. Generally first has higher priority over the second, registers are faster, but there are some situations when you do not want to keep data into registers. The main drawback of hardware registers is that they can change outside program control and this is where volatile keyword comes into play – when you define a variable as volatile actually you are telling the compiler to store it into the memory and not in one of CPUs registers. Critical resources, like an I/O register which needs to be constantly read or written, shared variables by many threads or processes, data referred by an ISR, need to be kept in memory, in other words in a safer place.

Another important thing to be added is that registers have a fixed length but data types may vary (ints have a different size than floats of chars).

Do not pass strcutures as parameters because this may overload the stack and produce the unwanted stack overflow. Generally is good to avoid large data types to be as return values from functions or function parameters.

Function inlining: there are two things to discuss about – macro functions and inline functions. The main difference is that macros do not check for type, macros usually refer to constant literals that are used in programs and a referred in many places, but can be very helpful for defining functions as well:

#define shift_right_8_bit (a) {a<<8;} //this is a macro function
// attention no ; has to be at the end

Macros are very used in embedded programming especially when dealing with register masks and bits. Below there are few example on how you can declare some macro functions for setting or clearing certain bits within a register.

#define bit 0x0001 // it is always first bit on which all operations applies
// 16-bit registers were considered
// bit masks have to adapt to register size
#define setbit(bit, register) {register = register | bit;}
#define clearbit(bit, register) { register = bit & ~ register;}
#define bitisset(bit, register) (register = register | bit )
#define bitisclear(bit, register) ( register = bit & ~ register)

Please remark the differences in between setting a bit and checking if a bit is set. When performing some actions, set or clear, curly brackets are used and when checking for a bit status round brackets are used. The result of a bit checking is one bit length (as it is obvious – false or true) and the result of a clear or set is same size as the register.

Be careful of parenthesizing the arguments because macros are not checked via the parser (that thing within the compiler that is checking syntax errors).
When you want to define something like:

#define square(x) x*x // INCORRECT
#define square(x) (x)*(x) // CORRECT

If in your code you write something like:

b=square(2+3) // b will get the value 2+3*2+3 which is not what you want

Inline functions behave just like usual functions except the fact that functions’ code is dumped everywhere the function is called. When an inline function is called program is not handled as usually, program counter value prior to the call is saved and it points to functions’ definition, but functions’ code is executed in each place this is called. (actually the compiler places called code in every place the inline function is called – too many called)This is substantially reducing program speed, there is no need to save program state prior to call and restore it when program returns, but increases the size due to the fact that the code that represents the definition of the function is duplicated as many times as the function is called. This is recommended for “small” function just not to produce a size overhead. Inline expressions are evaluated only once by the compiler.Inline functions, despite macros, perform type checking.

If you define an inline function as:

inline int max (int a, int b) { a>b ? a : b;} // so you can use it only when comparing two integers.

The main drawback of macros is that they are expanded in the place where they are used.

#define max(a,b) {a>b ? a:b;} // this is more portable but is not type-safe, it won’t help you if you need to compare two floats asfloat a = 2.5;
float b = 2.1;
float c;
c = max(a,b); // macro is useless here

Usually in your embedded code you want to stop the execution of program without using debuggers’ breakpoints, it may be the case that you’re using an IDE without Debug facilities and how do you handle this?

By using C infinite loops:

while(1);
for(;;);Label: //this is used more by those with an assembly programming background
goto Label;

Comparison between qualified and unqualified types: this is very interesting and quite obscure point in C programming. Here’s how it works: ANSI C standard defines that whenever is a comparison between a qualified type (like int) and an unqualified type(like unsigned int), the unqualified one gets promoted to the qualified and then the comparison takes place. In other words you’ll have to be careful when comparing negative numbers with positive ones:

unsigned int u = 10;
int i = -1;if (i<u) // do something
else // do other thing

In this case the else branch will be taken

I will go briefly through some other tips that could be useful:

Whenever need to increment a variable use ++ (increment operator) instead of a=a+1, because many CPUs have implemented an increment instruction, so ++ will be faster than anything else
Whenever need to compare something in a loop it is better to decrement corresponding counter and to repetitively compare with 0

for (i=10; i>0; i--) {}

is better than

for (i=0; i<10; i++) {}

many CPUs have implemented the compare to 0 operation, so it gives a speed increase to your program.
Whenever you’ll need a one’s complement of a register or variable make use of ~ operator
Use switch instead of multiple if’s when a multipath is available

I will stop here for the moment and I will extend this topic into another post. Things like interrupts and handling fixed memory locations via pointers (by the way – not so many things about pointers have to be told) are mandatory to be discussed.

Advertisements

12 Responses to C tips for embedded programming

  1. Pingback: Review: Above All Co. L74995CN, Lived up to the hype - cool.. | ReviewMasters.info

  2. vasiauvi says:

    It will be GREAT to make a tutorial about embedded C. I would love to learn embedded C and the majority of the tutorials on the Internet are very hard. It’s truth that is hard to make a general tutorial, but maybe you could try!

    • Actually this is somehow the beginning of such embedded C tutorial that you are talking about. I won’t start a C tutorial from scratch because it doesn’t make sense, I will just try to focus on how to write C code for microcontroller (or for embedded systems). I think embedded C is all about efficient coding, you just help the compiler by writing directly into your source code what is easier for the target to execute.

  3. ubot says:

    Super information,I have Digged this site to my seo list for future and will keep a eye on your other posts.

  4. Lokesh says:

    Nice tutorial dear,
    It is very helpful

  5. Ras says:

    I didn’t get the delay section.

    • It is quite simple. It refers to the fact that it is better to avoid user-defined delay functions as long as you can use either a library function or the system clock. Some instructions like those presented in the “delay” function may be considered useless by the compiler as a consequence no assembly instruction is not generated.
      The funny thing about this is that me also I used such functions on a TMS470 microcontroller and worked fine, even if I recommend here not to use them.

  6. embyo says:

    its really a nice tutorial

  7. SRIKANTH says:

    NICE

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

%d bloggers like this: