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

115 lines
4.9 KiB
Markdown

# 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
```bash
# 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
```bash
# 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: `StartSensorTask``sensor_task()`, `StartControlTask``control_task()`, `StartMonitorTask``monitor_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.