如何自制一款丝杆滑台?
这篇文章解决一个问题:网上步进电机的教程一大堆,但没几个把“电机转起来”和“平台精确到位”说清楚。用42步进电机做丝杆滑台,你会立刻撞上开环控制的三个坑:丢步、回程间隙、联轴器打滑。本文从开环一路做到闭环,提升重复定位精度,再接入大夏龙雀 DX-NR11 2.4G 无线串口模组,让平台支持远程遥控——一套“精确定位 + 无线控制”的完整闭环系统。

适用对象:会用 STM32CubeMX / HAL 库、想玩机电一体化的学生和创客。
你会得到:完整接线、脉冲当量计算、开环 / 闭环 C 代码、实测数据表、踩坑清单。
前置条件:STM32F405RGT6 最小系统板 + HAL 库基础;机械部分可直接用成品丝杆滑台。
项目背景:为什么开环的丝杆滑台“不听话”

很多人以为“发了 N 个脉冲,滑台就该走 N 步”。实际工程里根本不是这样,主要误差源有三个:
-
失步(丢步):电机启动加速度太大、负载过重或速度超过矩频特性,转子跟不上脉冲直接丢步——脉冲发出去了,轴没转。
-
回程间隙:丝杆和螺母之间有间隙,换向时先空转一段才带动工作台,造成“正着走准、反着走偏”。
-
传动打滑:刚性不够时电机轴转了,丝杆端没跟上。
所以开环只能“大概准”。要真正精确,就得把实际位置量出来,反馈给单片机做闭环——这就是本项目的核心。
硬件清单与接线
电子硬件清单
|
部件 |
型号 / 规格 |
作用 |
|---|---|---|
|
主控 |
STM32F405RGT6 最小系统板 |
控制核心 |
|
步进电机 |
42BYGH,1.8°,两相四线 |
驱动丝杆 |
|
驱动器 |
TB6600 |
脉冲 → 驱动电流 |
|
丝杆滑台 |
T8 丝杆(导程 8mm)+ 光轴×2 + 滑块 |
直线进给机构 |
|
限位开关 |
轻触开关 ×2 |
回零 |
|
磁编码器 |
AS5600 模块 |
丝杆转角反馈 |
|
磁铁 |
径向充磁圆磁铁 D6 |
配合 AS5600 |
|
通信模块 |
DX-NR11-24T12S(2.4G 无线串口) |
无线遥控 |
|
电源 |
12V 3A |
驱动供电 |
|
线材 |
杜邦线、接线端子 |
连接 |
引脚定义
|
STM32 引脚 |
连接对象 |
说明 |
|---|---|---|
|
PA0 |
TIM2_CH1 → TB6600 PUL |
步进脉冲(硬件 PWM) |
|
PB0 |
TB6600 DIR |
运动方向 |
|
PB1 |
TB6600 ENA |
使能(低有效) |
|
PB12 |
左限位开关 |
回零原点 |
|
PB13 |
右限位开关 |
行程保护 |
|
PB6 |
I2C1 SCL(AS5600 + OLED) |
I2C 时钟,总线共用 |
|
PB7 |
I2C1 SDA(AS5600 + OLED) |
I2C 数据,总线共用 |
|
PA2 |
USART2 TX → NR11 RXD |
串口发送(3.3V 直连) |
|
PA3 |
USART2 RX ← NR11 TXD |
串口接收无线命令 |
系统架构
执行链是“STM32发脉冲 → TB6600 驱动 → 步进电机 → 丝杆 → 工作台”;反馈链是“丝杆转角 → AS5600 磁编码器 → 主控”,构成位置闭环。限位开关负责回零和防撞。此外,STM32 的串口接一块 DX-NR11 2.4G 无线串口模组:遥控端的 NR11(电脑 USB-TTL)通过 2.4G 空中链路与滑台端透传命令帧,实现远程定位、回零与坐标回读——本地闭环 + 无线遥控,就是这套平台的核心功能。

关键参数:脉冲当量怎么算


