and Hi-Tech C Archive
Q. How do I handle many things at once on a PIC?
A. Go to www.pumpkininc.com
Look at their 'Salvo' real time operating system. The quote below says it all.
" ... I use this system for near on 2 years and I can tell only the best things for it (even I can say that my life like PIC programmer has 3 periods - assembler, HiTech C and RTOS SALVO - the difference between assembler and C is the same like between no RTOS and RTOS)." - Luben Christov
Neither the author of this web page nor Luben Christov have any affiliation with PumpkinInc - but we both share a liking for it!
See also http://www.bknd.com/cc5x/multitasking.shtml for a discussion on multitasking and state machines.
I wrote the rest of the document below before I came across Salvo. The technique works nicely enough, but Salvo is has far superior power.
A. Alternatively, use a State Machine or Time sliced multi-tasking system
A state machine is recommended for most solutions where there is non time-critical tasks to perform. However, the time sliced multi-tasking is useful when:
Using a State
Complete Example Project
//(c)Shane Tolmie, http://www.microchipc.com/,
distribute freely for non commercial use on the condition that you include this
web link somewhere in your document.
when a task is going to happen again
variable referenced in both interrupt and main() must be declared volatile.
You have been warned!
/*We want to wait 2000 clock cycles, or 500us @ 16MHz (instructions are 1/4 speed of clock). Timer 1 interrupts when it gets to 0xFFFF or 65535. Therefore, we set timer 1 to 65535 minus 2000 = 63535, then wait 2000 ticks until rollover at 65535. To test, use simulator to find that its exactly correct*/
/*this task takes a long time, 100ms for example, lots of maths. Is extremely low priority, but has to be done at regular intervals, so all this does is trigger it. In main(), it will, at leisure, poll task2_go and then execute it in the background.*/
On Wed, Jun 10, 2009 at 12:56 AM, Mauricio Gendelman wrote:
You are welcome.
> The whole paragraph cause me problems. It is more or less evident for me
Yes, this is correct.
There are two types of tasks: short tasks, and long tasks.
A "short task" is so fast, that it can always be executed entirely within the ISR, i.e. it takes less than 500us to execute. An example is "increment a timer, and set a flag if the timer goes over a certain value".
A "long task" is so slow that it cannot be executed entirely within the ISR, e.g. it takes 50ms to execute. An example of a long task would be "calculated the cosine of an an angle, send it out the serial port".
Here is the key: put the short tasks inside the ISR, and move the long tasks into main() so they will run in the background.
The short tasks get executed immediately, entirely within the ISR.
The long tasks are executed in main(), in the background. The long tasks can be triggered to run by the short tasks in the ISR.
> Q1. Are you referring (with this paragraph) to the case in which one single
> Q2. Why you mention one task per tick? (when there is no guarrantee about a
The "time slot" is the amount of time allocated to the ISR.
For example, if you set up the ISR to execute 10 times per second, then there would be 100ms time between each interrupt. If you set up the ISR to execute 100 times per second, then there would be 10ms between each interrupt. The time between each interrupt can be lengthened or shortened, depending on the time that it takes everything within the ISR to execute.
Here is a worked example to illustrate this:
Imagine we set up a timer to trigger an interrupt every 10ms.
At time=10ms, the timer trips, and the program flow jumps into the ISR.
Under normal circumstances, the ISR would take 2ms to execute, and exit at time=12ms.
However, if the ISR took 25ms to execute, then nothing would ever work. The reason is that the interrupt happens every 10ms, and if the ISR takes 25ms, then the program would never get to execute anything running in the background, in main().
How about this situation: 99% of interrupts take 2ms, but the odd interrupt takes 25ms. How would we detect this? Well, at the end of the interrupt, just before we exit and program control returns to main(), we check the flag TMR1IF. If this is set, then its clear that the interrupt must have taken more than 10ms, because the timer tripped the interrupt flag before the interrupt code could exit. In this situation, we would set a variable to let us know. Then, we could react by increasing the amount of time for each interupt to say 50ms.
The TMR1IF check is a piece of code to detect if we need to allocate more time to handle each interrupt. See the previous answer.
Here is an example.
1. Imagine that we set up timer 2 to trigger every 10ms, and set the interrupt flag.
However, if we actually measured the time between interrupts, we would find that they are occuring about once every 11ms. Where did this extra 1ms come from? Well, it took 1ms to actually get into the interrupt, to the point where we could set the timer to tick in another 10ms. Another way of saying this is that it took 1ms betweensteps 2 and 3, which would increase the total amount of time to 11ms between interrupts.
So, all we do is set timer 2 to trigger in another 9ms (see step 3). This adjusts the error so that the ISR will trip precisely every 10ms.
I mean that in the event that every single line of code in the interrupt is executed, there should still be enough time to finish the interrupt before the next one happens. Another way of saying this is that the longest code path should still be quicker than the time allocated to the interrupt.
As an example, imagine that you set up the interrupts to happen every 10ms. This means that program flow would jump into the interrupt routine every 10ms.
Imagine that normally, the interrupt took 2ms. Now imagine that we added an "if" statement in this interrupt which only triggered occasionally - but when it triggered, it added an additional 15ms of processing time to the interrupt. This wouldn't work, as the entire interrupt would now take 17ms, which is 7ms more than the maximum available time of 10ms. To fix this, we would have to move this code within the "if" statement into main(), and trigger this code running in main() with a flag which is set in the interrupt.
Many thanks for giving us the MicrochipC web site - it is a truly invaluable resource.
I have a question for your regarding the time-sliced multitasking system for Hi-Tech C which you discuss, at http://www.microchipc.com/Hi-Tech_C_multitask.htm. I understand how the time slicing works, along with the variable execution rates (determined by the TASKX_COUNTER_MAX value for each task).
I see that task0 (or whatever
the first 'task' in the ISR is called) gets executed at precisely the time
expected. However, what happens if task0 has some conditional code which causes
the execution path through the task0 code to vary from one interrupt to the
next? Then, the next task in the ISR call, e.g. task1, would get executed
at varying times after the point when the ISR was triggered. The same goes
for any task due to run in the current interrupt - the timeliness of each
task is dependent upon all previous
Have I got the wrong end of the stick or is my theory correct? How do you cope with this when doing serial comms? Presumably the variable latency limits the speed at which serial comms can be performed?
Many thanks again for the great web site. I look forward to hearing your opinion on my query.
Yes, you're right. This is known as interrupt jitter. However, with serial, there is a 2 byte internal buffer, so this can cope with a lot of variance in the exact time that the time-sliced interrupts happen.
The trick is to do all the processing in the background, triggered from the interrupts, and have minimal processing done in the actual interrupt. Work it out so that even if all the interrupts happen at once, there's still enough time for everything.
We welcome any suggesions or comments! Send them to Shane Tolmie on firstname.lastname@example.org. This site is a completely separate site to www.microchip.com, and is maintained independently of Microchip Ltd., manufacturers of the PIC micro. All code on this site is free for non-commercial use, unless stated otherwise. Commercial use normally free, however, it is prohibited without contacting email@example.com for permission. All content on this site created by Shane Tolmie is copyrighted by Shane Tolmie 1999-2009. Click to advertise on this website - $29.90 for a banner ad which will reach 55,000 user sessions per month. One months free trial!