在上一个过程帖的基础上,完成进阶任务,在基础任务上实现:速度调节功能、加速/减速控制、按键或串口控制运动,例如:启动、停止、反转。以及,自由发挥方向(不做强制要求):梯形加减速控制、多种运动模式、定位控制(步数控制)、PWM方式生成STEP。
参考过程帖的接线方式,最后代码及展示效果如下,视频链接为https://b23.tv/BZQyX2d,实现了以下功能:
| 命令交互 | 启动 (s / start) | 使能电机,进入加速段,开始运行 |
| 停止 (x / stop) | 立即停止电机,禁用使能,状态置为 stop | |
| 反转方向 (r / reverse) | 需停止状态下切换方向,修改方向引脚 | |
| 加速 (+ / faster) | 减少目标脉冲间隔(提高目标速度),若正在运行则重新进入加速段 | |
| 减速 (- / slower) | 增加目标脉冲间隔(降低目标速度),若正在运行则重新进入减速段 | |
| 查看状态 (i / info) | 显示当前运行状态、方向、当前/目标速度、使能状态、位置 | |
| 帮助 (h / help) | 打印所有可用命令 | |
| 运动控制 | S曲线加减速 | 加速段与减速段均使用正弦平方插值(S曲线),平滑启动与停止 |
| 三段式状态机 | 支持 accel → run → decel 自动过渡,以及手动干预时的状态重设 | |
| 速度范围限制 | 最快 MIN_DELAY_US (100µs),最慢 MAX_DELAY_US (5000µs) | |
| 启动速度可配置 | START_DELAY_US = 2000µs,避免急停或急启 | |
| 加速/减速步数独立设定 | ACCEL_STEPS / DECEL_STEPS 均为 800 步,可独立调整 | |
| 硬件驱动 | 步进脉冲输出 | 通过 GPIO 引脚 step 产生方波,脉冲宽度等于当前计算出的延迟 |
| 方向控制 | 单独方向引脚,高低电平分别对应正反向 | |
| 使能控制 | 低电平有效,启动时使能,停止时禁能(节省功耗,防止误动作) | |
| 状态与计数 | 位置累计 | 每发一个脉冲更新 position,方向决定增减 |
| 总步数统计 | total_steps 累计所有已发送的脉冲数 | |
| 实时速度反馈 | current_delay 随加速/减速段实时更新,匀速段等于目标速度 | |
| 串口通信 | 非阻塞输入轮询 | 使用 select.poll,延时 1ms 检查串口,不阻塞电机脉冲输出 |
| 命令解析与容错 | 支持大小写、别名,未知命令提示帮助 |

