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>
95 lines
4.6 KiB
CMake
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()
|