# ============================================================================ # 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) # - hardware_dma: DMA controller API (used by MCU_UART non-blocking TX) target_link_libraries(${target} PRIVATE pico_stdlib hardware_uart hardware_dma ) # 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()