先做开环:让滑台按脉冲走
开环的核心是“用定时器、硬件 PWM 按固定频率输出 PUL 脉冲”。代码如下:
// CubeMX 配置:TIM2 通道1 PWM 输出到 PA0,预分频 0
// TIM2 时钟 = APB1×2 = 84MHz,PWM 频率 = 84MHz / (ARR+1)
volatile uint32_t pulse_remain; // 剩余脉冲数
volatile uint8_t is_moving; // 运动标志
// TIM2 的 CC 中断:每个 PWM 周期 = 1 个脉冲
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
if (pulse_remain > 0)
{
pulse_remain--;
if (pulse_remain == 0)
{
is_moving = 0;
HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_1); // 停发脉冲
}
}
}
}
// 设置脉冲频率(Hz)
void step_set_freq(uint32_t freq_hz)
{
uint32_t arr = 84000000UL / freq_hz - 1;
__HAL_TIM_SET_AUTORELOAD(&htim2, arr);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, arr / 2); // 50% 占空比
}
// 按步数运动
void move_steps(int32_t steps, uint32_t freq_hz)
{
if (steps == 0) return;
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin,
(steps > 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
pulse_remain = (steps > 0) ? steps : -steps;
step_set_freq(freq_hz);
is_moving = 1;
HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1); // 开 PWM 并开中断
while (is_moving); // 阻塞等待
}
回零逻辑:启动时向负方向低速走,直到触发左限位(PB12),把当前位置记为原点,同时清零 AS5600 累计值。
闭环改造:AS5600 磁编码器反馈

为什么反馈装在丝杆上而不是电机上
装在电机轴上只能证明“电机转了”,证明不了“丝杆转了”。联轴器打滑、丝杆间隙都不会在电机端体现,所以必须测最终输出——丝杆转角。
AS5600 安装

3D 打印一个小支架,把径向充磁的圆磁铁粘在丝杆末端,AS5600 模块正对磁铁,固定在支架上。上电后用串口打印原始角度,转动丝杆确认读数连续变化、无跳变。
读取与多圈累计
// CubeMX 配置:I2C1(PB6=SCL / PB7=SDA),标准模式 100kHz
// AS5600 地址 0x36,OLED 地址 0x3C,共用同一总线
#define AS5600_ADDR 0x36
#define REG_ANGLE 0x0C // RAW ANGLE (12bit)
uint16_t as5600_read(void)
{
uint8_t reg = REG_ANGLE;
uint8_t buf[2];
HAL_I2C_Master_Transmit(&hi2c1, (AS5600_ADDR << 1), ®, 1, 10);
HAL_I2C_Master_Receive (&hi2c1, (AS5600_ADDR << 1) | 1, buf, 2, 10);
return ((uint16_t)buf[0] << 8 | buf[1]) & 0x0FFF;
}
// 多圈累计
static uint16_t last_angle;
volatile int32_t total_cnt;
void sample_position(void)
{
uint16_t a = as5600_read();
int16_t diff = (int16_t)(a - last_angle);
if (diff > 2048) diff -= 4096; // 跨零回绕
if (diff < -2048) diff += 4096;
total_cnt += diff;
last_angle = a;
}
位置环:误差补脉冲 + 回程间隙补偿
#define PULSE_PER_CNT (3200.0f / 4096.0f) // 1 个编码器计数 ≈ 0.78 个脉冲
volatile int32_t target_cnt; // 目标位置
volatile int32_t act_cnt; // 实际位置
#define DEAD_ZONE 3
void position_loop(void)
{
sample_position();
act_cnt = total_cnt;
int32_t err = target_cnt - act_cnt;
if (err > DEAD_ZONE || err < -DEAD_ZONE)
{
int32_t need = (int32_t)(err * PULSE_PER_CNT);
move_steps(need, 300); // 低速补齐
}
}
#define BACKLASH_CNT 52 // 标定值
static uint8_t last_dir = 0;
void backlash_comp(uint8_t dir)
{
if (dir != last_dir)
{
int32_t gap = dir ? BACKLASH_CNT : -BACKLASH_CNT;
move_steps((int32_t)(gap * PULSE_PER_CNT), 200);
}
last_dir = dir;
}
主循环里周期性调用 position_loop(),把目标位置与实测位置之差换算成脉冲补上去,直到误差进入死区。间隙补偿在每次换向前先“预走”一个实测间隙量。
闭环实测
|
测试项 |
命令位移 (mm) |
实测位移 (mm) |
|---|---|---|
|
正向 10 |
10.000 |
10.002 |
|
正向 20 |
20.000 |
19.998 |
|
正向 30 |
30.000 |
30.004 |
|
高速 (2000pps) 20 |
20.000 |
20.011 |
|
往返重复定位 ×10 |
回到 0 |
≤0.018 |
结论:闭环后高速丢步被拉回(20.011mm),重复定位从 ±0.1mm 级别收敛到 ≤0.02mm。对 2.5μm 理论分辨率来说,剩余误差主要来自丝杆本身的间隙与 AS5600 的标定误差。
自制STM32F405RGT6核心板设计
最小系统板原理图

实拍图

无线遥控:NR11 2.4G 远程控制

