STM32最小CLion开发环境

发布于:2025-06-05 ⋅ 阅读:(26) ⋅ 点赞:(0)

1 必须文件

ST 提供的头文件支持 MDK-ARM, GCC, IAR 3种编译器, 下面采用 GCC

编译器 Arm GNU Toolchain Downloads – Arm Developer安装包版
调试器服务端 OpenOCD
基础头文件仓库 STMicroelectronics/cmsis-core
F1头文件仓库 STMicroelectronics/cmsis-device-f1
F1头文件仓库 STMicroelectronics/stm32f1xx-hal-driver
外设 SVD 文件 stm32 svd

上述所有项目拉取/解压/解包后各自一个文件夹放在 stm32kits 中

2 工具链

新建 stm32kits/cmake/startup.bat, 内容如下

set CMAKE_TOOLCHAIN_FILE=%~dp0arm.toolchain.cmake

新建 stm32kits/cmake/arm.toolchain.cmake, 内容如下

set(CMAKE_SYSTEM_NAME               Generic)
set(CMAKE_SYSTEM_PROCESSOR          arm)

set(CMAKE_C_COMPILER_ID GNU)
set(CMAKE_CXX_COMPILER_ID GNU)

set(CMAKE_EXECUTABLE_SUFFIX_ASM     ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_C       ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_CXX     ".elf")

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)

set(STM32_KITS_DIR "${CMAKE_CURRENT_LIST_DIR}/..")
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_LIST_DIR}")

# MCU specific flags
set(TARGET_FLAGS "-mcpu=cortex-m3 ")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}")
set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -fdata-sections -ffunction-sections -Wl,--no-warn-rwx-segments")

set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_C_FLAGS_RELEASE "-Os -g0")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_CXX_FLAGS_RELEASE "-Os -g0")

set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics")

set(CMAKE_C_LINK_FLAGS "${TARGET_FLAGS}")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} --specs=nano.specs")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group -lc -lm -Wl,--end-group")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--print-memory-usage")

