pi_pico/cmake/cmake_config/mcu_config.cmake
Mohamed Salem 3687e48684 Add complete firmware stack with USB-CDC proof of life
Dockerized build system (Dockerfile, docker-compose, build.sh) with
Pico SDK cross-compilation. Modular CMake split into project_config,
mcu_config, and sources_config under cmake/. Component architecture
following inc/prg/cfg convention: STD_TYPES, MCU_USB, HAL_COM,
APP_CLSW, SYS_ECU. Full call chain SYS_ECU -> APP_CLSW -> HAL_COM
-> MCU_USB verified end-to-end on RP2040-Zero hardware over USB-CDC.
Includes flash.sh for automated .uf2 flashing on macOS and
devcontainer config for VS Code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 18:23:24 +02:00

95 lines
4.6 KiB
CMake

# ============================================================================
# mcu_config.cmake
# ----------------------------------------------------------------------------
# Encapsulates all Raspberry Pi Pico / RP2040 specific configuration.
# Depends on project_config.cmake having been included first (reads
# PROJECT_C_STANDARD).
#
# Because CMake forces a strict order (SDK bootstrap before project(),
# pico_sdk_init() after project(), linking after add_executable), this file
# exposes three macros/functions that the top-level CMakeLists.txt calls
# at the correct points in the build flow:
#
# mcu_init() - includes pico_sdk_import.cmake (pre-project)
# mcu_sdk_config() - runs pico_sdk_init() (post-project)
# mcu_link_target(target) - links SDK libs, configures stdio, emits UF2
# ============================================================================
# ----------------------------------------------------------------------------
# Step 1/3 - called BEFORE project() in the top-level CMakeLists.txt.
# Pulls in the Pico SDK bootstrap file so the ARM cross-compile toolchain
# (arm-none-eabi-gcc) is configured before CMake enables the project
# languages. Calling project() before this runs would cause CMake to detect
# the host compiler instead and produce a broken build.
# ----------------------------------------------------------------------------
macro(mcu_init)
# pico_sdk_import.cmake lives alongside the top-level CMakeLists.txt
# inside the cmake/ folder, so CMAKE_SOURCE_DIR (which points at cmake/
# when that file is the one being processed) is the right base path.
include(${CMAKE_SOURCE_DIR}/pico_sdk_import.cmake)
endmacro()
# ----------------------------------------------------------------------------
# Step 2/3 - called AFTER project() in the top-level CMakeLists.txt.
# Applies the C standard chosen in project_config.cmake and registers every
# Pico SDK library as a CMake target (pico_stdlib, hardware_uart,
# hardware_gpio, ...) so we can later pick only the ones we need.
# Note: pico_sdk_init() does NOT pull any libraries into the final binary
# by itself - it only makes them available as link targets.
# ----------------------------------------------------------------------------
macro(mcu_sdk_config)
set(CMAKE_C_STANDARD ${PROJECT_C_STANDARD})
set(CMAKE_C_STANDARD_REQUIRED ON)
pico_sdk_init()
endmacro()
# ----------------------------------------------------------------------------
# Step 3/3 - called AFTER add_executable() in the top-level CMakeLists.txt.
# Links only the Pico SDK libraries we actually use, routes stdio over
# USB-CDC so printf output is visible on the host serial monitor without
# any extra hardware, and emits the .uf2 file used for drag-and-drop
# flashing onto the Pico's mass-storage bootloader.
#
# Parameters:
# target - name of the executable target created with add_executable()
# ----------------------------------------------------------------------------
function(mcu_link_target target)
# Pick only the libraries we need:
# - pico_stdlib: core runtime, GPIO, clocks, basic init
# - hardware_uart: UART peripheral API (used by the MCU_UART driver)
target_link_libraries(${target} PRIVATE
pico_stdlib
hardware_uart
)
# Route stdio over USB-CDC: the Pico will appear as a virtual serial
# port on the host when plugged in, so printf/getchar are visible in
# any serial monitor without needing a USB-to-UART adapter.
# 1 = enabled, 0 = disabled.
pico_enable_stdio_usb(${target} 1)
pico_enable_stdio_uart(${target} 0)
# Ask the SDK to generate the .uf2 (plus .hex, .bin, .map) alongside
# the .elf so the firmware can be flashed by dragging it onto the
# Pico's USB mass-storage device after holding BOOTSEL.
pico_add_extra_outputs(${target})
# Custom "flash" target: builds the firmware first (DEPENDS ensures
# the .uf2 is up to date), then runs flash.sh on the host to copy
# it to the Pico in BOOTSEL mode.
#
# Usage: cmake --build build --target flash
#
# NOTE: This target only works when cmake runs on the HOST macOS
# (not inside Docker), because it needs access to /Volumes/RPI-RP2.
# When running inside Docker, the target will fail with a clear error
# from flash.sh ("not found" or "/Volumes/RPI-RP2 not accessible").
add_custom_target(flash
COMMAND bash ${PROJECT_ROOT_DIR}/flash.sh
DEPENDS ${target}
WORKING_DIRECTORY ${PROJECT_ROOT_DIR}
COMMENT "Flashing firmware to Pico via USB mass storage"
VERBATIM
)
endfunction()