How does the simple "round-robin" main loop (a super loop) schedule tasks, and where does it break down?
It initialises the system, then calls each task function one after the other in an endless while (1) loop. It is simple, but every task delays all the others, so response time grows as code is added.
int main(void) {
System_Init();
while (1) { /* round-robin over the tasks */
Task1(); /* a task is just a function call */
Task2();
}
return 0; /* never reached */
}
This is the most basic way to run several jobs on a microcontroller without an OS, often called a super loop or cyclic executive. It needs no kernel and no stacks per task, and it is easy to reason about while it stays small.
The problem is that the loop's timing is the sum of every task's execution time. If Task1() takes 2 ms and Task2() takes 30 ms to redraw a display, a button handled in Task1() is only checked every 32 ms. Every feature added to the main loop makes every other task less responsive, and one task that waits (for example, polling a slow sensor) stalls the whole system. There are no priorities: urgent and unimportant work wait in the same line.
That is why this technique works only for very simple applications.
Tip: note the two meanings of "round-robin". This loop calls tasks in turn but never interrupts them; a preemptive round-robin scheduler takes the CPU away at each time slice.
Go deeper:
DigiKey — Introduction to RTOS Part 1 — the super loop and why an RTOS replaces it once timing matters.