avr-os: Multitasking on Arduino

Arduino is an open source prototyping platform for electronics. There is so much you can do with Arduino and the community is proof. In playing with Arduino I decided that it would be a great project to create a small multitasking library for use on AVR platforms, which includes Arduino.

A small introduction

avr-os is a library that provides a very basic rutime that enables your program to multitask.

The library uses pre-emptive multitasking to switch tasks and each task has its own stack that is restored when a task is resumed. An AVR timer is used to provide ticks and this interrupt is used to switch tasks.

Hello World, avr-os style

The following sketch is a basic example of how multitasking can be used in your Arduino sketches. This sketch has two tasks that continously print the task name and some information to the LCD.

It demonstrates how a task is started, how os_sleep can be used to sleep tasks, and the ability to use spinlocks.

#include <Arduino.h>
#include <LiquidCrystal.h>

#include <util/delay.h>

extern "C" {
    #include <os.h>
}

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

spinlock_t testLock;

void kernel_task(void *arg) {
    while(1) {
        spinlock_acquire(&testLock);
        lcd.setCursor(0, 0);
        lcd.print("kernel: " + String((long)os_get_uptime()));
        spinlock_release(&testLock);
        os_sleep(1000);
    }
}

void user_task(void *arg) {
    int x = 0;
    while(1) {
        spinlock_acquire(&testLock);
        lcd.setCursor(0, 1);
        lcd.print("user_task: " + String(x++));
        spinlock_release(&testLock);
        os_sleep(5000);
    }
}

void setup() {
    os_init();
    lcd.begin(16, 2);
    lcd.print("Starting up...");
}

void loop() {
    spinlock_init(&testLock);

    os_schedule_task(kernel_task, NULL, 0);
    os_schedule_task(user_task, NULL, 0);
    lcd.clear();
    os_loop(); 
}

The image below shows the sketch in action:

Using the OS

The following is the header for avr-os that demonstrates the features available:

#include <stdint.h>

typedef void(*os_task_function)(void *arg);

/**
 * Initializes the OS
 *
 * This should be the first call in the
 * application's main method.
 */
void os_init();

/**
 * Starts the OS loop.
 *
 * This will execute tasks.
 * Make sure to create at least one task before 
 * starting the loop.
 */
void os_loop();

int os_schedule_task(os_task_function function, void *arg, uint16_t start_delay_secs);

void os_exit_task();

void os_sleep(uint16_t millis);

/**
 * Returns the amount of seconds since the OS has started.
 */
uint64_t os_get_uptime();

/**
 * Returns the amount of milliseconds since the OS has started.
 *
 * This will overflow after about 
 */
uint64_t os_get_uptime_millis();

typedef volatile int spinlock_t;

void spinlock_init(spinlock_t *lock);
void spinlock_acquire(spinlock_t *lock);
void spinlock_release(spinlock_t *lock);

Try it out

The code is available here and contributions are definitely welcome.


comments powered by Disqus