STM32最小CLion开发环境
文章目录
- 1 必须文件
- 2 工具链
- 3 CLion 全局配置
- 4 CLion 新项目配置
- ST-Link 调试
- 5 点亮 LED
- 6 分析 elf 文件
- 7 项目模板
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
中
<toolchainname="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 $GDBTargetCommand$ 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 支持, 新项目都要手动重新配置 工具链,调试服务器