FreeRTOS.h not found in RP2040 project (Cmake with C/C++ SDK and FreeRTOS)

Description

What MCU/Processor/Board and compiler are you using?

I’m using the RP2040 microcontroller on a custom circuit board. The firmware project was created using the Raspberry Pi Pico extension on VSCode and is using the GNU arm-none-eabi toolchain in combination with CMake.

What do you want to achieve?

I’m currently trying to add the LVGL library to my firmware project, which uses the Raspberry Pico C/C++ SDK and FreeRTOS. Adding the library via CMake makes the references available, but when trying to compile, the LVGL files require the FreeRTOS.h header. This is rather weird, since the FreeRTOS library can be used in the main file…

What have you tried so far?

The project can be compiled with FreeRTOS + no LVGL, so FreeRTOS gets correctly recognized for the main project. I can’t seem to figure out how to enable the FreeRTOS library for the LVGL library.

I fiddled around with the CMake commands to try and add the files one at a time, but it didn’t really work that well (the same errors).

Code to reproduce

The CMakeLists.txt in the top directory level of the project.

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)

# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
    set(USERHOME $ENV{USERPROFILE})
else()
    set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.2.0)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.2.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
    include(${picoVscode})
endif()
# ====================================================================================
set(PICO_BOARD pico CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

set(PROJECT time-tracker-firmware)
project(${PROJECT} C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

add_subdirectory(freertos)

set(LV_BUILD_CONF_PATH src/lv_conf.h)

option(CONFIG_LV_BUILD_EXAMPLES OFF)
option(CONFIG_LV_BUILD_DEMOS OFF)
add_subdirectory(lvgl)


# Add executable. Default name is the project name, version 0.1
add_executable(${PROJECT} src/main.cpp)

pico_set_program_name(${PROJECT} "time-tracker-firmware")
pico_set_program_version(${PROJECT} "0.1")

# Modify the below lines to enable/disable output over UART/USB
pico_enable_stdio_uart(${PROJECT} 0)
pico_enable_stdio_usb(${PROJECT} 1)

# Add the standard library to the build
target_link_libraries(${PROJECT}
    pico_stdlib
)

# Add the standard include files to the build
target_include_directories(${PROJECT} PRIVATE
    ${CMAKE_CURRENT_LIST_DIR}
    modules
)

include_directories(${PROJECT}
    src
)

# Add any user requested libraries
target_link_libraries(${PROJECT}
    freertos
    lvgl
    hardware_spi
    hardware_i2c
)

pico_add_extra_outputs(${PROJECT})

Here’s the main.cpp file:

#include <stdio.h>

#include "FreeRTOS.h"
#include "task.h"
#include "message_buffer.h"
#include "queue.h"
#include "semphr.h"

#include "lvgl/lvgl.h"

#include "hardware/i2c.h"
#include "hardware/spi.h"
#include "hardware/uart.h"
#include "pico/stdlib.h"

#include "modules/nina_w13.h"
#include "modules/pcf85363.h"
#include "modules/sdcard.h"
#include "modules/st7789.h"

SemaphoreHandle_t mutex_spi;
SemaphoreHandle_t mutex_i2c;

#define RESOLUTION_HORIZONTAL_PIXEL   (240)
#define RESOLUTION_VERTICAL_PIXEL     (240)

/* Declare buffer for 1/10 screen size; BYTES_PER_PIXEL will be 2 for RGB565. */
#define BYTES_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(LV_COLOR_FORMAT_RGB565))

static uint8_t buf1[RESOLUTION_HORIZONTAL_PIXEL * RESOLUTION_VERTICAL_PIXEL / 10 * BYTES_PER_PIXEL];

void put_px(uint16_t x, uint16_t y, uint16_t pixel) {

}

void my_flush_cb(lv_display_t * display, const lv_area_t * area, uint8_t * px_map)
{
    /* The most simple case (also the slowest) to send all rendered pixels to the
     * screen one-by-one.  `put_px` is just an example.  It needs to be implemented by you. */
    uint16_t * buf16 = (uint16_t *)px_map; /* Let's say it's a 16 bit (RGB565) display */
    int32_t x, y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            put_px(x, y, *buf16);
            buf16++;
        }
    }

    /* IMPORTANT!!!
     * Inform LVGL that flushing is complete so buffer can be modified again. */
    lv_display_flush_ready(display);
}


int main() {
  stdio_init_all();
  lv_init();

  lv_tick_set_cb(xTaskGetTickCount);
  lv_display_t* display = lv_display_create(RESOLUTION_HORIZONTAL_PIXEL, RESOLUTION_VERTICAL_PIXEL);
  
  lv_display_set_buffers(display, buf1, NULL, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);
  lv_display_set_flush_cb(display, my_flush_cb);

  mutex_spi = xSemaphoreCreateMutex();
  mutex_i2c = xSemaphoreCreateMutex();
  
  vTaskStartScheduler();
  for(;;);
}

