The Embedded New Testament

The "Holy Bible" for embedded engineers


Project maintained by theEmbeddedGeorge Hosted on GitHub Pages — Theme by mattgraham

Task Creation and Management in RTOS

Understanding task management through concepts, not just API calls. Learn why tasks matter and how to think about concurrent execution.

📋 Table of Contents


Concept → Why it matters → Minimal example → Try it → Takeaways

Concept: Tasks are like workers in a factory, each with a specific job to do. The RTOS is like a smart manager that ensures each worker gets the right amount of time and resources to complete their work on schedule.

Why it matters: Without proper task management, your embedded system becomes like a factory with no manager - workers bump into each other, resources get wasted, and deadlines get missed. Good task management ensures everything runs smoothly and predictably.

Minimal example: A simple system with three tasks: one blinks an LED every 100ms, another reads a sensor every 500ms, and a third sends data every 1000ms. Each task runs independently but the system coordinates them all.

Try it: Start with a single task, then add more tasks and observe how the RTOS manages them. Change priorities and see how it affects execution order.

Takeaways: Task management is about designing independent workers that can cooperate effectively, with the RTOS handling the complex coordination so you can focus on what each task should do.


📋 Quick Reference: Key Facts

Task Fundamentals

Task States

Task Design Principles

Priority Guidelines


🧠 Core Concepts

What is a Task?

A task is an independent unit of work that runs concurrently with other tasks. Think of it as a separate program that can run at the same time as other programs.

┌─────────────────────────────────────────────────────────────┐
│                    What is a Task?                         │
├─────────────────────────────────────────────────────────────┤
│                                                           │
│  Traditional Program:                                      │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  main() {                                          │   │
│  │    while(1) {                                      │   │
│  │      blink_led();                                  │   │
│  │      read_sensor();                                │   │
│  │      send_data();                                  │   │
│  │      delay(100);                                   │   │
│  │    }                                               │   │
│  │  }                                                 │   │
│  └─────────────────────────────────────────────────────┘   │
│  ❌ Everything runs in sequence                          │
│  ❌ Can't prioritize different operations               │
│  ❌ Hard to make operations independent                 │
│                                                           │
│  RTOS with Tasks:                                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │ Task 1:     │  │ Task 2:     │  │ Task 3:     │        │
│  │ Blink LED   │  │ Read Sensor │  │ Send Data   │        │
│  │ Every 100ms │  │ Every 500ms │  │ Every 1000ms│        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
│  ✅ Each task runs independently                          │
│  ✅ Different priorities possible                         │
│  ✅ Easy to add/remove/modify tasks                     │
└─────────────────────────────────────────────────────────────┘

Why Use Tasks Instead of One Big Loop?

Single Loop Problems:

┌─────────────────────────────────────────────────────────────┐
│                    Single Loop Problems                    │
├─────────────────────────────────────────────────────────────┤
│                                                           │
│  while(1) {                                               │
│    // What if sensor reading takes too long?             │
│    sensor_value = read_sensor();                          │
│                                                           │
│    // LED blinking gets delayed!                          │
│    blink_led();                                           │
│                                                           │
│    // What if data sending fails?                        │
│    send_data();                                           │
│                                                           │
│    // Everything waits for everything else                │
│    delay(100);                                            │
│  }                                                        │
│                                                           │
│  ❌ One slow operation delays everything                  │
│  ❌ Can't prioritize critical operations                  │
│  ❌ Hard to handle failures gracefully                    │
│  ❌ Timing becomes unpredictable                          │
└─────────────────────────────────────────────────────────────┘

Task-Based Benefits:

┌─────────────────────────────────────────────────────────────┐
│                    Task-Based Benefits                     │
├─────────────────────────────────────────────────────────────┤
│                                                           │
│  Task 1 (High Priority):                                  │
│  while(1) {                                               │
│    blink_led();                                           │
│    vTaskDelay(100);                                       │
│  }                                                        │
│                                                           │
│  Task 2 (Medium Priority):                                │
│  while(1) {                                               │
│    sensor_value = read_sensor();                          │
│    vTaskDelay(500);                                       │
│  }                                                        │
│                                                           │
│  Task 3 (Low Priority):                                   │
│  while(1) {                                               │
│    send_data();                                           │
│    vTaskDelay(1000);                                      │
│  }                                                        │
│                                                           │
│  ✅ Each task runs at its own pace                        │
│  ✅ Critical operations (LED) aren't delayed              │
│  ✅ Failures in one task don't affect others              │
│  ✅ Timing is predictable and reliable                    │
└─────────────────────────────────────────────────────────────┘

🔄 Task Lifecycle

Task States

Tasks move through different states during their lifetime:

┌─────────────────────────────────────────────────────────────┐
│                    Task State Machine                      │
├─────────────────────────────────────────────────────────────┤
│                                                           │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐                │
│  │ Created │───▶│  Ready  │───▶│ Running │                │
│  └─────────┘    └─────────┘    └────┬────┘                │
│                                      │                     │
│                                      ▼                     │
│  ┌─────────┐    ┌─────────┐    ┌─────────┐                │
│  │Deleted  │◄───│ Blocked │◄───│         │                │
│  └─────────┘    └─────────┘    └─────────┘                │
│                                                           │
│  State Transitions:                                        │
│  • Created → Ready: Task is ready to run                  │
│  • Ready → Running: Scheduler selects this task           │
│  • Running → Ready: Task yields or is preempted           │
│  • Running → Blocked: Task waits for something            │
│  • Blocked → Ready: What task was waiting for happens     │
│  • Any → Deleted: Task is removed from system             │
└─────────────────────────────────────────────────────────────┘

