| name | stm32-project-init |
| description | Initialize a new STM32 development project. 交互式收集芯片型号、开发模式(HAL/CMSIS, C/C++)、时钟方案、外设需求,生成 CMakeLists.txt 和 hal_conf.h,编译验证,烧录。Call after stm32-dev-setup is complete. |
STM32 项目初始化
引导用户通过人机交互方式完成新项目的创建、配置、编译和烧录。
跨平台支持:Windows / macOS / Linux。
先确保 stm32-dev-setup 已完成。
Pre-flight: 确保开发环境到位
arm-none-eabi-gcc --version 2>/dev/null | head -1
cmake --version 2>/dev/null | head -1
ls ~/stm32-tools/stm32-cmake/cmake/stm32_gcc.cmake 2>/dev/null
| 检查项 | 缺失时的操作 |
|---|
arm-none-eabi-gcc 缺失 | 调用 stm32-dev-setup |
~/stm32-tools/stm32-cmake 缺失 | 调用 stm32-dev-setup |
Step 1: 芯片型号
AI: "请确认你的 STM32 芯片型号(看芯片表面丝印)?"
示例回答: "STM32L431VCTx" / "STM32F103C8T6"
从丝印可以确定:Flash 大小、RAM 大小、封装。常见型号见文末芯片型号速查。
Step 2: 开发模式(核心决策)
AI: "你希望用哪种方式开发?"
┌──────────────────────────────────────────────────────────────┐
│ [1] HAL 库 + C ← 推荐新手/快速开发 │
│ 入口 main.c │ project(C C ASM) │ 需 hal_conf.h │
│ │
│ [2] HAL 库 + C++ ← 面向对象封装 HAL │
│ 入口 main.cpp │ project(C CXX ASM)│ 需 hal_conf.h │
│ ⚠️ HAL 回调必须 extern "C" │
│ │
│ [3] CMSIS 寄存器 + C ← 极致精简、学习底层 │
│ 入口 main.c │ project(C ASM) │ 无需 hal_conf.h │
│ │
│ [4] CMSIS 寄存器 + C++ ← C++ 风格底层编程 │
│ 入口 main.cpp │ project(C CXX ASM)│ 无需 hal_conf.h │
│ ISR 必须 extern "C" │
└──────────────────────────────────────────────────────────────┘
四种模式的 CMakeLists.txt 关键差异
| 配置项 | HAL + C | HAL + C++ | CMSIS + C | CMSIS + C++ |
|---|
project(...) | C C ASM | C CXX ASM | C ASM | C CXX ASM |
find_package(HAL) | ✅ 必需 | ✅ 必需 | ❌ | ❌ |
hal_conf.h | ✅ 必需 | ✅ 必需 | ❌ | ❌ |
| 入口文件 | main.c | main.cpp | main.c | main.cpp |
extern "C" ISR | 不需要 | ✅ 必需 | 不需要 | ✅ 必需 |
Step 3: 时钟配置
AI: "期望的系统主频是多少?使用什么时钟源?"
时钟配置速查
| 系列 | 可用内部时钟 | PLL 输入范围 | VCO 范围 | 最高主频 |
|---|
| STM32F1 | HSI 8MHz | 4-16MHz | — | 72MHz |
| STM32F4 | HSI 16MHz | 1-2MHz | 100-432MHz | 168MHz |
| STM32L4 | MSI 100k-48M / HSI 16MHz | 4-16MHz | 64-344MHz | 80MHz |
| STM32G0 | HSI 16MHz | 2.66-8MHz | 96-344MHz | 64MHz |
| STM32H7 | HSI 64MHz | 1-16MHz | 150-836MHz | 550MHz |
常用时钟方案
| 方案 | 芯片 | PLL 链 | 结果 | FLASH_LATENCY |
|---|
| MSI 内部 | STM32L4 | MSI 4M /1 ×40 /2 | 80MHz | 4 |
| HSI 内部 | STM32F1 | HSI 8M /2 ×12 | 48MHz | 1 |
| HSE 外部 | STM32F1 | HSE 8M ×9 | 72MHz | 2 |
| HSI 内部 | STM32G0 | HSI 16M /1 ×8 /2 | 64MHz | 2 |
PLL 计算公式
PLL_IN = PLL_SRC / PLLM → 必须落在 PLL 输入范围内
PLL_VCO = PLL_IN * PLLN → 必须落在 VCO 范围内
PLL_R = PLL_VCO / PLLR → SYSCLK(系统主频)
STM32L4 电压缩放
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
Step 4: 中间件选择
AI: "需要 RTOS 吗?"
| 中间件 | 引入方式 |
|---|
| FreeRTOS | find_package(CMSIS COMPONENTS {chip} RTOS::FreeRTOS REQUIRED) |
| FatFS | 通过 CMSIS + HAL SD/SPI 手动集成 |
| lwIP | 通过 HAL ETH + 手动集成 |
| USB Stack | 通过 HAL PCD + 手动集成 |
⚠️ 使用 FreeRTOS 时,HAL 时基不能再使用 SysTick(FreeRTOS 占用),需用硬件定时器替代。
Step 5: 外设配置
AI: "需要哪些外设?"
示例回答: "UART1, PA9/PA10, 115200" 或 "SPI1 + TIM3 CH1 PWM"
可一次列出多个。
AI 根据回答自主判断是否追问具体引脚、波特率等参数。
Step 6: 确认 + 生成项目
AI 完成交互后,输出配置汇总并让用户确认:
── 项目配置汇总 ──
芯片 : STM32L431VCTx
模式 : HAL + C++
时钟 : MSI 4MHz → PLL 80MHz
中间件 : 无
外设 : UART1 (PA9/PA10, 115200) + GPIO (PB0, 推挽输出)
确认以上配置?我将开始生成 CMakeLists.txt 和 hal_conf.h。
Step 7: 生成项目框架
目录结构
<project>/
├── CMakeLists.txt
├── code/
│ ├── include/
│ │ └── stm32xxx_hal_conf.h ← HAL 模式必需
│ └── src/
│ └── main.c / main.cpp ← 按开发模式
└── build/ ← cmake -B build 自动生成
CMakeLists.txt 模板(按 4 种模式生成)
模式 1: HAL + C
cmake_minimum_required(VERSION 3.22)
set(CMAKE_TOOLCHAIN_FILE "$ENV{HOME}/stm32-tools/stm32-cmake/cmake/stm32_gcc.cmake")
project(my-project C C ASM)
stm32_fetch_cube({FAMILY})
find_package(CMSIS COMPONENTS {CHIP} REQUIRED)
find_package(HAL COMPONENTS {FAMILY} REQUIRED)
file(GLOB_RECURSE PROJECT_SOURCES code/src/*.c)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE code/include)
target_link_libraries(${PROJECT_NAME} PRIVATE
CMSIS::STM32::{TYPE}
HAL::STM32::{FAMILY}
STM32::NoSys
# 按启用的外设添加 HAL modules
)
模式 2: HAL + C++(外设同上,额外加 C++ 选项)
project(my-project C CXX ASM) # ⚠️ 必须包含 C
file(GLOB_RECURSE PROJECT_SOURCES code/src/*.cpp)
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-std=c++17>
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>
$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
$<$<COMPILE_LANGUAGE:CXX>:-fno-threadsafe-statics>
)
模式 3: CMSIS + C
project(my-project C ASM)
stm32_fetch_cube({FAMILY})
find_package(CMSIS COMPONENTS {CHIP} REQUIRED)
# 不加 find_package(HAL)
target_link_libraries(${PROJECT_NAME} PRIVATE
CMSIS::STM32::{TYPE}
STM32::NoSys
)
模式 4: CMSIS + C++(同 CMSIS+C + C++编译选项)
hal_conf.h(HAL 模式必需)
从 ~/stm32-tools/stm32-cmake/examples/ 下对应家族的模板复制,根据启用的外设去掉对应 #define 注释。
#define HAL_UART_MODULE_ENABLED
#define HAL_DMA_MODULE_ENABLED
#define HSE_VALUE 8000000U
#define HSI_VALUE 16000000U
#define MSI_VALUE 4000000U
#define LSE_VALUE 32768U
Step 8: 编译
cmake -S . -B build -G Ninja
cmake --build build 2>&1
CMakeLists.txt 编译通过后,AI 才开始写 code/src/main.c(pp)。
Step 9: 应用代码(按模式生成模板)
HAL + C 入口
#include "stm32xxx_hal.h"
static void SystemClock_Config(void) { }
int main(void) {
HAL_Init();
SystemClock_Config();
while (1) { }
}
HAL + C++ 入口
#include "stm32xxx_hal.h"
extern "C" void SysTick_Handler(void) { HAL_IncTick(); }
int main(void) {
HAL_Init();
while (1) { }
}
CMSIS + C 入口
#include "stm32f1xx.h"
static void delay_ms(uint32_t ms) {
SysTick->LOAD = 72000 - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
for (uint32_t i = 0; i < ms; i++) {
while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
}
SysTick->CTRL = 0;
}
int main(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
GPIOC->CRH &= ~(0xF << 20);
GPIOC->CRH |= (0x3 << 20);
while (1) {
GPIOC->ODR ^= (1 << 13);
delay_ms(500);
}
}
CMSIS + C++ 入口
#include "stm32f1xx.h"
extern "C" void SysTick_Handler(void) {}
int main(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
while (1) { }
}
Step 10: 烧录
路径 A(CubeProgrammer CLI)
CUBEIDE_ROOT=$(ls -d /c/ST/*/STM32CubeIDE /d/ST/*/STM32CubeIDE 2>/dev/null | head -1)
PROGRAMMER=$(ls "$CUBEIDE_ROOT/plugins/.../cubeprogrammer..."_*/tools/bin/STM32_Programmer_CLI.exe | head -1)
"$PROGRAMMER" -c port=SWD -w build/my-project.elf -v -rst -run
路径 B(st-flash)
arm-none-eabi-objcopy -O binary build/my-project.elf build/my-project.bin
st-flash --reset write build/my-project.bin 0x08000000
🔥 st-flash 必须烧 .bin 不是 .elf! 烧 .elf 会把 ELF 头写入 flash,CPU 读到垃圾 SP/PC,程序完全不执行但显示 verified OK。
硬件接线
ST-Link 目标板
SWDIO ────────────────→ SWDIO
SWCLK ────────────────→ SWCLK
GND ────────────────→ GND
3.3V ────────────────→ 3.3V(如不独立供电)
外设模块速查
HAL 模块名 = HAL 驱动文件名去掉前缀后全大写。如 stm32l4xx_hal_uart.c → HAL::STM32::L4::UART
| 分类 | 模块 | CMake Target |
|---|
| 必选 | RCC / GPIO / CORTEX / DMA / FLASH / PWR | HAL::STM32::{F}::{MOD} |
| 通信 | UART / USART / SPI / I2C / CAN | HAL::STM32::{F}::{MOD} |
| 定时 | TIM / RTC | HAL::STM32::{F}::{MOD} |
| 模拟 | ADC / DAC | HAL::STM32::{F}::{MOD} |
| 看门狗 | IWDG / WWDG | HAL::STM32::{F}::{MOD} |
| 存储 | SD / QSPI | HAL::STM32::{F}::{MOD} |
| USB | PCD | HAL::STM32::{F}::PCD |
| 扩展 | RCCEx / PWREx | HAL::STM32::{F}::{MOD} |
芯片型号速查
| 系列 | 芯片 | Flash | RAM | 封装 | CMSIS 类型 |
|---|
| F1 | F103C8T6 | 64KB | 20KB | LQFP48 | F103xB |
| F1 | F103RCT6 | 256KB | 48KB | LQFP64 | F103xE |
| F1 | F103VCT6 | 512KB | 64KB | LQFP100 | F103xG |
| L4 | L431VCTx | 256KB | 64KB | LQFP100 | L431xx |
| L4 | L476RGTx | 1MB | 128KB | LQFP64 | L476xx |
| F4 | F407VGT6 | 1MB | 192KB | LQFP100 | F407xx |
| F4 | F429ZIT6 | 2MB | 256KB | LQFP144 | F429xx |
故障排错
编译错误
| # | 报错 | 原因 | 修复 |
|---|
| 1 | UART_HandleTypeDef undeclared | hal_conf.h 中对应 #define 被注释 | 去注释 |
| 2 | undefined reference to HAL_xxx | CMakeLists 未链接对应 HAL module | 添加 HAL::STM32::{F}::xxx |
| 3 | cannot find -lSTM32::NoSys | 未链接 NoSys | 添加 STM32::NoSys |
| 4 | fatal error: stm32xxx_hal.h | include 路径不对 | target_include_directories |
| 5 | '-std=c++17' is valid for C++ but not for C | C++ 选项传给 C | 用 $<$<COMPILE_LANGUAGE:CXX>:...> |
| 6 | C++ 项目 undefined reference to HAL_xxx | project 只写了 CXX 没 C | 改为 project(... C CXX ASM) |
| 7 | XferAbortCallback not in struct | DMA 模块未启用 | hal_conf.h 中启用 HAL_DMA_MODULE_ENABLED |
运行时问题
| # | 现象 | 原因 | 修复 |
|---|
| 8 | HAL_Delay 不工作 | PLL 后 SysTick 未重配 | HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000) |
| 9 | L4 PLL 80MHz 死机 | 未设置电压等级 | HAL_PWREx_ControlVoltageScaling(SCALE1) |
| 10 | 烧录 verified 但程序不跑 | st-flash 烧了 .elf | objcopy -O binary 后烧 .bin |
| 11 | 串口乱码 | 时钟配置与实际不符 | 检查 SystemClock_Config |
| 12 | FreeRTOS + HAL_Delay 都不工作 | HAL 时基用 SysTick(被 FreeRTOS 占用) | 改为硬件定时器 |
烧录问题
| # | 现象 | 修复 |
|---|
| 13 | chipid: 0x000 + Failed to enter SWD | SWD 被锁。断电 → BOOT0=1 上电 → 擦除 → BOOT0=0 |
| 14 | st-flash LIBUSB_ERROR_TIMEOUT | 检查 ST-Link 驱动 |