【STM32U3 评测】串口控制步进电机与LabVIEW数据采集
本文介绍了 NUCLEO-U3C5ZI-Q 开发板结合 TMC2209 驱动器、STM32CubeMX 和 CubeIDE,实现 42 步进电机的串口指令精确控制,设计 LabVIEW 上位机实现数据的自动连续采集的项目设计,包括环境搭建、工程创建、关键代码、流程图、效果演示等。
项目介绍
Nucleo-U3C5ZI-Q 开发板结合 TMC2209 驱动板实现串口和 LabVIEW 上位机控制 42 步进电机旋转。
- 准备工作:步进电机参数、环境搭建、硬件连接等;
- 工程测试:工程配置、流程图、关键代码,串口 JSON 指令控制步进电机旋转方向、角度和速度;
- LabVIEW 设计:前面板、程序面板设计,自动发送 JSON 消息,实现步进电机的自动化控制、数据采集与存储。
42 步进电机
42步进电机(NEMA17)是一种安装尺寸为 42mm×42mm 的两相混合式步进电机,广泛应用于3D打印机、数控机床和自动化控制系统。

42步进电机标准步距角为1.8°,最大输出力矩为0.5Nm,步距精度为5%;其定位精度与步距精度相关,通常采用脉冲信号进行控制。
42步进电机由定子和转子的齿数共同决定,如定子有48齿,分为8组绕组;转子有50齿。绕组分为A、B两相,通过特定的绕线方式,在同一相中,相对的绕组磁极相同,相邻的绕组磁极相反。

42步进电机的参数一般包括相数、步距角、额定电压、相电流、相电阻、相电感、绝缘等级、保持扭矩(静扭矩)、动态扭矩、定位扭矩(失步转矩)、额定转速、响应频率、温升、工作温度等。
详见:【STM32U3 评测】步进电机驱动 .
硬件连接
- NUCLEO-U3C5ZI-Q 与 TMC2209 驱动板的接线方式如下
| TMC2209 |
NUCLEO-U3C5ZI-Q |
Note |
| Dir |
PB10 |
Direction |
| Step |
PC6 |
Step pulse |
| EN |
PE13 |
Enable |
| GND |
GND |
Ground |
| VIO |
3V3 |
Power |
- 使用 Type-C 数据线连接开发板和电脑,结合 ST-LINK 虚拟串口进行通信;
- TMC2209 与步进电机的接线方式如下
| 42步进电机 |
TMC2209 |
Note |
| B- |
2B |
B phase |
| B+ |
2A |
B phase |
| A+ |
1A |
A phase |
| A- |
1B |
A phase |

示意图

实物图

串口打印
从开发板创建工程,串口重定位 printf 并实现字符串打印。
工程创建
- 打开 STM32CubeMX 软件,选择从开发板创建工程;

- 在 Board 标签下搜索并选择 NUCLEO-U3C5ZI-Q 型号开发板;
- 右下方选中目标开发板,点击右上角 Start Project 按钮,进入 CubeMX 配置界面;

- 在引脚配置标签页下,左侧工具目录展开 System Core,选中 GPIO;
- 在右侧图形引脚下方搜索 PE13 ,将其设置为 GPIO 输出模式,同时配置标签为
EN_PIN ;
- 同理,将 PC6 和 PB10 引脚设置为 GPIO 输出模式,配置标签分别为
STEP_PIN 和 DIR_PIN ;

-
点击上方 Clock 标签配置时钟参数,这里使用默认值;
-
点击上方 Project Manager 标签,设置工程名称、路径、工具栏等,这里使用 STM32CubeIDE 加载工程;

-
点击右上角 GENERATE CODE 按钮,生成并打开工程。

-
展开左侧工程文件目录,打开 Core/Src/main.c 文件,点击上方工具栏小锤子按钮,编译工程,无报错;

工程代码
展开左侧工程文件目录,双击打开 Core/Src/main.c 文件,关键代码如下
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
UART_HandleTypeDef huart1;
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE {
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
/* USER CODE END 0 */
int main(void)
{
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
printf("Hello World!\r\n");
HAL_Delay(500);
}
/* USER CODE END 3 */
}
保存代码。
编译上传
- 右键工程文件夹,选择构建项目,或点击工具栏小锤子按钮;
- 构建完成后,无报错产生,点击工具栏运行按钮,自动上传固件至开发板并运行;
效果演示
- 运行串口调试助手软件,配置波特率等参数;
- 打开串口,接收到开发板连续发送的字符串;

