Adding Linux Platforms to Advanced Embedded Projects
This tutorial shows how to add Linux platforms to Advanced CMake-based embedded projects in order to target both barebone and Linux-based devices from the same project.
We will create a basic “Blinking LED” application for the STM32H747I-Discovery board and will then add a Linux platform for targeting Raspberry Pi using a cross-toolchain.
Before you begin, install VisualGDB 5.5 or later.
- Start Visual Studio and open the VisualGDB Embedded Project Wizard:
- Enter the name and location of the project:
- Select “Create a new project -> Embedded Application -> Advanced CMake“:
- On the next page of the wizard select the ARM toolchain and the target device. In this tutorial we will use the STM32H747XI device, however the steps shown here will also work with other devices as well:
- Select the basic “LEDBlink (HAL)” sample and click “Next”:
- Finally, pick debugging settings that work for your board and click “Finish” to generate the project:
- VisualGDB will generate a basic “Blinking LED” project for your target. The project will include the main() function, as well as the STM32-specific hardware drivers (HAL):
- To demonstrate very basic portability between STM32 and Linux, we will modify the main file to print some text using the printf() call. For optimal printf() performance on STM32, open VisualGDB Project Properties and reference the Fast Semihosting and Embedded Profiler framework:
- If you have not included the <stdio.h> file before, calling printf() will result in an error, however VisualGDB can automatically find the missing header:
- Once you click “Include”, VisualGDB will automatically include <stdio.h> from the main source file and the code will build:
- Update the main loop to print “Iteration <number>” messages and press F5 to test it out:
- Now we will show how to create a Linux platform to run the same code on Raspberry Pi. Open VisualGDB Project Properties and click the “Manage” button near the configuration list:
- In the platform/configuration manager, remove the MinSizeRel and RelWithDebInfo configurations, then click “Add a new Linux-based platform”:
- Rename the platform to “Linux” and make sure it has separate Project, Build and Debug settings. In order to allow distinguishing between Embedded and Linux platforms in CMakeLists.txt, add the USE_LINUX definition to it:
- Click “OK” to apply the changes. Go to the Project Settings page of VisualGDB Project Properties and select the machine where you would like to build and deploy the application. In this tutorial we will build the code on Windows using a cross-toolchain, however you can also build it directly on Raspberry Pi:
- Go to the CMake Build Settings page and select a toolchain compatible with Raspberry Pi. If you are building on the target, also update the CMake and Ninja commands to use valid paths on the Raspberry Pi itself:
- Go to the Debug Settings page and make sure the project is configured to debug $(TargetPath) or $(SelectedCMakeTarget):
- Press “OK” to apply the new settings. Now you can switch the current Visual Studio platform to “Linux”. This will very likely cause a configuration error, because the find_bsp() statement is only supported for embedded targets:
- You can work around it by making the LinuxPlatformDemo definition conditional:
12345if (USE_LINUX)add_executable(LinuxPlatformDemo LinuxPlatformDemo.cpp)else()find_bsp(...)endif()
- In order to build our application for Linux, we will need to make the STM32-specific code conditional. Open VS properties for the application target and add “USE_LINUX” to preprocessor definitions:
- Make sure the target_compile_definitions() statement adding it ends up inside the if(USE_LINUX) condition. This ensures that the macro will only be defined when using the “Linux” platform defining USE_LINUX on CMake level:
- Now you can wrap STM32-specific code with #ifdef USE_LINUX and the code will build:
- Switching back to the “VisualGDB” platform will build the code for the STM32 target again:
You can improve the code reuse between Linux and barebone targets by refactoring platform-specific code into a separate abstraction layer, and using the abstractions from the main code. See our tutorial on multi-target embedded projects for more details.