Friday, 7 March 2014

RTOS in PIC Microcontrollers: The Devil Inside!


 
A modern digital circuit with microcontrollers at its heart. (courtesy wallpoper.com)

 Microcontrollers are workhorses for most digital electronic devices. A lot of people like me love the power of processing and control delivered by these tiny computers. If you are reading this blog, I can safely assume you are passionate about microcontrollers and want to push the tiny beasts to their full potential and design real world electronic systems.

I assume that you have prior understanding of what microcontrollers are and have been working with them for some time. If you are new to this field it would be great searching and learning about Microcontrollers and their use for various simpler problems.

Focus areas: RTOS based parallel thread application design using PIC microcontrollers with OSA RTOS in mikroC pro for PIC series compilers.

Why do I need RTOS?

Most microcontroller programmers write sequential code for their applications. The microcontroller would read through each function called and do what it is asked to. This is okay for smaller applications where microcontroller is supposed to do only one particular thing. Lets say you want to use your microcontroller to blink an LED and nothing else, you would be okay doing it without feeling the need for running an operating system inside your microcontroller. But for any other applications where more than one thing is supposed to be done simultaneously, you are stuck.

Did you ever feel that your computer works beautifully while you write documents, surf the web and listen to music, all simultaneously. This is made possible by operating systems that share your processor's time amongst tasks. All modern embedded electronic devices you have been using use an operating system of some kind or other. 

Think about a very simple problem, suppose now instead of one LED blinking from your microcontroller, you need 5 leds blinking, all with different frequencies. Sounds like a stupid problem with no real use and any actual device would be more complicated than this but you still can't do it with sequential code you have been writing!. 

Practical problems generally require a larger number of tasks running in parallel. A couple of thread might only be functioning to allow for user interaction while the device's core activity keeps active in the background. 

A RTOS(Real Time Operating System) allows you to run a number or tasks periodically or otherwise providing services such as scheduling to share processor's time amongst different tasks. All processors run only one line of code at a moment.

This tutorial will take you through developing a multi thread application on a PIC18 microcontroller. The compiler used will be MikroC Pro for PIC, just because I love it, no other reason. The operating system used is OSA. We will design an application that will provide you with a basic template to use OSA in pretty much most applications you would come across.

About OSA

I have worked with different RTOSes until now, and never really found a great ultra-low footprint and efficient RTOS to be ported onto devices like microcontrollers with extremely limited amount of memory. A PIC18f452 microcontroller would generally have about 1.5KB of RAM. You would never want to use most of its memory for the scheduler itself.

OSA is a non-preemptive cooperative RTOS for low memory devices and has been ported onto all popular microcontrollers and is generally supported by almost all famous compilers including MPLAB, CCS PICC, AVR studio and MikroC Pro. A very large library of functions are provided by the OS kernel. The program structure ends up being very similar to what you have been working with. While consuming only about 60 byte of memory in RAM, OSA is an ideal choice for microcontroller based applications. You will fell in its love, I suspect :) .

Some terms that I will be using around:
Context switching: Pausing the task where it is and attending to another task, the operating will come back to this task later.

Task: A piece of code that the operating system will execute. Task is a function that the OS will call repeatedly.

Thread: This could be used interchangeably with Task, but in this tutorial's context it is a function that never ends(has a while(1) loop inside) and runs in parallel with other threads.

Limitations:
Being non-preemptive is a disadvantage, it means if your program gets stuck in a long loop inside a low priority task, the scheduler would not automatically preempt or forcefully switch context. Switching of context is upto user in all cooperative RTOSes. This is however not a major setback and you can cope with these issues with some intelligent programming. This is what makes this microcontroller based RTOS different from complex operating systems like Windows, so its good enough with a tiny computer like PIC. 

Making your first multi-thread program

The Plan is to solve the same simple problem that I talked about, We will make a program that blinks 4 LEDs with very different frequencies, all simultaneously. We will launch 4 threads. You are used to having a main function in your microcontroller program where you would have an endless loop to keep the program going. We will write a code that will give you 4 such functions that 'apparently' operate in parallel and give you a feel as if you had 4 main functions in your program. When you are good with blinking 4 LEDs in parallel, you could just fill in the code and do pretty much whatever you like.

Downloads
To get going with writing our first application,  things that you need to download are:
It would be advisable to browse through the OSA website and study its documentation. It is very well written, something you won't expect for most open source softwares. I Loved it!.

Step one:

Create a new folder and extract the OSA zip file there.Launch MikroC pro and Create a project. Select the same folder where you extracted OSA. OSA works for most microcontrollers but I would prefer going with 18F452, it has a reasonable memory for our work. Use a 8 to 12Mhz crystal.



Why am I selecting such a low frequency crystal?, afterall I am not planning any mercy on this little creature. You are right, there is no mercy today, we will use a 12Mhz crystal and enable PLL to use our microcontroller at 48Mhz!!!, thats the other name of torture but It gives us extremely small context switching overhead and supreme performance.



Now we need to add the operating system's files to our project. Click Add and select osa.h and osa.c files


