Scan to FollowLearn Embedded Together, learn and grow together

This series of articles on FreeRTOS aims to help beginners quickly get started and master the basic principles and usage methods of FreeRTOS while organizing knowledge for themselves.
FreeRTOS Quick Start – Initial Exploration of the System
FreeRTOS Official Chinese Website is Now Live
FreeRTOS Coding Standards and Data Types
FreeRTOS Quick Start – Task Management
FreeRTOS Learning: Detailed Explanation of Message Queues
FreeRTOS Learning: Counting Semaphores
FreeRTOS Learning: Mutex Semaphores
FreeRTOS Learning: Event Groups
FreeRTOS Learning: Task Notifications
FreeRTOS Learning: Software Timers
FreeRTOS Learning: Memory Management
FreeRTOS Learning: Resource Management
FreeRTOS Learning: Interrupt Management
This article introduces the system porting related content of FreeRTOS.
FreeRTOS is a lightweight real-time operating system kernel designed for embedded systems.
It has the following features:
- Open source and free (under the MIT license)
- Highly portable, supporting various processor architectures
- Small kernel, occupying only 6-12KB ROM and a few hundred bytes of RAM in minimal configuration
- Provides basic functions such as task management, time management, semaphores, message queues, software timers, etc.
- Supports preemptive, cooperative, and hybrid task scheduling
Preparation Before Porting
1. Hardware Platform Assessment
- Confirm the architecture of the target MCU (ARM Cortex-M, RISC-V, MIPS, etc.)
- Evaluate whether Flash and RAM resources meet the requirements
- Confirm whether the required peripherals (such as system timers) are available
2. Obtain FreeRTOS Source Code
Get the latest version of the source code from the official website (https://www.freertos.org) or GitHub, which mainly includes the following key directories:
<span>Source/</span>– Core kernel code<span>Source/portable/</span>– Platform-specific porting layer code<span>Demo/</span>– Demonstration projects for various platforms
3. Development Environment Preparation
- Install a suitable IDE (Keil, IAR, Eclipse, etc.)
- Configure the cross-compilation toolchain
- Prepare debugging tools (J-Link, ST-Link, etc.)
Detailed Porting Steps
1. Create Basic Project Structure
MyProject/
├── Core/ # Application code
├── Drivers/ # Hardware drivers
├── FreeRTOS/
│ ├── Source/ # FreeRTOS core source code
│ └── Portable/ # Porting layer code
└── Project/ # IDE project files
2. Add Necessary FreeRTOS Source Files
Core files that must be included:
<span>tasks.c</span>– Task scheduling<span>queue.c</span>– Queue management<span>list.c</span>– List management<span>timers.c</span>– Software timers (optional)<span>event_groups.c</span>– Event groups (optional)<span>heap_x.c</span>– Choose an appropriate memory management scheme
3. Implement Porting Layer
3.1 Port Layer Implementation (port.c)
This is the core part of the porting, and the following content needs to be implemented:
1. Stack Initialization
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
{
/* Architecture-specific stack initialization */
pxTopOfStack--; *pxTopOfStack = portINITIAL_XPSR; /* xPSR */
pxTopOfStack--; *pxTopOfStack = (StackType_t)pxCode; /* PC */
/* ... Other register initialization ... */
return pxTopOfStack;
}
2. Start Scheduler
void vPortStartFirstTask(void)
{
__asm volatile(
" ldr r0, =0xE000ED08 \n" /* Use VTOR register to get vector table address */
" ldr r0, [r0] \n"
" ldr r0, [r0] \n" /* The first vector table entry is the initial stack value */
" msr msp, r0 \n" /* Set MSP */
" cpsie i \n" /* Globally enable interrupts */
" cpsie f \n"
" dsb \n"
" isb \n"
" svc 0 \n" /* Call SVC to start the first task */
" nop \n"
);
}
3. Context Switching
void xPortPendSVHandler(void)
{
__asm volatile(
" mrs r0, psp \n"
" ldr r3, =pxCurrentTCB \n"
" ldr r2, [r3] \n"
" stmdb r0!, {r4-r11} \n" /* Save remaining registers */
" str r0, [r2] \n" /* Save new task stack top */
/* ... Restore new task context ... */
);
}
3.2 System Clock Configuration
Typically, the MCU’s SysTick timer is used as the heartbeat for FreeRTOS:
void vPortSetupTimerInterrupt(void)
{
/* Calculate SysTick reload value */
uint32_t ulReloadValue = configCPU_CLOCK_HZ / configTICK_RATE_HZ - 1;
/* Configure SysTick */
portNVIC_SYSTICK_LOAD_REG = ulReloadValue;
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |
portNVIC_SYSTICK_INT_BIT |
portNVIC_SYSTICK_ENABLE_BIT;
}
4. Memory Management Implementation
FreeRTOS provides five memory management schemes (heap_1.c to heap_5.c), and custom schemes can also be implemented:
Example: malloc implementation in heap_4.c
void *pvPortMalloc(size_t xWantedSize)
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
static BaseType_t xHeapHasBeenInitialised = pdFALSE;
/* Initialize heap on first call */
if(xHeapHasBeenInitialised == pdFALSE) {
prvHeapInit();
xHeapHasBeenInitialised = pdTRUE;
}
/* Align requested size */
if(xWantedSize > 0) {
xWantedSize += heapSTRUCT_SIZE;
if((xWantedSize & portBYTE_ALIGNMENT_MASK) != 0) {
xWantedSize += (portBYTE_ALIGNMENT - (xWantedSize & portBYTE_ALIGNMENT_MASK));
}
}
/* Search free linked list */
// ... Find suitable memory block ...
return (void *)(((uint8_t *)pxBlock) + heapSTRUCT_SIZE);
}
5. Interrupt Handling
1. Interrupt Priority Configuration
#define configKERNEL_INTERRUPT_PRIORITY 255 /* Lowest priority */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* Interrupts above this priority cannot call FreeRTOS API */
2. Interrupt Service Routine Template
void USART1_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
/* Interrupt handling logic */
/* If a task needs to be woken */
if(xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
Key Configuration Adjustments (FreeRTOSConfig.h)
1. Basic Configuration
#define configUSE_PREEMPTION 1 /* 1 for preemptive scheduling, 0 for cooperative */
#define configUSE_IDLE_HOOK 0 /* Whether to use idle task hook function */
#define configUSE_TICK_HOOK 0 /* Whether to use Tick hook function */
#define configCPU_CLOCK_HZ (SystemCoreClock) /* CPU clock frequency */
#define configTICK_RATE_HZ ((TickType_t)1000) /* System tick frequency (Hz) */
#define configMAX_PRIORITIES (7) /* Number of task priorities */
#define configMINIMAL_STACK_SIZE ((uint16_t)128) /* Idle task stack size */
#define configTOTAL_HEAP_SIZE ((size_t)10240) /* Total heap size */
2. Component Configuration
#define configUSE_MUTEXES 1 /* Use mutexes */
#define configUSE_RECURSIVE_MUTEXES 1 /* Use recursive mutexes */
#define configUSE_COUNTING_SEMAPHORES 1 /* Use counting semaphores */
#define configUSE_16_BIT_TICKS 0 /* 1 for 16-bit Tick counter, 0 for 32-bit */
3. Hook Function Configuration
void vApplicationIdleHook(void); /* Idle task hook */
void vApplicationTickHook(void); /* Tick hook */
void vApplicationMallocFailedHook(void); /* Memory allocation failure hook */
Testing and Validation
1. Create Test Tasks
void vTask1(void *pvParameters)
{
const char *pcTaskName = "Task 1 is running\r\n";
for(;;) {
vPrintString(pcTaskName);
vTaskDelay(pdMS_TO_TICKS(1000)); /* Delay 1 second */
}
}
void vTask2(void *pvParameters)
{
const char *pcTaskName = "Task 2 is running\r\n";
for(;;) {
vPrintString(pcTaskName);
vTaskDelay(pdMS_TO_TICKS(2000)); /* Delay 2 seconds */
}
}
int main(void)
{
xTaskCreate(vTask1, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vTask2, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
vTaskStartScheduler(); /* Start scheduler */
for(;;); /* Normally should not reach here */
}
2. Common Issues Troubleshooting
- Startup Failure
- Check stack pointer initialization
- Verify vector table location
- Confirm system clock configuration is correct
- Check
<span>configTICK_RATE_HZ</span>setting - Verify SysTick interrupt is triggered
- Check task priority settings
- Increase
<span>configTOTAL_HEAP_SIZE</span> - Try different memory management schemes
- Check memory alignment requirements
Advanced Considerations
1. Low Power Support
void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
{
/* Configure low power mode */
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
PWR->CR |= PWR_CR_LPDS | PWR_CR_FPDS;
/* Enter low power mode */
__WFI();
}
2. Multi-Core Porting Considerations
For multi-core MCUs, consider:
- Each core runs an independent scheduler or master-slave core design
- Inter-core communication mechanisms
- Shared resource protection
3. Debugging Support
- Integrate SystemView or Tracealyzer for visual debugging
- Implement
<span>vApplicationStackOverflowHook</span>to detect stack overflow - Use FreeRTOS+Trace functionality
Conclusion
Key points for FreeRTOS porting include:
- Correctly implement key functions in the port layer (port.c)
- Properly configure system clock and interrupts
- Select an appropriate memory management scheme
- Adjust FreeRTOSConfig.h according to application requirements
- Thoroughly test and validate system stability
By following these steps, FreeRTOS can successfully run on most embedded platforms, providing reliable real-time multitasking support for applications.

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