Question

Hope someone can help - apologies for this very basic question, but I'm using standard C to write code for some experiments with PIC microcontrollers, and I'm very new to the C Language.

I have various logical groups of code, such as functions to control an LCD display, that I'd like to make re-usable to PIC-based projects and would like to know how best to break-up these logical code groups for re-usability.

In the example of the LCD functions, I presume I split the declarations into a header named 'lcd.h' (including in 'Header Files' project directory of my IDE) and the function definitions in an include 'lcd.c' (including in 'Source Files' project directory of my IDE) - would this be correct?

What are the naming conventions for breaking up code in this way? For instance should all global declarations be in a header file named 'main.h'?

Many thanks, Alex

Was it helpful?

Solution

It's good that you are focusing on modularity. Not only will it bring you re-usability, but it will also make debugging easier.

Naming convention

You are correct. For LCD functions, create the files lcd.c and lcd.h. There are no universal function naming conventions, but here's what I use:

lcd.h:

void LCD_PublicFunction(void);

lcd.c:

static void PrivateFunction(void);

void LCD_PublicFunction(void)
{
    // Function goes here
}

static void PrivateFunction(void)
{
    // Function goes here
}

I place a LCD_ prefix in front of all my public functions in order to prevent namespace collisions and also to help me find the location of a function at any point. The static keyboard prevents any functions outside of lcd.c from seeing PrivateFunction and I drop the prefix to denote private.

Globals

Please avoid global variables. As your project grows, it will become harder to trace the logic. Instead, use getter and setter functions.

static int brightness;

void LCD_SetBrightness(int var)
{
    brightness = var;
}

int LCD_GetBrightness(void)
{
    return brightness;
}

This gives you more flexibility. Perhaps you'll need to add a little logic each time the brightness is set. Perhaps you want the variable to be read-only, so you can drop the setter.

Granularity

Try to break up your project as much as possible. I'm assuming you'll be using some sort of serial port to communicate with the LCD. Break up the communications firmware from the LCD display firmware.

For instance, if it uses SPI, then you should create an spi.c and a spi.h.

I've seen this practice go too far. I've seen people wrap functionality around all of the I/O ports such that they have functions to set digital pins high and low.

Bad example:

void IO_PortA7 (char val)
{
    LATAbits.LATA7 = val;
}

I haven't really gained anything here other than adding some syntactic sugar. Simply use LATAbits.LATA7 in the code since it's the standard way to turn I/O on and off on a PIC.

Good example:

void FX_SetBuzzer (char is_active)
{
    LATAbits.LATA7 = is_active;
}

Just by reading the code, you can tell that I've connected a buzzer to pin A7. Furthermore, the rest of the code doesn't care how I've connected the buzzer, and there's only one change I have to make if I have to move the buzzer to a different pin. Finally, by using a variable name of is_active, I document the fact that the buzzer is active-high. I try to use questions for all boolean variable to remember what happens in the true condition.

Testing

Finally piece of advice. In each of your .c files, create a test harness.

#ifdef LCD_TEST

int main(void)
{
    // Enable LCD communication.
    LCD_Init();

    // Display friendly greeting.
    LCD_Display("Hello, world!");

    // Wait for power-down.
    for(;;);
}

#endif

This way, you can build a tiny program that tests your LCD by itself. It serves several purposes.

  • It follows Test-driven_development, which is a Good Thing.
  • It provides a basic form of documentation by showing a functional example.
  • In the future, your LCD might suddenly stop working. Simply invoke this code and see what happens. If the test works, you know your latest changes broke the LCD functionality somehow. If it doesn't, you know the problem is with the LCD itself or the connections between it and the PIC.

Good luck!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top