Click next and include all libraries, then select open configuration for project and select oscillator as HS oscillator with PLL enabled.



Now, we turn back to writing code. Save the project. Open the OSAcfg_Tool.exe file and select the following options. Set Tasks to 4 because we will be having 4 threads in our application, select settings as shownm when done click Save and save the configuration file in the folder with your current project.



The saved file OSAcfg.h requires adding into the project. In MikroC, with your project open, goto Project Menu and add OSAcfg.h file into the project (failure to do so will cause error).

Configuring the timer:

I will not go very technical and briefly discuss why an RTOS program requires configuring a timer. All RTOS need to keep a sense of physical time. So at every moment the Kernel would know what time it is and based on this information generate precise delays, and schedule function calls. In simple words, you can't follow your daily routine without having any sense of time. You know that there are timers inside PIC that allows you to count time. But our Operating system's clock runs much slower than the timer. So we would increment the Operating system's clock by one TICK every time the timer overflows.

 I am planning to have a tick coming every 1ms. This means something, my application will not be able to measure any time less than that 1ms, you won't be able to call delays smaller than 1ms, this is generally okay if none of the thread requires a time delay less than 1ms. Selecting a longer period for tick has a benefit that your controller will spend less time servicing the timer overflow interrupt service routine and more time doing what it is supposed to do.

Yeah, you forgot the prescalers, configuration registers and that stuff you studied in class, I hated them because there are programs made to remember those things. Launch the timer calculator. It knows all that you forgot :).


The timer utility generates precise settings for your timer. It generates an InitTimer0 function that you would call at the start of the program to configure the timer. Copy both the functions to your project.

In your main function, insert a call to InitTimer0. At the start of the project, inculde the OS headers by using the #include <osa.h>  directive. To increment clock every overflow of timer, insert a call to OS_Timer() in the interrupt service routine. So your code should look something like this by now:



Now we need to add the Threads and run scheduler to complete the program. To Define a task that would be called by the scheduler it is defined in the following way:



You should be able to understand the importance of OS_Yeild and OS_Delay() functions. In your RTOS program, OS_Delay is just a very efficient counterpart of Delay_ms you have been using before. Delay_ms is a very inefficient way to waste your processor's time by just tying it up in a useless loop. When you call OS_Delay, rather than wasting time on some dummy instructions, you program will attend to other threads. This should help you see the benefit of employing an RTOS in your application. But remember, an RTOS program thread that never Yields or calls delay will never allow other threads any time on the processor. So you must always call OS_Yeild often in your threads so justice is done to all threads. The scheduler will decide based on the priority of each thread, who gets what percentage of total time.

The RTOS developers generally encourage use of global variables. Particularly because during context switching, the local variables must be conserved in memory. so when all threads are running in parallel, all local variables exist in the memory at the same time. So having local variables hardly has a benefit. But just in case if it allows some memory optimization in your code, remember to use 'static' identifier with local variable declarations.

Just like one thread defined above, you can define any number of threads provided you have informed the OS about them while creating the configuration file.

Last step is create the threads and call the OS scheduler from the main function.


Thats it!!, just fill in the respective tasks with code that you like and you have OSA running with 4 parallel threads in your program. OSA is powerful and a well developed operating system, the routines that I have discussed are the crucial ones and would usually be enough for standard programs. However, you must explore all of its services in its documentation.

The entire example discussed above is available for download at the following link, including a proteus simulation of how it works on your microcontroller. The zipped file also contains the timer utility, OSA configuration file generation tool and OSA documentation.



Credits to Dr Jahangir Ikram and my fellow team members at LUMS for the priceless debates and discussions on technical issues.

