本次活动使用SeeedStudio的步进电机,产品介绍页面如下
https://wiki.seeedstudio.com/Gear_Stepper_Motor_Driver_Pack/
产品的主要规格如下
| Operating Voltage | 5V |
| Phase | 4 |
| Reduction ratio | 1/64 |
| Step Angle | 5.625°/64 |
| Diameter | 28mm / Nema 11 |
| Idle In-traction Frequency | >500HZ |
| Idle Out-traction Frequency | >1000HZ |
| Resistance | 21±7% |
| Noise | ≤40dB |
| Drive mode | four-phase eight-stepping |
步进电机通过ULN2003驱动,驱动方式为四相八拍(半步)模式,是在“单相通电”和“两相通电”之间交替切换,从而将步距角减半,使转动更平滑。

| 1 | 1 | 0 | 0 | 0 | 仅A相 |
| 2 | 1 | 1 | 0 | 0 | A+B相 |
| 3 | 0 | 1 | 0 | 0 | 仅B相 |
| 4 | 0 | 1 | 1 | 0 | B+C相 |
| 5 | 0 | 0 | 1 | 0 | 仅C相 |
| 6 | 0 | 0 | 1 | 1 | C+D相 |
| 7 | 0 | 0 | 0 | 1 | 仅D相 |
| 8 | 1 | 0 | 0 | 1 | D+A相 |
循环执行这8步,电机就完成一个“电气周期”的旋转。反转时,只需将序列顺序倒过来执行即可。
使用NUCLEO-C562RE的Arduino接口的数字引脚来控制步进电机

NUCLEO-C562RE开发板和步进电机的驱动接口的连接如下
| Arduino D7(PA8) | IN1 |
| Arduino D6(PB10) | IN2 |
| Arduino D5(PB4) | IN3 |
| Arduino D4(PC1) | IN4 |
| GND | GND |
| 3V3 | VCC |
| 5V | VM |
之前的工程中使用的CubeMX2工程将工程另存为新的工程文件,可以达到工程复用的目的。

在此基础上添加步进电机驱动引脚的设置,将PA8、PB10、PB4、PC1设置为GPIO输出模式,初始电平设置为低电平。

保存并生成代码

按照四相八拍的顺序控制步进电机的相位变化,从而驱动电机转动,定义电机驱动引脚的宏定义和函数声明如下
stepmotor.h
#ifndef __STEPMOTOR_H_
#define __STEPMOTOR_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/* 步进电机引脚定义(PA8、PB10、PB4、PC1)*/
#define STEPPER_PIN0_PORT HAL_GPIOA
#define STEPPER_PIN0_PIN HAL_GPIO_PIN_8
#define STEPPER_PIN1_PORT HAL_GPIOB
#define STEPPER_PIN1_PIN HAL_GPIO_PIN_10
#define STEPPER_PIN2_PORT HAL_GPIOB
#define STEPPER_PIN2_PIN HAL_GPIO_PIN_4
#define STEPPER_PIN3_PORT HAL_GPIOC
#define STEPPER_PIN3_PIN HAL_GPIO_PIN_1
/* 方向定义 (正数=顺时针,负数=逆时针)*/
#define STEPPER_CW 1 /* 顺时针 */
#define STEPPER_CCW -1 /* 逆时针 */
/* 公共函数 */
void stepper_init(void);
void stepper_step(int32_t steps, uint16_t delay_ms);
void stepper_rotate(int32_t angle, uint16_t delay_ms);
void stepper_stop(void);
void stepper_test(void); /* 测试 */
#ifdef __cplusplus
}
#endif
#endif /* C562RE_STEPMOTOR_CMAKE_STEPMOTOR_H_ */步进电机驱动函数具体实现以及测试代码的实现如下
#include "stepmotor.h"
/* 四相八拍励磁序列(8 个状态)
位序: [PA8、PB10、PB4、PC1] 对应 [A, B, C, D] 相 */
static const uint8_t step_sequence[8] = {
0b0001, /* 1: A */
0b0011, /* 2: A+B */
0b0010, /* 3: B */
0b0110, /* 4: B+C */
0b0100, /* 5: C */
0b1100, /* 6: C+D */
0b1000, /* 7: D */
0b1001 /* 8: D+A */
};
/* 每圈步数(参考实测值)*/
#define STEPPER_STEPS_PER_REV 509U
/* 私有:将励磁状态写入 4 个引脚 */
static void stepper_write_phase(uint8_t seq)
{
HAL_GPIO_WritePin(STEPPER_PIN0_PORT, STEPPER_PIN0_PIN,
(seq & 0x01) ? HAL_GPIO_PIN_SET : HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEPPER_PIN1_PORT, STEPPER_PIN1_PIN,
(seq & 0x02) ? HAL_GPIO_PIN_SET : HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEPPER_PIN2_PORT, STEPPER_PIN2_PIN,
(seq & 0x04) ? HAL_GPIO_PIN_SET : HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEPPER_PIN3_PORT, STEPPER_PIN3_PIN,
(seq & 0x08) ? HAL_GPIO_PIN_SET : HAL_GPIO_PIN_RESET);
}
/**
* @brief 初始化步进电机
* @note 引脚已在 CubeMX 中配置为输出,需确保初始状态为低电平
*/
void stepper_init(void)
{
stepper_stop();
}
/**
* @brief 驱动电机旋转指定步数
* @note steps 单位为"完整序列周期"(8 个相位/周期)
* @param steps: 正数=顺时针,负数=逆时针
* @param delay_ms: 步间延时(ms)
*/
void stepper_step(int32_t steps, uint16_t delay_ms)
{
int32_t direction = (steps >= 0) ? 1 : -1;
uint32_t cycles = (steps >= 0) ? (uint32_t)steps : (uint32_t)(-steps);
uint32_t i, j;
for (i = 0; i < cycles; i++)
{
for (j = 0; j < 8; j++)
{
uint8_t phase_idx;
if (direction > 0)
{
phase_idx = j; /* 正序 0,1,2,...,7 */
}
else
{
phase_idx = 7 - j; /* 逆序 7,6,5,...,0 */
}
stepper_write_phase(step_sequence[phase_idx]);
HAL_Delay(delay_ms);
}
}
}
/**
* @brief 旋转指定角度
* @param angle: 正数=顺时针,负数=逆时针
* @param delay_ms: 步间延时(ms)
*/
void stepper_rotate(int32_t angle, uint16_t delay_ms)
{
int32_t steps = (int32_t)(angle * (STEPPER_STEPS_PER_REV / 360.0f));
stepper_step(steps, delay_ms);
}
/**
* @brief 停止电机:所有线圈断电(释放扭矩)
*/
void stepper_stop(void)
{
HAL_GPIO_WritePin(STEPPER_PIN0_PORT, STEPPER_PIN0_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEPPER_PIN1_PORT, STEPPER_PIN1_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEPPER_PIN2_PORT, STEPPER_PIN2_PIN, HAL_GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEPPER_PIN3_PORT, STEPPER_PIN3_PIN, HAL_GPIO_PIN_RESET);
}
/**
* @brief 测试:正转 90° → 停 → 反转 90° → 停,循环
* @note 用于验证电机接线和驱动是否正常
*/
void stepper_test(void)
{
/* 顺时针转 90° */
stepper_rotate(90, 2);
stepper_stop();
HAL_Delay(500);
/* 逆时针转 90° */
stepper_rotate(-90, 2);
stepper_stop();
HAL_Delay(500);
}将步进电机的驱动代码添加到工程中,并在CMakeLists.txt添加源码的管理

