Tips for Handling Embedded Systems 1


If you have dealt with embedded systems before you may have noticed they are very different from programming an app or a website. For some these differences can be very difficult to deal with. I am going to talk about some tips and tricks I have learned from my experiences dealing with embedded systems. The topics I am covering are:

  • Finite State Machines (FSM)
  • Finite State Machines in Practice
  • Debugging
  • Other Helpful Information

Note: All the sample code I provide will be for Arduino 1.6x IDE.

Finite State Machines (FSM)

A “state” is the condition of a thing at a specific time. A finite state machine is something that can accomplish tasks and utilizes states at its core. Since the state machine is finite, we know all possible states it can be in at any time.

The key to the state machine is the idea of time and history. The state of the machine gets evaluated periodically. Each time the machine gets evaluated a new state is chosen and the output or action occurs.

Here is a basic FSM

fsm_transition

The starting state is A in this diagram. The circle represents a state in this case there are two states ( State A and State B). The arc represents a transition from one state to another. Each arch has a condition associated with it, meaning for one state to become another state a certain condition must occur. A condition can be a variety of things, for example:

  • a button press occurred
  • a specific button press occurred
  • a signal input from outside
  • a certain amount of time has elapsed
  • etc.

Finite State Machines in Practice

Finite state machines are very helpful in breaking down complex embedded system projects into small logical parts. But how do you actually represent FSM in code? Below I have a sample Arduino project that implements a FSM whose task is to blink a LED at a one second interval. Also note by using a FSM I am able to avoid busy waiting (delay) in the code.

FSM

blink_fsm

task.h

#ifndef _TASK_H_
#define _TASK_H_

class Task {
    private:
        unsigned long period;
        unsigned long elapsed_time;
        signed char state;
        int (*TickFct)(int);
    public:
        Task();
        void setState(const signed char &);
        signed char getState();
        void setPeriod(const unsigned long &);
        unsigned long getPeriod();
        void setElapsedTime(const unsigned long &);
        unsigned long getElapsedTime();
        void increaseElapsedTime(const unsigned long &);
        void setTickFunction(int (*tick_function)(int));
        signed char runTickFunction(const signed char &);
        ~Task();
};
#endif

task.cpp

#include "task.h"

/**
 * Constructor
 * Set variables to default values here.
 */
Task::Task() {
    // do nothing
}

/**
 * function: setState
 * This method set the state of the task to the state
 * passed in.
 */
void Task::setState(const signed char & new_state) {
    state = new_state;
}

/**
 * function: getState
 * This method returns the current state of the task.
 */
signed char Task::getState() {
    return state;
}

/**
 * function: setPeriod
 * This method sets the period of the task. This influences
 * how often the finite state machine ticks.
 */
void Task::setPeriod(const unsigned long & new_period) {
    period = new_period;
}

/**
 * function: getPeriod
 * This method returns the period of the task.
 */
unsigned long Task::getPeriod() {
    return period;
}

/**
 * function: setElapsedTime
 * This method sets the time elapsed for the task. Use for initialization
 * and debugging.
 */
void Task::setElapsedTime(const unsigned long & time_elapsed) {
    elapsed_time = time_elapsed;
}

/**
 * function: getElapsedTime
 * This method returns the time elapsed since the last time the finite
 * state machine ticks.
 */
unsigned long Task::getElapsedTime() {
    return elapsed_time;
}

/**
 * function: increaseElapsedTime
 * This method adds a small amount of time to the time elapsed. The small
 * amount of time is the interval between the last check time and current.
 */
void Task::increaseElapsedTime(const unsigned long & delta_time) {
    elapsed_time += delta_time;
}

/**
 * function: setTickFunction
 * This method sets the function to invoke when it is time for task action to tick.
 * Ideally, the function is a FSM, but it can be any function that fits the parameter
 * and return type.
 */
void Task::setTickFunction(int (*tick_function)(int)) {
    TickFct = tick_function;
}

/**
 * function: runTickFunction
 * This method invokes the function (FSM) of this task. 
 */
signed char Task::runTickFunction(const signed char & current_state) {
    return TickFct(current_state);
}

/**
 * Destructor
 * This gets invoke when the program closes to do clean up. Free or delete
 * any dynamically allocated variables here.
 */
Task::~Task()
{
    // do nothing
}

blink_led_sample.ino

#include "task.h"
static const int LED_PIN = 13;

// variables for tasks
static const unsigned char num_tasks = 1;
unsigned long current_time = 0;
unsigned long previous_time = 0;

// declare task specific info
static Task led_blink_task;
unsigned long led_blink_task_period = 1000;    // tick every 1 second (1000 milliseconds)

// declare task specific info for another task here ...

unsigned long tasks_gcd = 1000;

