Files
motor-controller/CLAUDE.md
robinson cf934cf6b7 [feat] UART command interface, per-motor control, filtering modules
- Add callback_task with ISR-based UART command parser (@command\n protocol)
- Add log_printf with mutex protection to prevent printf interleaving
- Add per-motor enable/disable (motor enable yaw|pitch)
- Add PID tuning via UART (pid roll kp 15)
- Add cmd_parser module (registration + tokenize + dispatch)
- Add UART layer architecture (interface → HAL → BSP)
- Add filter modules (lowpass, moving_average, notch, rate_limiter)
- Rewrite I2C bus and UART bus modules
- Rewrite PID controller and MF4010V2 motor driver
- Fix soft I2C driver

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 01:38:11 +08:00

4.9 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

CloudPlant — a 2-axis gimbal stabilization controller for STM32F407xx (Cortex-M4F). Uses FreeRTOS (CMSIS V2 API) with a 100Hz PID control loop reading IMU angles over soft I2C and driving MF4010V2 brushless motors over CAN bus.

Build Commands

# Configure (one-time; Debug is the default preset)
cmake --preset Debug

# Build
cmake --build build/Debug

# Or build with ninja directly
ninja -C build/Debug

# Build a specific object file only
ninja -C build/Debug CMakeFiles/CloudPlant.dir/path/to/file.c.obj

The project uses CMake with Ninja as the generator and the arm-none-eabi-gcc toolchain (cmake/gcc-arm-none-eabi.cmake). A starm-clang toolchain file also exists as an alternative. Compile commands are exported to build/Debug/compile_commands.json for clangd.

Flash

# Flash the Debug ELF (requires STM32CubeProgrammer CLI)
python flash.py build/Debug/CloudPlant.elf

# Erase chip only
python flash.py --erase-only

# Flash a release build
python flash.py build/Release/CloudPlant.elf

The script converts ELF to BIN with arm-none-eabi-objcopy, then flashes via STM32_Programmer_CLI at 0x08000000 over SWD.

Architecture: Strict 6-Layer Stack

Every hardware access must follow this chain. Cross-layer calls are forbidden.

APP          →  Business logic, task orchestration
  ↓
Module       →  Device/algorithm encapsulation (motor, PID, LED, soft_i2c)
  ↓
Interface    →  Abstract ops-based API (_if.h with function pointer structs)
  ↓
HAL          →  Hardware operations using STM32 HAL, registers ops to Interface
  ↓
BSP          →  Hardware description only — pin maps, CAN handles, no logic
  ↓
Driver       →  STM32 HAL / CMSIS (generated by CubeMX, in Core/, Drivers/, Middlewares/)

Key rules:

  • Module must NOT call HAL/BSP directly — it goes through Interface APIs.
  • Interface defines a xxx_ops_t struct with function pointers; HAL implements and registers ops via xxx_register_ops().
  • BSP files contain only static data arrays (pin mappings, handle arrays) — no if/for/while or business logic.
  • APP never calls HAL or BSP directly; it calls Module functions, which call Interface APIs.

Key Directories (User Code)

Directory Purpose
app/ RTOS tasks: app_init, control_task, sensor_task, monitor_task
modules/device/motor/ MF4010V2 brushless motor driver (CAN-based, speed/position/torque modes)
modules/device/led/ LED device abstraction
modules/control/pid/ Generic PID controller (float-based, anti-windup via output clamping)
modules/bus/soft_i2c/ Bit-banged I2C master (frame-level API)
interfaces/ Abstract APIs: can_if, led_if, gpio_if, i2c_if, soft_i2c_if
hal/stm32f4/ HAL implementations per peripheral — registers ops with interfaces
bsp/stm32f4/ Board-specific pin/instance mappings
3rdparty/jy901p/ JY901P IMU SDK (I2C protocol)
3rdparty/cjson/ cJSON library

CubeMX Integration

CubeMX-generated code lives in Core/, Drivers/, and Middlewares/. It is compiled as three static libraries defined in cmake/stm32cubemx/CMakeLists.txt:

  • stm32cubemx (INTERFACE — headers and defines)
  • STM32_Drivers (OBJECT — HAL driver sources)
  • FreeRTOS (OBJECT — FreeRTOS kernel sources)

User code is added to the main CMakeLists.txt via target_sources() and target_include_directories(). Global defines USE_HAL_DRIVER, STM32F407xx, and STM32_THREAD_SAFE_STRATEGY=2 come from the CubeMX CMake.

FreeRTOS task creation happens in Core/Src/freertos.c (CubeMX-managed). The tasks delegate to app/ entry points: StartSensorTasksensor_task(), StartControlTaskcontrol_task(), StartMonitorTaskmonitor_task().

Data Flow (Gimbal Stabilization)

sensor_task (IMU poll via soft I2C)
  → osMessageQueue (imu_data_t: acc, gyro, angle)
    → control_task (100Hz PID loop)
      → CAN bus (motor speed commands to MF4010V2)

The IMU queue (g_imu_queue) is created in app_init() with depth 4. sensor_task reads JY901P IMU registers via soft I2C and pushes data into the queue. control_task reads the queue with a 20ms timeout, runs two PID controllers (Roll → Yaw motor, Pitch → Pitch motor), and sends speed commands over CAN.

Interface Pattern

Every hardware peripheral follows this pattern:

interfaces/<periph>/<periph>_if.h  — ops struct + register function + API declarations
interfaces/<periph>/<periph>_if.c  — dispatching through registered ops
hal/stm32f4/<periph>/hal_<periph>.c — implements ops, registers them at init
bsp/stm32f4/<periph>/bsp_<periph>.c — static hardware mapping

Module code (e.g., modules/device/led/led.c) calls led_if_init(), led_if_write(), etc. — never hal_led_* or bsp_led_* directly.