这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【let's_do|2026年第3期】步进电机的串口控制

共1条 1/1 1 跳转至

【let's_do|2026年第3期】步进电机的串口控制

高工
2026-09-22 20:47:01     打赏

【let's do|2026年第3期】步进电机的串口控制

本文介绍了 Let's Do|2026年第3期 活动,通过主控 NUCLEO-C562RE 开发板实现 28BYJ48 步进电机驱动和串口控制的项目设计,包括硬件连接、环境搭建、工程测试等流程。

项目介绍

NUCLEO-C562RE 开发板通过串口中断控制 28BYJ-48 步进电机旋转的角度、方向、速度的项目设计。

cover_stepmotor_run_uart.jpeg

  • 步进电机和驱动板介绍、硬件连接;

  • 串口通信:工程创建、参数配置、IDE 导入、代码、编译运行等;

  • 电机驱动:头文件和源文件、驱动旋转测试;

  • 角度控制:关键代码、串口中断、指令发送、自定义旋转;


28BYJ-48

28BYJ-48 是一款常见的低成本、小扭矩 5 线单极步进电机,可使用 ULN2003 控制器和单片机实现旋转控制,广泛用于打印机、扫描仪、摄像机云台、空调、家电、玩具、消费电子等领域。

stepper_28byj48.jpg

参数

28BYJ-48 是一款适应性强的 5V DC步进电机,设计紧凑,适用于各种应用。

它具有四个相位,速度变化比为1/64,步距角为5.625°/64步,允许精确控制。

电机在100Hz频率下工作,在25°C时的直流电阻为50Ω ±7%。

它的空载牵引频率大于600Hz,空载脱离频率超过1000Hz,确保在不同场景下的可靠性。

28BYJ-48 在 120Hz 时的自定位转矩和牵引转矩均超过 34.3mN.m,提供强劲性能。

其摩擦转矩范围为600到1200 gf.cm,而拉入转矩为300 gf.cm。


参数值描述
电机类型单极 4 相永磁式步进电机(5线制)
步距角5.625°(64 步/圈),配合减速齿轮后 0.0879°(实际输出轴 4096 步/圈)
减速比1:64(内部齿轮组减速)
额定电压5V 或 12V DC
相电流约 100mA(每相)
保持扭矩约 0.1 N·m(输出轴,受减速齿轮影响)
绕组电阻约 50Ω/相

实际输出轴步距角为 5.625°/64 ≈ 0.0879°,转一圈理论上需要 64×64=4096 步,实际可能存在误差。

详见:Seeed Studio Wiki .

ULN2003

ULN2003 是一款常用的达林顿晶体管阵列芯片,专为驱动高电流负载(如继电器、步进电机、LED阵列等)设计。其作用是将 MCU 输出的弱电流信号转换为大电流输出,是控制 28BYJ-48 步进电机的核心驱动芯片。

28byj-48_module.jpg

原理图

SCH_28byj-48_module.jpg

详见:Seeed Studio Wiki .

硬件连接

connect_stepper.jpg

由原理图可知,将驱动板的 4 个信号输入引脚分别与 STM32C562RET6 的 PA9、PC6、、PC9、PA7 相连;

sch_stepper_gpio.jpg

信号引脚电平为 3.3V,电机驱动电压为 5V,因此需将开关拨至 VM 端;


STM32C562RET6ULN2003 Module
Digital Pin 8 (PA9)IN1
Digital Pin 9 (PC6)IN2
Digital Pin 10 (PC9)IN3
Digital Pin 11 (PA7)IN4
GNDGND
3V3VCC
5VVM


串口通信

使用串口中断控制步进电机,因此需首先调通串口通信。

由原理图可知,板载 ST-LINK 虚拟串口 (VCP) 连接对应 USART2,引脚为 PA2 (TX) 和 PA3 (RX) ;

sch_uart2_pin.jpg

工程创建

打开 STM32CubeMX2 软件,从开发板新建工程;