后续训练中需要控制电机的速度,用于采集不同工况下的传感器数据,通过串口指令控制步进电机的旋转角度以及速度是很不错的选择。程序运行流程如下
[PC/串口终端] │ (USB/串口指令: "90,CW") ▼ [NUCLEO-C562RE] │ │ 1. 解析指令 → 角度、方向 │ 2. 角度 → 步数 (步数 = 角度/360 × 4096) │ 3. 根据方向选择相序遍历顺序 ▼ [ULN2003 驱动板] │ (IN1~IN4 接收相序信号,放大电流) ▼ [28BYJ-48 步进电机] │ (内置 1/64 减速箱) ▼ [输出轴旋转指定角度]
从指令中提取电机旋转的角度和速度的函数实现如下
/**
* @brief 解析 JSON 命令并执行电机动作
*/
static void parse_json_cmd(int *angle, uint32_t *speed)
{
char *p = (char *)uart_rx_buf;
/* 解析两种格式:带空格和不带空格 */
if (sscanf(p, "{\"angle\":%d,\"speed\":%lu}", angle, speed) != 2)
{
sscanf(p, "{\"angle\": %d, \"speed\": %lu}", angle, speed);
}
}
/**main函数中初始化串口和电机接口,在主循环中解析串口接收到的指令,提供其中的角度和速度值,用于控制电机的转角和速度
mx_basic_stdio_init();
stepper_init();
printf("\r\n=== Stepper Motor JSON Command Test ===\r\n");
printf("Usage: {\"angle\": 5, \"speed\": 1.2}\r\n");
printf("angle>0 CW, angle<0 CCW, speed=ms/step\r\n\r\n");
/* 启动 UART 中断接收 */
HAL_UART_ReceiveToIdle_IT(mx_usart2_uart_gethandle(), rx_buf, RX_BUF_SIZE);
while (1) {
if (rx_data_ready)
{
/* 解析指令 */
parse_json_cmd(&target_angle, &target_speed);
printf("OK: angle=%d, speed=%lu\r\n", target_angle, target_speed);
/* 电机转动:正角度=顺时针,负角度=逆时针 */
stepper_rotate(target_angle, (uint16_t)target_speed);
stepper_stop();
/* 清空接收缓冲区 */
memset(rx_buf, 0, sizeof(rx_buf));
rx_data_len = 0;
rx_data_ready = 0;
HAL_UART_ReceiveToIdle_IT(mx_usart2_uart_gethandle(), rx_buf, RX_BUF_SIZE);
}
HAL_Delay(10);
}烧录程序到开发板,串口输入电机控制指令{"angle": 5, "speed": 1.2},实现电机控制。电机控制的效果见视频中的演示。
我要赚赏金
