FreeRTOS Learning: System Porting

Scan to FollowLearn Embedded Together, learn and grow together

FreeRTOS Learning: System Porting

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

  1. Startup Failure
  • Check stack pointer initialization
  • Verify vector table location
  • Confirm system clock configuration is correct
  • Task Not Scheduling
    • Check<span>configTICK_RATE_HZ</span> setting
    • Verify SysTick interrupt is triggered
    • Check task priority settings
  • Memory Allocation Failure
    • 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:

    1. Correctly implement key functions in the port layer (port.c)
    2. Properly configure system clock and interrupts
    3. Select an appropriate memory management scheme
    4. Adjust FreeRTOSConfig.h according to application requirements
    5. 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.

    FreeRTOS Learning: System Porting

    Follow 【Learn Embedded Together】 to become better together.

    If you find this article helpful, click “Share”, “Like”, or “Recommend”!

    Leave a Comment