Scan to FollowLearn Embedded Together, learn and grow together

FreeRTOS defines its own set of data types primarily to ensure portability across different processor architectures and compilers.
These data types are defined in the <span>portmacro.h</span> file, and the specific implementation will be adjusted according to different compilers and processors.
Basic Data Types
FreeRTOS defines the following basic data types:
| Type | Description |
|---|---|
| TickType_t | Used for system tick counting, typically defined as <span>uint32_t</span> (16-bit systems may use <span>uint16_t</span>) |
| BaseType_t | The most basic integer data type, usually the most efficient integer for the processor (typically int) |
| UBaseType_t | The unsigned version of BaseType_t |
| StackType_t | Data type used for task stacks, typically <span>uint32_t</span> |
| size_t | size_t from the standard C library |
| configMAX_SYSCALL_INTERRUPT_PRIORITY | Defines the highest interrupt priority that can call FreeRTOS API |
Special Data Types
| Type | Description |
|---|---|
| TaskHandle_t | Task handle, which is actually a pointer to the task control block (TCB) |
| QueueHandle_t | Queue handle, a pointer to the queue structure |
| SemaphoreHandle_t | Semaphore handle, which is actually an alias for QueueHandle_t |
| TimerHandle_t | Software timer handle |
| EventGroupHandle_t | Event group handle |
| StreamBufferHandle_t | Stream buffer handle |
| MessageBufferHandle_t | Message buffer handle |
Example of Data Type Definitions
In the FreeRTOS source code, these types are typically defined as follows:
#define portCHAR char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT short
#define portSTACK_TYPE uint32_t
#define portBASE_TYPE long
typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;
Coding Style
FreeRTOS has a very consistent coding style that runs throughout the project, making the code easy to read and maintain.
Naming Conventions
FreeRTOS adopts a strict naming convention:
-
Variable Naming:
Example:
BaseType_t xResult; char cByte; uint8_t ucCount; void *pvBuffer;
<span>x</span>: Non-standard types (e.g.,<span>BaseType_t</span>,<span>TickType_t</span>, etc.)<span>c</span>: Character type<span>s</span>: String<span>uc</span>: Unsigned character<span>p</span>: Pointer<span>pc</span>: Pointer to character<span>ul</span>: Unsigned long<span>v</span>: void
- Variable names use lowercase letters
- Multiple words are separated by underscores
- The prefix indicates the variable type or purpose:
Function Naming:
Example:
void vTaskDelete(TaskHandle_t xTask);
BaseType_t xQueueSend(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait);
void *pvPortMalloc(size_t xSize);
<span>v</span>: void return type<span>x</span>: returns BaseType_t<span>pv</span>: returns void pointer
<span>vTask</span>: Task related<span>xQueue</span>: Queue related<span>vSemaphore</span>: Semaphore related<span>pvPortMalloc</span>: Memory management related
- Use prefixes to indicate the module to which the function belongs:
- The prefix is followed by the action and object, using camel case naming
- The return type is also reflected in the naming:
Macro Naming:
Example:
#define configUSE_PREEMPTION 1
#define pdTRUE (1)
#define errQUEUE_FULL (0)
<span>config</span>: Macros in the configuration file<span>pd</span>(port dependent): Macros related to portability<span>err</span>: Error codes
- All uppercase
- Words are separated by underscores
- The prefix indicates the module to which it belongs:
Coding Style
-
Indentation and Braces:
Example:
if( xCondition == pdTRUE ) { /* Indent 4 spaces */ vFunction(); }
- Use 4 spaces for indentation
- The opening brace is on the same line as the statement
- The closing brace is on a new line, aligned with the starting statement
Comment Style:
Example:
/*
* Create a new task and add it to the ready task list.
*
* @param pxTaskCode Task function pointer
* @param pcName Descriptive name of the task
* @param usStackDepth Task stack size (in words)
* @param pvParameters Parameters passed to the task
* @param uxPriority Task priority
* @param pxCreatedTask Used to return the task handle
* @return Returns pdPASS on success, error code on failure
*/
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask );
- Use standard C comments (/* */)
- Important parts use multi-line comments
- Function header comments describe functionality, parameters, and return values
Code Layout:
Example:
xQueue = xQueueCreate(
uxQueueLength,
uxItemSize
);
- If function parameters are too long, they are displayed on separate lines, each parameter on its own line
- Spaces around operators
- Spaces after commas
Specific Coding Habits
-
Return Value Check:
Example:
if( xQueueSend( xQueue, &xData, xTicksToWait ) != pdPASS ) { /* Error handling */ }
- FreeRTOS functions typically return
<span>pdPASS</span>or<span>pdFAIL</span> - Error codes are typically defined as macros
Critical Section Protection:
Example:
taskENTER_CRITICAL();
{
/* Critical section code */
}
taskEXIT_CRITICAL();
- Use macros to enter and exit critical sections
- Keep critical sections as short as possible
Assertions:
Example:
configASSERT( xQueue != NULL );
- Use the
<span>configASSERT()</span>macro for runtime checks - Enabled during development, can be disabled in production
Example Code Analysis
Below is a typical FreeRTOS task creation code that demonstrates data types and coding style:
/* Task function prototype */
void vTaskFunction( void *pvParameters );
/* Task handle */
TaskHandle_t xHandle = NULL;
/* Create task */
BaseType_t xReturned = xTaskCreate(
vTaskFunction, /* Task function pointer */
"Demo Task", /* Task name */
configMINIMAL_STACK_SIZE, /* Stack size */
NULL, /* Parameters passed to the task */
tskIDLE_PRIORITY + 1, /* Task priority */
&xHandle /* Return task handle */
);
/* Check if task was created successfully */
if( xReturned == pdPASS )
{
/* Task created successfully */
vTaskStartScheduler(); /* Start scheduler */
}
else
{
/* Handle error */
for( ;; );
}
/* Task implementation */
void vTaskFunction( void *pvParameters )
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = pdMS_TO_TICKS( 1000 ); /* 1 second period */
/* Initialize variable */
xLastWakeTime = xTaskGetTickCount();
for( ;; )
{
/* Task functionality code */
/* Delay until next period */
vTaskDelayUntil( &xLastWakeTime, xFrequency );
}
}
Porting Related Data Types
When porting FreeRTOS to different platforms, special attention should be paid to the following data types and macro definitions:
- Defined in portmacro.h:
- Processor-specific data types
- Macros for entering/exiting critical sections
- Macros related to task switching
- Definitions related to system ticks
- Stack initialization
- Starting the first task
- System tick timer configuration
- Context switching
Conclusion
The design of FreeRTOS’s data types and coding style has the following characteristics:
- Portability: Ensures compatibility across different platforms through custom data types
- Consistency: Strict naming rules and coding style make the code easy to read and maintain
- Clarity: Naming prefixes clearly express the type and purpose of variables and functions
- Efficiency: Choosing the most suitable base data types for the target processor to improve efficiency
Understanding and following FreeRTOS’s data types and coding style is crucial for developing high-quality FreeRTOS applications, especially when porting to different platforms or collaborating with teams.
This consistent style also makes the FreeRTOS source code itself an excellent example for learning embedded real-time system programming.

Follow 【Learn Embedded Together】 to become better together。
If you find this article helpful, click “Share”, “Like”, or “Recommend”!