set(CMAKE_CXX_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group")

新建 stm32kits/cmake/FindSTM32Driver.cmake, 内容如下

function(stm32_parse_mcu_name MCU_NAME)
    string(TOUPPER "${MCU_NAME}" MCU_NAME_UPPER)
    if(NOT MCU_NAME_UPPER MATCHES "^STM32([C|F|G|H|L|M|N|U|W][A-Z0-9][A-Z0-9]?[A-Z0-9]?)([0-9][0-9])([A-Z])([0-9A-IZ])([BDG-KMPQTUVY]?)([6A7B3CD]?)$")
        set(STM32Driver_FOUND PARENT_SCOPE)
        return()
    endif()

    string(TOLOWER "${CMAKE_MATCH_1}" MATCH_1_LOWER)
    SET(MCU_SERIES_VARIANT_LOWER "${MATCH_1_LOWER}" PARENT_SCOPE)
    SET(MCU_SERIES_VARIANT "${CMAKE_MATCH_1}" PARENT_SCOPE)
    SET(MCU_VARIANT "${CMAKE_MATCH_2}" PARENT_SCOPE)
    SET(MCU_PIN_COUNT "${CMAKE_MATCH_3}" PARENT_SCOPE)
    string(TOLOWER "${CMAKE_MATCH_4}" CMAKE_MATCH_4_LOWER)
    SET(MCU_FLASH_LOWER "${CMAKE_MATCH_4_LOWER}" PARENT_SCOPE)
    SET(MCU_FLASH "${CMAKE_MATCH_4}" PARENT_SCOPE)
    SET(MCU_PACKAGE "${CMAKE_MATCH_5}" PARENT_SCOPE)
    SET(MCU_TEMP_RANGE "${CMAKE_MATCH_6}" PARENT_SCOPE)
    set(STM32Driver_FOUND TRUE PARENT_SCOPE)
endfunction()

if(NOT DEFINED MCU_NAME)
    list(POP_FRONT STM32Driver_FIND_COMPONENTS MCU_NAME)
endif()
if(NOT DEFINED MCU_NAME)
    message(FATAL_ERROR "MCU_NAME variable must be defined before calling find_package(STM32Driver)")
endif()

stm32_parse_mcu_name("${MCU_NAME}")
if(NOT STM32Driver_FOUND)
    message(WARNING "Invalid STM32 MCU name: ${MCU_NAME}")
    return()
endif()

set(STM32Driver_FOUND)
foreach(flash_fit IN ITEMS 0 1 2 3 4 5 6 7 8 9 A B Z C D E F G H I)
    if(${flash_fit} STRGREATER_EQUAL ${MCU_FLASH})
        string(TOLOWER "${flash_fit}" MCU_FLASH_FIT_LOWER)
        if(EXISTS "${STM32_KITS_DIR}/cmsis-device-${MCU_SERIES_VARIANT_LOWER}/Source/Templates/gcc/startup_stm32${MCU_SERIES_VARIANT_LOWER}${MCU_VARIANT}x${MCU_FLASH_FIT_LOWER}.s")
            set(STM32Driver_FOUND TRUE)
            set(MCU_FLASH_FIT "${flash_fit}")
            break()
        endif()
    endif()
endforeach()
if(NOT STM32Driver_FOUND)
    message(WARNING "Invalid STM32 MCU Flash: ${MCU_FLASH}")
    return()
endif()

enable_language(C ASM)
set(STM32Driver_FOUND TRUE)
add_library(STM32Driver STATIC
        "${STM32_KITS_DIR}/cmsis-device-${MCU_SERIES_VARIANT_LOWER}/Source/Templates/gcc/startup_stm32${MCU_SERIES_VARIANT_LOWER}${MCU_VARIANT}x${MCU_FLASH_FIT_LOWER}.s"
        "${STM32_KITS_DIR}/cmsis-device-${MCU_SERIES_VARIANT_LOWER}/Source/Templates/system_stm32${MCU_SERIES_VARIANT_LOWER}xx.c"
        "${STM32_KITS_DIR}/stm32${MCU_SERIES_VARIANT_LOWER}xx-hal-driver/Src/stm32${MCU_SERIES_VARIANT_LOWER}xx_hal.c"
        "${STM32_KITS_DIR}/stm32${MCU_SERIES_VARIANT_LOWER}xx-hal-driver/Src/stm32${MCU_SERIES_VARIANT_LOWER}xx_hal_cortex.c"
)
foreach(feature IN LISTS STM32Driver_FIND_COMPONENTS)
    target_sources(STM32Driver PRIVATE
            "${STM32_KITS_DIR}/stm32${MCU_SERIES_VARIANT_LOWER}xx-hal-driver/Src/stm32${MCU_SERIES_VARIANT_LOWER}xx_hal_${feature}.c"
    )
endforeach()
target_include_directories(STM32Driver PUBLIC
        "${STM32_KITS_DIR}/cmsis-core/Include"
        "${STM32_KITS_DIR}/cmsis-device-${MCU_SERIES_VARIANT_LOWER}/Include"
        "${STM32_KITS_DIR}/stm32${MCU_SERIES_VARIANT_LOWER}xx-hal-driver/Inc"
        "${STM32_KITS_DIR}/easyheader"
)
target_compile_definitions(STM32Driver PUBLIC "STM32${MCU_SERIES_VARIANT}${MCU_VARIANT}x${MCU_FLASH_FIT}")
target_link_options(STM32Driver PUBLIC
        "SHELL:-T\"${STM32_KITS_DIR}/cmsis-device-${MCU_SERIES_VARIANT_LOWER}/Source/Templates/gcc/linker/STM32${MCU_SERIES_VARIANT}${MCU_VARIANT}X${MCU_FLASH_FIT}_FLASH.ld\""
)

新建 stm32kits/easyheader/stm32f1xx_hal_conf.h, 内容如下

#include <stm32f1xx_hal_conf_template.h>

至此 stm32kits 已手工创建完成

3 CLion 全局配置

设置→构建、执行、部署→工具链→"+"→系统
环境文件: stm32kits\cmake\startup.bat
C 编译器: arm-none-eabi-gcc.exe
C++ 编译器: arm-none-eabi-g++.exe
调试器: arm-none-eabi-gdb.exe

上述全局配置实际保存在 %APPDATA%\JetBrains\CLion2025.1\options\windows\toolchains.xml

      <toolchain
          name="ArmMinGW"
          toolSetKind="SYSTEM_WINDOWS_TOOLSET"
          customCCompilerPath="stm32kits\arm-gnu-toolchain-14.2.rel1-mingw-w64-x86_64-arm-none-eabi\bin\arm-none-eabi-gcc.exe"
          customCXXCompilerPath="stm32kits\arm-gnu-toolchain-14.2.rel1-mingw-w64-x86_64-arm-none-eabi\bin\arm-none-eabi-g++.exe"
          debuggerKind="CUSTOM_GDB"
          customGDBPath="stm32kits\arm-gnu-toolchain-14.2.rel1-mingw-w64-x86_64-arm-none-eabi\bin\arm-none-eabi-gdb.exe"
          environment="stm32kits\startup.bat" />

4 CLion 新项目配置

任意创建一个 C++ 项目后, 进行如下配置
设置→构建、执行、部署→CMake→配置文件
工具链: ArmMinGW

CMakeLists.txt 添加:

find_package(STM32Driver COMPONENTS STM32F103C8T6 gpio spi)
target_link_libraries(<项目名> PRIVATE STM32Driver)

之后程序就可编译了

ST-Link 调试

下面配置嵌入式 GDB 调试服务端, 需安装 Embedded Development Support 插件
设置→构建、执行、部署→调试器→调试服务器→"+"→泛型→GDB 服务器
可执行文件: stm32kits/OpenOCD/bin/openocd.exe
实参: -f interface\stlink.cfg -f target\stm32f1x.cfg
在这里插入图片描述
下面配置调试客户端
设置→构建、执行、部署→调试器→调试服务器→调试器
调试器: stm32kits\arm-gnu-toolchain\bin\arm-none-eabi-gdb.exe
连接/实参: tcp:localhost:3333 (用于启动客户端后连接服务端)

上述配置将在 ${CMAKE_CURRENT_SOURCE_DIR}/.idea/debugServers 生成 OpenOCD.xml, 该文件以后可直接复制使用, 其内容如下

<component name="DebugServers">
  <generic-debug-target name="OpenOCD" uniqueID="c136ea77-b81f-463e-8e9b-ef6a84b56628" selected="true">
    <debugger version="1">
      <debugger toolchainName="ArmMinGW" />
      <env />
    </debugger>
    <gdbserver exe="C:/program/stm32kits/OpenOCD-20240916-0.12.0/bin/openocd.exe" args="-f interface\stlink.cfg -f target\stm32f1x.cfg">
      <env />
    </gdbserver>
    <console enabled="true" port="4444" />
    <target reset-before="false" />
    <connection remote-string="tcp:localhost:3333" custom-script="echo Connecting to target...\n&#10;$GDBTargetCommand$&#10;echo Connected to target!\n" warmup-ms="500" />
  </generic-debug-target>
</component>

最后再设置一下当前调试服务器, 之后可通过 ST-Link 走 SWD 接口 下载程序和调试

ST-Link 也要装好驱动, OpenOCD已内置了ST-Link的驱动, 位置在 stm32kits/OpenOCD/drivers/ST-Link/dpinst_amd64.exe

在运行被中断时, 可以用调试浮窗的内存视图直接查看 SRAM 内存
而功能寄存器需要在调试浮窗可找到外设页(需安装 Embedded Development Support 插件), 加载 svd 文件来读取


调试时会出现 info pretty-printer 执行失败的错误, 原因是 arm-none-eabi-gdb 编译时移除了 python 支持, 否则通常可以在 gdb 隔壁 ../share/gdb/python/gdb/command/pretty_printers.py 找到该命令

5 点亮 LED

以tb上常见的STM32F103C8T6最小系统板为例, 一个 LED 负极接在 PC13 的位置

下面闪烁该 LED

#include <stm32f1xx_hal.h>
#include <stm32f1xx_hal_rcc.h>
#include <stm32f1xx_hal_gpio.h>

extern "C" void SysTick_Handler() {
    HAL_IncTick();
}

int main() {
    HAL_Init();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.Pin = GPIO_PIN_13;
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStructure.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStructure);
    while (true) {
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_Delay(100);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
        HAL_Delay(100);
    }
}

6 分析 elf 文件

IDA 加载 elf 文件后, Processor type 选 ARM Little-endian [ARM], Processor options→Edit ARM architecture options 选 ARMv7-M

可以看到08000000开始是向量表, 第2个是程序入口 Reset_Handler, 在0x0800021C

7 项目模板

CLion 暂不支持将项目保存为项目模板 (详见 Save project as template in CLion. How? ), 只有 Idea 其他 IDE 支持, 新项目都要手动重新配置 工具链,调试服务器