Embedded Operating Systems
This course will present the important notions of embedded operating systems and show how a primitive operating system (protothreads) can be deployed on teensy.
Slides
Slides are available here
A teensy program with protothreads
Download the protothreads_blink.tar archive and uncompress it.
Have a look at the protothread_blink.ino program. This programs defines two protothreads:thread_red_led that switch the LED state every seconds, and thread_sine that changes the sine tune every 0.25 seconds. Both these thread are unlock by a dedicated timer.
Can you understand how it works?
This example, of course, is a bit trivial because the changes in the LED state and the sine tune could have been done in timer callbacks. But, first, we try to avoid writing to much code in the call back and, second, one has to imagine other conditions for unlocking the threads such as internal variable states or input on UART...
What happens if one PT_WAIT_UNTIL is commented out? For instance in thread_led_red:
//PT_WAIT_UNTIL(pt, TIMER_LED_RED_ON == 1);
Note finaly, note that many variables have been set as global variables as local protothread variables are not remanent.
Add a UART printing
copy this protothreads_blink directory to a new protothreads_count and modify the program to print on UART serial port, every second, the number of blinking of the LED since the beginning of the program. This imply the following tasks (see UART teensy Documentation):
- Initialize serial communication at 9600 bauds using
Serial.begin(9600); - Declare a global variable
blink_count - Declare a new timer counting each second (up to four interval timers can be used in teensy 4.0)
- Increment
blink_countat each blink - Print (using
Serial.print) the value ofblink_countin new timer callback.
Use the command minicom -D /dev/ttyACM0 or the serial monitor to visualize what is send on serial port by the teensy.
For the moment we do not really need a new protothread, as we can assume that two calls to Serial.printf are fast enough not to perturbate audio.
Imagine that we want to control our audio device using the keyboard. Receiving a character from the keyboard and performing the action necessary might be too long because it imply a busy wait (i.e. check that a character has been typed before reading it). Hence we will add a protothread to do that.
Control LED and sound with your keyboard
Try to control (i.e. activate or de-activate), sound production and led blinking from you AZERTY keybord.
copy this protothreads_count directory to a new protothreads_control
declare a new protothread `static PT_THREAD(thread_receive(struct pt *pt)) that will receive characters on UART serial port for the host computer. the condition in the PT_WAIT_UNTIL will be Serial.available()>0 (indicating that the receiving buffer contains some characters, see UART teensy Documentation). As soon as a character is typed, echo its ascii code (standard "echo mode" for UART interaction).
Use that mecanism to control the sound and LED:
- Typing character 's' (for "sound") will switch off/on the sound (i.e. setting
`myDsp.setFreq(0)) - Typing character 'l' (for "led") will switch off/on the LED blinking.
Hint: you can switch the value of a variable var between 0 and 1 using the Xor operator: var = var ^ 1;
** Solution **
#include <Arduino.h>
#include <Audio.h>
#include "MyDsp.h"
#include "protothreads/pt.h"
static struct pt pt[3];
// Teensy 3.x and 4.x have the LED on pin 13
const int ledPin = LED_BUILTIN;
volatile int ledState = LOW;
volatile unsigned int TIMER_LED_RED_ON = 0;
volatile unsigned int TIMER_SINE_ON = 0;
volatile unsigned int SOUND_ON = 1;
volatile unsigned int LED_ON = 1;
//blink count
volatile unsigned long blink_count = 0; // use volatile for shared variables
IntervalTimer TimerLed;
IntervalTimer TimerSine;
IntervalTimer TimerCount;
MyDsp myDsp;
AudioOutputI2S out;
AudioControlSGTL5000 audioShield;
AudioConnection patchCord0(myDsp,0,out,0);
AudioConnection patchCord1(myDsp,0,out,1);
static PT_THREAD(thread_led_red(struct pt *pt))
{
PT_BEGIN(pt);
while(1)
{
if (ledState == HIGH)
ledState = LOW;
else
if (LED_ON == 1)
ledState = HIGH;
else
ledState = LOW;
digitalWrite(ledPin, ledState);
TIMER_LED_RED_ON = 0;
PT_WAIT_UNTIL(pt, TIMER_LED_RED_ON == 1);
}
PT_END(pt);
}
static PT_THREAD(thread_sine(struct pt *pt))
{
PT_BEGIN(pt);
while(1)
{
TIMER_SINE_ON = 0;
if (SOUND_ON == 1)
myDsp.setFreq(random(50,1000));
else
myDsp.setFreq(0);
PT_WAIT_UNTIL(pt, TIMER_SINE_ON == 1);
}
PT_END(pt);
}
static PT_THREAD(thread_receive(struct pt *pt))
{
PT_BEGIN(pt);
while(1)
{
PT_WAIT_UNTIL(pt, Serial.available() > 0);
while (Serial.available() > 0) {
int incomingByte = Serial.read();
Serial.print("UART received: ");
Serial.println(incomingByte, DEC);
if (incomingByte == 's')
{
SOUND_ON = SOUND_ON ^ 1;
Serial.print("SOUND_ON is now: ");
Serial.println(SOUND_ON, DEC);
}
if (incomingByte == 'l') {
LED_ON = LED_ON ^ 1;
Serial.print("LED_ON is now: ");
Serial.println(LED_ON, DEC);
}
}
}
PT_END(pt);
}
void timer_interrupt_led() {
TIMER_LED_RED_ON = 1;
//blink countxs
blink_count++;
}
//blink count
void timer_interrupt_count() {
Serial.print("number of blink=");
Serial.println(blink_count);
}
void timer_interrupt_sine() {
TIMER_SINE_ON = 1;
}
void setup(void)
{
pinMode(ledPin, OUTPUT);
//blink count
Serial.begin(9600);
AudioMemory(2);
audioShield.enable();
audioShield.volume(0.5);
TimerLed.begin(timer_interrupt_led, 500000); // blinkLED to run every 0.5 seconds
TimerSine.begin(timer_interrupt_sine, 100000); // Sine to run every 0.1 seconds
TimerCount.begin(timer_interrupt_count, 2000000); // Count blink every 2 seconds
PT_INIT(&pt[0]);
PT_INIT(&pt[1]);
}
void loop(void)
{
thread_led_red(&pt[0]);
thread_sine(&pt[1]);
thread_receive(&pt[2]);
}