| name | embedded-stm32-framework |
| description | Use when: developing, debugging, refactoring, or reviewing this STM32H723VGT6 embedded flight-control project. Covers STM32CubeMX/HAL, CMake builds, BSP modules, DMA/cache, ISR safety, attitude/TVC/altitude-control loops, USB CDC telemetry, and hardware-safety checks. |
| argument-hint | Describe the embedded feature, bug, driver, control-loop change, or build/debug task |
Embedded STM32 Framework
Use this skill for project-specific embedded work in the STM32H723VGT6 coaxial vector rocket / UAV verification firmware. It turns requests into changes that fit the existing HAL + BSP architecture, timing model, and safety constraints.
Project Model
- Target MCU: STM32H723VGT6, Cortex-M7, STM32 HAL generated by CubeMX.
- Generated/platform layer:
Core/, Drivers/, Middlewares/, USB_DEVICE/, cmake/stm32cubemx/.
- Application/BSP layer:
BSP/ and BSP/BMI088/.
- Build system: CMake preset/build directory under
build/Debug.
- Core modules:
BSP/attitude.c/h: BMI088 + RM3100 assisted attitude estimation, yaw wrapping, gyro bias, DWT timing.
BSP/tvc_pid.c/h: cascaded attitude/rate TVC control, servo mixing, yaw differential throttle, base throttle handoff.
BSP/altitude_hold.c/h: ultrasonic altitude hold, target slew, PID throttle correction, sonar-loss descent behavior.
BSP/esc_pwm.c/h: ESC PWM and arming/throttle output.
BSP/ultrasonic.c/h, BSP/rm3100.c/h, BSP/vbat_adc.c/h, BSP/ano_protocol.c/h, BSP/usb_printf.c/h: sensor, telemetry, voltage, and debug support.
When To Use
Use this workflow when the user asks to:
- Add or modify an embedded feature, BSP module, sensor driver, actuator output, PID/control loop, telemetry frame, or safety behavior.
- Debug IMU, magnetometer, ultrasonic, SBUS, USB CDC, PWM, DMA, cache, timing, arming, failsafe, or build problems.
- Review embedded C code for real-time safety, hardware safety, generated-code boundaries, or STM32H7-specific issues.
- Tune or explain attitude, TVC, altitude-hold, yaw, ESC, or telemetry behavior in this project.
Development Workflow
-
Identify the layer being changed.
- Prefer
BSP/<module>.c/h for application logic and reusable drivers.
- Touch
Core/Src/main.c only for orchestration, initialization order, scheduler periods, callbacks, or USER CODE blocks.
- Avoid editing generated HAL/CubeMX sections outside
USER CODE BEGIN/END unless the user explicitly requests CubeMX-level changes.
- Update
CMakeLists.txt when adding new BSP .c files or include directories.
-
Read the relevant module pair and call sites.
- For API changes, inspect both
.h and .c plus callers in Core/Src/main.c and timer/UART callbacks.
- For control-loop changes, read the producer data path, output actuator path, and safety gates (
is_armed, is_crashed, sbus_connected, VBAT_IsShutdown).
- For DMA/UART/SPI changes on STM32H7, inspect buffer placement, alignment, callbacks, and cache maintenance.
-
Preserve real-time boundaries.
- Keep ISR and callback work short; defer heavy parsing, USB sends, printing, floating-point loops, and blocking waits to the main loop when possible.
- Do not call blocking HAL delays or verbose debug output inside high-rate ISR paths.
- Use
volatile for state shared between ISR/callbacks and the main loop.
- Keep periodic work tied to
HAL_GetTick() timestamps or existing timer ISR cadence; avoid adding uncontrolled busy loops.
-
Respect hardware-safety gates.
- Motors and servos must fail to safe outputs: ESC minimum throttle, TVC center, outputs limited and clamped.
- Control outputs should be active only when the relevant gates are satisfied: armed, not crashed, receiver connected, voltage valid, sensor data valid.
- Reset PID integrators when arming, mode switching, sensor invalidation, or forced shutdown can otherwise leave stale control effort.
- Clamp every actuator-facing value in physical units before writing PWM compare registers or throttle APIs.
-
Handle STM32H7 DMA/cache explicitly.
- DMA buffers that the CPU reads should be 32-byte aligned and placed in a DMA-accessible section such as
.dma_buffer when the project pattern uses it.
- Invalidate D-Cache before reading data written by DMA.
- Clean D-Cache before starting DMA from CPU-written buffers if that pattern is introduced.
- Avoid placing DMA buffers in DTCM RAM.
-
Keep units and coordinate conventions explicit.
- Mark degrees vs radians, cm vs m, microseconds PWM, Hz/ms update periods, and raw protocol units.
- Keep yaw in the project convention
(-180, 180] when interacting with attitude/TVC/ANO telemetry unless a module states otherwise.
- Convert gyro rad/s to deg/s at module boundaries where PID gains are expressed in degrees.
-
Make changes in the local style.
- Prefer small C functions with clear module APIs in headers.
- Use fixed-width integer types for protocol, DMA, packed-frame, and hardware-facing data.
- Keep macros for tunable embedded constants near the module top with units in the name or comment.
- Add comments only for timing, hardware, protocol, calibration, or safety behavior that is not obvious from the code.
-
Verify the change.
- For CMake STM32 builds, prefer the VS Code CMake/build integration or the existing build task when available.
- If using workspace tasks, run
编译 STM32 项目.
- Check compile diagnostics after build failures and fix only issues related to the requested change.
- For behavioral changes that cannot be fully tested without hardware, state what was build-verified and what must be hardware-verified.
New BSP Module Template Flow
Use this flow when adding a new project-owned driver, control feature, telemetry helper, or hardware abstraction under BSP/.
-
Choose the module boundary.
- File names should be lowercase with underscores:
BSP/<module_name>.c and BSP/<module_name>.h.
- Keep the module responsible for one hardware device, algorithm, or feature boundary.
- Put private state and helper functions in the
.c file as static.
- Expose only the minimal API needed by
main.c, timer callbacks, or other BSP modules.
-
Create the header API.
- Use a conventional include guard matching the module name.
- Include
<stdint.h> when public APIs use fixed-width types.
- Keep public types small and explicit; use enums for states and structs for grouped sensor/control data.
- Prefer this shape unless the neighboring module has a stronger local pattern:
#ifndef __MODULE_NAME_H__
#define __MODULE_NAME_H__
#include <stdint.h>
typedef enum {
MODULE_STATE_IDLE = 0,
MODULE_STATE_READY,
MODULE_STATE_ERROR
} Module_State_t;
void Module_Init(void);
void Module_Update(void);
Module_State_t Module_GetState(void);
uint8_t Module_IsReady(void);
#endif
- Create the source implementation.
- Include the module header first, then HAL/peripheral headers such as
main.h, spi.h, tim.h, usart.h, or adc.h.
- Keep tunable constants near the top as macros with units in the name or nearby comment.
- Initialize all module state in
Module_Init().
- Do not start motors, servos, heaters, or other risky outputs at non-safe values during init.
- Use this skeleton for ordinary periodic modules:
#include "module_name.h"
#include "main.h"
#define MODULE_UPDATE_PERIOD_MS 10U
static Module_State_t g_state = MODULE_STATE_IDLE;
static uint32_t g_last_update_ms = 0U;
void Module_Init(void)
{
g_state = MODULE_STATE_IDLE;
g_last_update_ms = HAL_GetTick();
g_state = MODULE_STATE_READY;
}
void Module_Update(void)
{
uint32_t now = HAL_GetTick();
if ((now - g_last_update_ms) < MODULE_UPDATE_PERIOD_MS) {
return;
}
g_last_update_ms = now;
if (g_state != MODULE_STATE_READY) {
return;
}
}
Module_State_t Module_GetState(void)
{
return g_state;
}
uint8_t Module_IsReady(void)
{
return (g_state == MODULE_STATE_READY) ? 1U : 0U;
}
-
Wire it into the application.
- Add
#include "module_name.h" inside /* USER CODE BEGIN Includes */ in Core/Src/main.c if main.c calls it.
- Call
Module_Init() from InitApplicationModules() or the nearest existing module init sequence after the required MX_*_Init() peripheral setup.
- Call
Module_Update() from the main loop using the module's own period guard, or use an existing scheduler block when the rate must match another subsystem.
- If a callback must hand data to the module, keep the callback minimal and let the module parse/process later from the main loop when possible.
-
Add the file to CMake.
- Edit the root
CMakeLists.txt, not cmake/stm32cubemx/CMakeLists.txt, for project-owned BSP files.
- Add the new
.c under target_sources(${CMAKE_PROJECT_NAME} PRIVATE ...) in the # Add user sources here list:
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
# Add user sources here
BSP/module_name.c
)
- If the module adds a new include folder, add it under
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ...):
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
# Add user defined include paths
BSP
BSP/new_driver/inc
)
- Do not list header files in
target_sources() unless the project already does it for IDE visibility; compilation depends on .c files.
- Verify integration.
- Build after adding the
.c file; unresolved references usually mean the source was not added to target_sources().
- Include-path errors usually mean a missing
target_include_directories() entry or a wrong relative include.
- If the new module uses a peripheral handle such as
hspi1, huart5, htim1, or hadc1, include that generated peripheral header rather than declaring the handle manually.
CubeMX Generated-Code Boundary
- Treat
Core/Inc, Core/Src, USB_DEVICE, Drivers, Middlewares, and cmake/stm32cubemx as generated or vendor-owned unless a request explicitly targets them.
- In generated files, place custom code only inside matching
/* USER CODE BEGIN ... */ and /* USER CODE END ... */ blocks.
- Do not edit
MX_*_Init() generated configuration bodies by default. Change peripheral behavior through CubeMX/.ioc regeneration, or add user code after MX_*_Init() when the adjustment is project-owned and safe.
- Do not modify HAL driver files under
Drivers/STM32H7xx_HAL_Driver/ for application fixes; wrap or configure behavior in BSP/application code instead.
- Do not modify middleware internals for USB CDC behavior unless the bug is proven to be inside the middleware. Prefer application-side buffering, gating, and retry/defer logic.
- If CubeMX regeneration is likely to overwrite a change, either move the logic into BSP or clearly mark that the edit belongs in a USER CODE section.
Decision Points
- New feature or driver: create
BSP/<name>.c and BSP/<name>.h, add source/include path if needed, initialize from InitApplicationModules() or the nearest existing init flow, and schedule updates in the main loop or proper callback.
- CMake source integration: add new BSP
.c files to the root CMakeLists.txt target_sources() list and add only genuinely new include directories to target_include_directories().
- Sensor input via DMA: use aligned DMA buffers, start receive after peripheral init, invalidate cache before parsing, recover in error callbacks, and keep parsing robust to partial frames.
- USB CDC / ANO telemetry: avoid competing CDC transmissions; combine frames or defer ISR-originated sends through a pending buffer serviced in the main loop.
- Control-loop tuning: preserve output clamps, slew limits, integrator limits, arming resets, and fail-safe behavior before changing gains.
- Generated CubeMX peripheral config: prefer editing
.ioc/CubeMX then regenerating when possible; if editing generated code directly, stay inside USER CODE regions.
- Hardware-risky request: default to conservative outputs and require explicit user intent before removing failsafes, widening actuator limits, or disabling voltage/receiver/sensor guards.
Completion Checks
Before finishing, confirm:
- New
.c files are included in CMakeLists.txt and headers are reachable.
- New module initialization and update calls are placed in USER CODE regions or project-owned BSP files.
- Public APIs are declared in matching headers and used consistently by callers.
- Generated CubeMX/vendor files were not edited outside USER CODE blocks.
- No blocking work was added to high-rate ISR/callback paths.
- Shared ISR/main-loop state is safely typed and initialized.
- DMA buffers and cache maintenance are correct for STM32H7.
- Actuator outputs are clamped and gated by safety state.
- Units, update rates, and coordinate signs are consistent with neighboring modules.
- The project builds, or any build limitation is reported with the exact remaining error category.
Useful Prompts
使用 embedded-stm32-framework,给这个项目添加一个新的 SPI 传感器 BSP 模块。
使用 embedded-stm32-framework,检查 TVC PID 改动是否有实时性或安全风险。
使用 embedded-stm32-framework,修复 UART DMA 接收偶发旧数据的问题。
使用 embedded-stm32-framework,帮我调整定高控制逻辑并编译验证。