TouchGFX simulator development in Visual Studio Code with CMake
Likely you have heard before about Visual Studio Code, shortly vscode (or even vsc), a powerful and extension-based text editor. Being open source and with support for many languages (through extensions) developers simply love it. On the other hand, TouchGFX is one of the most advanced graphics stacks for embedded systems. It is FREE for any STM32 application, let it be simply UI with no touch or more sophisticated GUI with 800×480 pixels (or more) with mobile-like UI design.
- TouchGFX graphic library for cutting-edge graphics library
- TouchGFX documentation
- STM32 – the leading ARM Cortex-M based portfolio on the market
- STMicroelectronics graphics MOOC – Youtube tutorials for getting started
- Github repository with always-up-to-date code related to this tutorial
A TouchGFX, being optimized for embedded systems, comes with Windows based TouchGFX-Designer tool, including WYSIWYG view and simulator. Simulator based application is compiled (usually, but not limited to) using GCC for windows. GUI library uses MVP development pattern, strongly separating data source (model) from display part (presenter, view). Keeping that in mind, a data source (Model class) is in charge to provide relevant data for the UI and interacts between UI and the rest of application logic.
Click here to explore MVP application pattern from TouchGFX docs 4.20.
This tutorial is focused on development of the GUI in the simulator. That is, a data source (Model) will provide random, simulation data, that will act as real sensor information.
New application with TouchGFX
I will start by simple application for STM32H735G-DK, that was the first board available in my hands.
- Download and install STM32CubeMX
- Download X-CUBE-TOUCHGFX from the embedded libraries section and install TouchGFX designer tool (It is available in the default STM32Cube repository, typically C:/users/username/STM32Cube/Repository/Packs/…)
- Open TouchGFX designer, start new project and select STM32H735G-DK board
- New empty project will start – you can add your widgets. Here is the view of my view, with one text, slider and dynamic graph.
- . After you are done with widget design, you can generate the UI code. You can use 3 buttons in the bottom right corner of the screen
- Left: Generate code and assets: It will generate the code of the UI and run the STM32CubeMX to generate part for microcontroller
- Middle: Run simulator – click this to generate the code and immediately run the simulator
- Right: Run on the target. It generates the code, compiles with target compiler (ARM none eabi GCC) and loads the firmware to the board.
- Generate the code and go to project folder to see the structure, that should look similar to the one from picture below (actually without cmake folder and CMakeLists.txt file – this is the part to be done on our side).
Project is now generated and simulator can run. There is a project structure available, under simulator/gcc and simulator/msvs (for Visual Studio, that is not free for corporations – community version exists tho).
TouchGFX, thanks to C++, significantly relies on class inheritance feature. We will not go into details in this tutorial. Important to keep in mind is that you should never modify any code, except the one in gui folder. The rest is always generated if you go back to TouchGFX designer and update/modify the widgets design. Tutorial assumes you are aware of this. If not, please read the TouchGFX user manual.
Prerequisities
As we will be using vscode and CMake, we shall ensure that
- VScode is installed, with extension pack ms-vscode.cpptools-extension-pack (you can download extension pack from inside vscode)
- CMake is installed on your computer and available through console. cmake –version must return valid reply. You can download it from official website or through MSYS2’s package manager (mingw-w64-x86_64-cmake).
- Ninja build system is installed and available through console. ninja –version must return valid reply. You can download it from official website or through MSYS2’s package manager (mingw-w64-x86_64-ninja).
- GCC toolchain for Windows is available
- TouchGFX installation comes with 32-bit windows GCC compiler, suitable for building the simulator. By default, it is available in c:\TouchGFX\4.20.0\env\MinGW\bin\ (depends on the TGFX version). Ensure it is part of environmental paths. You should get valid reply to i686-w64-mingw32-gcc –version command from cmd or powershell
- Alternatively, you can download compiler through MSYS2 pacman tool. As I am using GCC for other applications already, I am using this option instead
Open vscode with TouchGFX folder as our root.
We will create new files and folders. Content of the files is available below, but always-up-to-date project will be available in my Github. You can always look there, in case of future changes.
- CMakeLists.txt in the root of the folder
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128cmake_minimum_required(VERSION 3.22)# Setup project if this is top-levelproject(TouchGFX)enable_language(C CXX ASM)# Root TouchGFX folder - depends on where CMakeLists.txt is locatedset(root_tgfx_dir ${CMAKE_CURRENT_LIST_DIR})# Setup all necessary include directories# These are used by compiler to search for C or C++ headersset(touchgfx_include_DIRS# Application includes${root_tgfx_dir}/gui/include${root_tgfx_dir}/generated/gui_generated/include${root_tgfx_dir}/generated/images/include${root_tgfx_dir}/generated/bitmaps/include${root_tgfx_dir}/generated/fonts/include${root_tgfx_dir}/generated/texts/include${root_tgfx_dir}/generated/videos/include# When running simulator but project started as STM32 project${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/include${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/include/common${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/common/include# When running simulator and project started as simulator project${root_tgfx_dir}/touchgfx/framework/include${root_tgfx_dir}/touchgfx/framework/include/common${root_tgfx_dir}/touchgfx/framework/common/include${root_tgfx_dir}/generated/simulator/include)# Search all source files in to be part of the build# Use the GLOB to scan everything in the gui and generated folders# These are essential for generic TouchGFX buildfile(GLOB_RECURSE graphics_core_generated_SRCS${root_tgfx_dir}/generated/*.cpp${root_tgfx_dir}/gui/*.cpp${root_tgfx_dir}/gui/*.c)# Add simulator files and append to current sources# Simulator files are only needed for simulation build (main file, etc)file(GLOB_RECURSE glob_tmp ${root_tgfx_dir}/simulator/*.cpp)set(graphics_core_generated_SRCS ${graphics_core_generated_SRCS} ${glob_tmp})# Add platform based source files and include them in list of files# This is used for Win32 buildfile(GLOB_RECURSE glob_tmp# When running simulator but project started as STM32 project${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/source/platform/*.cpp# When running simulator and project started as simulator project${root_tgfx_dir}/touchgfx/framework/source/platform/*.cpp)set(graphics_core_generated_SRCS ${graphics_core_generated_SRCS} ${glob_tmp})# Prepare include paths for the simulatorset(touchgfx_include_DIRS ${touchgfx_include_DIRS}${root_tgfx_dir}/simulator# When running simulator but project started as STM32 project${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/mvp/include${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/include/platform/hal/simulator/sdl2${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/include/platform/hal/simulator/sdl2/vendor${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/include/platform/hal/simulator/sdl2/vendor/win32${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/include/platform/hal/simulator/sdl2/3rdparty/sdl/include/win32${root_tgfx_dir}/../Middlewares/ST/touchgfx/framework/include${root_tgfx_dir}/../Middlewares/ST/touchgfx/3rdparty/libjpeg/include# When running simulator and project started as simulator project${root_tgfx_dir}/touchgfx/framework/mvp/include${root_tgfx_dir}/touchgfx/framework/include/platform/hal/simulator/sdl2${root_tgfx_dir}/touchgfx/framework/include/platform/hal/simulator/sdl2/vendor${root_tgfx_dir}/touchgfx/framework/include/platform/hal/simulator/sdl2/vendor/win32${root_tgfx_dir}/touchgfx/framework/include/platform/hal/simulator/sdl2/3rdparty/sdl/include/win32${root_tgfx_dir}/touchgfx/framework/include${root_tgfx_dir}/touchgfx/3rdparty/libjpeg/include)# Prepare linker directories - for the simulatorset(link_dirs# When running simulator but project started as STM32 project${root_tgfx_dir}/../Middlewares/ST/touchgfx/lib/win/mingw32${root_tgfx_dir}/../Middlewares/ST/touchgfx/lib/sdl2/win32${root_tgfx_dir}/../Middlewares/ST/touchgfx/3rdparty/libjpeg/lib/win32# When running simulator and project started as simulator project${root_tgfx_dir}/touchgfx/lib/win/mingw32${root_tgfx_dir}/touchgfx/lib/sdl2/win32${root_tgfx_dir}/touchgfx/3rdparty/libjpeg/lib/win32)link_directories(${CMAKE_PROJECT_NAME} ${link_dirs})# Build executable with sources, include paths and compile definitionsadd_executable(${CMAKE_PROJECT_NAME})target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${graphics_core_generated_SRCS})target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${touchgfx_include_DIRS})target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE WIN32 NDEBUG _CONSOLE SIMULATOR)# target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC -Wall -Wextra -Wpedantic)# Add linked librariestarget_link_libraries(${CMAKE_PROJECT_NAME}:libtouchgfx.a:libSDL2.a:libSDL2_image.a:libjpeg-8.a)# Copy DLL files to the project pathfile(GLOB_RECURSE dll_files# When running simulator but project started as STM32 project${root_tgfx_dir}/*.dll# When running simulator and project started as simulator project${root_tgfx_dir}/../Middlewares/ST/*.dll)# Copy all DLL files to build output folder# Otherwise we won't be able to run the simulationforeach(dll_file ${dll_files})add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${dll_file} $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>)endforeach() - cmake/i686-w64-mingw32-gcc.cmake with content
1234567set(CMAKE_SYSTEM_NAME Windows)# Some default GCC settingsset(CMAKE_C_COMPILER i686-w64-mingw32-gcc)set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - CMakePresets.json with content
123456789101112131415161718192021{"version": 3,"configurePresets": [{"name": "default","generator": "Ninja","toolchainFile": "${sourceDir}/cmake/i686-w64-mingw32-gcc.cmake","binaryDir": "${sourceDir}/build/${presetName}","cacheVariables": {"CMAKE_EXPORT_COMPILE_COMMANDS": "ON","CMAKE_BUILD_TYPE": "Debug"}}],"buildPresets": [{"name": "default","configurePreset": "default"}]}
This is enough and allows us to build the project with CMake extension from within vscode context. In the light-blue bottom line, select configuration preset (default) and click Build button next to it. Build configuration followed by actual build should start.
Let’s create some more files, that will help you with fast build, run, clean, debug and C/C++ intellisense.
- .vscode/c_cpp_properties.json file for C/C++ intellisense. Build macros, include paths (for go to definition search) and other key parameters are provided through CMake plugin.
1234567891011121314{"version": 4,"configurations": [{/** Full configuration is provided by CMake plugin for vscode,* that shall be installed by user*/"name": "Win32","intelliSenseMode": "${default}","configurationProvider": "ms-vscode.cmake-tools"}]} - .vscode/launch.json for easier debugging
12345678910111213141516{"version": "0.2.0","configurations": [{/* GDB must in be in the PATH environment */"name": "(Windows) Launch","type": "cppdbg","request": "launch","program": "${command:cmake.launchTargetPath}","args": [],"stopAtEntry": false,"cwd": "${fileDirname}","environment": []}]} - .vscode/tasks.json for quick access to build (CTRL+SHIFT+B), project clean and application run (without debug mode)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748{// See https://go.microsoft.com/fwlink/?LinkId=733558// for the documentation about the tasks.json format"version": "2.0.0","tasks": [{"type": "cppbuild","label": "Build project","command": "cmake","args": ["--build", "${command:cmake.buildDirectory}", "-j", "8"],"options": {"cwd": "${workspaceFolder}"},"problemMatcher": ["$gcc"],"group": {"kind": "build","isDefault": true}},{"type": "shell","label": "Re-build project","command": "cmake","args": ["--build", "${command:cmake.buildDirectory}", "--clean-first", "-v", "-j", "8"],"options": {"cwd": "${workspaceFolder}"},"problemMatcher": ["$gcc"],},{"type": "shell","label": "Clean project","command": "cmake","args": ["--build", "${command:cmake.buildDirectory}", "--target", "clean"],"options": {"cwd": "${workspaceFolder}"},"problemMatcher": []},{"type": "shell","label": "Run application","command": "${command:cmake.launchTargetPath}","args": [],"problemMatcher": [],},]}
With all the files added, new structure shall look similar to
Good to know
- All the files we created in this tutorial are generic, can be simply reused for any other TouchGFX project for simulation.
- If you regenerate project from TouchGFX designer and add new widgets and resources, you should rebuild cmake to parse newly added files and add them to build, or you will face build errors
- Thanks to CMake, intellisense will scan files and include paths as described in CMake. CMakeLists.txt is therefore unique entry point of project settings, that are later used in vscode.
- Use F5 to enter debug and set breakpoint at your desired instruction
- Use CMake plugin (on the left side of vscode window) to browse all files being part of the build
- Use Model class to feed simulation data to UI -> through ModelListener interface – read TouchGFX user manual for more information
- Simulator conditional compilation is available through #ifdef SIMULATOR C/C++ option
- Open Github and access the code
Recent comments