And lastly here’s the lv_conf.h file

Screenshot and/or video

Although it’s not a screenshot, here’s the CMake Configuration output and build output:

cmake_build_output.txt (18.6 KB)
cmake_config_output.txt (2.1 KB)

The folder structure of the project is as follows (with the folder levels reduced, the LVGL has been cloned from the official git repo):

.
├── CMakeLists.txt
├── docs                [directory]
│   └── Part1PhysicalLayerSimplifiedSpecificationVer9.10Fin_20231201.pdf
├── freertos                [directory]
│   ├── CMakeLists.txt
│   ├── FreeRTOSConfig.h
│   └── FreeRTOS-Kernel                [directory]
├── lvgl                [directory]
│   ├── CMakeLists.txt
│   ├── CMakePresets.json
│   ├── component.mk
│   ├── configs [directory]
│   ├── COPYRIGHTS.md
│   ├── demos [directory]
│   ├── docs [directory]
│   ├── env_support                [directory]
│   ├── examples                [directory]
│   ├── idf_component.yml
│   ├── Kconfig
│   ├── library.json
│   ├── library.properties
│   ├── libs [directory]
│   ├── LICENCE.txt
│   ├── lv_conf_template.h
│   ├── lvgl.h
│   ├── lvgl.mk
│   ├── lvgl.pc.in
│   ├── lvgl_private.h
│   ├── lv_version.h
│   ├── lv_version.h.in
│   ├── README.md
│   ├── SConscript
│   ├── scripts                [directory]
│   ├── src                [directory]
│   ├── tests                [directory]
│   ├── xmls                [directory]
│   └── zephyr                [directory]
├── modules                [directory]
│   ├── CMakeLists.txt
│   ├── ...
├── pico_sdk_import.cmake
├── raspberrypi-swd.cfg
├── README.md
└── src                [directory]
    ├── lv_conf.h
    └── main.cpp

I have the same issue. Changing the lvgl version to 8.3.11 seems to fix it for me.

Here is my CMakeLists.txt file that runs into the same error:

cmake_minimum_required(VERSION 3.22)

#
# This file is generated only once,
# and is not re-generated if converter is called multiple times.
#
# User is free to modify the file as much as necessary
#

# Setup compiler settings
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)

# Define the build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug")
endif()

# Set the project name
set(CMAKE_PROJECT_NAME STM32GUICAN)


include(FetchContent)

# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})

# Enable CMake support for ASM, C and C++ languages
enable_language(C CXX ASM)

# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})

# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)

# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined library search paths
)

# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user sources here
    Core/Src/st7789.c
)

# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined include paths
)

# Add project symbols (macros)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined symbols
)

# Remove wrong libob.a library dependency when using cpp files
list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_LIBRARIES ob)

# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
    stm32cubemx

    # Add user defined libraries
    freertos_kernel
    freertos_config
    lvgl
)

# Get and Include FreeRTOS

FetchContent_Declare( 
  freertos_kernel
  GIT_REPOSITORY https://github.com/FreeRTOS/FreeRTOS-Kernel.git
  GIT_TAG        V11.2.0 #Note: Best practice to use specific git-hash or tagged version
)

# Add the freertos_config for FreeRTOS-Kernel.
add_library(freertos_config INTERFACE)

target_include_directories(freertos_config SYSTEM
INTERFACE
    Core/Inc
)

target_compile_definitions(freertos_config
  INTERFACE
    projCOVERAGE_TEST=0
)

set( FREERTOS_HEAP "4" CACHE STRING "" FORCE)
# Select the native compile PORT
set( FREERTOS_PORT "GCC_ARM_CM0" CACHE STRING "" FORCE)

FetchContent_MakeAvailable( freertos_kernel )


# Get and Include LVGL

FetchContent_Declare(
    lvgl
    GIT_REPOSITORY https://github.com/lvgl/lvgl.git
    GIT_TAG v9.2.2
)
set(LV_CONF_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Core/Inc/lv_conf.h CACHE STRING "" FORCE) # Adjust path as needed


FetchContent_MakeAvailable(lvgl)

Apparently 8.3.11 doesnt have this issue because it doesn’t include lv_freertos.h and its nice FreeRTOS optimizations.

I think I found a solution. I had to add the freertos_kernel include and portable directories as global includes within cmake.

lv_freertos.h no longer says it cannot find FreeRTOS.h.

CMakeLists.txt

cmake_minimum_required(VERSION 3.22)

#
# This file is generated only once,
# and is not re-generated if converter is called multiple times.
#
# User is free to modify the file as much as necessary
#

# Setup compiler settings
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)

# Define the build type
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Debug")
endif()