串口中断
通过串口中断实现发送 JSON 消息控制步进电机旋转角度和速度。
工程配置
- 打开 STM32CubeMX 软件,加载 NUCLEO-U3C5ZI-Q 开发板;
- 在左侧 BSP 标签页下点击目标开发板,进入配置界面;
- 取消勾选 VCOM 虚拟串口预设置;

- 点击 USART1 ,进入 NVIC Setting,开启串口 1 全局中断;

- 工程命名、设置保存路径、编译器选择 STM32CubeIDE ;
- 点击右上角 Generate Code 按钮,生成代码并打开工程;
工程代码
展开左侧工程文件目录,双击打开 Core/Src/main.c 文件,添加如下代码
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stepmotor.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
/* USER CODE END Includes */
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
volatile bool uart_rx_ready = false;
uint8_t uart_rx_buf[256] = {0};
uint32_t uart_rx_len = 0;
uint8_t rx_temp; // 变量用于串口接收
int target_angle = 0;
uint32_t target_speed = 2; // 步间延时(ms)
/* USER CODE END PV */
/* Private function prototypes --------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ----------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE {
if (HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY) != HAL_OK) {
return EOF;
}
return ch;
}
// 串口接收中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
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(&huart1, &rx_temp, 1);
}
}
// 解析JSON {"angle":90,"speed":2}
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);
}
}
/* USER CODE END 0 */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
// 初始化步进电机
step_motor_init();
// 启动串口接收中断
HAL_UART_Receive_IT(&huart1, &rx_temp, 1);
printf("System Ready!\r\n");
printf("Waiting for JSON command: {\"angle\":90,\"speed\":2}\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(uart_rx_ready){
// 解析指令
parse_json_cmd(&target_angle, &target_speed);
printf("OK: angle=%d, speed=%lu\r\n", target_angle, target_speed);
// 电机使能 + 转动
step_motor_enable();
step_motor_rotate_degrees((float)target_angle, target_speed);
step_motor_stop();
// 转动结束 释放扭矩
step_motor_disable();
// 清空接收缓冲区
memset(uart_rx_buf, 0, sizeof(uart_rx_buf));
uart_rx_len = 0;
uart_rx_ready = false;
}
else
{
// 无数据时释放扭矩
step_motor_disable();
}
HAL_Delay(10);
}
/* USER CODE END 3 */
}
保存代码。
驱动代码
新建 ./Core/Inc/stepmotor.h 头文件
/*
* stepmotor.h
*
* Created on: 2026-06-10
* Description: TMC2209步进电机驱动封装
*/
#ifndef INC_STEPMOTOR_H_
#define INC_STEPMOTOR_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include <stdbool.h>
#include <stdint.h>
/* 宏定义 --------------------------------------------------------------------*/
// 步进电机参数
#define STEPS_PER_REVOLUTION 1600 // 每圈总步数 = 200步 * 8细分
// 方向定义
#define DIR_CW GPIO_PIN_SET // 正转
#define DIR_CCW GPIO_PIN_RESET // 反转
/* 函数声明 ------------------------------------------------------------------*/
/**
* @brief 初始化步进电机
*/
void step_motor_init(void);
/**
* @brief 使能电机(通电)
*/
void step_motor_enable(void);
/**
* @brief 失能电机(断电)
*/
void step_motor_disable(void);
/**
* @brief 旋转指定步数
* @param steps: 步数(正数正转,负数反转)
* @param step_delay_ms: 步间延时(毫秒)
*/
void step_motor_rotate_steps(int32_t steps, uint32_t step_delay_ms);
/**
* @brief 旋转指定角度
* @param degrees: 角度(度,正数正转,负数反转)
* @param step_delay_ms: 步间延时(毫秒)
*/
void step_motor_rotate_degrees(float degrees, uint32_t step_delay_ms);
/**
* @brief 停止电机
*/
void step_motor_stop(void);
/**
* @brief 测试函数(正反转演示)
*/
void step_motor_test_sequence(void);
#ifdef __cplusplus
}
#endif
#endif /* INC_STEPMOTOR_H_ */
保存代码。
源文件
新建 ./Core/Src/stepmotor.c 源文件,添加如下代码
/*
* stepmotor.c
*
* Created on: 2026-06-10
* Description: TMC2209步进电机驱动实现
*/
#include "stepmotor.h"
#include <stdio.h>
#include <stdlib.h>
// ===================== 函数声明 =====================
static void motor_pulse_send(void);
// ===================== 函数实现 =====================
/**
* @brief 初始化步进电机
*/
void step_motor_init(void)
{
// 初始化引脚:EN=高电平(失能),STEP=低,DIR=低
HAL_GPIO_WritePin(EN_PIN_GPIO_Port, EN_PIN_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(STEP_PIN_GPIO_Port, STEP_PIN_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(DIR_PIN_GPIO_Port, DIR_PIN_Pin, GPIO_PIN_RESET);
printf("TMC2209 Stepper Motor Initialized.\r\n");
printf("Steps per revolution: %lu\r\n", STEPS_PER_REVOLUTION);
}
/**
* @brief 使能电机(通电)
*/
void step_motor_enable(void)
{
HAL_GPIO_WritePin(EN_PIN_GPIO_Port, EN_PIN_Pin, GPIO_PIN_RESET);
}
/**
* @brief 失能电机(断电)
*/
void step_motor_disable(void)
{
HAL_GPIO_WritePin(EN_PIN_GPIO_Port, EN_PIN_Pin, GPIO_PIN_SET);
}
/**
* @brief 发送一个步进脉冲(TMC2209 上升沿触发)
*/
static void motor_pulse_send(void)
{
HAL_GPIO_WritePin(STEP_PIN_GPIO_Port, STEP_PIN_Pin, GPIO_PIN_SET);
HAL_Delay(1); // 脉冲宽度1ms(实际应更短,但HAL_Delay最小1ms)
HAL_GPIO_WritePin(STEP_PIN_GPIO_Port, STEP_PIN_Pin, GPIO_PIN_RESET);
}
/**
* @brief 旋转指定步数
*/
void step_motor_rotate_steps(int32_t steps, uint32_t step_delay_ms)
{
if (steps == 0) return;
int8_t dir_flag = (steps > 0) ? 1 : -1;
uint32_t abs_steps = (uint32_t)abs(steps);
// 设置方向
if (dir_flag > 0)
{
HAL_GPIO_WritePin(DIR_PIN_GPIO_Port, DIR_PIN_Pin, DIR_CW);
printf("Rotating CW, steps: %lu, delay: %lu ms\r\n", abs_steps, step_delay_ms);
}
else
{
HAL_GPIO_WritePin(DIR_PIN_GPIO_Port, DIR_PIN_Pin, DIR_CCW);
printf("Rotating CCW, steps: %lu, delay: %lu ms\r\n", abs_steps, step_delay_ms);
}
// 使能电机
step_motor_enable();
// 发送步数脉冲
for (uint32_t i = 0; i < abs_steps; i++)
{
motor_pulse_send();
HAL_Delay(step_delay_ms);
}
}
/**
* @brief 旋转指定角度
*/
void step_motor_rotate_degrees(float degrees, uint32_t step_delay_ms)
{
// 角度转步数(四舍五入)
int32_t steps = (int32_t)(degrees * (STEPS_PER_REVOLUTION / 360.0f) + 0.5f);
if (steps != 0)
{
step_motor_rotate_steps(steps, step_delay_ms);
}
}
/**
* @brief 停止电机
*/
void step_motor_stop(void)
{
// 保持使能,停止发脉冲即可
HAL_GPIO_WritePin(STEP_PIN_GPIO_Port, STEP_PIN_Pin, GPIO_PIN_RESET);
printf("Motor stopped\r\n");
}
/**
* @brief 测试函数(正反转演示)
*/
void step_motor_test_sequence(void)
{
while (1)
{
step_motor_rotate_degrees(180.0f, 2); // 正转180°
step_motor_stop();
HAL_Delay(1000);
step_motor_rotate_degrees(-90.0f, 2); // 反转90°
step_motor_stop();
HAL_Delay(1000);
}
}
保存代码。
编译上传
- 右键工程文件夹,选择构建项目,或点击工具栏小锤子按钮;
- 构建完成后,无报错产生,点击工具栏运行按钮,自动上传固件至开发板并运行;
效果演示

LabVIEW 上位机
包括前面板和程序面板设计。
前面板
前面板设计包括串口配置、单步测试、连续运行测试、实时演化曲线、数据保存、程序控制等模块。

程序面板
程序面板采用模块化设计,将串口指令函数封装,确保单次发送、连续发送任务均准确执行。

数据采集
- 配置目标串口,运行程序;
- 设置步长、目标角度、延时、存储路径等;
- 点击 START 按钮,开始运行步进电机并采集数据;
动态演示

数据存储
- 数据采集完成后,串口停止发送消息,数据自动存储至目标路径;
- 数据保存格式为第一列旋转角度,第二列模拟采集数值;

总结
本文介绍了 NUCLEO-U3C5ZI-Q 开发板结合 TMC2209 驱动器、STM32CubeMX 和 CubeIDE,实现 42 步进电机的串口指令精确控制,设计 LabVIEW 上位机实现数据的自动连续采集的项目设计,包括环境搭建、工程创建、关键代码、流程图、效果演示等,为相关产品在工业领域的快速开发和应用设计提供了参考。