C Bit Field Structures For Microcontroller Multitasking

So you’re getting better at programming microcontrollers and now you want to do several things at once? You know better than that, microcontrollers are only capable of processing one thing at a time. But if you’re clever with your coding you can achieve something that behaves as if several things are going on at once. The most common way to do this is to set a flag using an interrupt, then use the main loop to check for that flag. [S1axter] posted a tutorial on this topic where he uses bit field structures to help simplify time sensitive events.

We think [S1axter] did a fantastic job of explaining this moderately difficult topic clearly and quickly. In the video after the break he begins by explaining what a bit field is and how it is defined. Basically you’re using a C structure to track a flag using just one bit of storage. This way the flag is either set or not. We suggest you pay careful attention to how he declares the structures as volatile, so you don’t have unexpected behavior when you try it yourself.

[youtube=http://www.youtube.com/watch?v=Voqg4Mx2Sg8&w=470]

27 thoughts on “C Bit Field Structures For Microcontroller Multitasking

  1. Volatile, it can be useful. But with warnings: http://lwn.net/Articles/233482/

    For larger systems volatile fails to account for a lot of situations. For microcontrollers it works most of the times. Still, your compiler is allowed to re-order instructions without changing the functionality of that function. (this can be a real problem if it happens to you!)

    1. I’m very curious to know more about the warnings because the link itself didn’t provide enough background/context for me to understand what it was referring to.

      I use volatile a LOT with microcontrollers because I do not know of any other way to keep the compilers from optimizing out reads of a variable that is shared between main code and an interrupt.

      As an example:

      volatile uint8_t flag;

      void isr()
      {
      flag = 1;
      }

      void main()
      {
      flag = 0;
      while (!flag);
      //more code ...
      }

      With most of my compilers, the while loop would never terminate if I didn’t include the volatile keyword as the optimizer would skip re-reading the flag variable and assume its value hadn’t changed. I understand that volatile and atomic are two VERY different things and that data consistency is not guaranteed on successive reads of a volatile variable (which is kind of the point…) but I don’t understand what that has to do with the stuff Linus was upset about.

      Forgive my ignorance, I’m mostly self-taught but I am trying to improve my understanding.

      1. @Will
        http://lwn.net/Articles/233479/
        “C programmers have often taken volatile to mean that the variable could be changed outside of the current thread of execution; as a result, they are sometimes tempted to use it in kernel code when shared data structures are being used.”

        One possible reason for that could be that in interpreted languages like Java and C#, volatile is different in that it’s atomic and thread safe (and should be called something else).
        In C/C++: defaults as a bug.

        There’s also good note in the comments:
        “One other use for volatile is when communicating with interrupt/NMI handlers running on the same CPU. In this case, all the accesses are on the same CPU, so CPU reordering is irrelevant — CPUs see their own accesses in order. The only requirement is that the compiler avoid optimizations that re-order the code.”

      2. Sorry, I forgot to link the memory barriers doc. Which is quite important in this case.

        Volatile does 1 thing, it instructs the compiler to do every read/write from memory instead of keeping values in registers. However, it does not force the compiler to keep the code where you want it.

        For example, if you fill some kind of data structure in a function, and then set the “data ready” flag, the data ready flag write might be moved above the filling of the data (because it could optimize better). I’ve actually had this happen to me once with -O3 in gcc.

        There are also problems with cache with multi-cpu systems. But that’s outside the scope of microcontrollers.

  2. Bitfields are great to make your source code even more readable. But as far as I remember I don’t use them especially with small 8-bits microcontrollers like the tiny13 as it seems like gcc uses lots of ressources even with the best optimization options. I might have missed something at that time but I remember seeing my source code increasing like if gcc was allocating one byte for each 1 or 2 bits bitfields I was using. Anyone’s seen something similar?

  3. Gotta be careful with setting bits from any code that can be interrupted.

    If your instruction set supports 1-bit atomic writes, use them! Make sure your compiler is optimizing in that direction.

    If you can’t, and you are interrupted in the middle of writing to that register, you can corrupt it easily.

  4. The numer one tip for C bitfields is “don’t use them”. Their implementation is completely up to the compiler, and without examining the generated code you have no idea what you’re getting. Manually shifting and masking will almost always produce better code.

    Never ever use bitfields to access hardware registers.

    1. Why never use bitfields if the compiler supports them in the manner that you desire? The C30 compiler for Microchip 16bit micros expand bitfield structures down to 2 asm instructions, (dereference then set/clear).

      An argument about portability is valid for x86/ARM based systems however when designing software for dedicated hardware why not leverage the compiler to easy the development?

      Desktop/application models don’t necessarily translate verbatim into the micro world.

      1. If that is a supported feature of the compiler that is no problem. But if that is just the way the compiler happens to handle them it could change with any compiler release.

      2. Why never use bitfields to access hardware? First you have to really know the rules to know when to break them :-) Also, for it to be a “rule” to _never_ use them, keep in mind that “all absolute statements are never always correct”. When ever I see the words always, never, etc, I wonder what “except for” was left out.
        – Steve

  5. I’d point out that bitfields aren’t essential to this methodology; they just save a little space versus using a whole byte for a boolean. It would be trivial to rewrite the example using bool (or _Bool or the platform equivalent). It would be crucial to use volatile in any case and, as mentioned above, to know how your compiler treats volatile for the data type you choose.

    The methodology demonstrated is conceptually similar to setTimeout() in JavaScript. I think a library for cooperative multitasking and/or coroutines would be pretty valuable, but perhaps a lot more intense than many will get with microcontrollers.

  6. For those on 8-bit AVR, there are some bit-addressable registers which are pretty much made for this sort of thing e.g. GPIOR0.

    With a little care, you don’t need to muck around trying to make the writes atomic by disabling interrupts or such so you can get a really compact fast event system going.

    1. It’s more a guide to using easy to use variables in space constricted systems that often require you to work out one bit value frequently. less so for pc code and by the comments more problematic there.

  7. single core x86 processors are also only capable at one thing at a time..that’s why OS kernels implement time shares..

    I’m with the others on the humor behind this being treated as news..

  8. wonder that the Parallax Propeller guys did not make a statement about the article introduction

    “You know better than that, microcontrollers are only capable of processing one thing at a time”

    Well they could do 8 things at a time without much headache :)

    1. Well if you really want to nitpick, even a humble MCU does a lot more than one thing at a time… Oh, you meant more than one of YOUR things at a time! :p

      Seriously though, just because the ALU has only one path through doesn’t mean that the hardware state machines controlling ADCs, UARTs, I2C, CAN, SPI, DAC, PWM, interrupt monitors, etc. don’t all run in parallel and to forget that fact can be just as deadly as any of the “volatile” mistakes. It is pure snobbery to think that only running one user defined software thread at a time is all that matters. Let’s give the hardware guys some credit. :)

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.