Archive for July, 2011

Extreme Pinewood Derby

A local group of guys decided to have a pinewood derby with no rules. The only stipulation was that we couldn’t cause harm to the track or spectators. We decided that this implied no combustibles. I knew the best way to win this would be some kind of CO2 powered vehicle. The bad news is that I was running out of time and didn’t know enough about CO2 valves to whip something up.

I decided to use the propeller, motor, and electronic speed controller from my Multiplex EasyStar RC Airplane. I also knew that only an autonomous vehicle would impress the crowd.

The track we were racing on held each car with a wooden rowel. The dowels dropped simultaneously to start the race. To make the car autonomous, I used a lever switch and the weight of the car to trigger an event to start the car.

The main controller of the car is an Arduino (yes, I know, I’m going against my own preaching). The Arduino held a simple state machine to control the motor. For safety reasons, I designed the state machine to only start the motor after the system had been intentionally armed.

Assertions in Microcontrollers

Introduction to Assertions:

Assertions are a simple way of testing your programming logic.  The basic idea behind assertions is testing a piece of code that you assume will always work as designed.  As programmers, we inheritantly do this all the time while writing code.  We find ourselves writing small print statements to check that what we just coded produced the right output.  Assertions are a formal way of doing this.  The typical assertion header file looks like this:

#ifndef _ASSERT_H_
#define _ASSERT_H_

#if ENABLE_ASSERTIONS==1
void assertion_failure(char* expr, char* file, char* baseFile, int line);
#define assert(expr) \
    if (expr) ; \
    else assertion_failure(#expr,__FILE__,__BASE_FILE__,__LINE__)
#else
#define assert(expr) /*nothing here*/
#endif

#endif // _ASSERT_H_

And a programmer would use it like this:

#define ENABLE_INSERTIONS 1
#include "assert.h"
double nasa_rocket() {
    int i;

    // thrust value for the four engines
    double thrust_vals[4];

    // average thrust percentage
    double thrust_percentage;

    // get thrust from all rocket engines
    for (i=0; i<4; i++)
        thrust_vals[i] = getMotorThrust(i);

    // call function to compute average thrust percentage
    thrust_percentage = computeAverageThrustPercentage(thrust_vals);

    // double check programming logic
    assert(thrust_percentage >= 0.0);
    assert(thrust_percentage <= 100.0);

    return thrust_percentage;
}

As shown in the first code snippet, when an assertion fails, it calls ‘assert_failure()’ and passes information about the tested expression, file names, and line number.  This function usually uses ‘fprintf()’ to write this information to the error console.  After printing the information, the ‘assertion_failure()’ function either calls a system halt function, kills the program, or puts itself into an infinite loop.

The NASA software engineer could verify here that the returned ‘thrust_percentage’ would not be out of the standard bounds of percentages (0% to 100%).  This would not be a check you’d want to do in code because it should always work and extra checks will just slow it down.  However, you do want to make sure it works before sending your billion dollar rocket into space.  After the engineer has verified that the code works, he can simply disable assertions using the ‘ENABLE_ASSERTIONS’ define statement.  Doing this causes the C preprocessor to produce blank lines for all calls to ‘assert()’.  After all, it’s better to use invalid numbers than to cause an infinite loop inside an ‘assertion_failure()’ function!

How to assert in a microcontroller:

The first program you write for a microcontroller is much different than the first program you write for a personal computer.  You don’t have any libraries or operating system to support the standard “Hello World!” program.  Usually your first program is making an LED flash.  GPIO is the easiest thing to implement in a microcontroller.  Setting up a console for a microcontroller is a big process.  What happens if you wish to use assertions to test the console driver your are creating?  How can you assert without a console to print to?  There are many answers to this.  This post attempts to give the most robust implementation for microcontrollers.

First thing is first, you need a working LED that you can set aside for assertion failures.  That’s it!  Using some simple code, you can be notified (by the LED) of a failure and check where it came from using the debugger.

Here is my implementation for assertions:

#ifndef _ASSERTS_H_
#define _ASSERTS_H_

void assert_init(void (*assert_indicator)(void));
#if ENABLE_ASSERTIONS==1
void assertion_failure(char* expr, char* file, char* basefile, int linenum)
#define assert(expr) \
    if (expr) ; \
    else assertion_failure(#expr,__FILE__,__BASE_FILE__,__LINE__))
#else
#define assert(expr) /*nothing*/
#endif // ENABLE_ASSERTIONS==1

#endif // _ASSERTS_H_
#define ENABLE_ASSERTIONS 1
#include "asserts.h"

// function pointer to assertion indicator
static void (*assert_ind)(void) = NULL;

// sets user's choice of failure indicator function
void assert_init(void (*assert_indicator)(void)) {
    assert_ind = assert_indicator;
}

// failure function called by macro.
#if ENABLE_ASSERTIONS==1
void assertion_failure(char* expr, char* file, char* basefile, int linenum) {
    // debugger can look at these
    volatile const char* v_expr;
    volatile const char* v_file;
    volatile const char* v_basefile;
    volatile int v_linenum;

    // assign variables to volatiles
    v_expr     = expr;
    v_file     = file;
    v_basefile = basefile;
    v_linenum  = linenum;

    // call function that indicates an assertion failure
    assert_ind();

    // halt
    while (1);
}
#endif

As shown, my implementation has an extra function for initialization. The user simply passes a function pointer to the ‘assert_init()’ function that corresponds to the function that would indicate to the user of an assertion failure.  The simplest solution is a function that turns on an LED designated for assertion failure indication.

The ‘assertion_failure()’ implementation simply copies the input values to local volatile versions.  It then calls the user’s function (which illuminates the LED) then puts itself into a infinite loop.

When the user sees the LED illuminate, he knows an assertion has failed.  Using the debugger, he can then pause the program (which is still in the ‘assertion_failure()’ function) and inspect the variables which indicate the failing expression, file location of the failing expression, and line number within the file of the failing expression.  Using this information, he can then fix the mistake(s).