Objects and Firmware - Basics

Several years ago this article got me into thinking about object-oriented programming in my work as a firmware developer. As the title says "Object Based Development Using UML and C" it is talking about object based, not object oriented development. According to Wikipedia object based programming has one or more of three constraints compared to object oriented programming (language):

  1. there is no implicit inheritance
  2. there is no polymorphism
  3. only a very reduced subset of the available values are objects (typically the GUI components)
In firmware projects written in C it is obvious that this definition typically holds. However it is important to notice that all of these constraints can be solved. It is quite a bit of work, and may be even complex, but it can be done. This post is about the basics, we will come to other stuff later.

Object Oriented problem #1 in small (or tiny) scale firmware development is that dynamic memory allocation and small microcontrollers - hmm, well, they just don't mix and match! If you ever have a C compiler that will generate code for malloc()'s and free()'s and you use them as intended, you will end up running out of memory because of fragmentation. You have couple of options here:

1. Don't care if you run out of memory, just manage the reset
2. Do your own simplified memory allocator, which is capable of allocating only for example three different object sizes.
3. Use static memory allocation, but program still in "object'ish fashion"

We have used the option 3 for couple of projects now. I'll explain briefly:

***** object.h *****

typedef struct _object
BYTE theValue;
} object;

extern void OBJECT_construct( BYTE meIndex );
extern BYTE OBJECT_getValue( BYTE meIndex );
extern BYTE OBJECT_addValue( BYTE meIndex, BYTE v );

***** object.c *****

#include "object.h"

object myObjects[2];
#define me myObjects[meIndex]

void OBJECT_construct( BYTE meIndex )
me.theValue = 0;

BYTE OBJECT_getValue( BYTE meIndex )
return me.theValue;

BYTE OBJECT_addValue( BYTE meIndex, BYTE v )
me.theValue += v;

***** main.c *****

#include "uc_defs.h"
#include "object.h"

void main( void )
while( 1 )
OBJECT_addValue( 0, 1 );
OBJECT_addValue( 1, 2 );

PORTA = OBJECT_getValue( 0 );
PORTB = OBJECT_getValue( 1 );

We use the word 'me' instead of 'this' because sometimes the code is compiled with C++ compiler. With some more macros you can make the code more readable.

I don't know if this is even object based (propably lots of people say no), but it works for us. It at least enforces capsulation better than relying only on developers discipline. There is an overhead from passing the meIndex -parameter (but it is a BYTE instead of a pointer) and the setters and the getters, but nothings free here - if you haven't noticed.

1 comment:

Anonymous said...

This site is one of the best I have ever seen, wish I had one like this.