2023-09-27 12:46 PM
Its an age old battle. Manufactuer eclipse-based IDE vs CMake. Chip manufactuers make an IDE available to simplify the software development process, and get users started quickly. Expericed SW engineers appreciate an IDE, particularly for debugging, but prefer to have more control over the build process with CMake. Usually CMake based builds are command-line driven, and lend themselves well to multiple build targets, tool integration, static analysis, unit test, CI, DevOps, Jenkins/buildbot, etc.
In order to setup my own CMake build that would run on the STM32F7 eval board, I took these steps.
When creating the IDE project, I chose my eval board from the wizard menu. When the project was created, it populated the source folder with a number of generated source files (see image below). I copied these files into my own project folder.
In my top level folder, i added x-cube-azrtos-f7 as a git submodule. This allowed access to the HAL source code and Azure RTOS (ThreadX, etc.).
This is the top-level cmake project file, please note building Azure RTOS is out of scope for this post. More on that later! The examples subdirectory has the application level code, 'hello_world' in this case. The hal subdirectory only has a single CMakeLists.txt file that references the HAL source code in the x-cube-azrtos-f7 folder.
cmake_minimum_required(VERSION 3.20)
project(my_project)
# Example projects for eval boards
add_subdirectory(examples)
# STM32F7 HAL (Hardware Abstraction Layer) library
add_subdirectory(hal)
# ThreadX library
set(THREADX_ARCH cortex_m7)
set(THREADX_TOOLCHAIN gnu)
# FIXME doesn't compile yet
# add_subdirectory(x-cube-azrtos-f7/Middlewares/ST/threadx)
The STM32 Cmake project created in the IDE generated a toolchain cmake file. I then renamed that as arm-gcc.cmake and put in my own project.
# CMake toolchain definition for STM32CubeIDE
set (CMAKE_SYSTEM_PROCESSOR "arm" CACHE STRING "")
set (CMAKE_SYSTEM_NAME "Generic" CACHE STRING "")
###################### CONSTANTS ######################################
set (PROJECT_TYPE_EXECUTABLE "exe")
set (PROJECT_TYPE_STATIC_LIBRARY "static-lib")
set (MCPU_CORTEX_M0 "-mcpu=cortex-m0")
set (MCPU_CORTEX_M0PLUS "-mcpu=cortex-m0plus")
set (MCPU_CORTEX_M3 "-mcpu=cortex-m3")
set (MCPU_CORTEX_M4 "-mcpu=cortex-m4")
set (MCPU_CORTEX_M7 "-mcpu=cortex-m7")
set (MCPU_CORTEX_M33 "-mcpu=cortex-m33")
set (MFPU_FPV4_SP_D16 "-mfpu=fpv4-sp-d16")
set (MFPU_FPV5_D16 "-mfpu=fpv5-d16")
set (RUNTIME_LIBRARY_REDUCED_C "--specs=nano.specs")
set (RUNTIME_LIBRARY_STD_C "")
set (RUNTIME_LIBRARY_SYSCALLS_MINIMAL "--specs=nosys.specs")
set (RUNTIME_LIBRARY_SYSCALLS_NONE "")
set (MFLOAT_ABI_SOFTWARE "-mfloat-abi=soft")
set (MFLOAT_ABI_HARDWARE "-mfloat-abi=hard")
set (MFLOAT_ABI_MIX "-mfloat-abi=softfp")
#######################################################################
# Skip link step during toolchain validation.
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# Specify toolchain.
# NOTE: When building from inside STM32CubeIDE the location of the toolchain
# is resolved by the "MCU Toolchain" project setting (via PATH).
set(TOOLCHAIN_PREFIX "arm-none-eabi-")
set(CMAKE_C_COMPILER "${TOOLCHAIN_PREFIX}gcc")
set(CMAKE_ASM_COMPILER "${TOOLCHAIN_PREFIX}gcc")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PREFIX}g++")
set(CMAKE_AR "${TOOLCHAIN_PREFIX}ar")
set(CMAKE_LINKER "{TOOLCHAIN_PREFIX}ld")
set(CMAKE_OBJCOPY "${TOOLCHAIN_PREFIX}objcopy")
set(CMAKE_RANLIB "${TOOLCHAIN_PREFIX}ranlib")
set(CMAKE_SIZE "${TOOLCHAIN_PREFIX}size")
set(CMAKE_STRIP "${TOOLCHAIN_PREFIX}ld")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
Creating cmake configuration for the HAL library was probably the most interesting part of this. When the IDE creates a project, it simply copies the hal source files it needs to the project folder. I'm not fond of having copies in two places, why not build directly from the source location (e.g. x-cube-azrtos-f7 ) ? Since we didn't want to put a CMakeLists.txt file in the x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver where the HAL source is, we created a separate folder with just a CMakeLists.txt that references the source code folder. <top level>/hal/CMakeLists.txt
The list of source files was chosen based on the list of files found in the original IDE project. The library is defined as a INTERFACE type (rather than STATIC or SHARED) because the HAL source includes the application specific generated configuration file stm32f7xx_hal_conf.h. It ensures the library source code it built along with the application, not as completely separate library.
project(stm32f7_hal C ASM)
# Hardware Abstration Layer for STM32F7 is part of ST x-cube-azrtos-f7 distribution
set(STM32F7_HAL_SOURCE_DIR ${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src)
set(STM32F7_HAL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Inc)
set(STM32F7_CMSIS_INCLUDE_DIR
${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/CMSIS/Include
${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Drivers/CMSIS/Device/ST/STM32F7xx/Include )
message(STATUS "STM32F7 HAL source file path: ${STM32F7_HAL_SOURCE_DIR}")
message(STATUS "STM32F7 HAL include file path: ${STM32F7_HAL_INCLUDE_DIR}")
set (SOURCE_FILES
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_adc_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_adc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_cortex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_dma_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_dma.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_dsi.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_exti.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_flash_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_flash.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_gpio.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_i2c_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_i2c.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_mmc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_nor.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pcd_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pcd.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pwr_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_pwr.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_qspi.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_rcc_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_rcc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sai_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sai.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sdram.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_spdifrx.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_sram.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_tim_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_tim.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_uart_ex.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal_uart.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_hal.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_ll_fmc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_ll_sdmmc.c
${STM32F7_HAL_SOURCE_DIR}/stm32f7xx_ll_usb.c
)
# debug only
# message(STATUS "STM32F7 HAL source files: ${SOURCE_FILES}")
# The HAL source code includes a configuration header file from the application, so the library can not be built
# in isolation.
# Defining the library as type INTERFACE allows the source to be built with the application that uses the library.
add_library(${PROJECT_NAME} INTERFACE )
target_sources(${PROJECT_NAME} INTERFACE ${SOURCE_FILES})
target_include_directories (${PROJECT_NAME} INTERFACE ${STM32F7_HAL_INCLUDE_DIR} ${STM32F7_CMSIS_INCLUDE_DIR} )
The hello world application is in the <proj dir>/examples/hello_world folder. It includes its own CMakeLists.txt with the source code copied from the generated IDE project.
¤ CMakeLists.txt
¤ README.md
¤
+---Inc
¤ main.h
¤ stm32f7xx_hal_conf.h
¤ stm32f7xx_it.h
¤
+---Src
¤ main.c
¤ stm32f7xx_hal_msp.c
¤ stm32f7xx_it.c
¤ syscalls.c
¤ sysmem.c
¤ system_stm32f7xx.c
¤
+---Startup
startup_stm32f769nihx.s
The hello world CMakeLists.txt has the much of the same cmake code copied from the STM32 CMake Project top level. Notice the linker (.ld) script file is included from the top level/link folder. It was moved there so it can be used by other cmake subprojects with in the same folder structure. The hal library is included using target_link_libraries identifying stm327_hal, the name of the hal project.
###################### VARIABLES ######################################
set (HELLO_WORLD_PROJECT_NAME "stm32f769i-eval_hello-world")
set (PROJECT_TYPE "exe")
set (LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/link/STM32F769NIHX_FLASH.ld")
set (MCPU "-mcpu=Cortex-M7")
set (MFPU "-mfpu=fpv5-d16")
set (MFLOAT_ABI "")
set (RUNTIME_LIBRARY "--specs=nano.specs")
set (RUNTIME_LIBRARY_SYSCALLS "--specs=nosys.specs")
set (PROJECT_SOURCES
# LIST SOURCE FILES HERE
Src/main.c
Src/stm32f7xx_hal_msp.c
Src/stm32f7xx_it.c
Src/syscalls.c
Src/sysmem.c
Src/system_stm32f7xx.c
Startup/startup_stm32f769nihx.s
)
set (PROJECT_DEFINES
# LIST COMPILER DEFINITIONS HERE
USE_HAL_DRIVER
STM32F769xx
)
set (PROJECT_INCLUDES
# LIST INCLUDE DIRECTORIES HERE
Inc/
)
################## PROJECT SETUP ######################################
project(stm32f769i-eval_hello-world C ASM)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
target_link_libraries (${PROJECT_NAME} stm32f7_hal )
#add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${CMAKE_PROJECT_NAME}>)
#if (${PROJECT_TYPE} MATCHES ${PROJECT_TYPE_EXECUTABLE})
#lseif (${PROJECT_TYPE} MATCHES ${PROJECT_TYPE_STATIC_LIBRARY})
# add_library(${PROJECT_NAME} ${PROJECT_SOURCES})
#endif()
add_compile_definitions (${PROJECT_DEFINES})
include_directories (${PROJECT_INCLUDES})
set (CMAKE_EXECUTABLE_SUFFIX ".elf")
set (CMAKE_STATIC_LIBRARY_SUFFIX ".a")
set (CMAKE_C_FLAGS "${MCPU} -std=gnu11 ${MFPU} ${MFLOAT_ABI} ${RUNTIME_LIBRARY} -mthumb -Wall -Werror -Wfatal-errors")
set (CMAKE_EXE_LINKER_FLAGS "-T${LINKER_SCRIPT} ${RUNTIME_LIBRARY_SYSCALLS} -Wl,-Map=test.map -Wl,--gc-sections -static -Wl,--start-group -lc -lm -Wl,--end-group")
set (CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp")
Finally when running the cmake configure and build commands, here's the command and output.
$ cmake -DCMAKE_TOOLCHAIN_FILE=arm-gcc.cmake -S ./ -B Debug -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug
-- The C compiler identification is GNU 11.3.1
-- The CXX compiler identification is GNU 11.3.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/ST/STM32CubeIDE_1.13.1/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.11.3.rel1.win32_1.1.0.202305231506/tools/bin/arm-none-eabi-gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/ST/STM32CubeIDE_1.13.1/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.11.3.rel1.win32_1.1.0.202305231506/tools/bin/arm-none-eabi-g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- The ASM compiler identification is GNU
-- Found assembler: C:/ST/STM32CubeIDE_1.13.1/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.11.3.rel1.win32_1.1.0.202305231506/tools/bin/arm-none-eabi-gcc.exe
-- STM32F7 HAL source file path: C:/Users/jerry.morrow/git/<proj>/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src
-- STM32F7 HAL include file path: C:/Users/jerry.morrow/git/<proj>/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Inc
-- Configuring done (8.1s)
-- Generating done (0.0s)
-- Build files have been written to: C:/Users/jerry.morrow/git/<proj>/Debug
This is the build/compile output.
$ cmake --build Debug/
[ 2%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/main.c.obj
[ 4%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/stm32f7xx_hal_msp.c.obj
[ 7%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/stm32f7xx_it.c.obj
[ 9%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/syscalls.c.obj
[ 11%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/sysmem.c.obj
[ 14%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Src/system_stm32f7xx.c.obj
[ 16%] Building ASM object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/Startup/startup_stm32f769nihx.s.obj
[ 19%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc_ex.c.obj
[ 21%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_adc.c.obj
....
....
[ 90%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal.c.obj
[ 92%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_fmc.c.obj
[ 95%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_sdmmc.c.obj
[ 97%] Building C object examples/hello_world/CMakeFiles/stm32f769i-eval_hello-world.dir/__/__/x-cube-azrtos-f7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_ll_usb.c.obj
[100%] Linking C executable stm32f769i-eval_hello-world.elf
[100%] Built target stm32f769i-eval_hello-world
Once you have the .elf file, you can open in IDE and it will automagically load the board and allow you step through code from the top of main function!
ENJOY CMAKE LOVERS!!
2023-09-27 12:50 PM
Hello folks, i'm mentioning you here since you've posted about cmake in the past. Hope you enjoy my post above, don't hesitate to ask questions.
2023-10-01 06:51 AM - edited 2023-10-01 10:36 AM
Fantastic effort @jerry_sandc!
We, like most modern embedded dev shops, have migrated to CMake years ago and we are fully invested in using it for cross-platform, cross-compiler projects all working together in the same CMake project. As of yet, we have not found anything yet that is as flexible and practical and so widely supported by IDEs that do fully integrate with CMake (we will continue to evaluate other build systems as we see traction pick up).
We tend to create much more elaborate CMake configurations because of what we need from it. I re-evaluated STM32CubeIDE a few months back with the 'new' CMake support and it is not a good fit at all for what we do (it's nothing more than a quick hack and not full CMake <=> IDE integration which many of the established IDEs have had now for quite a while - ask ST about the exact truth of that statement). Still on my todo list is to report back to ST on my findings, but quite frankly, I cannot see how it could be 'fixed' at all.
We split our code into many many small libraries for testability, which are statically linked together with the target. This caused no end of pain with the current STM32CubeIDE CMake system having the libraries in one project and targets in other projects. We multi-target to produce different firmware for different board configurations. The lack of seamless x86 unit-testing support in STM32CubeIDE was yet another nail (we unit-test the application logic and higher-level drivers host-side with GoogleTest and the like to greatly speed up the development process).
After trying everything we could think of for a few days (reading STM32CubeIDE docs, looking at .cproject files, attempting tweaks, hacks, etc), we went with the fallback setup, using CMake alongside standard STM32 STM32CubeIDE projects (keeping both sides in sync manually)...
We attempted this on a very small project for new client. It was an absolute maintenance nightmare. Almost every time someone raised pull request they had merge conflicts on one or more of the 'vast cryptic xml containing magic numbers' .cproject files, the the STM32 project include folders and source folders partially out of sync with the cmake side. These constant 3-way merge conflicts which were inflicted on us were many and painful, wasting client billed hours.
I cannot see how the current version of STM32CubeIDE (without CMake) could ever work for any team practising modern source-control standards. I'm not sure this is an Eclipse-thing, but it is best avoided by any sane team! (and we thought IAR was difficult to keep in sync with cmake...) For a one-man-band, sure it will work wonders, sunbeams and rainbows. Add just one more person to the mix and the constant staring at conflicts and praying start to begin.
Most of these issues could have been, and still might be resolved by allowing use of two the very well-established marketplace CMake extensions:
https://marketplace.eclipse.org/content/cmake4eclipse
https://marketplace.eclipse.org/content/cmake-editor
Those extensions worked well enough a few years back for me when using CMake with yet-another-vendor-packaged-eclipse-IDE. Would this still work today? I have no idea, and definitely no enthusiasm to try that again. I only know that whatever Atollic did with TrueSTUDIO to block the cmake4eclipse extension UI from appearing in the project settings, it was still not blocked last I tried.
Our final decision...
We will stick to using CMake with our IDEs of comfort which have deep CMake integration (CLion, VS Code, vim, etc) and which happily do everything including basic debugging (perfectly fine for the bulk of the code). On the odd need try use STM32CubeIDE with the target .elf file for any deeper debugging insight - if we can make that work without endless teeth-gnashing and hair-pulling. Or for some of us, just stick to using SEGGER Ozone or a GDB terminal and avoid the possibility of wasting allocated client time.
Please accept my sincere apologies for the rant, but, we've wasted many man-weeks over the years desperately trying to get STM32CubeIDE to work with CMake (STM32CubeIDE has fantastic debugging insights for free, sadly the only real drawcard for us).
If anyone is reading this and wants to try CMake with STM32CubeIDE, I can only suggest to do it in anger with a non-trivial project, and you might possibly come to the same conclusions as us if you've been using CMake for more than a year on serious commercial work.
Matthew.
2023-10-04 11:59 AM
Keep in mind I *was* able to open the elf file produced by cmake build in the IDE, and have all the debugging available. I just couldn't look through the source code to set breakpoints.
Qt moved to use CMake as its project file starting with Qt6. I did project with Qt6, and it was build heaven! I wish Eclipse would read the writing on the wall, or maybe someone will come along with new cmake-based IDE framework. :grinning_face:
2023-10-04 04:50 PM
One of the long term frustration with ST's tools is the lack of "Dog Fooding" where the devs are forced to use their own tools/processes in real world use cases. ie actual projects and multiple contributors. If this were the case things like CMake, Source Code Control, merging / conflict reduction would be addressed / solved with practical rather than hacked soltions.
2023-11-16 07:58 AM
Just an update on this, realizing its better to put the CMAKE_C_FLAGS and CMAKE_ASM_FLAGS in the arm-gcc.cmake toolchain file.
2023-12-06 04:46 AM
Another heads up.. I'm working on project with Azure RTOS, to include threadx, netxduo, and filex. I thought just build these as library at the top level CMake, but realized they each need a 'user file' when they are compiled. This is how I handled. Please note that x-cube-azrtos-f7 is included as submodule at the top level, and my particular project is in a subfolder. Luckily Azure RTOS uses cmake! :)
# must set these variables used by threadx, netxduo, and filex cmake
set(TX_USER_FILE ${CMAKE_CURRENT_LIST_DIR}/core/Inc/tx_user.h)
set(NX_USER_FILE ${CMAKE_CURRENT_LIST_DIR}/NetXDuo/App/nx_user.h)
set(FX_USER_FILE ${CMAKE_CURRENT_LIST_DIR}/FileX/App/fx_user.h)
# ThreadX library
set(THREADX_ARCH cortex_m7)
set(THREADX_TOOLCHAIN gnu)
# specify out of tree source build so each library can pickup the user variables (i.e. *_USER_FILE)
add_subdirectory(${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Middlewares/ST/threadx example_app_threadx)
add_subdirectory(${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Middlewares/ST/netxduo example_app_netxduo )
add_subdirectory(${CMAKE_SOURCE_DIR}/x-cube-azrtos-f7/Middlewares/ST/filex example_app_filex)