Detailed Explanation of FreeRTOS Configuration File

Detailed Explanation of FreeRTOS Configuration File —— Understanding freertos_config.h from Scratch

As one of the most popular real-time operating systems in the embedded field, FreeRTOS’s core configuration file<span>FreeRTOSConfig.h</span> acts like the “control panel” of the system. This article will guide you through this configuration file line by line, allowing you to truly understand the meaning of each configuration item. For now, just understand the role of these configurations, and in subsequent articles, I will detail how to use them in actual projects.

1. What is freertos_config.h?

In simple terms, this file is the “remote control” of FreeRTOS. You can make FreeRTOS work the way you want by modifying the macro definitions in this file.

For example:

  • • Want more task priorities? Just change a number.
  • • Want a larger memory heap? Just change a number.
  • • Want to enable software timers? Change 0 to 1.

Isn’t it simple? Let’s take a look at each item!

2. Basic Configuration Section —— Getting the System Running

2.1 Scheduling Method Configuration

#define configUSE_PREEMPTION  1  // Use preemptive scheduling

What is preemptive scheduling? For example, you are watching TV (a low-priority task), and suddenly the delivery arrives (a high-priority task), you will immediately go to open the door and continue watching TV after the delivery. This is preemptive scheduling — high-priority tasks can “cut in” at any time.

If set to 0, it is cooperative scheduling, which means you have to finish watching this episode before going to open the door, not recommended for beginners.

2.2 Clock Configuration —— The Heartbeat of the System

#define configCPU_CLOCK_HZ       (SystemCoreClock)      // CPU frequency, STM32F407 generally 168MHz
#define configTICK_RATE_HZ       ((TickType_t) 1000)    // System tick frequency 1000Hz = 1ms

What is configTICK_RATE_HZ? This is the “heartbeat frequency” of the system. Setting it to 1000 means the system “ticks” once every 1 millisecond, and this tick is referred to as a tick.

💡 Tip: If your task delays are in milliseconds, then 1000Hz is just right. If you need more precise microsecond delays, you can increase this value, but it will increase CPU load.

2.3 Priority and Stack Configuration

#define configMAX_PRIORITIES        (5)     // Up to 5 priorities (0~4)
#define configMINIMAL_STACK_SIZE    ((unsigned short) 130)  // Minimum stack of 130 words

Is a higher priority number better? Yes! In FreeRTOS:

  • • Priority 0 is the lowest (idle task)
  • • Priority 4 is the highest (5 levels set here)

How to set stack size? Note that the unit here is **words,** in STM32, 1 word = 4 bytes, so:

130 words × 4 bytes = 520 bytes

This is the minimum stack for the idle task. The stack for tasks you create can be larger, depending on task complexity, generally recommended:

  • • Simple LED blinking task: 128~256 bytes
  • • Serial communication task: 512~1024 bytes
  • • Complex algorithm task: over 2048 bytes

2.4 Heap Configuration

#define configTOTAL_HEAP_SIZE  ((size_t)(75 * 1024))  // 75KB heap space

What is heap space? This is the memory pool used by FreeRTOS to allocate memory for tasks, queues, semaphores, and other objects. STM32F407 has 128KB SRAM, allocating 75KB to FreeRTOS is quite reasonable.

⚠️ Note: If you receive a memory insufficient error when creating tasks, you need to increase this value!

3. Feature Switch Section —— Enable What You Need

3.1 Debugging Features

#define configUSE_TRACE_FACILITY    1   // Enable trace feature
#define configQUEUE_REGISTRY_SIZE   8   // Queue registry size

By enabling these features, you can use the <span>vTaskList()</span> function to view the status of all tasks, which is very convenient for debugging!

3.2 Mutexes and Semaphores

#define configUSE_MUTEXES               1   // Enable mutexes
#define configUSE_RECURSIVE_MUTEXES     1   // Enable recursive mutexes
#define configUSE_COUNTING_SEMAPHORES   1   // Enable counting semaphores

What is a mutex? It is like a lock on a public restroom door, ensuring that only one person (task) can use the resource at a time.

What is the use of recursive mutexes? They allow the same task to “lock” multiple times, for example, if function A locks, and A calls function B, which also needs to lock, a recursive mutex will prevent deadlock.

3.3 Stack Overflow Detection

#define configCHECK_FOR_STACK_OVERFLOW  0   // Stack overflow detection: 0 off, 1/2 on

💡 Suggestion: Set to 2 during debugging to detect stack overflow issues in time! You can turn it off during production to save performance.

4. Software Timer Section —— The Magic Tool for Timed Task Execution