点击左侧工具栏 Open Peripherals 按钮,展开 Connectivity ,使能 USART2 并配置为异步通信;

mx2_uart2_enable.jpg

展开 System - GPIO ,将 USART2 对应引脚配置为 PA2 和 PA3,并使能全局中断;

mx2_uart2_gpio_nvic.jpg

进入 Pinout 标签页,将 ULN2003A 模块的信号引脚 PA9、PC6、、PC9、PA7 均设置为输出模式,初始低电平;

mx2_gpio_config.jpg

保存配置,生成 CMake工程;

详见:【let's_do|2026年第3期】介绍、环境搭建、工程测试 .

导入 IDE

打开 STM32CubeIDE 软件,依次选择 File - Import ,选择 STM32 CMake Project ,选择目标工程文件所在路径;

ide_import_cmake.jpg

配置工具链,选择主控芯片型号 STM32C562RETx ;

ide_toolchain.jpg

待导入完成,点击工具栏 小锤子 按钮,编译工程,无报错。

代码

打开 StepMotor_cmake/main.c 文件,添加代码如下

 #include "main.h"
 #include <stdio.h>
 #include <string.h>
 /* Private typedef -----------------------------------------------------------*/
 /* Private define ------------------------------------------------------------*/
 #define RX_TIMEOUT 10000U
 #define TX_TIMEOUT 1000U
 #define MAX_BUFFER_SIZE 128U
 /* Private macro -------------------------------------------------------------*/
 /* Private variables ---------------------------------------------------------*/
 uint8_t RxBuffer[MAX_BUFFER_SIZE];
 /* Private functions prototype -----------------------------------------------*/
 
 int __io_getchar(void) {
   uint8_t ch = 0;
   HAL_UART_Receive(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 10000);
   return ch;
 }
 
 int __io_putchar(int ch) {
   HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 100);
   return ch;
 }
 
 int main(void)
 {
   if (mx_system_init() != SYSTEM_OK)
   {
     return (-1);
   }
   else
   {
     while (1) {
      printf("Hello World!\r\n");
      HAL_Delay(500);
     }
   }
 } /* end main */

保存代码。

效果

重新编译工程、点击工具栏 Run 按钮,配置调试器为 ST-LINK ,运行程序;

ide_run_debug_config.jpg

运行串口调试软件,配置波特率等参数,打开串口;

连续接收开发板输出的字符串;

print_hello.gif


电机驱动

在 IDE 项目中新建步进电机驱动的头文件 stepmotor.h 和源文件 stepmotor.c 。

stepmotor.h

 #ifndef __STEPMOTOR_H_
 #define __STEPMOTOR_H_
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #include "main.h"
 
 /* 步进电机引脚定义(PA9, PC6, PC9, PA7)*/
 #define STEPPER_PIN0_PORT   HAL_GPIOA
 #define STEPPER_PIN0_PIN    HAL_GPIO_PIN_9
 #define STEPPER_PIN1_PORT   HAL_GPIOC
 #define STEPPER_PIN1_PIN    HAL_GPIO_PIN_6
 #define STEPPER_PIN2_PORT   HAL_GPIOC
 #define STEPPER_PIN2_PIN    HAL_GPIO_PIN_9
 #define STEPPER_PIN3_PORT   HAL_GPIOA
 #define STEPPER_PIN3_PIN    HAL_GPIO_PIN_7
 
 /* 方向定义 (正数=顺时针,负数=逆时针)*/
 #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_ */

保存代码。

stepmotor.c

 #include "stepmotor.h"
 
 /* 四相八拍励磁序列(8 个状态)
    位序: [PA9, PC6, PC9, PA7] 对应 [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 文件,将新增的驱动文件 stepmotor.c 添加至编译路径;

 # Stubs to be able to add compiler flag overrides, e.g. -Os
 target_sources(${CMAKE_PROJECT_NAME} PRIVATE
   # Add additional source files here
   stepmotor.c
 )

保存文件。

测试

修改 main.c 文件,添加如下代码

 int main(void)
 {
   if (mx_system_init() != SYSTEM_OK)
   {
     return (-1);
   }
   else
   {
     /* 初始化步进电机 */
     stepper_init();
     while (1)
     {
     /* 旋转测试 */
     printf("Stepper test: CW 90deg -> CCW 90deg...\r\n");
     stepper_test();
     }
   }
 }