What Happens in Each State?

Created: Task exists but isn’t scheduled yet Ready: Task is ready to run, waiting for CPU time Running: Task is currently executing on the CPU Blocked: Task is waiting for something (delay, data, resource) Deleted: Task has been removed and won’t run again


🎨 Task Design Principles

Single Responsibility Principle

Each task should have one clear job to do:

┌─────────────────────────────────────────────────────────────┐
│                    Good vs Bad Task Design                 │
├─────────────────────────────────────────────────────────────┤
│                                                           │
│  ❌ Bad: One task does everything                         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ void vDoEverythingTask(void *pvParameters) {       │   │
│  │   while(1) {                                        │   │
│  │     // Too many responsibilities!                    │   │
│  │     read_sensors();                                  │   │
│  │     process_data();                                  │   │
│  │     control_motors();                                │   │
│  │     update_display();                                │   │
│  │     send_telemetry();                                │   │
│  │     vTaskDelay(100);                                 │   │
│  │   }                                                  │   │
│  │ }                                                    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                           │
│  ✅ Good: Each task has one job                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │ Sensor Task │  │ Control     │  │ Display     │        │
│  │ Read data   │  │ Task        │  │ Task        │        │
│  │ every 100ms │  │ Process &   │  │ Update UI   │        │
│  └─────────────┘  │ control     │  │ every 50ms  │        │
│                   │ every 50ms  │  └─────────────┘        │
│                   └─────────────┘                         │
│                                                           │
│  Benefits of good design:                                 │
│  • Easier to understand and debug                         │
│  • Can optimize each task independently                    │
│  • Easier to test individual components                   │
│  • More flexible and maintainable                         │
└─────────────────────────────────────────────────────────────┘

Task Granularity

Tasks should be neither too big nor too small:

Too Big (Coarse Granularity):

Too Small (Fine Granularity):

Just Right:


🎯 Priority Management

Why Priorities Matter

Priorities determine which task runs when multiple tasks are ready:

┌─────────────────────────────────────────────────────────────┐
│                    Priority System                         │
├─────────────────────────────────────────────────────────────┤
│                                                           │
│  Priority 5: Emergency Stop (highest)                    │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ ██████████████████████████████████████████████████ │   │
│  │ Emergency stop - must run immediately!              │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                           │
│  Priority 4: Safety Monitoring                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ ██████████████████████████████████████████████████ │   │
│  │ Safety checks - critical for system safety          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                           │
│  Priority 3: Control Loop                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ ██████████████████████████████████████████████████ │   │
│  │ Main control - keeps system running                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                           │
│  Priority 2: Data Logging                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ ██████████████████████████████████████████████████ │   │
│  │ Logging - important but not critical                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                           │
│  Priority 1: Status Updates (lowest)                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ ██████████████████████████████████████████████████ │   │
│  │ Status - nice to have but not essential            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                           │
│  ⚠️  Higher priority tasks can interrupt lower ones!      │
└─────────────────────────────────────────────────────────────┘

Priority Assignment Guidelines

High Priority (4-5):

Medium Priority (2-3):

Low Priority (1):


💾 Resource Management

Stack Management

Each task needs its own stack space:

┌─────────────────────────────────────────────────────────────┐
│                    Task Stack Allocation                    │
├─────────────────────────────────────────────────────────────┤
│                                                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                System Memory                        │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │   │
│  │  │ Task 1      │  │ Task 2      │  │ Task 3      │ │   │
│  │  │ Stack       │  │ Stack       │  │ Stack       │ │   │
│  │  │ 1KB         │  │ 2KB         │  │ 512B        │ │   │
│  │  └─────────────┘  └─────────────┘  └─────────────┘ │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │                Heap (shared)                        │   │
│  ├─────────────────────────────────────────────────────┤   │
│  │                Global Variables                      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                           │
│  Stack Size Considerations:                               │
│  • Too small: Stack overflow crashes                     │
│  • Too large: Wastes memory                              │
│  • Monitor actual usage with stack checking              │
│  • Consider function call depth and local variables      │
└─────────────────────────────────────────────────────────────┘

Memory Allocation Strategies

Static Allocation:

Dynamic Allocation:


🧪 Guided Labs

Lab 1: Single Task System

Objective: Understand basic task creation and execution.

Setup: Create a single task that performs a simple operation.

Steps:

  1. Create a task function that blinks an LED
  2. Configure the task with appropriate stack size and priority
  3. Start the scheduler and observe the task running
  4. Modify the task timing and observe changes

Expected Outcome: Understanding of basic task creation and how tasks run independently.

Lab 2: Multi-Task Coordination

Objective: Learn how multiple tasks work together.

Setup: Create two tasks that need to coordinate their work.

Steps:

  1. Create a producer task that generates data
  2. Create a consumer task that processes the data
  3. Use a queue to pass data between tasks
  4. Observe how tasks coordinate without interfering with each other

Expected Outcome: Understanding of task communication and coordination.

Lab 3: Priority Management

Objective: Learn how priorities affect task execution.

Setup: Create multiple tasks with different priorities.

Steps:

  1. Create tasks with different priorities
  2. Make high-priority tasks run frequently
  3. Make low-priority tasks do heavy work
  4. Observe how priorities affect execution order

Expected Outcome: Understanding of priority-based scheduling and its effects.


Check Yourself

Understanding Check

Application Check

Analysis Check


Further Reading

Industry Standards