It has been a while since I took "Embedded Systems" course where we studied "Super-Loops", "RTOS", Semaphores, Multitasking (cooperative vs premptive) so sorry for this question.

We have an an application where we have an ADCinterface to a number of signals and where we are sending SMS at the same time. We are using super-loop. The problem is:

When we are sending SMS's, the rest of application has to wait on it.

My question is: do microcontrollers (32 bit PIC, ARM or 8051 based etc) support threading so that one thread can take care of SMS while we read ADCs in a separate thread, or it is just a matter of using RTOS which supports multi threading vs super-loop which does not?

• Threading is a software feature, not HW. Dec 20 '17 at 21:31
• "super loops" handle this fine too, it just means you aren't programming the individual finite state machines properly Dec 20 '17 at 21:35
• A question I'd have is whether or not your SMS process can tolerate randomly inserted (relative to its internal actions or steps) interrupts of arbitrary duration. If it can tolerate such interrupts, then a pre-emptive threaded system or a timer interrupt used to maintain pace with the ADC requirements can work fine. If the SMS messaging process cannot readily tolerate such interruptions, then you have a very different problem and will need to address the SMS aspect using co-operative coroutines (threading, but without pre-emption permitted) where the SMS code selects when to allow interrupts.
– jonk
Dec 20 '17 at 21:48
• Comments are not for extended discussion; this conversation has been moved to chat. Dec 21 '17 at 2:53
• @jus: A state machine called from blooper-loops is klunky. Use a state machine, but let the PC be the state variable. That makes it much easier to write and much easier to maintain. Dec 22 '17 at 13:23

As long as the microcontroller supports interrupts, has enough memory and gives you full control over the state which the CPU assumes (program counter, registers, stack) when you return from an interrupt, full fledged preemptive multitasking is possible. This is generally accomplished by setting up a hardware timer to generate an interrupt at regular intervals, and context switching (AKA changing the currently executing task) in the interrupt service routine (by backing up the registers and stack of the currently running task to RAM, restoring the previously backed up state of another task from RAM and returning from the interrupt). Most microcontrollers can do this, albeit some 8-bit microcontrollers have insufficient RAM for saving the state of multiple pending tasks (like some ATtiny and PIC parts).

Whether or not this is practical is a completely different story. It introduces a lot of extra complexity, some overhead but perhaps most importantly unpredictable timing to the firmware.

I think your problem is better solved with cooperative multitasking (super loop). You just need to refactor the SMS code into a non-blocking form (think state variable + switch statement), so that transmitting a SMS doesn't block the CPU from running your ADC code. Essentially you must remove all blocking code (delays, polling loops) from both your ADC code and your SMS code.

Even simpler would be to just service the ADC in a conversion complete interrupt. This way you can send the SMS with a simpler, blocking driver implementation (programmed as a sequence of steps, branches and delays instead of an explicit state machine), while the ADC is still handled transparently in the "foreground" by your interrupt handler.

Yes, most microcontrollers have enough hardware support so that threading is possible. If the hardware call/return mechanism is stack based, then all you really need is arbitrary access to the stack.

I've done multi-tasking many times on various PICs and dsPICs. Everything from the PIC 18 on up support true threading. The 14 and 12 bit core parts don't give you full access to the call stack, so you can't do real threading. I've done what I call pseudo-threading on those a few times. You can still have something that mostly looks like a independent thread to the software, although there are restrictions.

In general, it's a useful software abstraction to handle any stateful asynchronous input with a separate task. I do this routinely, for example, for processing the UART command stream from the host.

Don't fall for the "super-loop" architecture, except for handling a number of individual events. It then really becomes a event loop, so it's better to call it that. I usually have the original task run the event loop, after initializing the system and starting whatever other tasks that are needed.

Also don't make the mistake of assuming that multi-tasking means preemption, or that it requires a RTOS. I've done well over 100 microcontroller projects, and have used multi-tasking many times. I have so far not found a case where preemptive multi-tasking or a RTOS would have made sense. If you find yourself wanting such software structure, then you're probably not really doing micro-controlling and are closer to general computing, even if it is embedded.

Basically, use a separate task whenever you would otherwise want to use a state machine called periodically from a blooper-loop. The code is much cleaner, easier to write, and easier to maintain when the PC is the state variable. Just make sure to call TASK_YIELD in any loop where you wait for a event.

Since the main event loop is its own separate task, it also calls TASK_YIELD periodically. I usually put the call to TASK_YIELD at the start of the main event loop. It then checks each event in sequence. When it finds a triggered event, it handles it, then jumps back to the start of the loop.

My multi-tasking code for PICs is available for free in the PIC Development Tools release at http://www.embedinc.com/pic/dload.htm. Look at files with "task" in their name in the SOURCE > PIC directory for the 8 bit parts, and SOURCE > DSPIC for the 16 bit parts. I have used these facilities extensively, so I think they are quite solid.

On a PIC 18, the call stack is in dedicated memory. TASK_YIELD therefore swaps out the used portion of the call stack to/from a save area for each task. On the 16 bit PICs, the stack is just W15 pointing to somewhere in main memory. That makes swapping tasks easier. Each task gets a stack area up front. Swapping tasks is pushing the registers on the stack, pointing W15 to the stack for the new task, and popping the registers off the stack.

I recently measured the task loop time on a current dsPIC33EP project. The processor is running at its maximum speed of 70 MHz instruction clock. In this case, there were 4 tasks. These were to process the UART stream from another micro, low level CAN frame receiving, higher level CAN frame processing, and the main event loop. When nothing was going on, the time for one full cycle thru all tasks was 7 µs.

• Thank you sir @olinLathrop for your valuable input. If you can elaborate please "The 14 and 12 bit core parts don't give you full access to the call stack, so you can't do real threading"would be useful for me and others. I might well ask you a couple of questions in chat. Thanks! Dec 23 '17 at 8:23