保存代码,编译并上传固件。

复位开发板,运行程序;

步进电机正反交替旋转;

stepper_run.gif

角度控制

进一步通过串口中断实现步进电机旋转角度和速度的精确控制。

流程图

flowchart_stepper_uart.jpg

代码

修改 main.c 代码如下

 #include "main.h"
 #include "stepmotor.h"
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdbool.h>
 /* Private typedef -----------------------------------------------------------*/
 /* Private define ------------------------------------------------------------*/
 #define RX_TIMEOUT 10000U
 #define TX_TIMEOUT 1000U
 #define MAX_BUFFER_SIZE 128U
 /* Private macro -------------------------------------------------------------*/
 /* Private variables ---------------------------------------------------------*/
 volatile bool uart_rx_ready = false;
 uint8_t uart_rx_buf[MAX_BUFFER_SIZE] = {0};
 uint32_t uart_rx_len = 0;
 uint8_t rx_temp = 0;    /* 变量用于串口接收 */
 
 int target_angle = 0;
 uint32_t target_speed = 1;  /* 步间延时(ms) */
 /* Private functions prototype -----------------------------------------------*/
 
 int __io_getchar(void) {
   uint8_t ch = 0;
   HAL_UART_Receive(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 10000);
   return ch;
 }
 
 int __io_putchar(int ch) {
   HAL_UART_Transmit(mx_usart2_uart_gethandle(), (uint8_t *)&ch, 1, 100);
   return ch;
 }
 
 /**
   * @brief  UART 接收中断回调
   */
 void HAL_UART_RxCpltCallback(hal_uart_handle_t *huart, uint32_t size_byte, hal_uart_rx_event_types_t rx_event)
 {
   uint8_t ch = rx_temp;
   if (uart_rx_len < sizeof(uart_rx_buf) - 1)
   {
     uart_rx_buf[uart_rx_len++] = ch;
   }
   /* 收到换行符或大括号(JSON结束)标记接收完成 */
   if (ch == '\n' || ch == '}')
   {
     uart_rx_buf[uart_rx_len] = '\0';  /* 添加字符串结束符 */
     uart_rx_ready = true;
   }
   /* 继续接收下一个字节 */
   HAL_UART_Receive_IT(mx_usart2_uart_gethandle(), &rx_temp, 1);
 }
 
 /**
   * @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);
   }
 }
 /**
   * @brief  主程序
 */
 int main(void)
 {
   if (mx_system_init() != SYSTEM_OK)
   {
     return (-1);
   }
   else
   {
    /* 初始化步进电机 */
    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_Receive_IT(mx_usart2_uart_gethandle(), &rx_temp, 1);
     while (1) {
         if (uart_rx_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(uart_rx_buf, 0, sizeof(uart_rx_buf));
           uart_rx_len = 0;
           uart_rx_ready = false;
         }
 
         HAL_Delay(10);
     }
   }
 } /* end main */

保存代码。

效果

重新编译运行程序;

打开串口调试助手,根据提示输入 JSON 格式指令,如 {"angle": -90, "speed": 1} ,点击发送;

stepper_uart_control_print.jpg

步进电机根据指令,以 1ms/step 的速度逆时针旋转 90 度;

stepper_uart_run.gif

总结

本文介绍了 Let's Do|2026年第3期 活动,通过主控 NUCLEO-C562RE 开发板实现 28BYJ48 步进电机驱动和串口控制的项目设计,包括硬件连接、环境搭建、工程测试等流程,为后续项目的研究探索提供了参考。





关键词: 步进电机     串口     驱动     控制     中断     配置     STM32    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]