Loading header...

Schedulers and State Machines

Bare-metal firmware becomes maintainable when time-based work is scheduled explicitly and behavior is represented as states. This avoids hidden blocking delays and makes timing review possible.

Learning Objectives

By the end of this lesson, you should be able to replace blocking delays with periodic tasks, design a cooperative scheduler, model behavior as a finite state machine, and verify timing and transitions.

Why Blocking Delays Fail

read_sensor();
delay_ms(1000);
update_display();

During the delay, the firmware cannot respond to buttons, communication, or faults unless interrupts do all the work.

Cooperative Scheduler

typedef void (*task_fn)(void);

typedef struct {
    task_fn run;
    uint32_t period;
    uint32_t next;
} task_t;

Each task must return quickly. Long operations are split into states.

State Machines

stateDiagram-v2 [*] --> Idle Idle --> Measuring: start Measuring --> Reporting: sample_ready Reporting --> Idle: sent Measuring --> Fault: timeout Fault --> Idle: reset

State machines prevent scattered flag logic and make reviews easier.

Worked Example: Sensor Polling

A nonblocking sensor transaction can use IDLE, START_CONVERSION, WAIT_READY, READ, FILTER, and FAULT. No state blocks for conversion time, so other tasks can run while the sensor is busy.

Timing Calculation

If tasks have periods 10 ms, 20 ms, and 100 ms, and worst-case runtimes 0.4 ms, 0.6 ms, and 2 ms:

$$
U=\frac{0.4}{10}+\frac{0.6}{20}+\frac{2}{100}=0.09
$$

About 9% of CPU time is scheduled work, leaving margin for interrupts and bursts.

Design Rules

  • Use monotonic tick comparisons that tolerate wraparound.
  • Keep task functions short and nonblocking.
  • Give each state a clear exit condition.
  • Log unexpected events and invalid transitions.
  • Separate transition logic from hardware register access when possible.

Common Mistakes

  • Calling blocking drivers from scheduled tasks.
  • Updating next_ms = now + period, which can accumulate jitter after delays.
  • Encoding state with many unrelated booleans.
  • Forgetting timeout transitions.
  • Letting one task monopolize the loop.

Summary

Schedulers decide when work runs; state machines decide what behavior happens next. Together they make bare-metal firmware deterministic, reviewable, and testable without requiring an RTOS.

Further Reading

  • Miro Samek, "Practical UML Statecharts in C/C++."
  • Jack Ganssle, "State Machines for Embedded Systems."
  • FreeRTOS documentation for comparison with preemptive scheduling.

Mind Map

mindmap root((Schedulers and FSMs)) Core concept Periodic tasks Explicit states No blocking delays Applications Sensor polling UI handling Protocol parser Motor sequence Calculations Utilization sum C over T Period jitter Timeout ticks Tick wrap Design rules Short tasks Monotonic time Clear transitions Timeouts everywhere Practical checks Worst runtime Missed deadline Invalid event State coverage Common mistakes Delay in task Boolean state soup No timeout Jitter drift