# Set the project name
set(CMAKE_PROJECT_NAME STM32GUICAN)


include(FetchContent)

# Enable compile command to ease indexing with e.g. clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

# Core project settings
project(${CMAKE_PROJECT_NAME})
message("Build type: " ${CMAKE_BUILD_TYPE})

# Enable CMake support for ASM, C and C++ languages
enable_language(C CXX ASM)

# Create an executable object type
add_executable(${CMAKE_PROJECT_NAME})

# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)

# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined library search paths
)

# Add sources to executable
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user sources here
    Core/Src/st7789.c
)

# Add include paths
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined include paths
    ${freertos_kernel_SOURCE_DIR}/include

)

# Add project symbols (macros)
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined symbols
)

# Remove wrong libob.a library dependency when using cpp files
list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_LIBRARIES ob)

# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
    stm32cubemx

    # Add user defined libraries
    freertos_kernel
    freertos_config
    lvgl
)

# Get and Include FreeRTOS

FetchContent_Declare( 
  freertos_kernel
  GIT_REPOSITORY https://github.com/FreeRTOS/FreeRTOS-Kernel.git
  GIT_TAG        V11.2.0 #Note: Best practice to use specific git-hash or tagged version
)

# Add the freertos_config for FreeRTOS-Kernel.
add_library(freertos_config INTERFACE)

target_include_directories(freertos_config SYSTEM
INTERFACE
    Core/Inc
)

target_compile_definitions(freertos_config
  INTERFACE
    projCOVERAGE_TEST=0
)

set( FREERTOS_HEAP "4" CACHE STRING "" FORCE)
# Select the native compile PORT
set( FREERTOS_PORT "GCC_ARM_CM0" CACHE STRING "" FORCE)

FetchContent_MakeAvailable( freertos_kernel )

# Get and Include LVGL

FetchContent_Declare(
    lvgl
    GIT_REPOSITORY https://github.com/lvgl/lvgl.git
    GIT_TAG v9.2.2
)


set(LV_CONF_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Core/Inc/lv_conf.h CACHE STRING "" FORCE) # Adjust path as needed

# Add FreeRTOS include paths globally
include_directories(
    ${freertos_kernel_SOURCE_DIR}/include
    ${freertos_kernel_SOURCE_DIR}/portable/GCC/ARM_CM0
)

FetchContent_MakeAvailable( lvgl )


1 Like

I’ve found a solution! I’m now passing the FreeRTOS-Kernel library to the lvgl library in the top-level CMakeLists.txt by adding

target_link_libraries(lvgl PRIVATE
    FreeRTOS-Kernel
)

Which leads to the complete cmake file

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)

# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
if(WIN32)
    set(USERHOME $ENV{USERPROFILE})
else()
    set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.2.0)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.2.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
    include(${picoVscode})
endif()
# ====================================================================================
set(PICO_BOARD pico CACHE STRING "Board type")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

project(time-tracker-firmware C CXX ASM)

# pico SDK configuration
set(PICO_DEOPTIMIZED_DEBUG 1)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()


# Add executable. Default name is the project name, version 0.1
add_executable(${CMAKE_PROJECT_NAME} src/main.cpp)

pico_set_program_name(${CMAKE_PROJECT_NAME} "time-tracker-firmware")
pico_set_program_version(${CMAKE_PROJECT_NAME} "0.1")

# Modify the below lines to enable/disable output over UART/USB
pico_enable_stdio_uart(${CMAKE_PROJECT_NAME} 0)
pico_enable_stdio_usb(${CMAKE_PROJECT_NAME} 1)


set(FREERTOS_KERNEL_PATH ${CMAKE_HOME_DIRECTORY}/freertos/)
message("FreeRTOS loaded")

include(FreeRTOS_Kernel_import.cmake)

set(LV_BUILD_CONF_PATH ${CMAKE_HOME_DIRECTORY}/src/lv_conf.h CACHE STRING "")
set(CONFIG_LV_BUILD_EXAMPLES OFF CACHE BOOL "")
set(CONFIG_LV_BUILD_DEMOS OFF CACHE BOOL "")
add_subdirectory(lvgl)
message("lvgl loaded")

add_subdirectory(modules)
add_subdirectory(modules/ATRP/)



# Add the standard include files to the build
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
    ${CMAKE_CURRENT_LIST_DIR}
    
)

target_link_libraries(lvgl PRIVATE
    FreeRTOS-Kernel
)


target_link_libraries(${CMAKE_PROJECT_NAME}
    pico_stdlib
    hardware_spi
    hardware_i2c
    hardware_pwm
    hardware_uart

    my_modules
    FreeRTOS-Kernel
    FreeRTOS-Kernel-Heap1
    lvgl
)


pico_add_extra_outputs(${CMAKE_PROJECT_NAME})

It works really well now!