定位闭环只解决了“本地精确”,要让这台平台真正“有用”,还得能远程控制。这里引入大夏龙雀 DX-NR11-24T12S:一块 2.4G 无线串口模组,板载 PCB 天线、半双工、默认 9600 波特率透明传输,空旷距离可达 200m。滑台端一块 NR11 接 STM32 的串口,遥控端再放一块 NR11(电脑 USB-TTL,或与 BT121 桥接连手机),两端同信道同地址即可透传——STM32 端解析命令帧执行定位,再把坐标回传。
接线与电平匹配

NR11 是 3.3V 逻辑器件(供电 2.5~3.6V,典型 3.3V),STM32F405 同为 3.3V,电平天然匹配,TXD、RXD 直连即可:
|
连接 |
说明 |
|---|---|
|
STM32 PA2(USART2_TX)→ NR11 RXD |
同为 3.3V 电平,直连 |
|
STM32 PA3(USART2_RX)← NR11 TXD |
3.3V 电平,直连 |
|
NR11 VCC |
接 STM32 板 3.3V |
|
NR11 GND |
与 STM32 共地 |
NR11 直接取 STM32 板的 3.3V 电源即可;注意 NR11 模块供电超过 3.6V 有烧毁风险。
模块配置(AT 指令)
两端模块出厂默认射频参数一致(空中速率 2Mbps、信道 000、地址 10,11),不改也能直接透传;要换信道、地址隔离干扰,进入 AT 模式配置:
通信协议(自定义帧)
透明传输只负责“传字节”,协议要自己定。这里用 6 字节定长帧,末尾带校验防误触发:
|
字段 |
长度 |
说明 |
|---|---|---|
|
0xAA 0x55 |
2B |
帧头 |
|
CMD |
1B |
命令码 |
|
D1 D2 |
2B |
参数:目标位置,单位 0.01mm |
|
SUM |
1B |
校验:AA^55^CMD^D1^D2 |
|
CMD |
功能 |
参数 |
|---|---|---|
|
0x01 |
回零 |
无 |
|
0x02 |
定位 |
D1D2 = 目标位置 ×100(0.01mm) |
|
0x03 |
查询位置 |
无,回传坐标帧 |
|
0x04 |
急停 |
无 |
例:定位到 20.00mm,目标值 2000 = 0x07D0,命令帧为 AA 55 02 07 D0 2A(SUM = AA^55^02^07^D0 = 0x2A)。
STM32 端程序
STM32 用 USART2 接收,接收用状态机逐字节解析帧头、命令和校验:
// CubeMX 配置:USART2(PA2=TX / PA3=RX),9600, 8, N, 1
volatile uint8_t rx_byte;
uint8_t rx_state = 0; // 接收状态机
uint8_t rx_cmd, rx_d1, rx_d2;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
uint8_t c = rx_byte;
switch (rx_state)
{
case 0: rx_state = (c == 0xAA) ? 1 : 0; break;
case 1: rx_state = (c == 0x55) ? 2 : 0; break;
case 2: rx_cmd = c; rx_state = 3; break;
case 3: rx_d1 = c; rx_state = 4; break;
case 4: rx_d2 = c; rx_state = 5; break;
case 5:
if ((0xAA ^ 0x55 ^ rx_cmd ^ rx_d1 ^ rx_d2) == c)
handle_cmd(rx_cmd, (uint16_t)(rx_d1 << 8 | rx_d2));
rx_state = 0;
break;
}
HAL_UART_Receive_IT(&huart2, &rx_byte, 1); // 继续接收
}
}
void uart_send(uint8_t c)
{
HAL_UART_Transmit(&huart2, &c, 1, 10);
}
效果验证
电脑端串口助手(sscom)接 USB-TTL + NR11,发一帧 AA 55 02 07 D0 2A,滑台走到 20.00mm 并回传坐标。


开发记录
-
AS5600 读数跳变:磁铁偏心或用了轴向充磁,换成径向充磁磁铁并确保对中。
-
TB6600 大电流时主控复位:主控与驱动分电源供电、共地。
-
限位开关必须接:第一次调试把最大速度调低,限位触发立即停,防止撞机顶坏丝杆。
-
NR11 是 3.3V 器件:不能 5V 直供;与 STM32 同为 3.3V 电平,TXD / RXD 直连即可。
-
NR11同信道同地址才能通信:两端射频参数不一致就“静默”,先发 AT 确认模块在线。
-
天线避开金属:NR11 用板载天线,别贴金属外壳或大面积铺铜,天线方向留至少 5mm 净空。
更多推荐



所有评论(0)