Search Microcontrollers

Friday, August 14, 2015

TI RTOS - thread synchronization

I don't have much free time these days, but when I have a spare hour, I like to play with the TI RTOS, following the great online workshop I discussed in my previous post.

The BIOS (Kernel) of the OS allows you to create threads (functions) that run with different priorities and that respond to an hardware interrupt (Hwis) or a software generated "event" (Swis, Tasks) or simply run continuously whenever the OS is not busy with other stuff (Idle).

All that is quite cool and also rather easy to implement, but soon you discover you may need to implement some sort of synchornization between your threads, eventually passing values from one thread to another.
Say you have a Hwi that triggers whenever data is received on the UART, it sends the buffer to a Swi that reads it and interprets data (i.e. commands you are sending remotely, let's imagine it's ASCII based and at every \n you mark the end of a command).

What happens is that once you receive a command, you want to react accordingly and this reaction may take a few CPU cycles, for instance to turn a stepping motor X steps in a given direction.
Meanwhile your UART is still receiving other commands.

You see it's easy to get in a difficult situation where buffers are overrun or changed while you are using them.

The worst solution to exchange data across the different threads is to use global variables as buffers, these will easily get manipulated while you are using them.
sure, there are a few ways to protect your system from this to happen, but most of those "solutions" are likely to introduce even worse problems.

But hey, we have an OS, remember?
The RTOS has built in capabilities to regulate the execution of the tasks.

Semaphores

A Semaphore can be used to pause (pend) a task untill another task tells it (post) to run.

It is somehow similar to an event, but it does not trigger a function (like an event would do), instead it tells it (whenever the bios scheduler decides to give back the Program Counter to it, based on priority settings) to keep running.

Example 

void myTask()
{
  .. do some init stuff...
  while (1) // run forever
 {
    ... do some other stuff ...
   Semaphore_pend(myCoolSemaphore);
   ... get the data and use it...
  }
}

It is cool, indeed, but if we need to echange data, the Semaphore is not enough.

So, there are features in the OS that allow one thread to send a buffer with a message.
This message can be in a Queue or in a Mailbox.

Queues and mailboxes are slightly different, the main difference is probably that messages in mailboxes are in fact copies of the data, meaning that the sender and the receiver do not share the same memory locations when accessing the data, while queues pass pointers.
Another important difference is that mailboxes have built-in semaphores, while Queues don't, you need to provide a semaphore for synchronization purposes.

The overall idea is that along with a synchronization signal you can send data, i.e. the buffer containing the command.
However we can have several mails in a mailbox, making the system automaGically multi-buffered.

So, going from a single buffer to a n-buffer solution if using queues and mailboxes is mainly a configuration task (you set up values in the GUI, no coding needed for this).


The picture (app.cfg edited in CCS 6.1.0) shows a double buffer implemented with a mailbox with 2 max messages.

The example, taken from the TI RTOS Workshop (see previous post) lab 9 does the following:

1) An Hardware timer generates an interrupt every 500ms
2) An Hwi function associated to the timer int posts a semaphore

void Timer_ISR(void)
{
    TimerIntClear(TIMER2_BASE, TIMER_TIMA_TIMEOUT);
Semaphore_post(mailbox_queue_Sem);
}

3) A mailbox management task waits (pends) for the semaphore and when it receiveds a green light, it posts to the mailbox.
Notice that in this case the "data" is generated here (msg.val is toggled).

void mailbox_queue(void)
{
  MsgObj msg;
  msg.val = 1;
  while(1)
  {
   msg.val ^= 1; // toggle msg.val (LED state)
   Semaphore_pend(mailbox_queue_Sem, 
                  BIOS_WAIT_FOREVER);
   Mailbox_post (LED_Mbx, &msg, 
                  BIOS_WAIT_FOREVER);      }
}

4) A task that actually reacts on the data (toggles a led) waits for the mailbox

void ledToggle(void)
{
  MsgObj msg;
  while(1)
  {
   Mailbox_pend(LED_Mbx, &msg, 
                BIOS_WAIT_FOREVER);
   if (msg.val)
   {     // turn LED on

    GPIOPinWrite(GPIO_PORTF_BASE,
           GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 8)  
   }
   else
   { // turn LED off
    GPIOPinWrite(GPIO_PORTF_BASE, 
            GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0)
   }
  }
}

We can observe what happens thanks to the UIA (Unified Instrumentation Architecture) that allows to interface with the OS via the JTAG port and gather execution details.



In the picture you can see that while the system is Idle (green line) a semaphore is posted (first green flag) by the Hwi function.
Control is handed over the mailbox management function (higher blue line) which is pending on the semaphore (first red flag).
It executes toggling the msg.val and then posts a mail (second green flag), then goes back pending on the semaphore again (second red flag).
Only then the bios scheduler (lower blue line) passes the control over to the ledToggle function (red line) which is pending on the mailbox, it gets the message (third green flag -it is green because it does not block the task, and it does not block the task because there is a message in the mailbox), manages the led based on the value in the message and finally waits again for the mailbox (third red flag).
At this point there are no other messages in the mailbox, so the flag is red, the task gives back control to the sceduler.
Guess what happens when a new timer int is fired?
Yup, rinse & repeat.

