Index

Cooperating Sequential Tasks

  1. Introduction
    1. Arduino
    2. Interrupts
  2. Simple Tasks
  3. Multiple Tasks
  4. Communicating Tasks
  5. Communicating Values
  6. Synchronising Tasks
  7. Buffered Communication
  8. Multiple Senders
  9. Conditional Tasks
  10. Transput
  11. Implementation
    1. Common Data
    2. Tasks
    3. task_builder
    4. task
    5. channel
    6. virtual_machine
    7. set
    8. queue
    9. clock
    10. standard
    11. Adjusting Limits

Describing Simple Tasks



The multitasking mechanism described here is specifically designed to be as independent as possible of any particular hardware it might be run on. This implies that we are not going to be using interrupts as in an operating system kernel, nor will we be worrying about starting and stopping tasks at arbitrary points in sequences of actual machine instructions. Instead the sequences actions to be performed by tasks will be described by short C++ functions. These can of course access the physical hardware, via the Arduino libraries, but the multitasking system is independent of what they do, as long as they don't spend too much time doing it.

It is important to understand that individual tasks are purely sequential, i.e they only perform one action at a time. Multitasking is the process of interleaving the actions of several tasks so that they appear to execute concurrently on a single processor.

Multitasking support is provided by a single library called "tasks". This provides routines for describing what we want tasks to do, and how they interact. Once this has been done, the execution of a set of tasks is managed entirely automatically. Using the Arduino IDE[1] each task can most conveniently be described in a single tab. The following example shows how we might write the equivalent of the standard "blink" example and demonstrates how to flash an LED at regular intervals. We are going to call it "blinker".

#include "Tasks.h"

const int led_pin = 13;

void blinker_setup()
{
  pinMode(led_pin, OUTPUT);
}

void led_off()
{
  digitalWrite(led_pin, LOW);
}

void led_on()
{
  digitalWrite(led_pin, HIGH);
}

void blinker(int interval)
{
  new_task(blinker_setup);
    action(led_off);
    pause(interval);
    action(led_on);
    pause(interval);
  end_task();
}


There are two parts to the task description. The first part describes the Arduino specific bits using normal C++ declarations. First we declare a constant to identify the pin to which the LED will be connected. Next comes a function to perform any initialisation required by the task. In this case, all that is required is to configure the LED pin as an output, As part of the operation of the task we want to be able to turn the LED off and on, so the next two functions use digitalWrite to do exactly that. Note that all these functions are one liners, but there is nothing significant about that. This is just a very simple example.

The final function, with the same name as the task, describes how we want to use the previous functions. The first and last lines, new_task and end_task, tell the multitasking library that we are starting to describe a new task, and when we have finished, so that it can create internal data to control the task's execution. The call of new_task is given a reference to the blinker_setup function so that it can be called when the task is started. If there were no initialisation required, we would call new_task without any parameter.

The next four lines are indented to emphasise that they constitute the body of the task. As with new_task and end_task, the functions called action and pause are provided by the multitasking library. Each action is indivisible and must complete its execution before any other action can be performed by this or any other task, i.e. tasks cannot interrupt each other in the middle of actions.

Firstly we want the task to perform an action which turns the LED off, and we indicate this by calling the action function with the function led_off as its parameter. We want to leave the LED off for the interval given as a parameter to the blinker function in milliseconds. Calling pause with interval as a parameter specifies this. The last two lines specify turning the LED on and then pausing for as long as we want it to remain on. When the blinker task is started, it should therefore turn the LED off, pause for some interval before turning it on, and then pause again. As with the usual Arduino loop function, the multitasking library will arrange to repeat this sequence of actions forever.

It is important to realise that none of the Arduino specific functions will be called when the "blinker" function is called. All it is doing is telling the multitasking library what the task is to do. The actions will not happen until the library starts the task. To cause this to happen, we need to define the setup and loop functions in the IDE main tab as shown below. The call of init_tasks is required to initialise the multitasking library, we are specifying a 100 millisecond interval to use in the blinker task, and the final call of call of run_system starts the execution of any the defined tasks.

#include "Tasks.h"

void setup()
{
  init_tasks();
  blinker(100);
}

void loop()
{
  run_system();
}


If we now compile all this and upload it to an Arduino with an LED attached to pin 13 in the usual fashion, the LED will blink rapidly approximately 5 times a second.

This is very slightly more work than getting an LED the blink using a conventional Arduino program but, as is often the case with powerful mechanisms, you only really get to see their advantages when you try to do more complex things.

Next: Describing Multiple Tasks

1. https://www.arduino.cc/en/Main/Software