The Pico C/C++ SDK, and its examples, make extensive use of CMake. If you develop projects that use the same principles, you 'll be familiar with the CMake configuration file CMakeList.txt.
A simple project, that builds a single firmware and relies on the SDK API only, will have a straightforward CMake config file. If your project builds more than one deliverable, the file can get big. You may end up with (almost identical) repeating sections. In that case, a macro can help to keep this manageable. And to maintain integrity of the build when making changes later.
My project C++ library for ST Teseo GPS builds 3 different firmware binaries, and each of them has a UART and I2C version. I used a macro to keep the CMake configuration doable |
A CMake macro is very similar to the C parameterised macro. During the project configuration, each declaration that uses the macro, will be expanded. The values used will replace the macro parameters.
Here is my firmware build macro:
# macro to build firmware binary for the examples macro(__build_firmware example protocol) message("building firmware for example ${example}, protocol ${protocol}") add_executable(${CMAKE_PROJECT_NAME}_${example}_${protocol} ${CMAKE_CURRENT_SOURCE_DIR}/teseo_${example}.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gps_nmea_lib/nmea/nmea.cpp ) target_link_libraries(${CMAKE_PROJECT_NAME}_${example}_${protocol} ${CMAKE_PROJECT_NAME}_shared_${protocol} ) target_include_directories(${CMAKE_PROJECT_NAME}_${example}_${protocol} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/gps_nmea_lib/nmea ) # select the debug output (optional, not used by the GPS interface) pico_enable_stdio_uart(${CMAKE_PROJECT_NAME}_${example}_${protocol} 1) pico_enable_stdio_usb(${CMAKE_PROJECT_NAME}_${example}_${protocol} 0) pico_add_extra_outputs(${CMAKE_PROJECT_NAME}_${example}_${protocol} ) endmacro()
The parameters are example and protocol.
Here is the code to build one of the 3 firmware binaries, for both their UART and I2C variant:
# build teseo reply response example -------------------------------------- __build_firmware("reply_response" "uart") __build_firmware("reply_response" "i2c")
I repeated this for each of the examples. The outcome is that a build all nicely creates all binaries:
building firmware for example reply_response, protocol uart
building firmware for example reply_response, protocol i2c
building firmware for example with_nmea_parse, protocol uart
building firmware for example with_nmea_parse, protocol i2c
building firmware for example nmea_with_data_processing, protocol uart
building firmware for example nmea_with_data_processing, protocol i2c
-rw-r--r-- 1 runner docker 161280 Aug 26 16:31 build/pico_gps_teseo_reply_response_uart.uf2
-rw-r--r-- 1 runner docker 161280 Aug 26 16:31 build/pico_gps_teseo_reply_response_i2c.uf2
-rw-r--r-- 1 runner docker 198144 Aug 26 16:31 build/pico_gps_teseo_with_nmea_parse_uart.uf2
-rw-r--r-- 1 runner docker 198144 Aug 26 16:31 build/pico_gps_teseo_with_nmea_parse_i2c.uf2
-rw-r--r-- 1 runner docker 195584 Aug 26 16:32 build/pico_gps_teseo_nmea_with_data_processing_uart.uf2
-rw-r--r-- 1 runner docker 195584 Aug 26 16:32 build/pico_gps_teseo_nmea_with_data_processing_i2c.uf2
This is one example of how macros can help. Further reading: https://cmake.org/cmake/help/latest/command/macro.html.
Link to all posts.