Neat eh?
If you are interested in knowing more on the subject, I encourage you to try the free TI RTOS Workshop, the example illustrated here (and the relative code) is taken from the Lab 9 material.

Thursday, August 6, 2015

Playing with the TI-RTOS

I always thought that dealing with an RTOS was some kind of nightmare.
Well, looks like I was wrong and yet again we prove that most of our beliefs are generated by ignorance, I happen to have a lot of it! :)

First thing first, what is an RTOS?

Real Time Operating System, that's what the acronym stands for, but to give a better explanation, we can say it is an Operating System that runs on micro-controllers and eventually CPUs.
That's the "OS" part, now for the "RT" one we can say that it is designed to have a minimal overhead and allow, depending on the clock cycle of the used hardware device, a quick reaction to events.

Say you want to control an electric motor and you feed back a number of signals (i.e. actual speed from an hall effect sensor , some user controls etc.), then you want the system to react as "realtime" as possible, you don't want you motor to react 5 minutes later because you are installing some windows updates.

Yup, you should not expect live updates by default in an RTOS :)

When you have many things potentially happening at the same time, then you normally use a loop that scans them (the quick and dirty solution) or better you set up some interrupts and maybe some timer based functions.

If you have an OS, then you can use threads.
An RTOS gives you the ability to schedule threads, neat eh?
However in giving you that functionality it makes sure that this does not generate much overhead and it allows you to define how to prioritize them.

If you played with systems running with multiple interrupts, then you know about nesting interrupts and while in theory this is pretty straight forward, but in an application that should not miss any interrupt and provide a reliable event handling this can be problematic.

You guessed, the RTOS threads make this an easier task.

... but... yeah... when I am dealing with an mcu I might not have much of power to run an OS you may think.
Well, the tiny MSP430G2 runs at 16Mhz, 16 bits, not much, right?
My old 80286 desktop computer was 16 bit , 10MHz and I thought it was running MS-DOS pretty neatly!
So, in reality, it depends on the OS you are using and an RTOS gives you WAY LESS than MS-DOS, and this makes it perfectly usable even on a MSP430 launchpad (don't try to install windows 10 on it, tho)!

TI created its own RTOS (and they called it TI-RTOS) and it is available for different kind of platforms : MSP430, CortexM4 (Stellaris, Tiva), C28x, C6x etc.

If you have any kind of launchpad, chances are you can freely use the TI-RTOS!
In CCS6.1.0, just go to the CCS App Center and download the version that matches your device



As in its best tradition, TI provides amazing training material, including an extremly well done workshop (yup, for free, online) titled "Introduction to the TI RTOS Kernel Workshop".
The instructor Eric Wilbur is absolutely great in those videos.
Lab activities help tremendously in understanding the tasks at hand and in discovering how much you can do with the OS and the related tools.

We said the RTOS has some kind of smart "scheduler" that allows you to run threads, but that's just a small (although important) part of it.
It provides memory management, debug functionality and GUI configuration features.
Also it comes with drivers, provided in a library, these reduce the need to access the system low level, instead they give you APIs you can use to interact with the rich hardware present in the MCUs.

I am currently enjoying the labs of the workshop, I suggest you do the same, but for those who are too lazy for it, I will post here some summary activity, this will be my way of taking minutes about what I do.

Meanwhile, if you think a typical "arduino like" application, it has a skeleton like the following


void init()
{
  // hw initialisation
}

void loop()
{
 while (1)
 {
   dosomeStuff();
   doSomeOtherStuff();
 }
}


a TI RTOS application instead looks like :

void main(void)
{

   hardware_init();// init hardware via Xware
   BIOS_start();
}


There is no more a while(1) loop because the BIOS is handling the task scheduling, you simply ask the bios to start and it will take care of the different threads.
Threads exist in different flavors and they can be assigned different priorities.
The easiest form of a thread is an idle function, which is executed with the lowest priority by the idle thread, basically when the CPU / MCU does not have anything more important to do.

Creating an idle function is fairly easy, say we have a function that toggles the onboard LED (code taken from lab 4 of the before mentioned workshop and slightly modified by myself)

void ledToggle(void)
{
if(GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_3))
{
GPIOPinWrite(GPIO_PORTF_BASE,                
                   GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0);
}
else
{
GPIOPinWrite(GPIO_PORTF_BASE, 
                   GPIO_PIN_3, GPIO_PIN_3);
}
delay() // create a delay of ~1/2sec
}

then you just need to register the function as an idle, which you can do in the GUI simply like this :

Pretty simple eh?
Now, you may think that the delay function call is blocking, but this "blocking" behavior only affects this specific thread, it does not prevent other threads, with higer priority, to run in parallel.

Now I guess you can see how the whole OS thing starts to make sense and at the same time, it does not necessarily add complexity to your application.

Give it a try, it's fun!