There are many operating systems: Linux, Windows, iOS,… But there are a few which can work in real time like Linux Xenomai. However, there are very little which can fit a microcontroller. One of them is a FreeRTOS. There are some variations of the FreeRTOS which can handle different conditions and requirements.
This blog post is not about such operating system, however…
When I started to create my own flight controller for the QuadroCopter I came up with a simple yet quite powerful OS. It can be run on a wide range of microcontrollers. But there is one condition, the used MCU has to have a timer which can periodically generate an interrupt. The overall rule of the operation of that kind of OS is quite simple. When an interrupt is captured than inside its handler it goes through the processes in queue. For example in FreeRTOS a SysTick handler is used. It is used to handle running tasks and if necessary preempt them.
The operating system which is being described here has no preemption. This means that if one task needs more time than the other already in queue it still runs without any interruption. Basically, if it crosses time bounds it still gets to run. When it is finished than the other tasks can be processed. Also there is no priorities. The most accurate description of the proposed operating system would be an operating system without preemption with round robin scheduler. From now on I will refer to this piece of software as SOS (Small Operating System).
Technicalities
How you can actually make this work? You may divide this into two parts:
- SysTick interrupt handler,
- Infinite loop in main() function.
Inside the SysTick handler you should put a call to sos function:
sos_handler();
This function look like this:
void sos_handler(void) { int i; ++sos_time; for (i = 0; i < SOS_MAX_EVENT_NUMBER; ++i) { if (sos_period[i] != SOS_EVENT_OFF) { ++sos_compare_counter[i]; if (sos_compare_counter[i] >= sos_period[i]) { sos_compare_counter[i] = 0; sos_flag[i] = 1; } } } }
Inside this function the SOS is checking if a flag for a callback function should be set. If the software counter overflows (it is checked against the period) the flag for event is set. That is all.
Inside infinite loop in main function you should put sos_run(). For example
int main(void) { sos_init(); for(;;) { sos_run(); } return 0; }
sos_run() function looks like this:
void sos_run(void) { int i; for (i = 0; i < SOS_MAX_EVENT_NUMBER; ++i) { if (sos_flag[i]) { sos_flag[i] = 0; sos_callback_array[i](); } } }
MCU is running through all events. If a flag for the event is set then a callback function is called.
Adding events
Also you can not actually forget about adding some tasks to the routing. This can be done with sos_add_event() function. Let’s say that we want add a task that would toggle a LED. This task routine is called toggle_led(). To add this to the task queue you should write a line:
sos_add_event( toggle_led, 1000, SOS_NEXT_FREE_SLOT);
This will add pointer of function toggle_led() to the task queue. It will be called periodically with 1000 ticks period. What is a tick I will explain in a moment. SOS_NEXT_FREE_SLOT tells SOS to put this task in the first available slot. If for some reason you would like to switch tasks or replace one task with another you can force a specific number.
Ticks …
Depending on the platform you are going to use the SysTick period and the actual frequency of calling tasks can differ. At the very beginning you should initialize the SOS. This can be done with sos_init() function. Inside this function you may want to initialize the SysTick. This is not obligatory and can actually make it less modular. Let’s assume that you set the SysTick to 1 ms period. Keeping that in mind when calling:
sos_add_event( toggle_led, 1000, SOS_NEXT_FREE_SLOT);
it will tell SOS to run toggle_led() precisely every 1 second. When you set SysTick period to 2 ms, the same toggle_led() function will be called every 2 seconds. Also it works the other way around. Good practise would be to set a macro or a constant which would define a proper ratio. Btw. this is how it is done in FreeRTOS.
Conclusion
As always also here is a place for improvement. This is very, very simple operating system, however. Quite an advantage is that SOS is lightweight. You can easily fit it on Atmel MCU, Arduino, ARM Cortex-Mx based MCU or any other platform as long as it fulfils requirements (actually one) mentioned at the beginning. As a quick reminder, you should have a timer capable of generating interrupts. The whole source code for SOS can be found here SOS_source.zip.
Nice!