STM32CubeMX Project Creator
Teach Claude to create a new STM32CubeMX project for STM32F407ZGTx, output a CMake build for VS Code, and prep debug tasks. The generated project must preserve the required USART2 behavior exactly, including DMA-based idle reception and the supporting user-code hooks in usart.c, main.c, and syscalls.c.
The skill must be self-contained. Do not inspect, copy from, diff against, or otherwise rely on other user projects as templates or references when generating a new project.
Preconditions
- Confirm CubeMX exists at
C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeMX/STM32CubeMX.exe; otherwise stop and report. - Confirm the installed CubeMX version before generating or editing the project. If the local version cannot be determined, stop and report instead of guessing.
- Ensure any written
.iocmetadata matches the local CubeMX version; do not stamp a newerMxCube.Versionthan the user's installation. - Use only the installed CubeMX, its bundled databases, and the requested project directory as inputs. Do not use another workspace or existing STM32 project as a reference.
- Have an output directory where the new project folder will be created.
- Ask the user for the project name.
- VS Code STM32 extensions are available (CMake Tools, Cortex-Debug, STM32 VS Code Extension).
MCU + clock configuration
- MCU:
STM32F407ZGTx, packageLQFP144. - Clock source: HSE crystal =
25_000_000 HzonPH0/PH1. - PLL: source HSE,
PLLM=25,PLLN=168,PLLP=2,PLLQ=4. - Resulting clocks:
SYSCLK=84 MHz,HCLK=84 MHz,APB1=42 MHz,APB2=84 MHz,I2S=96 MHz,MCO2=84 MHz,Systick=1 ms. - Ensure RCC:
SYSCLK source = PLLCLK,LSI = 32000, and keep the divider values listed here.
Generation targets
- Toolchain/IDE: CMake (Ninja or Make). Enable
CMAKE_EXPORT_COMPILE_COMMANDS. - Languages: C + ASM, C standard C11.
- Linker script: device-generated
STM32F407ZGTx_FLASH.ld. - Peripherals to enable by default: RCC, SYS, NVIC, GPIO, DMA, USART2.
- GPIO baseline:
- SWD:
PA13=SYS_JTMS-SWDIO,PA14=SYS_JTCK-SWCLK - Oscillators:
PH0/PH1for HSE - USART pins:
PD5=USART2_TX,PD6=USART2_RX - Remove all other GPIO assignments unless the user explicitly asks for them
- SWD:
- NVIC baseline:
NVIC_PRIORITYGROUP_4- System handlers enabled as generated by CubeMX
SysTick_IRQnenabled with priority15- Enable
DMA1_Stream5_IRQnandDMA1_Stream6_IRQn
- DMA baseline:
USART2_RX:DMA1_Stream5,DMA_CHANNEL_4,DMA_PERIPH_TO_MEMORY,DMA_NORMAL, byte alignment, memory increment enabled, peripheral increment disabled, priority low, FIFO disabledUSART2_TX:DMA1_Stream6,DMA_CHANNEL_4,DMA_MEMORY_TO_PERIPH,DMA_NORMAL, byte alignment, memory increment enabled, peripheral increment disabled, priority low, FIFO disabled
- USART2 baseline:
- Virtual mode:
VM_ASYNC - Parameters in generated init:
115200,8N1,TX_RX,UART_HWCONTROL_NONE,UART_OVERSAMPLING_16 HAL_UART_MspInit()must configurePD5/PD6asGPIO_AF7_USART2and link both DMA handles with__HAL_LINKDMAstm32f4xx_it.cmust keepDMA1_Stream5_IRQHandler()andDMA1_Stream6_IRQHandler()callingHAL_DMA_IRQHandler(&hdma_usart2_rx)andHAL_DMA_IRQHandler(&hdma_usart2_tx)
- Virtual mode:
- Do not add other peripherals unless the user explicitly requests them.
USART2 runtime behavior
printfredirection pattern:- Keep
syscalls.cwithextern int __io_putchar(int ch) __attribute__((weak)); - Do not implement
__io_putchar()insyscalls.c - Re-define
int __io_putchar(int ch)inCore/Src/usart.c - Implementation must call
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 1000);and returnch
- Keep
- DMA receive flow:
- In
main.c, afterMX_GPIO_Init();,MX_DMA_Init();, andMX_USART2_UART_Init();, callHAL_UARTEx_ReceiveToIdle_DMA(&huart2, receiveBuf50, sizeof(receiveBuf50)); - Implement
HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)in user code, not the legacyHAL_UART_RxCpltCallback() - In that callback, when
huart->Instance == USART2, terminate the buffer safely, process the received command, then immediately re-armHAL_UARTEx_ReceiveToIdle_DMA(&huart2, receiveBuf50, sizeof(receiveBuf50));
- In
- Ensure the generated project includes all symbols used by this flow:
UART_HandleTypeDef huart2DMA_HandleTypeDef hdma_usart2_rxDMA_HandleTypeDef hdma_usart2_tx- a receive buffer such as
uint8_t receiveBuf50[50]
VS Code integration (mirror reference patterns)
Create .vscode/ with:
settings.json:"cmake.configurePreset": "Debug""cmake.buildPreset": "Debug""STM32VSCodeExtension.cubeCLT.path": "C:/ST/STM32CubeCLT_1.19.0"(or user path)
c_cpp_properties.json:configurationProvider=ms-vscode.cmake-toolstasks.json: include- A fixed
CMake: configuretask that runscmake --preset Debug - A fixed default build task that runs
cmake --build --preset Debug - A fixed clean rebuild task that runs
cmake --build --preset Debug --clean-first - Mark the fixed build task as the default build group so VS Code
Ctrl+Shift+Bworks immediately - Flash task:
STM32_Programmer_CLI --connect port=swd --download ${workspaceFolder}/build/Debug/<proj>.elf -hardRst -rst --start - Aggregate "Build + Flash" task depending on rebuild + flash
- Avoid depending on
${command:cmake.activeBuildPresetName},${command:cmake.activeConfigurePresetName}, or${command:cmake.launchTargetPath}for the default build path, becauseCtrl+Shift+Bmay fail before CMake Tools initializes those values
- A fixed
launch.json: Cortex-Debug launch + attach configs:servertype: stlink,device: STM32F407ZGTx,interface: swdexecutable: ${workspaceFolder}/build/Debug/<proj>.elfsvdFile: ${config:STM32VSCodeExtension.cubeCLT.path}/STMicroelectronics_CMSIS_SVD/STM32F407.svdserverpath/stlinkPath:${config:STM32VSCodeExtension.cubeCLT.path}/STLink-gdb-server/bin/ST-LINK_gdbserverarmToolchainPath:${config:STM32VSCodeExtension.cubeCLT.path}/GNU-tools-for-STM32/bingdbPath:${config:STM32VSCodeExtension.cubeCLT.path}/GNU-tools-for-STM32/bin/arm-none-eabi-gdbpreLaunchTask:CMake: clean rebuild(for launch config)
CMake scaffolding (root)
CMakeLists.txt: include toolchaincmake/gcc-arm-none-eabi.cmake, setCMAKE_C_STANDARD 11, enable C & ASM, create executable${CMAKE_PROJECT_NAME},add_subdirectory(cmake/stm32cubemx), add user sources, linkstm32cubemx.CMakePresets.json: generator Ninja, binary dirbuild/${presetName}, presets Debug/RelWithDebInfo/Release/MinSizeRel inheriting default.cmake/gcc-arm-none-eabi.cmake: setTOOLCHAIN_PREFIX=arm-none-eabi-, compilers, target flags-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard, link scriptSTM32F407ZGTx_FLASH.ld, typical link flags with gc-sections and map file.cmake/stm32cubemx/CMakeLists.txt: interface lib withUSE_HAL_DRIVER,STM32F407xx, includes forCore/IncandDrivers, and CubeMX-generated sources for the minimal setup (at minimummain.c,gpio.c,dma.c,usart.c,stm32f4xx_it.c,stm32f4xx_hal_msp.c,system_stm32f4xx.c,startup_stm32f407xx.s, and required HAL driver files). Keep it aligned with CubeMX output; regenerate if peripherals differ.
Execution steps
- Verify CubeMX binary exists; if missing, stop.
- Detect and record the installed CubeMX version before doing any
.iocwork. Use that exact version when validating compatibility and when writingMxCube.Version/ related metadata. - Do not handcraft a full STM32 project when CubeMX generation is available locally. Generate with the installed CubeMX first, then patch only the required user sections and build files.
- Do not inspect or copy from any other project on disk. This skill must work independently from CubeMX metadata, generated output, and the instructions in this file only.
- Ask user for project name and target directory.
- Create project folder and open CubeMX:
- Set MCU to STM32F407ZGTx (LQFP144).
- Configure RCC: HSE 25 MHz on
PH0/PH1,PLLM=25,PLLN=168,PLLP=2,PLLQ=4,SYSCLKfrom PLL. - Enable required peripherals for the default template: SYS, RCC, NVIC, GPIO, DMA, USART2.
- Configure only these GPIO entries by default:
PH0,PH1,PA13,PA14,PD5,PD6. - Configure
USART2as asynchronous onPD5/PD6. - Configure DMA requests exactly as specified here:
USART2_RX -> DMA1_Stream5,USART2_TX -> DMA1_Stream6. - Set
NVIC_PRIORITYGROUP_4, keepSysTick_IRQnat priority15, and enable both DMA stream IRQs. - Project Manager -> Toolchain/IDE: CMake; set project name and path.
- Code Generator: Generate peripheral initialization as "HAL", keep user sections.
- Generate code.
- Validate the generated output before patching:
- Confirm
main.cexists andSystemClock_Config()usesHSE,PLL_ON,PLLM=25,PLLN=168,PLLP=RCC_PLLP_DIV2,PLLQ=4,SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK,AHB = DIV1,APB1 = DIV2,APB2 = DIV1. - Confirm the generated project includes
USART2,DMA1_Stream5,DMA1_Stream6, andUSART2_IRQn. - Confirm the generated
.iocdoes not report required-field incompatibilities for RCC, DMA, SYS, GPIO, or USART2. - If CubeMX output falls back to
HSI,PLL_NONE, missing DMA streams, or missing USART2 init, treat generation as failed and fix generation inputs before patching.
- Confirm
- Patch the generated sources to preserve the required
USART2runtime behavior:- In
Core/Src/usart.c, add__io_putchar()in user code and keep it bound toHAL_UART_Transmit(&huart2, ...). - In
Core/Src/syscalls.c, leave__io_putchar()as a weak external declaration only. - In
Core/Src/main.c, declare a receive buffer, callHAL_UARTEx_ReceiveToIdle_DMA(&huart2, receiveBuf50, sizeof(receiveBuf50));during initialization, and implementHAL_UARTEx_RxEventCallback()to process and re-arm reception. - In
Core/Src/stm32f4xx_it.c, keep both DMA IRQ handlers forUSART2.
- In
- Normalize the generated layout to the required root structure if CubeMX emits a different but equivalent layout:
- Required end state is root
Core/Inc,Core/Src,Drivers,cmake,.vscode, linker script, startup file, and root.ioc. - If CubeMX emits
Inc/andSrc/at the root, move them toCore/IncandCore/Srcbefore patching build files.
- Required end state is root
- Add/adjust the CMake scaffolding files (root CMakeLists, presets, toolchain, stm32cubemx CMakeLists) to match the templates above if CubeMX did not emit them.
- Add
.vscodefiles as above; setexecutablepaths to the chosen project name. - Validate VS Code build behavior:
- Confirm the default VS Code build task maps to
cmake --build --preset Debug. - Confirm build tasks do not rely on unresolved CMake Tools command substitutions for the default build path.
- If needed, prefer fixed
Debugpreset names and fixed${workspaceFolder}/build/Debug/<proj>.elfpaths over dynamic commands.
- Before finishing, re-check the generated
.iocheader and confirm its CubeMX version metadata still matches the local installation. Fix it if it drifted. - Run
cmake --preset Debugthencmake --build --preset Debugto verify build; ensure${workspaceFolder}/build/Debug/<proj>.elfexists. - Verify VS Code
Ctrl+Shift+Bcan invoke the generated default build task without manual preset reselection. - For flashing, run VS Code task "Build + Flash" or
STM32_Programmer_CLI --connect port=swd --download build/Debug/<proj>.elf -hardRst -rst --start. - For debugging, launch "Build & Debug Microcontroller - ST-Link" in VS Code (Cortex-Debug).
Notes & guardrails
- Keep
SKILL.mdconcise; move verbose reference to separate files if added. - If user chooses another MCU, re-derive clocks accordingly instead of using the above constants.
- Never assume the CubeMX version from memory, from a previous machine, or from the latest release. Detect the local version for the current task.
- On this machine, prefer headless generation via
jre/bin/java.exe -jar STM32CubeMX.exe -q <script>when automating CubeMX. Do not assume directSTM32CubeMX.exe -qbehaves the same unless verified for the current install. - Do not continue from an invalid CubeMX load that logs missing required RCC, DMA, SYS, GPIO, or USART fields. Fix the
.ioc/ generation inputs first. - Do not continue patching if generated
main.cshowsHSIorPLL_NONEinstead of the requiredHSE + PLLclock tree. - This skill must remain independent. Do not read another STM32 project to discover valid
.iockeys, clock parameters, or file layouts. - Prefer deterministic VS Code tasks. Do not assume VS Code
Ctrl+Shift+Bwill work if the tasks depend on unresolved extension-provided command variables. - Do not refer to the VS Code build shortcut as
Ctrl+B; in standard VS Code usage the build shortcut isCtrl+Shift+B. - For the default build path, prefer fixed
Debugpreset tasks over dynamic${command:...}substitutions unless those substitutions are explicitly verified in the current workspace. - Unless the user asks for more peripherals, remove any auto-enabled middleware or peripheral blocks beyond DMA, GPIO, NVIC, RCC, SYS, and USART2 from both the CubeMX configuration and generated build files.
- When stripping peripherals from a copied
.ioc, also remove their dependent pins, DMA requests, NVIC entries, generated source files, and middleware references so the result stays internally consistent. - For GPIO specifically, keep only these default pins:
PH0,PH1,PA13,PA14,PD5,PD6. - Do not replace the required
HAL_UARTEx_ReceiveToIdle_DMA()+HAL_UARTEx_RxEventCallback()flow with polling, interrupt-only receive, orHAL_UART_Receive_DMA(). - Do not move
__io_putchar()intosyscalls.c; keep the actual implementation inusart.csoprintfstays coupled toUSART2.