enum blink_led_states {
    OFF = 0,
    ON = 1
}

Task* tasks[] = {&led_blink_task};

// implementation of Blink Led FSM
int blink_led_fsm(int current_state) {
    // transitions
    switch (current_state) {
        case OFF:
            current_state = ON;
            break;
        case ON:
            current_state = OFF;
            break;
        default:
            // do nothing
            break;
    };

    // actions
    switch (current_state) {
        case OFF:
            digitalWrite(LED_PIN, LOW);
            break;
        case ON:
            digitalWrite(LED_PIN, HIGH);
            break;
        default:
            // do nothing
            break;
    };

    return current_state;
}

// required function for Arduino
void setup() {
    led_blink_task.setState(OFF);
    led_blink_task.setPeriod(led_blink_task_period);
    led_blink_task.setElapsedTime(led_blink_task_period);
    led_blink_task.setTickFunction(&blink_led_fsm);

    pinMode(LED_PIN, OUTPUT);
}

// required function for Arduino
void loop() {
    current_time = millis();
    for (int i = 0; i < num_tasks; i++) {
        if (tasks[i]->getElapsedTime() >= tasks[i]->getPeriod()) {
            // tick FSM corresponding to task
            int task_current_state = tasks[i]->getState();
            tasks[i]->setState(tasks[i]->runTickFunction(task_current_state));
            tasks[i]->setElapsedTime(0);
        }
        tasks[i]->increaseElapsedTime(current_time - previous_time);
    }
    previous_time = current_time;
}

Debugging

Here are some tools and techniques for debugging:

  • Simulators
    • They can model internal state of device, which makes them great for testing logic. You can also specifically keep track of the values of variables for more in-depth testing.
  • Output (LCD, LED, FPGA)
    • FPGA (Field Programmable Gate Array)
      • If you have access to a FPGA you can emulate the behavior. Simulators cannot catch every bug, sometimes you need to see what happens on the hardware itself.
    • LED
      • The simplest form of debugging. It is the print statement equivalent in software development. You can set the LED to output a binary value of variables. You can also use the LED to indicate if a certain state has occurred.
    • LCD
      • Similar usage as LED, but can display more information. This allows you to keep track of multiple variables and states.
  • UART (Universal asynchronous receiver/transmitter)
    • Some microcontroller have UART functionality, usually via usb connection. The UART function allows you to send messages to your computer via USB to help with debugging. You must have a serial terminal monitor up and connected to the communication port of the micro-controller for UART to work.
  • Logic Analyzer
    • You can capture and display multiple signals. It can display timing diagrams, state machine traces, raw signal or other formats. A logic analyzer is useful for seeing exactly what signals are coming form analyzing sensor data.
  • OCD (on-chip-debugger)
    • This is a mechanism for monitoring and controlling execution on the device. Application is not being emulated or simulated, but actually running on the target hardware. This functionality is not available on all micro-controllers.

Other Helpful Information

Port Manipulation

Port manipulation allows you to directly communicate with the data registers of the micro-controller. By doing so you skip all the overhead from the digitalRead() and digitalWrite() functions in Arduino. However, it makes your code harder to debug and not portable. I only advise you to use port manipulation if speed is absolutely a requirement. You can learn more about port manipulation here.

Note: There are arduino libraries out there that overrides the default digitalRead() and digitalWrite() functions to use port manipulation. Here is one you can check out: digitalIOPerformance.

Here are two helper functions I use for setting and getting pin values with port manipulation:

bit.h

#ifndef BIT_H
#define BIT_H
/**
 * function: setBit
 * This method sets a bit on a PORTx.
 * @param pin             PORTx or PINx
 * @param pin_number      pin to set binary value (0 index)
 * @param binary_value    value to set pin
 * @return                the new value of PORTx
 */
unsigned char
setBit(unsigned char pin, unsigned char pin_number, unsigned char binary_value) {
    return (binary_value ? pin | (0x01 << pin_number) : pin & ~(0x01 << pin_number));
}

/**
 * function: getBit
 * This method gets the bit value from a PINx.
 * @param port            PINx or PORTx
 * @param pin_number      pin to get binary value from (0 index)
 * @return                the value of pin_number in PINx.
 */
unsigned char
getBit(unsigned char port, unsigned char pin_number) {
    return (port & (0x01 << pin_number));
}

#endif

I hope some of the tips I offer will help you in the future with embedded systems development. Until next post, take care and happy coding!


About Steven To

Steven To is a software developer that specializes in mobile development with a background in computer engineering. Beyond his passion for software development, he also has an interest in Virtual Reality, Augmented Reality, Artificial Intelligence, Personal Development, and Personal Finance. If he is not writing software, then he is out learning something new.

One thought on “Tips for Handling Embedded Systems

Comments are closed.