Here are some PIC multi-tasking systems

Status
Not open for further replies.

Mr RB

Well-Known Member
Hi guys, I've been playing with some PIC multi-thread concepts.

It is still in early stages but I have been trying to create some multi-thread systems in PIC firmware (in C language).

The goal of these systems is to act as a simple framework to create PIC projects using a multi-thread type methodology where the code tasks are defined as separate "threads" and as a whole the code will allow a level of multi-task "parallel" type performance;

# Will run many threads, each is independent
# Processor time can be divided between threads
# Threads are fast sequenced, and for many purposes "run at the same time"
# Threads can be easily added without affecting application performance
# Threads can be placed in any order
# Each thread can be executed (or not) with no effect on other threads
# RAM variables and special timers can be shared or dedicated to one thread
# Threads can be prioritised
# It may be a good general structure for many microcontroller projects

This work is still in a very early stage but I can see some possibilities. It seems to be a good framework for creating new projects from scratch that will need to have features added or changed later.

The web page is mainly descriptions of possible multi-thread systems and lots of source code examples, and there are 4 actual projects using the PIC-thread system including an LCD clock demo and two nice performance RS232 baudrate converters;

**broken link removed**

The PIC-thread web page can be seen here;

PIC-thread, simple PIC multi-thread systems
 
I've often thought of writing a multi-tasking kernel for the 16 bit pics, so it's interesting to see what you've been doing here. Alas, there are so many projects I want to do and this isn't very high priority atm.

Have you though of writing an actual kernel for true pre-emptive multi-tasking? I doubt it would be worth it on the 8-bits, but the 16-bit pics I think have enough power and memory to handle a simple one.
 
I just went and Wiki'd preemptive multi-tasking so I would know what you were talking about.

Actually I did use preemptive multitasking in the second version of that baudrate converter, the one that displays the buffer on a 62 bar bargraph shown on a graphics LCD. The "preemptive" process of interrupting the task was done manually by that task itself which just draws one of the 62 bars every time that task is executed in the task sequence.

I hadn't heard of the term preemptive multitasking (or cooperative multitasking) but the simple systems I've suggested can do what looks to be a blend of both, it's quite preemptive because there can be an interrupt (separate to the task) or sequencing engine separate to the task that will cause that task to end and also cooperative because the task has to choose to end.

Given the limitations of the PIC hardware I doub't it would be worth performing TRUE preemptive multitasking. You could interrupt the task directly, but then would need to save the entire stack specific to that task including the program counter. The overhead of the interrupt and saving and restoring the entire stack for every task just doesn't seem worth it especially with all the extra code and chance of trashing a stack being a catastrophic failure.

Which is the reason for exploring faster simpler more reliable ways of getting basically the same end result.
 
Hi Roman,

Forgive me but I don't see the advantage of the "thread" architecture in your examples. Take your "multi-thread3.c" program for example. I don't see why you would dedicate 1-msec of processing time to "thread 0" and 1-msec of processing time to "thread 1" when the task in "thread 0" is keyed to an interrupt flag that occurs once every second and the tasks in "thread 1" are keyed to the once-per-second update of the "secs" variable in "thread 0".

