Electronic – Multitasking on PIC microcontrollers


Multitasking is important these days. I wonder how we can achieve it in microcontrollers and embedded programming. I am designing a system which is based on a PIC microcontroller. I have designed its firmware in MplabX IDE using C and then designed an application for it in Visual Studio using C# .

Since I've gotten used to using threads in C# programming on the desktop to implement parallel tasks, is there a way to do the same in my microcontroller code? MplabX IDE provides pthreads.h but it is just a stub with no implementation. I know there is FreeRTOS support but using that makes your code more complex. Some forum says that interrupts can also be used as multi tasking but I don't think interrupts are equivalent to threads.

I am designing a system which sends some data to a UART and at the same time it need to send data to a website via (wired) ethernet. A user can control the output through the website but the output turns ON/OFF with a delay of 2-3 sec. So that is the problem I am facing. Is there any solution for multi tasking in microcontrollers?

Best Answer

There are two main types of multitasking operating systems, preemptive and cooperative. Both allow multiple tasks to be defined in the system, the difference is how the task switching works. Of course with a single core-processor only one task is actually running at a time.

Both types of multitasking OS's require a separate stack for each task. So this implies two things: first, that the processor allows stacks to be placed anywhere in RAM and therefore has instructions to move the stack pointer (SP) around -- i.e. there is no special purpose hardware stack like there is on the low-end PIC's. This leaves out the PIC10, 12 and 16 series.

You can write an OS almost entirely in C, but the task switcher, where the SP gets move around has to be in assembly. At various times I've written task switchers for the PIC24, PIC32, 8051, and 80x86. The guts are all quite different depending on the architecture of the processor.

The second requirement is that there is enough RAM to provide for multiple stacks. Usually one would like at least a couple hundred bytes for a stack; but even at just 128 bytes per task, eight stacks is going to require 1K bytes of RAM -- you don't have to allocate the same size stack for each task though. Remember you need enough stack to handle the current task, and any calls to its nested subroutines, but also stack space for an interrupt call since you never know when one is going to occur.

There are fairly simple methods to determine how much stack you are using for each task; for example you can initialize all of the stacks to a particular value, say 0x55, and run the system for a while and then stop and examine memory.

You don't say what kind of PIC's you want to use. Most PIC24's and PIC32's will have plenty of room to run a multitasking OS; the PIC18 (the only 8-bit PIC to have stacks in RAM) has a maximum RAM size of 4K. So that's pretty iffy.

With cooperative multitasking (the simpler of the two), task switching is only done when the task "gives up" its control back to the OS. This happens whenever the task needs to call an OS routine to perform some function which it will wait for, such as an I/O request or timer call. This makes it easier for the OS to switch stacks, since it is not necessary to save all of the registers and state information, the SP can just be switched to another task (if there are no other tasks ready to run, an idle stack is given control). If the current task doesn't need to make an OS call but has been running for a while, it needs to give up control voluntarily to keep the system responsive.

The problem with cooperative multitasking is if the task never gives up control, it can hog the system. Only it and any interrupt routines that happen to be given control can run, so the OS will seem to lock up. This is the "cooperative" aspect of these systems. If a watchdog timer is implemented that is only reset when a task switch is performed, then it is possible to catch these errant tasks.

Windows 3.1 and earlier were cooperative operative systems, which is partly why their performance wasn't so great.

Preemptive multitasking is more difficult to implement. Here, tasks are not required to give up control manually, but instead each task can be given a maximum amount of time to run (say 10 ms), and then a task switch is performed to the next runable task if there is one. This requires arbitrarily stopping a task, saving all of the state information, and then switching the SP to another task and starting it. This makes the task switcher more complicated, requires more stack, and slows the system down a little bit.

For both cooperative and preemptive multitasking, interrupts can occur at any time which will temporarily preempt the running task.

As supercat points out in a comment, one advantage cooperative multitasking has is it is easier to share resources (e.g. hardware like a multi-channel ADC or software like modifying a linked list). Sometimes two tasks want access to the same resource at the same time. With preemptive scheduling, it would be possible for the OS to switch tasks in the middle of one task using a resource. So locks are necessary to prevent another task from coming in and accessing the same resource. With cooperative multitasking, this not necessary because the task controls when it will release it self back to the OS.