30 comments:

  1. nice one! i'm going to try this out!. thanks for this information

    ReplyDelete
    Replies
    1. Sure, Do let me know how it goes with you.

      Delete
  2. It's really useful for MikroE's users.

    ReplyDelete
  3. Can I use it with MikroE Mini PIC32 ? is there any version for Mini M4 STM32 ?

    ReplyDelete
    Replies
    1. You can check out compatibility at this link:
      http://www.pic24.ru/doku.php/en/osa/ref/introduction/intro#compilers

      I think its not ported for STM32 yet.

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Yes, it should in principle, but its always worth experimenting.

      Delete
  5. Good write up was looking forward to giving it a go. But after downloading the files I found that the configuration program will not run under Windows 7 64bit. (even an manual install fails). Tried to send email to the OSA but the email listed is not valid.

    ReplyDelete
    Replies
    1. I think you wont need installation for that, you can download the complete package(at the end of blog) and run the configuration tool directly. it should work, if it causes problem, run it in compatibility

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. The OS kernel takes the execution out of the threads when you call OS_Yeild or you call OS_Delay, It automatically goes to other tasks to service them

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. That is not ported onto PIC18, PIC16 and atmega8 which are majority of low end microcontrollers

      Delete
  8. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. I will check that our as well. But to me, the ability to yeild at times when I want gives more control in smaller applications. Besides that, I will that that out,

      Delete
  9. can you explain how it work OS_Task_Continue() and OS_Task_Pause() and how to get V
    OST_TASK_POINTER.

    thank you

    ReplyDelete
    Replies
    1. The documentation has instructions explaining to get the task's handle. In general you can pause or continue a thread with a semaphore or with a simple logic check after which your task yeilds.

      Delete
  10. Not one word to say thank you and to credit the Russian author of the freeware OSA RTOS. I have used it with MikroC on a large project before, and I gave credit, so why didn't you?

    ReplyDelete
    Replies
    1. I completely appreciate their effort. I have personally offered them due credits and that does voice through this blog when I appreciate OSA. Certainly a great work.

      Delete
  11. Hi,

    I really like your article about RTOS. I tried it in the past, but put it away because I couldn't get it to work. But with this nice tutorial I'm capable of writing a very nice program: five different tasks (Network, HW Serial, SW Serial, LED indicators and LCD). I'm able to initiate and start all tasks running nicely, but it would be nice if I could use messaging in the OS to send text from different tasks to the task handling the LCD. Do you have experience (or a tutorial) on using Queue's? I cannot get it to work. I tried to debug it with the ICD (MikroC and mikroProg), but the send message is not added to the queue. I'm using a 18F4685 PIC controller on the easyPIC6 developerboard.
    Thanks in advance.

    Greetings,

    Erik

    ReplyDelete
    Replies
    1. I haven't tested queue's with OSA, but in your case what I would generally do is to declare a global char[] for LCD text, which any thread could write, and you could use a flag to see if new data has been written to the char[] reserved for LCD.
      So if Thread1 needs to write something on the LCD, it clears its existing content(simply set first element to null). Write the message into the array and set a global boolean variable(a flag) . The LCD thread would launch and see if new data is available to write by checking the flag and if not, simply Yeild. If data is available, I should display on the LCD and clear the flag.
      I think this would be a better implementation in the case of an RTOS in a microcontroller because message passing would generally consume a similar amount of memory as a global array. I hope this helps

      Delete
    2. That is true, writing in reserved variable of char[16 or 20] will work. But I'm really curious in the messaging from the RTOS itself. The only thing I could find is that the queue should be stored in another memory bank. But I'm not able to get it to work....

      Delete
  12. hii i wanna build rtos on 33FJ128MC802 by using ICD3 and MPLAB. so can i use same data ?
    And can you share any programming tips i can use?
    Thanks

    ReplyDelete
  13. Hi

    I am using Mplab X ide V2.20 and hitech c V 9.80 in standard mode,

    i had included osa.h and osa.c file and Os init function called in main function, after compilation it gives warning and errors below

    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:453: error: syntax error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:473: error: syntax error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:493: error: syntax error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:450: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:451: error: phase error
    make[2]: *** [dist/default/production/simpleTest.X.production.hex] Error 1
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:465: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:466: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:467: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:468: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:469: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:485: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:486: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:487: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:488: error: phase error
    D:/OAS/osa\port\pic16\osa_pic16_htpicc.h:489: error: phase error
    make[1]: *** [.build-conf] Error 2
    make: *** [.build-impl] Error 2


    can u able to give which compiler version you have used to build.



    thanks and regards
    Rajesh

    ReplyDelete
  14. Hi! sir, can please send me the details for LCD data rotating on 16*2 LCD display?

    ReplyDelete
  15. Hats Off to this post. Could you please design a simple OS. Let me clarify, all the device drivers like LCD has to be defined in the ROM area. Simply the user has to call the functions in the compiler from the ROM area. A simple LED example.

    ReplyDelete
  16. Hello Sir,
    I wonder if the mc9s12p12 freescale microcontroller can support a real-time operating system, such as OSA RTOS.
    If this is not the case with which I can replace this RTOS.
    Thanks a lot for your interesting article Sir.

    Regards.

    ReplyDelete
  17. Getting following error when downloaded and extracted entire package
    126 395 Invalid declarator expected'(' or identifier __lib_mmcfat16.h
    22 373 Array dimension must be greater than 0 __Lib_MmcFat16Constants.c
    0 102 Finished (with errors): 13 Jun 2016, 16:02:01 MyProject.mcppi

    ReplyDelete
  18. Hello!! this RTOS is amazing, thanks for the example. But i have a problem, I have a project that works great with 4 tasks, but when I add more tasks, it does nothing, I mean, the first 4 tasks works and the following are like don´t exist. I have modified the file OSAcfg.h, in the line #define OS_TASKS 7, but doesn't works, and of course the file is attached to the project like your example says, so I don´t know what is the issue, please help!!

    ReplyDelete
    Replies
    1. I found the problem. I made I copy of your project in a new folder and when I add more tasks in the project and modify correctly the file OSAcfg.h for 7 tasks, mikroc used the file OSAcfg.h of original folder (which had four tasks). So I created a new project and add the files osa.c, osa.h and OSAcfg.h manually and after compile it works with 7 tasks, what is I wanted

      Delete