Code:
  while(1)
  {
    // THREAD 0 ===========================================
    while(thread == 0)
    {
      // this thread looks for a new second and updates the seconds
      if(newsecond)
      {
        newsecond = 0;
        secs++;
        refresh_display = 1;  // tell other thread to redraw LCD
      }
    }
    // THREAD 1 ===========================================
    while(thread == 1)
    {
      // this thread updates the minutes and hours
      if(secs >= 60)
      {
        secs = 0;
        mins++;
      }
      if(mins >= 60)
      {
        mins = 0;
        hours++;
      }
      if(hours > 12) hours = 1;
    }
It seems you're adding a level of complexity to the program that just isn't necessary but perhaps I'm missing something more subtle? Is there an advantage to your "threaded" program over a simpler program running at any loop speed that I'm missing?
Code:
  while(1)
  {
    if(newsecond)           // if "newsecond" isr flag
    { newsecond = 0;        // reset "newsecond" isr flag
      secs++;               // and bump "secs"
      if(secs >= 60)        // if "secs" overflow
      { secs = 0;           // reset "secs" var'
        mins++;             // and bump "mins"
      }
      if(mins >= 60)        // if "mins" overflow
      { mins = 0;           // reset "mins" var'
        hours++;            // and bump "hours"
      }
      if(hours > 12)        // if "hours" overflow
      { hours = 1;          // reset "hours" var'
      }
      ByteToStr(secs,txt);  // format and display secs
      txt[0] = ':';
      if(txt[1] == ' ') txt[1] = '0';
      RomanLCD_Out(0,6,txt);

      ByteToStr(mins,txt);  // format and display mins
      txt[0] = ':';
      if(txt[1] == ' ') txt[1] = '0';
      RomanLCD_Out(0,3,txt);

      ByteToStr(hours,txt); // format and display hours
      RomanLCD_Out(0,0,txt);
    }
    if(thread2timer > 250)  // if 250-msec interval
    { thread2timer = 0;     // reset 250-msec timer
      if(PORTC.F0)          // if set mins button
      { mins++;             //
        mscount = 0;        //
        secs = 0;           //
        newsecond = 1;      //
      }
      if(PORTC.F1)          // if set hours button
      { hours++;            //
        mscount = 0;        //
        secs = 0;           //
        newsecond = 1;      //
      }
    }
  }
 
I find it hard to believe that there are not a million RTOS's out there for the PIC already. FreeRTOS says it has ports for the PIC.


Actually, doing a google search comes up with a bunch, but I'm too lazy to investigate any more.
 
Last edited:

Hi Mike, the first code examples on the page are multi-threading by dividing the processor time between tasks. So you can allocate 30% of time to task1, and 10% of the time to task2 etc.

That first clock project I did is just a poor example because it does not really take advantage of the system ability to divide processor time between tasks. Like I said these are experimental systems in the early stages so even though that one project itself doesn't really gain much from being coded as multi-thread doesn't meant that other projects won't benefit.

Ill summarise because that page is a bit long;

Top section of the page;
Multi-thread examples that divide the processor TIME between threads.
So tasks get a percentage of processor time each. I haven't found a good project to demo it yet.

Bottom section of the page;
Multi-thread examples that divide processor between tasks based on FREQUENCY.
So some tasks are performed every loop, some every X loops, and some can be every X milliseconds.
The 2 baudrate converter projects are pretty good demo examples.

Advantages of all of the systems;
All the systems have benefits because tasks can be added without affecting other tasks, tasks can have totally independent timers and execute every X seconds without affecting other tasks, tasks can be structured to operate without affecting other tasks and without being affected by other tasks.

Tasks can be independently prioritised so important tasks run often or get lots of processor time, while less important tasks run infrequently or get less processor time, that can all be adjusted without tasks affecting each other.

I also think there are actual coding benefits from using these structures as a starting point for projects. Like a beginner can add a "read the buttons" thread with its own timer that reads the buttons every 300mS, and that thread slots straight in with absolutely no affect on the other threads that flash the LEDs every 1000mS etc.


DirtyLude; said:
...
I find it hard to believe that there are not a million RTOS's out there for the PIC already. FreeRTOS says it has ports for the PIC.
...

I think this is really a level lower than a RTOS and a heap simpler. It is more a "template", a simple system to structure a program so that tasks can easily be added or removed from the program without interfering with each other.
 
Hi Mike, the first code examples on the page are multi-threading by dividing the processor time between tasks. So you can allocate 30% of time to task1, and 10% of the time to task2 etc.
Why would you want to dedicate 30% or 20% or 10% of processing time to a task that doesn't require that amount of time? Why not just run it once when needed? Time-Slice "threading" is probably not the best way to use the limited resources on a small PIC mcu.

All of the advantages you listed can be achieved without using time-slice threads. The Arduino guys were pretty clever in this regard. They have a 1-msec system timer that you can use to schedule tasks that run asynchronously (or synchronously) at any interval -- you can have one task that runs every 250-msecs, one that runs every 1250-msecs, and another one that runs every 60000-msecs (1 second), etc. And you still have event driven tasks running at the main loop speed and can use almost any method you like for linking dependant and/or synchronous tasks.

Regards...
 
Last edited:
Why would you want to dedicate 30% or 20% or 10% of processing time to a task that doesn't require that amount of time? ...

Because as a system it MIGHT be useful. Imagine if you need to do some massive calculations that will take a lot of time. You could allocate X% of the time to doing that task while other more urgent tasks get more of the timeslice.


Yep. That is just ONE of the features I already had implemented in the PIC-thread systems.
 
Because as a system it MIGHT be useful. Imagine if you need to do some massive calculations that will take a lot of time. You could allocate X% of the time to doing that task while other more urgent tasks get more of the timeslice.
Unfortunately that's just too rigid and too inflexible a system to be useful for more than a few simple applications. If you have a single task that requires a large time slice you wouldn't be able to execute other tasks that require servicing at intervals that are shorter than your large time slice task.
 
Have to agree with Mike. Multi tasking on a pic is not usually required.

Mike.
 
Last edited:
It might be useful is not a reason to write code! You write code because you need it, or someone else needs it. If you don't have a specific application your code can fill then you're just writing code for the sake of writing code. There are many multitasking and RTOS systems out there for PIC as far as I know. Unless you're designing something for a specific need I'd question the usefulness of the project as there are no applications which require it. We're not talking about some ground breaking new method of multitasking here, just something that for possibly not obvious reasons has never been required on a PIC, just because it doesn't exist isn't a reason to write it.
 
I was writing multi tasking programs for the PIC many years ago.

Its basically about time division multiplexing the threads.

One way is to just give so much time to each thread.

Another way is to have each thread do so much then pass on control to the next thread.

The two are subtlely different.
 
Did everyone swallow a handful of grumpy pills over Christmas? Hmmmm.. maybe it's "Back to work Monday"...
 
Did everyone swallow a handful of grumpy pills over Christmas? Hmmmm.. maybe it's "Back to work Monday"...

Hmmm... Maybe you went into this one without doing the research.

I wrote a tutorial that includes my version of cooperative multitasking. The goal was to create a easily understood system to teach the concepts. Some people who have used the tutorial are now using the system in their production code.

Each task is broken down into a state machine. After each task state finishes it returns to the kernel which selects the next task to run. It features task blocking based on time (non blocking delays) and IO.

You can tune task priority by modifying the task seletion code/criteria. For example if a task must be run at least once every X units of time lower priority tasks can be skipped by moving the next task pointer to the top of the priority ordered task list. In short anything is possible here because you write the code.

For a lightweight system it provides a lot of power and is easily adapted to any processor with a timer interrupt.

See the last entry "Cooperative Multitasking" in the table. The tutorial is in pdf format and the source code is a sip.

http://www.rocklore.com/3v0/Programming.htm
 
Last edited:

You are starting to sound like a broken record (can we still say that). It seem of late you want everyone to use what exists.

There is a lot to be learned by writing a multitasking kernel. That is justification enough for doing so.

Multitasking is one more wrench in the programmers toolbox. It is a tool you can do without in most cases. Most uC programmers never use it. But like the ratchet or the breaker bar in you socket set it can come in handy. When a system increases in complexity past a given point multitasking makes sense. Prior to that point we can use a single main with a few ISRs.

Programing methodology goes much further then what most uC programmers use. That does not make the methodology wrong. With 8 bit processors a poor fit due to limited resources perhaps. But when we can find a way to use them it is as I said earlier, one more wrench in the toolbox.
 
3v0, the first question anyone should ask when embarking on a project of any kind is "Has someone else done this before?" research should be done to find that one thing out. If the answer is yes then you need to compare others results to your intentions and then you have a volume of work to start from! This is 1000 times more valuable then trying to do something from scratch on your own with no other assistance, which is a waste of effort if you could have learned better what to do or what not to do before you'd started. This is common sense.

Have you never heard the term don't try to re-invent the wheel?

There is absolutely nothing whatsoever wrong with novel research or someone trying to do something for education. But not putting effort into determining what else was done before or even what possible use what they were doing would fill is not a methodology anyone should endorse.

Because as a system it MIGHT be useful. Imagine if you need to do some massive calculations that will take a lot of time. You could allocate X% of the time to doing that task while other more urgent tasks get more of the timeslice.

There's nothing to imagine there, universities and governments buy up PS3's by the score because of their parallel processing abilities, it's cheaper than buying over the counter super computing clusters =\ Such a system is not practical for micro controllers however unless an application could be imagined where it would be useful. The core multitasking idea is nothing without a supporting network of software that could intelligent transfer applications and data transparently. So it's a good idea, but needs a little more follow through. The processing power of even a network of a thousand micro controllers wouldn't even come close to a single more advanced processor. And distributed computing doesn't make sense on that scale.

Don't for one second think that means I think your project is trivial or pointless Mr RB. My opinions are just that, my own, and are not meant in spite or menace of any kind. It's a bad habbit of mine to play the devils advocate, but an important one. The points I bring up are however valid.
 

I guess you didn't read, or didn't understand the first post in this thread, because that is not at all what this is about.

Mr RB is talking about multi tasking on ONE micro, and while there are a plethora of solutions extant, there is always room for more. Sometimes, when you have time critical code which must run, a super loop just doesn't cut it!
 
3v0- I read your cooperative multitasking tutorial, it's very well written and your systems have an elegant simplicity. I noticed we both approached some of the solutions in very similar ways. Your systems definitely seem more "cooperative" with the tasks having a great control over the task pointers etc where I went more for a preemptive flavour with tasks more controlled by external factors like the loop/int sequencing and the real world timers. I do like my timer interrupt better with the individual thread timers all running in exact mS and my loop synch system that allows lots of overtime situations and then re-syncs the entire loop beck to perfect timed sync again afterward.

Scaedwian- I personally LOVE to re-invent the wheel and plan to keep doing it a lot. It was one of those silly people wasting time "re-inventing the wheel" that came up with the pneumatic tyre.

I work on the theory that there's already 99 guys who sat in a classroom and were told the "right" way to do something, but it's the other 1 guy who is unhindered by that knowledge (assuming he is intelligent and innovative) who has a good chance of coming up with something new that the 99 others would never have tried. Anytime I want to know the "proper" way to do something i'll just google it.
 
Roman,

It can see that you want to learn about multitasking. Let me make some comments in that spirit.

Your system is not preemptive. To do that you have to stop the task without it cooperation, and save the program counter and registers. Your tasks are cooperative which is OK.

The task examples you show are rather on the simple side. Some tasks are but many are not. For the longer tasks we need to use state machines. Exectute a bit (state) of each task then yeild.
 
Even though most people don't write a full threading system in their code, EVERYONE does write a multi-tasking system into their code; most probably don't even realize it. Learning how to separate it into it's own subsystem is not only a great learning experience, but becomes almost necessary to learn when you get to larger and larger processors. Sure, he's doing it on a PIC now, but that experience can easily be used elsewhere. You do realize too, there are more versions of PICs than just the 8-bit series. I think some people forget that around here.
 
Last edited:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…