import machine
import utime
import select
import sys
import math
# ========== 引脚定义 ==========
motor_dir = machine.Pin(5, machine.Pin.OUT) # 方向
motor_step = machine.Pin(4, machine.Pin.OUT) # 脉冲
motor_en = machine.Pin(1, machine.Pin.OUT) # 使能(低有效)
# ========== 梯形加减速参数 ==========
MIN_DELAY_US = 100 # 最高速度(最小间隔)
MAX_DELAY_US = 5000 # 最低速度(最大间隔)
START_DELAY_US = 2000 # 启动速度(较平缓起步)
ACCEL_STEPS = 800 # 加速段步数
DECEL_STEPS = 800 # 减速段步数
# ========== 状态变量 ==========
state = 'stop' # 'stop', 'accel', 'run', 'decel'
current_delay = START_DELAY_US # 当前实际速度
target_delay = START_DELAY_US # 用户设定的目标速度
step_counter = 0 # 当前段内已走步数
total_steps = 0 # 总运行步数
direction = 1 # 1=正向, 0=反向
position = 0 # 当前位置
# ========== 初始化 ==========
motor_en.value(1) # 初始禁用电机(高电平)
motor_dir.value(1)
motor_step.value(0)
poll = select.poll()
poll.register(sys.stdin, select.POLLIN)
print("=" * 50)
print("步进电机串口控制系统(梯形加减速)")
print("=" * 50)
print("命令列表:")
print(" s / start : 启动(自动梯形加速)")
print(" x / stop : 停止(自动梯形减速)")
print(" r / reverse : 反转方向(需先停止)")
print(" + / faster : 提高目标速度")
print(" - / slower : 降低目标速度")
print(" i / info : 查看状态")
print(" h / help : 显示帮助")
print("=" * 50)
print(f"速度范围: {MIN_DELAY_US}us(快) ~ {MAX_DELAY_US}us(慢)")
print(f"当前目标速度: {target_delay}us")
def show_status():
dir_str = '正向' if direction else '反向'
en_str = '使能' if motor_en.value() == 0 else '禁用'
print(f"[状态] 运行: {state} | 方向: {dir_str}")
print(f" 当前速度: {current_delay}us | 目标速度: {target_delay}us")
print(f" 电机: {en_str} | 位置: {position}")
def parse_cmd(cmd_str):
cmd = cmd_str.strip().lower()
if cmd in ('s', 'start', 'go'):
return 'start'
elif cmd in ('x', 'stop', 'halt'):
return 'stop'
elif cmd in ('r', 'reverse', 'rev'):
return 'reverse'
elif cmd in ('+', 'faster', 'up'):
return 'faster'
elif cmd in ('-', 'slower', 'down'):
return 'slower'
elif cmd in ('i', 'info', 'status'):
return 'info'
elif cmd in ('h', 'help', '?'):
return 'help'
return None
def calculate_s_curve_delay(step, total_steps, start_d, end_d):
"""S曲线插值:从 start_d 单调变化到 end_d"""
if total_steps == 0:
return end_d
ratio = min(1.0, step / total_steps)
factor = math.sin(math.pi / 2 * ratio) ** 2
return int(start_d + (end_d - start_d) * factor)
# ========== 主循环 ==========
while True:
# ---- 检查串口输入 ----
events = poll.poll(1)
if events:
try:
line = sys.stdin.readline().strip()
if not line:
continue
cmd = parse_cmd(line)
if cmd == 'start':
if state in ('run', 'accel'):
print("[提示] 电机已在运行中")
else:
state = 'accel'
step_counter = 0
motor_dir.value(direction)
motor_en.value(0) # 使能电机
print("[启动] 开始梯形加速...")
show_status()
elif cmd == 'stop':
state = 'stop' # 关键:停止状态
motor_en.value(1) # 禁用电机(高电平)
print("[提示] 电机已停止")
show_status()
elif cmd == 'reverse':
if state != 'stop':
print("[错误] 请先停止电机再反转方向!")
else:
direction = 0 if direction else 1
motor_dir.value(direction)
print(f"[反转] 方向: {'正向' if direction else '反向'}")
show_status()
elif cmd == 'faster':
old = target_delay
target_delay = max(MIN_DELAY_US, target_delay - 300)
print(f"[加速] 目标: {old}us → {target_delay}us")
# 如果电机正在运行,重新调整运动曲线
if state == 'run':
state = 'accel'
step_counter = 0
print(" 重新加速")
elif state == 'decel':
state = 'accel'
step_counter = 0
print(" 改为加速")
show_status()
elif cmd == 'slower':
old = target_delay
target_delay = min(MAX_DELAY_US, target_delay + 300)
print(f"[减速] 目标: {old}us → {target_delay}us")
if state == 'run':
state = 'decel'
step_counter = 0
print(" 重新减速")
elif state == 'accel':
state = 'decel'
step_counter = 0
print(" 改为减速")
show_status()
elif cmd == 'info':
show_status()
elif cmd == 'help':
print("命令: s/start, x/stop, r/reverse, +/faster, -/slower, i/info, h/help")
else:
print(f"[未知命令] '{line}',输入 h 查看帮助")
except Exception as e:
print(f"[错误] {e}")
# ---- 电机驱动(S曲线加减速)----
if state == 'stop':
utime.sleep_ms(1)
continue
if motor_en.value() == 1:
motor_en.value(0) # 确保使能
# 加速段
if state == 'accel':
current_delay = calculate_s_curve_delay(step_counter, ACCEL_STEPS, START_DELAY_US, target_delay)
step_counter += 1
if step_counter >= ACCEL_STEPS or abs(current_delay - target_delay) <= 1:
state = 'run'
step_counter = 0
current_delay = target_delay
print("[加速完成] 进入匀速")
show_status()
# 减速段(复用插值函数,从 target_delay 渐变到 START_DELAY_US)
elif state == 'decel':
current_delay = calculate_s_curve_delay(step_counter, DECEL_STEPS, target_delay, START_DELAY_US)
step_counter += 1
if step_counter >= DECEL_STEPS or abs(current_delay - START_DELAY_US) <= 1:
state = 'run'
step_counter = 0
current_delay = target_delay
print("[减速完成] 进入匀速")
show_status()
continue
# 发送一个脉冲
motor_step.value(1)
utime.sleep_us(current_delay)
motor_step.value(0)
utime.sleep_us(current_delay)
position += 1 if direction else -1
total_steps += 1
我要赚赏金