#define configUSE_TIMERS                1    // Enable software timers
#define configTIMER_TASK_PRIORITY       (2)  // Timer task priority
#define configTIMER_QUEUE_LENGTH        10   // Up to 10 timer commands
#define configTIMER_TASK_STACK_DEPTH    (configMINIMAL_STACK_SIZE * 2)  // Stack depth

What is a software timer? Imagine you are cooking instant noodles:

  • • Create a 3-minute timer
  • • When the time is up, automatically execute a callback function (reminding you that the noodles are ready)
  • • No need to create a dedicated task to wait

How to set priority? Set to 2 here, which is medium priority. If the timer callback is very important (e.g., for safety monitoring), you can set a higher priority.

Why double the stack? Because the timer callback function may be more complex, giving it a bit more stack space is safer!

5. API Function Selection Section —— Include as Needed

#define INCLUDE_vTaskPrioritySet      1  // Allow dynamic modification of task priority
#define INCLUDE_vTaskDelete           1  // Allow task deletion
#define INCLUDE_vTaskSuspend          1  // Allow task suspension/resumption
#define INCLUDE_vTaskDelay            1  // Allow task delay
#define INCLUDE_vTaskDelayUntil       1  // Allow periodic delay

Why selectively include? Unused features are not compiled in, which can save Flash space. But as a beginner, it is recommended to set all to 1, and optimize later when you are familiar!

What is the difference between vTaskDelay and vTaskDelayUntil?

// vTaskDelay: Relative delay
void Task1(void *param) {
    while(1) {
        // Do something (assume it takes 5ms)
        vTaskDelay(100);  // Delay 100 ticks = 100ms
        // Actual period = 5ms + 100ms = 105ms, will accumulate error
    }
}

// vTaskDelayUntil: Absolute delay, suitable for periodic tasks
void Task2(void *param) {
    TickType_t lastWakeTime = xTaskGetTickCount();
    while(1) {
        // Do something (assume it takes 5ms)
        vTaskDelayUntil(&amp;lastWakeTime, 100);  
        // Execute accurately every 100ms, will not accumulate error
    }
}

6. Interrupt Priority Configuration Section —— The Most Important!

#define configPRIO_BITS                               4     // STM32F4 has 4 bits of priority
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY       0xF   // Lowest priority 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY  5     // Highest priority for callable APIs

6.1 Interrupt Priority Rules in STM32

In STM32, the rule for interrupt priority is: The smaller the number, the higher the priority!

Priority 0  ← Highest priority (most important)
Priority 1
Priority 2
...
Priority 15 ← Lowest priority (least important)

6.2 FreeRTOS Interrupt Level Management

FreeRTOS divides interrupts into three areas:

┌─────────────────────────────────────┐
│  Priority 0~4  │ Super high priority area        │ ← Cannot call FreeRTOS API
│             │ (emergency interrupts, such as motor protection) │
├─────────────────────────────────────┤
│  Priority 5~14 │ Managed interrupt area      │ ← Can call FromISR API
│             │ (serial port, timer, etc.)      │
├─────────────────────────────────────┤
│  Priority 15   │ FreeRTOS kernel interrupt      │ ← System tick, task switching
└─────────────────────────────────────┘

7. Assertion Mechanism —— A Good Helper for Debugging

#define configASSERT(x) \
    if((x) == 0) { \
        taskDISABLE_INTERRUPTS();  // Disable interrupts
        for(;;);                    // Infinite loop
    }

What is an assertion? It is a “checkpoint”. When the condition is not met, the program will hang here, allowing you to use the debugger to check the issue.

Practical Application:

void MyTask(void *param) {
    QueueHandle_t queue = (QueueHandle_t)param;
  
    // Assertion check: queue handle cannot be NULL
    configASSERT(queue != NULL);
  
    // If queue is NULL, the program will hang here, and you can see the problem at a glance!
    xQueueSend(queue, &amp;data, 0);
}

8. Interrupt Vector Mapping

#define vPortSVCHandler        SVC_Handler      // System call
#define xPortPendSVHandler     PendSV_Handler   // Task switching
#define xPortSysTickHandler    SysTick_Handler  // System tick (1ms)

These three interrupts are the “three pillars” of FreeRTOS:

  • SysTick: Generates an interrupt every 1ms, driving the system operation
  • PendSV: Responsible for task switching, with the lowest priority, ensuring it does not affect other interrupts
  • SVC: Used to start the scheduler

💡 You do not need to modify this part, just understand!

📌 Next Issue Preview

In the next article, we will explain in detail task creation and deletion, including the differences between dynamic and static creation, etc. Stay tuned!

Follow me for more embedded development insights! 🚀

Leave a Comment