STM32 蜗轮蜗杆电机控制系统设计文档
目录
1. 系统概述
本系统以 STM32F103(72MHz) 为主控,运行 FreeRTOS ,接收三路遥控信号源(PPM / SBUS / PX4 MAVLink),经优先级仲裁后驱动 蜗轮蜗杆执行机构。
执行机构由 步进电机 + DM860H 驱动器 组成,STM32 通过 TIM1 输出可变频率 PWM 作为 STEP 脉冲,GPIO 控制方向:
遥控信号源
├── PPM (PA7 / TIM3 输入捕获)
├── SBUS (PB11 / USART3)
└── PX4 (PA3 / USART2 MAVLink)
│
▼ 优先级仲裁(wlwg_Tasks)
│
▼
TurboControl_SetCommandUs(command_us)
│
┌────┴────────────────────┐
PA9 (TIM1 CH2 STEP 脉冲) PB15 (DIR 方向)
│ │
└──────────┬──────────────┘
▼
DM860H 驱动器
│
▼
步进电机 → 蜗杆机构
2. 硬件接线
2.1 引脚总览
| 引脚 | 复用功能 | 方向 | 连接到 |
|---|---|---|---|
| PA7 | TIM3_CH2 输入捕获 | ← 输入 | PPM 接收机信号线 |
| PA9 | TIM1_CH2 AF_PP | → 输出 | DM860H PUL+(STEP 脉冲) |
| PB15 | GPIO 推挽输出 | → 输出 | DM860H DIR+(方向) |
| PA2 | USART2_TX | → 输出 | PX4 飞控 RX |
| PA3 | USART2_RX | ← 输入 | PX4 飞控 TX(MAVLink) |
| PB11 | USART3_RX | ← 输入 | SBUS 接收机(100k 8E2 反相) |
2.2 DM860H 接线
STM32 DM860H
────────────────────────────────────────
PA9 ─── ----------- ──→ PUL+
GND ──────────────────→ PUL−
PB15 ───-------------──→ DIR+
GND ──────────────────→ DIR−
(ENA+ / ENA− 可不接,默认使能)
PUL 脉冲宽度需 ≥ 2.5μs,本设计占空比 50%,最低频率 200Hz 时
脉冲宽度 2500μs,最高频率 5kHz 时脉冲宽度 100μs,均满足要求。
2.3 定时器配置
TIM1(STEP 脉冲输出)
| 参数 | 值 |
|---|---|
| 时钟源 | 72MHz APB2 |
| 预分频 Prescaler | 71(÷72 → 1MHz) |
| 分辨率 | 1 μs / count |
| 模式 | PWM1,CH2,AF_PP |
| ARR(初始) | 999(运行时动态修改) |
| 占空比 | 50%(CCR = (ARR+1)/2) |
TIM3(PPM 输入捕获)
| 参数 | 值 |
|---|---|
| 预分频 | 71(→ 1MHz) |
| 捕获极性 | 下降沿 |
| 计数上限 | 65535 |
3. 代码架构设计
3.4 涡轮涡杆 完整控制流(设计目标)
当前代码使用硬编码测试值 TurboControl_SetCommandUs(1200U),完整实现应为:
c
void Start_wlwg_Tasks(void *argument)
{
for (;;) {
uint32_t now_ms = HAL_GetTick();
/* 仲裁选源 */
osMutexAcquire(g_controlMutexHandle, osWaitForever);
select_active_channels_locked(now_ms);
osMutexRelease(g_controlMutexHandle);
/* CH2(油门通道)→ 蜗杆执行机构 */
TurboControl_SetCommandUs(g_active_channels[2]);
osDelay(10);
}
}
4. 调试问题排查
问题一:CLion 监视窗口报 "No symbol in current context"
现象 :监视 calculated_freq_hz、calculated_period_us、last_command_us 报错。
原因 :这三个名字是结构体 g_turbo_dbg 的成员,不是独立全局变量,GDB 无法直接按裸名查找。
解决:在 CLion Watches 中改用完整路径:
# 错误
calculated_freq_hz
# 正确
g_turbo_dbg.calculated_freq_hz
g_turbo_dbg.calculated_period_us
g_turbo_dbg.last_command_us
g_turbo_dbg.update_count
g_turbo_dbg.pwm_running
g_turbo_dbg.direction
问题二:wlwg_Tasks 从未运行------FreeRTOS 堆溢出
现象:
g_turbo_dbg.update_count = 0 ← TurboControl_SetCommandUs 从未被调用
g_turbo_dbg.last_command_us = 1000 ← 停在 Init 初始值,从未变为 1200
s_working_channels[] 正常跳变(PPM 任务在运行)
update_count = 0 直接证明 wlwg_Tasks 从未被调度器运行过,
原因是任务在创建阶段就已失败(osThreadNew 返回 NULL)。
根本原因:堆空间不足
c
// FreeRTOSConfig.h 原值
#define configTOTAL_HEAP_SIZE ((size_t)3072) // 仅 3KB
FreeRTOS 使用 heap_4 动态分配,每次 osThreadNew 从堆申请:
| 申请项 | 大小(估算) |
|---|---|
| 任务 TCB(含 Trace / Mutex 字段) | ~96 字节 |
| 任务栈(128 words × 4) | 512 字节 |
| heap_4 块头 × 2 | 16 字节 |
| 每个任务小计 | ~624 字节 |
5 个用户任务:5 × 624 = 3120 字节
1 个 Mutex : ~88 字节
─────────────────────────────────
合计需求: ~3208 字节 > 3072 字节(超出!)
wlwg_Tasks 是第 5 个被创建的任务,堆在此之前已耗尽,
osThreadNew 返回 NULL,任务句柄无效,调度器永远不调度它。
Idle Task 和 Timer Task 因
configSUPPORT_STATIC_ALLOCATION = 1使用静态内存,不占堆,但五个用户任务和 mutex 均为动态分配。
修复:
c
// FreeRTOSConfig.h 修改后
#define configTOTAL_HEAP_SIZE ((size_t)8192) // 8KB,剩余 SRAM 仍有 12KB
预防方法:任务全部创建完毕后在调试器控制台执行:
p xPortGetFreeHeapSize()
返回 0 说明堆已耗尽;建议保留至少 512 字节裕量。
每次增加任务时的估算公式:
新增需求 = sizeof(TCB_t) + 栈字数 × 4 + 16
≈ 96 + stack_words × 4 + 16
增加一个 128 words 栈的任务 ≈ 624 字节
wlwg 调试变量快速检查清单
在 CLion Live Watch 中添加以下表达式,可实时判断控制器状态:
| 监视表达式 | 正常值 | 异常说明 |
|---|---|---|
g_turbo_dbg.update_count |
持续递增 | 为 0 → 任务未运行(堆溢出) |
g_turbo_dbg.last_command_us |
950~2000 | 停在 1000 → 从未被调用 |
g_turbo_dbg.pwm_running |
0 或 1 | 长时间为 0 → 命令在死区内 |
g_turbo_dbg.calculated_freq_hz |
200~5000 | 为 0 → 停止状态 |
g_turbo_dbg.calculated_period_us |
200~5000 | 为 0 → 停止状态 |
g_turbo_dbg.direction |
+1 或 -1 | 0 → 停止 |
5. 控制器配置详解
5.1 DM860H 拨码开关设置
DM860H 通过 SW1~SW8 配置电流和细分:
电流设置(SW1~SW3):根据步进电机额定电流选择,常用设置:
| SW1 | SW2 | SW3 | 峰值电流 |
|---|---|---|---|
| ON | ON | ON | 2.4A |
| OFF | ON | ON | 2.7A |
| ON | OFF | ON | 3.0A |
| OFF | OFF | ON | 3.7A |
| ON | ON | OFF | 4.3A |
| OFF | ON | OFF | 5.0A |
| ON | OFF | OFF | 6.0A |
| OFF | OFF | OFF | 7.2A |
细分设置(SW5~SW8):细分越高转动越平滑但最高转速越低:
| SW5 | SW6 | SW7 | SW8 | 细分数 | 步/转(1.8°电机) |
|---|---|---|---|---|---|
| ON | ON | ON | ON | 1 | 200 |
| OFF | ON | ON | ON | 2 | 400 |
| ON | OFF | ON | ON | 4 | 800 |
| OFF | OFF | ON | ON | 8 | 1600 |
| ON | ON | OFF | ON | 16 | 3200 |
| OFF | ON | OFF | ON | 32 | 6400 |
| ON | OFF | OFF | ON | 64 | 12800 |
| OFF | OFF | OFF | ON | 128 | 25600 |
推荐从 8 细分(SW5=OFF SW6=OFF SW7=ON SW8=ON)开始调试,
此时最高频率 5kHz 对应 5000/1600 = 3.125 转/秒 = 187.5 RPM。
SW4:静止时电流(ON = 半流,OFF = 全流)。推荐 ON(半流锁轴,减少发热)。
5.2 频率映射与速度计算
控制量 command_us(1000 为中位)经过如下变换得到 PWM 频率:
step 1:提取偏移量
centered = command_us - 1000
delta = |centered|,限制在 [50, 1000]
step 2:线性映射到频率
freq_hz = 200 + (delta - 50) × 4800 / 950
delta=50 → freq_hz = 200 Hz(最慢)
delta=1000 → freq_hz = 5000 Hz(最快)
step 3:计算周期
period_us = 1_000_000 / freq_hz
限制范围:[200μs, 5000μs]
step 4:写入定时器
ARR = period_us - 1
CCR = (ARR + 1) / 2 (50% 占空比)
实际转速换算(以 8 细分、1.8° 步进电机为例):
转速(RPM) = freq_hz × 60 / steps_per_rev
= freq_hz × 60 / 1600
200 Hz → 7.5 RPM
1000 Hz → 37.5 RPM
3000 Hz → 112 RPM
5000 Hz → 187 RPM
5.3 死区与方向逻辑
command_us
│
├─ 950 ~ 1050 ──→ 死区
│ HAL_TIM_PWM_Stop()
│ PWM 停止,电机锁轴
│
├─ > 1050 ────→ 正转
│ DIR = PB15 HIGH(GPIO_PIN_SET)
│ delta = command_us - 1000
│
└─ < 950 ─────→ 反转
DIR = PB15 LOW(GPIO_PIN_RESET)
delta = 1000 - command_us
换向保护 :检测到方向改变时先停 PWM,延时 2ms 再重启,
防止步进电机在高速时反向冲击损坏蜗杆机构:
c
if (direction != s_last_direction && s_pwm_running) {
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);
HAL_Delay(2); // 等待电机惯性消耗
s_pwm_running = 0;
}
HAL_GPIO_WritePin(turbo_dir_GPIO_Port, turbo_dir_Pin, dir_pin);
// 方向稳定后再启动 PWM
5.4 调试变量结构体说明
c
// turbo_control.h
typedef struct {
volatile uint32_t last_command_us; // 最后输入的 command_us
volatile uint32_t calculated_period_us; // 实际写入 ARR 的周期(μs)
volatile uint32_t calculated_freq_hz; // 对应频率(Hz)
volatile int32_t direction; // +1=正转 -1=反转 0=停止
volatile uint32_t pwm_running; // TIM1 CH2 是否在输出
volatile uint32_t last_update_ms; // 最后一次调用的时间戳(ms)
volatile uint32_t update_count; // 调用总次数(任务活跃性指标)
volatile uint32_t error_count; // 保留,暂未使用
} turbo_control_debug_t;
extern volatile turbo_control_debug_t g_turbo_dbg;
在 CLion 的 Watches 窗口直接添加 g_turbo_dbg,
展开结构体即可实时查看所有字段,无需逐个添加成员。
5.5 参数调整指南
| 调整目标 | 修改位置 | 参数 |
|---|---|---|
| 最高转速 | turbo_control.c TurboControl_MapToPeriod() |
freq_hz 上限(当前 5000) |
| 最低转速 | 同上 | freq_hz 下限(当前 200) |
| 死区范围 | turbo_control.c TurboControl_SetCommandUs() |
950U / 1050U |
| 换向延时 | 同上 | HAL_Delay(2U) |
| 通道映射 | freertos.c Start_wlwg_Tasks() |
g_active_channels[2](当前油门通道) |
| FreeRTOS 堆 | FreeRTOSConfig.h |
configTOTAL_HEAP_SIZE(当前 8192) |
| 任务栈大小 | freertos.c wlwg_Tasks_attributes |
.stack_size = 128 * 4 |