Skip to content

Start a Basic FreeRTOS Program on the Pico 2W

A complete example project built by following this guide is available as a downloadable zip.

Create an empty project in VS Code using the Raspberry Pi Pico plugin. We’ll name the project FreeRTOS_pico2w (this name will be used later when editing CMakeLists.txt).

Create project

In this example, stdio support is included on both UART and USB, because a second RPi Pico 2W is used as a debugger and stdio on the UART is relayed by the debugger to the virtual COM port on the PC.

Download the FreeRTOS RP2350 port from https://github.com/raspberrypi/FreeRTOS-Kernel/tree/main.

Download FreeRTOS

You can either clone the repo or download a zip.

First, copy the FreeRTOS-Kernel-main folder to the project you created.

Then copy the file FreeRTOS-Kernel-main\portable\ThirdParty\GCC\RP2350_ARM_NTZ\FreeRTOS_Kernel_import.cmake to the root of the project, where the CMakeLists.txt file is.

Add the following to CMakeLists.txt to include the file you just copied and let CMake know where the FreeRTOS files are located.

# Add FreeRTOS
set(FREERTOS_KERNEL_PATH "${CMAKE_CURRENT_LIST_DIR}/FreeRTOS-Kernel-main")
include(FreeRTOS_Kernel_import.cmake)
# FREERTOS: FreeRTOSConfig.h needs to be in the include path
target_include_directories(FreeRTOS_pico2w PRIVATE ${CMAKE_CURRENT_LIST_DIR})
# Add the FreeRTOS library to the build
target_link_libraries(FreeRTOS_pico2w
FreeRTOS-Kernel-Heap4
)

Download the files FreeRTOSConfig.h and FreeRTOSConfig_examples_common.h from https://github.com/raspberrypi/pico-examples/tree/master/freertos and add them to the root of the project.

In embedded systems, we are often required to use static allocation of memory. This needs to be enabled in FreeRTOSConfig_examples_common.h. Find and edit the following section:

/* Memory allocation related definitions. */
#ifndef configSUPPORT_STATIC_ALLOCATION
#define configSUPPORT_STATIC_ALLOCATION 1
#endif
#ifndef configSUPPORT_DYNAMIC_ALLOCATION
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#endif

Now we have to provide FreeRTOS with the required functions to provide static memory for the IdleTask, PassiveIdleTask, and TimerTask. Use the file from https://github.com/carlk3/FreeRTOS-FAT-CLI-for-RPi-Pico/blob/master/src/FreeRTOS%2BFAT%2BCLI/src/freertos_callbacks.c.

Add freertos_callbacks.c to the list of files in the add_executable section of CMakeLists.txt:

# Add executable. Default name is the project name, version 0.1
add_executable(FreeRTOS_pico2w FreeRTOS_pico2w.cpp freertos_callbacks.c)

freertos_callbacks.c has a section that includes RP2040.h and RP2350.h, but those files are not found in the source code we use, so comment that section out:

/*
#if PICO_RP2040
# include "RP2040.h"
#endif
#if PICO_RP2350
# include "RP2350.h"
#endif
*/

Use the one from https://github.com/raspberrypi/FreeRTOS-Kernel/blob/main/examples/cmake_example/main.c.

Modify the code to add initialization of stdio to the main method and printf a message from the task loop, like this:

static void exampleTask( void * parameters )
{
/* Unused parameters. */
( void ) parameters;
for( ; ; )
{
printf("Task 1\r\n");
/* Example Task Code */
vTaskDelay( 100 ); /* delay 100 ticks */
}
}
/*-----------------------------------------------------------*/
int main()
{
stdio_init_all();
static StaticTask_t exampleTaskTCB;
static StackType_t exampleTaskStack[ configMINIMAL_STACK_SIZE ];
( void ) printf( "Example FreeRTOS Project\n" );
( void ) xTaskCreateStatic( exampleTask,
"example",
configMINIMAL_STACK_SIZE,
NULL,
configMAX_PRIORITIES - 1U,
&( exampleTaskStack[ 0 ] ),
&( exampleTaskTCB ) );
/* Start the scheduler. */
vTaskStartScheduler();
for( ; ; )
{
/* Should not reach here. */
}
return 0;
}

Compile, run, and open the Serial Monitor in VS Code (or another terminal program). You should now see the program starting and the message from the task.

The compiler sometimes optimizes the code in a way where code is removed or restructured. This can make the debugging session a bit weird. To disable this, open CMakeLists.txt and add the following section, which disables optimization (sets the compiler flag -O0 and prevents inlining of code):

target_compile_options(blink PRIVATE -O0 -g -fno-inline -fno-inline-functions)

Add it after the target_link_libraries(blink pico_stdlib) section.