STM32最小CLion开发环境

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, 内容如下

if(NOT DEFINED STM32Driver_FIND_COMPONENTS)
    message(FATAL_ERROR "MCU name must be passed via COMPONENTS")
endif()

enable_language(C ASM)
foreach(mcu_name IN LISTS STM32Driver_FIND_COMPONENTS)
    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]?)$")
        message(FATAL_ERROR "Invalid MCU name: ${mcu_name}")
    endif()

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

    set(mcu_flash_fit_ok) # 自适配头文件保证 SRAM 能完全访问, 但 Flash 可能会超出可访问区域, 此时需从模板文件做自定义
    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(mcu_flash_fit_ok TRUE)
                set(MCU_FLASH_FIT "${flash_fit}")
                break()
            endif()
        endif()
    endforeach()
    if(NOT mcu_flash_fit_ok)
        message(FATAL_ERROR "Invalid MCU Flash: ${MCU_FLASH}")
    endif()

    if(NOT TARGET "${mcu_name}_CORE") # 直接从模板创建模板目标方便使用, 可自定义目标替换之
        add_library("${mcu_name}_CORE" 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"
        )
        target_include_directories("${mcu_name}_CORE" PUBLIC
                "${STM32_KITS_DIR}/cmsis-core/Include"
                "${STM32_KITS_DIR}/cmsis-device-${MCU_SERIES_VARIANT_LOWER}/Include"
                "${STM32_KITS_DIR}/easyheader"
        )
        target_link_options("${mcu_name}_CORE" 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\""
        )
        target_compile_definitions("${mcu_name}_CORE" PUBLIC
                "STM32${MCU_SERIES_VARIANT}${MCU_VARIANT}x${MCU_FLASH_FIT}"
        )
    endif()

    if(TARGET "${mcu_name}_HAL")
        continue()
    endif()

    add_library("${mcu_name}_HAL" STATIC
            "${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"
    )
    target_link_libraries("${mcu_name}_HAL" PUBLIC "${mcu_name}_CORE")
    target_include_directories("${mcu_name}_HAL" 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"
    )

    add_library("${mcu_name}_HAL_ALL" INTERFACE) # 编译器能自动裁剪未使用的代码
    target_link_libraries("${mcu_name}_HAL_ALL" INTERFACE "${mcu_name}_HAL")

    foreach(feature_lower IN ITEMS adc can cec crc dac dma eth exti flash fsmc gpio hcd i2c i2s irda iwdg mmc nand nor pccard pcd pwr rcc rtc sd sdmmc smartcard spi sram tim uart usart usb utils wwdg)
        string(TOUPPER "${feature_lower}" feature_upper)
        set(filename_base "${STM32_KITS_DIR}/stm32${MCU_SERIES_VARIANT_LOWER}xx-hal-driver/Src/stm32${MCU_SERIES_VARIANT_LOWER}xx")
        if(EXISTS "${filename_base}_hal_${feature_lower}.c")
            add_library("${mcu_name}_${feature_upper}" STATIC)
            target_link_libraries("${mcu_name}_${feature_upper}" PUBLIC "${mcu_name}_HAL")
            target_link_libraries("${mcu_name}_HAL_ALL" INTERFACE "${mcu_name}_${feature_upper}")
            target_sources("${mcu_name}_${feature_upper}" PRIVATE "${filename_base}_hal_${feature_lower}.c")
            if(EXISTS "${filename_base}_hal_${feature_lower}_ex.c")
                target_sources("${mcu_name}_${feature_upper}" PRIVATE "${filename_base}_hal_${feature_lower}_ex.c")
            endif()
            target_compile_options("${mcu_name}_${feature_upper}" PRIVATE "-Wno-unused-parameter")
        endif()
        if(EXISTS "${filename_base}_ll_${feature_lower}.c")
            add_library("${mcu_name}_LL_${feature_upper}" STATIC)
            target_link_libraries("${mcu_name}_LL_${feature_upper}" PUBLIC "${mcu_name}_CORE")
            target_include_directories("${mcu_name}_LL_${feature_upper}" PUBLIC "${STM32_KITS_DIR}/stm32${MCU_SERIES_VARIANT_LOWER}xx-hal-driver/Inc")
            target_sources("${mcu_name}_LL_${feature_upper}" PRIVATE "${filename_base}_ll_${feature_lower}.c")
            target_compile_definitions("${mcu_name}_LL_${feature_upper}" PUBLIC USE_FULL_LL_DRIVER register=)
            target_compile_options("${mcu_name}_LL_${feature_upper}" PRIVATE "-Wno-unused-parameter")
        endif()
    endforeach()

    target_link_libraries("${mcu_name}_UART" PUBLIC "${mcu_name}_RCC") # 打补丁
endforeach()

set(STM32Driver_FOUND TRUE)

新建 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)
target_link_libraries(<项目名> PRIVATE STM32F103C8T6_GPIO)

源码模板

#include <stm32f1xx_hal.h>

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

int main() {
    HAL_Init();
    while (true);
}

之后程序就能编译了

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 支持, 新项目都要手动重新配置 工具链,调试服务器

串口调试

可尝试 CLion 插件 Serial Port Monitor

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值