FreeRTOS follows a strict and intentional naming convention.
This is not cosmetic. It is an architectural decision that supports:
- Portability across 8-bit, 16-bit, and 32-bit MCUs
- Readability in large embedded projects
- Static analysis and MISRA compliance
- Long-term maintainability
As embedded engineers, especially when building safety-critical or scalable systems, understanding this structure allows us to read FreeRTOS code like a book instead of guessing what each symbol means.
Let’s dissect it professionally.
Why Naming Conventions Matter in RTOS Design
In embedded systems:
Memory is limited.
Architectures vary.
Compilers behave differently.
If your RTOS code assumes:
intis 32-bitlongis always 32-bit- pointer size equals register size
You are already introducing portability bugs.
FreeRTOS solves this by:
+--------------------------+
| Abstracted Data Types |
| Strict Prefix Rules |
| File-based Namespacing |
+--------------------------+
This makes the kernel portable from:
- 8-bit AVR
- 16-bit MSP430
- 32-bit ARM Cortex-M
- 64-bit RISC-V (in theory)
Core FreeRTOS Data Types
FreeRTOS does not rely on raw C types like int, long, or short.
Instead, it introduces architecture-aware typedefs.
TickType_t — The System Time Counter
TickType_t represents the RTOS tick counter.
+---------------------------+
| SysTick Interrupt |
| increments tick |
+-------------+-------------+
|
v
TickType_t
Depending on configuration:
- 16-bit unsigned
- 32-bit unsigned
The size is controlled via:
#define configUSE_16_BIT_TICKS 1
Why does this matter?
On small MCUs:
16-bit tick:
Max tick count = 65535
On larger systems:
32-bit tick:
Max tick count = 4,294,967,295
Example:
TickType_t xStartTime;
TickType_t xDelay = pdMS_TO_TICKS(1000);
xStartTime = xTaskGetTickCount();
vTaskDelayUntil(&xStartTime, xDelay);
Notice:
- We never assume its size.
- We never use
uint32_tdirectly.
This guarantees portability.
BaseType_t — Architecture-Dependent Signed Type
BaseType_t is used for:
- Return values
- Boolean-like status
- Small arithmetic values
- Architecture width dependent operations
It is:
- 16-bit on 16-bit MCUs
- 32-bit on 32-bit MCUs
Why not just use int?
Because:
int size varies across compilers
FreeRTOS controls it explicitly.
Example:
BaseType_t xResult;
xResult = xTaskCreate(
vMyTask,
"Task1",
256,
NULL,
2,
NULL
);
if (xResult == pdPASS)
{
// Task created successfully
}
Here:
pdPASSis of typeBaseType_t- Consistent across architectures
UBaseType_t — Unsigned Version
FreeRTOS also defines:
UBaseType_t
Used for:
- Unsigned counters
- Priority values
- Indexing
Example:
UBaseType_t uxPriority = uxTaskPriorityGet(NULL);
Variable Naming Prefix Rules
FreeRTOS uses Hungarian-style prefixes for clarity.
This allows you to understand variable type without reading the declaration.
Common Prefixes
| Prefix | Meaning |
|---|---|
| c | char |
| s | short (16-bit) |
| l | long (32-bit) |
| u | unsigned |
| p | pointer |
| x | BaseType_t or custom type |
| ux | UBaseType_t |
| px | pointer to struct |
| pc | pointer to char |
| pv | pointer to void |
Example Analysis:
BaseType_t xReturn;
TickType_t xDelay;
char cStatus;
unsigned long ulCounter;
void * pvBuffer;
TaskHandle_t xTaskHandle;
Immediately readable:
xReturn→ BaseType_tulCounter→ unsigned longpvBuffer→ pointer to voidxTaskHandle→ FreeRTOS type
Real Kernel Example
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
BaseType_t xSwitchRequired = pdFALSE;
/* Increment the RTOS tick */
xTickCount++;
if( xTickCount >= xNextTaskUnblockTime )
{
xSwitchRequired = pdTRUE;
}
return xSwitchRequired;
}
Let’s decode:
xTaskIncrementTick → returns BaseType_t
pxTCB → pointer to TCB structure
xSwitchRequired → BaseType_t flag
This is self-documenting.
Function Naming Convention
FreeRTOS function naming follows a structured pattern:
<returnTypePrefix><ModuleName><Action>
Return Type Prefix
| Prefix | Meaning |
|---|---|
| v | void |
| x | BaseType_t |
| ux | UBaseType_t |
| pc | pointer to char |
| pv | pointer to void |
File/Module-Based Prefix
Functions are grouped by module:
| Module File | Function Prefix |
|---|---|
| task.c | vTask / xTask / uxTask |
| queue.c | xQueue / vQueue |
| timers.c | xTimer / vTimer |
| semphr.h | xSemaphore |
Example Breakdown
vTaskPrioritySet()
void vTaskPrioritySet(
TaskHandle_t xTask,
UBaseType_t uxNewPriority
);
Decode:
v→ returns voidTask→ defined in task modulePrioritySet→ action
xQueueSend()
BaseType_t xQueueSend(
QueueHandle_t xQueue,
const void * pvItemToQueue,
TickType_t xTicksToWait
);
Decode:
x→ returns BaseType_t (success/fail)Queue→ queue moduleSend→ operationpvItemToQueue→ pointer to void
uxTaskPriorityGet()
UBaseType_t uxTaskPriorityGet(
TaskHandle_t xTask
);
Decode:
ux→ unsigned BaseType_tTask→ task modulePriorityGet→ action
Complete Visual Overview
+----------------------+
| FreeRTOS Naming |
+----------------------+
Data Types
---------
TickType_t
BaseType_t
UBaseType_t
Variable Prefix
---------------
x → BaseType_t
ux → UBaseType_t
px → pointer to struct
pv → pointer to void
c → char
ul → unsigned long
Function Prefix
---------------
vTaskDelay()
xQueueSend()
uxTaskPriorityGet()
Practical Example — Full Mini Program
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
static QueueHandle_t xQueue;
void vProducerTask(void * pvParameters)
{
int lValueToSend = 100;
for (;;)
{
xQueueSend(xQueue, &lValueToSend, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vConsumerTask(void * pvParameters)
{
int lReceivedValue;
for (;;)
{
if (xQueueReceive(xQueue, &lReceivedValue, portMAX_DELAY) == pdPASS)
{
// Process value
}
}
}
int main(void)
{
xQueue = xQueueCreate(5, sizeof(int));
xTaskCreate(vProducerTask, "PROD", 256, NULL, 1, NULL);
xTaskCreate(vConsumerTask, "CONS", 256, NULL, 1, NULL);
vTaskStartScheduler();
for (;;);
}
Notice the consistency:
xQueue→ object handlevProducerTask→ void returnpvParameters→ pointer to voidlValueToSend→ longpdPASS→ portable define
FreeRTOS Macro Names
Macros in FreeRTOS are everywhere.
They configure:
- Scheduling behavior
- Interrupt masking
- Return codes
- Memory allocation
- Architecture abstraction
And like everything in FreeRTOS — they follow strict rules.
General Macro Naming Rules
FreeRTOS macros follow two main patterns:
- All uppercase for configuration and constants
- Lowercase prefix + UPPERCASE body for file scoping
pdTRUE
pdFALSE
portMAX_DELAY
configUSE_PREEMPTION
taskENTER_CRITICAL
pdPASS
This gives us pseudo-namespacing in pure C.
Since C has no namespaces, FreeRTOS simulates it using prefixes.
Prefix Indicates Ownership (File/Module)
Macro prefix usually tells you where it lives.
Think of it as: <prefix><ACTUAL_NAME>
Let’s decode the most important ones.
portXXX Macros
Defined inside: portable/[compiler]/[architecture]/portmacro.h
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
Meaning:
port→ defined in port layerMAX_DELAY→ actual semantic meaning
Because each architecture can redefine it.
On Cortex-M:
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
On 16-bit MCU:
#define portMAX_DELAY ( TickType_t ) 0xffffU
Application code stays identical.
Real Usage Example
xQueueReceive(xQueue, &xData, portMAX_DELAY);
Task A
|
|---- waits forever ---->
| until queue has data
If architecture changes → behavior preserved.
pdXXX Macros (Project Definitions)
Defined in: projdefs.h
pd stands for: Project Definition
These represent generic logical constants.
Common examples:
#define pdTRUE ( ( BaseType_t ) 1 )
#define pdFALSE ( ( BaseType_t ) 0 )
#define pdPASS ( pdTRUE )
#define pdFAIL ( pdFALSE )
Why not use 1 and 0 directly?
Because:
- BaseType_t may not be int
- Ensures consistent casting
- MISRA compliance
- Static analysis friendliness
Example:
if (xQueueSend(xQueue, &xData, 0) == pdPASS)
{
// Success
}
Clear semantic meaning.
Not magic numbers.
taskXXX Macros
Defined in: task.h
These operate on task-level behavior.
taskENTER_CRITICAL()
Purpose:
Disable interrupts to protect shared resources.
taskENTER_CRITICAL();
/* Critical section */
sharedCounter++;
taskEXIT_CRITICAL();
Execution Flow:
Normal Mode
|
|----> Interrupts Enabled
|
taskENTER_CRITICAL()
|
|----> Interrupts Disabled
| (No context switch)
|
taskEXIT_CRITICAL()
|
|----> Interrupts Enabled
Important:
On Cortex-M this maps to: __disable_irq()
Through the port layer.
Again — abstraction.
configXXX Macros
Defined by the USER in: FreeRTOSConfig.h
This is your configuration contract with the kernel.
configUSE_PREEMPTION
#define configUSE_PREEMPTION 1
If set to:
1 → Preemptive scheduling
0 → Cooperative scheduling
Scheduler Behavior Diagram:
Preemptive:
High Priority Task becomes READY
|
v
Immediate Context Switch
Cooperative:
High Priority Task becomes READY
|
v
Wait until running task yields
configTICK_RATE_HZ
#define configTICK_RATE_HZ 1000
Defines: 1 tick = 1 ms
Changes timing resolution system-wide.
Macro Prefix Map Summary
| Prefix | Defined In | Purpose |
|---|---|---|
| port | portmacro.h | Architecture layer |
| pd | projdefs.h | Project definitions |
| task | task.h | Task control |
| config | FreeRTOSConfig.h | User configuration |
| queue | queue.h | Queue internals |
| timer | timers.h | Software timers |
Final Integrated View
FreeRTOS Naming System
---------------------------------
Data Types
TickType_t
BaseType_t
UBaseType_t
Variables
x → BaseType_t
ux → UBaseType_t
px → pointer
pv → pointer void
ul → unsigned long
Functions
vTaskDelay()
xQueueSend()
uxTaskPriorityGet()
Macros
portMAX_DELAY
pdTRUE
taskENTER_CRITICAL()
configUSE_PREEMPTION