一、 项目介绍
在精密仪器、自动化、机器人以及智能家居等领域对电机驱动系统的性能要求日益提高,传统的步进电机驱动方案普遍存在噪音大、振动强、功耗高、低速运行不平滑等问题,这在许多对静音和舒适性有高要求的应用场景中成为了关键短板。本项目设计并实现一套高性能、超静音的步进电机驱动系统。使用nuleo-h533开发板作为主控板,结合TMC4361运动控制芯片和TMC2160电机驱动芯片,通过软硬件协同优化,彻底挖掘步进电机的潜力,实现超强静音性能和平滑度。
TMC4361 集成了位置、速度、加速度闭环控制。支持S形曲线、梯形曲线等多种速度剖面,有效减小启停阶段的冲击和振动。
TMC2160 具有StealthChop2 驱动技术,通过在超声波范围内进行平稳的电流控制,从根本上消除了电机可闻噪音。
项目目标:
- 正确连接系统。
- 合理配置寄存器,实现静音电机驱动。
- 实现串口交互系统,控制电机运行。
- 实现精准的运动控制性能
二、 设计思路
根据官方数据手册正确连接mcu、tmc4361和tmc2160,需要注意mcu需要给tmc4361时钟信号。

串口中断监听用户输入,显示帮助界面,或执行指定命令,并对异常输入返回提示。
程序流程图:

三、 实现过程
1. 正确连接SPI接口
根据推荐的方式,将MCU SPI连接到TMC4361,TMC4361再连接TMC2160

根据 BOB 开发板走线复刻的兼容arduino的拓展板,方便连接电机电源和电机,可选 3.3V 和 5V IO。
2. 串口中断处理
接收中断触发后,处理接收到的字符,直到回车或换行 - 命令结束
/**
* @brief UART接收完成回调函数
* @param huart UART句柄
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
char received_char = (char)uart_ctrl.rx_char;
// 回显字符(除了某些控制字符)
if (received_char >= 32 && received_char <= 126) {
HAL_UART_Transmit(&huart2, &uart_ctrl.rx_char, 1, 100);
}
// 处理接收到的字符
if (received_char == '\r' || received_char == '\n') {
// 回车或换行 - 命令结束
if (uart_ctrl.cmd_index > 0) {
uart_ctrl.cmd_buffer[uart_ctrl.cmd_index] = '\0';
uart_ctrl.cmd_ready = 1; // 标记命令就绪
// 发送换行
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n", 2, 100);
} else {
// 空命令,只显示提示符
HAL_UART_Transmit(&huart2, (uint8_t*)"\r\n> ", 4, 100);
}
}
else if (received_char == '\b' || received_char == 127) {
// 退格键处理
if (uart_ctrl.cmd_index > 0) {
uart_ctrl.cmd_index--;
// 发送退格序列:\b \b (退格-空格-退格)
HAL_UART_Transmit(&huart2, (uint8_t*)"\b \b", 3, 100);
}
}
else if (received_char >= 32 && received_char <= 126) {
// 可打印字符
if (uart_ctrl.cmd_index < CMD_BUFFER_SIZE - 1) {
uart_ctrl.cmd_buffer[uart_ctrl.cmd_index++] = received_char;
}
}
// 继续接收下一个字符
HAL_UART_Receive_IT(&huart2, &uart_ctrl.rx_char, 1);
}
}
3. 命令解析
解析传递过来的字符串,判断是否为有效命令,并传递给对应的电机控制功能
/**
* @brief 解析命令类型
*/
CommandType_t parse_command(char* cmd)
{
// 跳过前导空格
while (*cmd == ' ' || *cmd == '\t') cmd++;
if (strncmp(cmd, "ls", 2) == 0) return CMD_LEFT_SPEED;
if (strncmp(cmd, "rs", 2) == 0) return CMD_RIGHT_SPEED;
if (strncmp(cmd, "la", 2) == 0) return CMD_LEFT_ANGLE;
if (strncmp(cmd, "ra", 2) == 0) return CMD_RIGHT_ANGLE;
if (strncmp(cmd, "lt", 2) == 0) return CMD_LEFT_TURN;
if (strncmp(cmd, "rt", 2) == 0) return CMD_RIGHT_TURN;
if (strncmp(cmd, "re", 2) == 0) return CMD_REVERSE;
if (strcmp(cmd, "?") == 0 || strcmp(cmd, "help") == 0) return CMD_HELP;
if (strcmp(cmd, "stop") == 0) return CMD_STOP;
if (strcmp(cmd, "show") == 0) return CMD_SHOW;
return CMD_UNKNOWN;
}
/**
* @brief 处理接收到的命令
*/
void process_command(char* cmd)
{
CommandType_t cmd_type = parse_command(cmd);
int param = 0;
// 对于需要参数的命令,提取参数
if (cmd_type >= CMD_LEFT_SPEED && cmd_type <= CMD_RIGHT_TURN) {
param = extract_parameter(cmd);
if (param == 0 && cmd_type != CMD_STOP) {
printf("Error: Invalid parameter\r\n");
return;
}
}
switch (cmd_type) {
case CMD_LEFT_SPEED:
{
int32_t speed = (int32_t)(param * MICROSTEPS_PER_DEGREE * 256.0f); //23位整数+8位小数
if (abs(speed) > TMC4361A_MAX_VELOCITY) {
printf("Error: Speed exceeds maximum limit (%d deg/s)\r\n",
TMC4361A_MAX_VELOCITY / (int)(MICROSTEPS_PER_DEGREE * 256.0f));
return;
}
motor_set_speed_mode(speed);
motor.direction = 0;
printf("Left speed: %d deg/s (%ld microsteps/s)\r\n", param, speed);
}
break;
case CMD_RIGHT_SPEED:
{
int32_t speed = -(int32_t)(param * MICROSTEPS_PER_DEGREE * 256.0f);
if (abs(speed) > TMC4361A_MAX_VELOCITY) {
printf("Error: Speed exceeds maximum limit (%d deg/s)\r\n",
TMC4361A_MAX_VELOCITY / (int)(MICROSTEPS_PER_DEGREE * 256.0f));
return;
}
motor_set_speed_mode(speed);
motor.direction = 1;
printf("Right speed: %d deg/s (%ld microsteps/s)\r\n", param, speed);
}
break;
case CMD_LEFT_ANGLE:
motor_rotate_angle(param, 0);
printf("Rotating left %d degrees\r\n", param);
break;
case CMD_RIGHT_ANGLE:
motor_rotate_angle(param, 1);
printf("Rotating right %d degrees\r\n", param);
break;
case CMD_LEFT_TURN:
motor_rotate_turns(param, 0);
printf("Rotating left %d turns\r\n", param);
break;
case CMD_RIGHT_TURN:
motor_rotate_turns(param, 1);
printf("Rotating right %d turns\r\n", param);
break;
case CMD_REVERSE:
motor_reverse_direction();
break;
case CMD_HELP:
show_help();
break;
case CMD_STOP:
motor_stop();
break;
case CMD_SHOW:
show_motor_status();
break;
case CMD_UNKNOWN:
printf("Unknown command: '%s'\r\n", cmd);
printf("Type 'help' or '?' for available commands\r\n");
break;
}
}
4. tmc寄存器读写
引用官方的抽象层头文件,改写tmc4361A_readWriteSPI函数,使用void tmc4361A_writeRegister(uint16_t icID, uint8_t address, int32_t value)对寄存器进行读写。
#include "TMC4361A.h"
#include "TMC2160_HW_Abstraction.h"
void tmc4361A_readWriteSPI(uint16_t icID, uint8_t *data, size_t dataLength)
{
// 参数验证
if (data == NULL || dataLength == 0 || dataLength > 5)
{
return; // 或者返回错误码
}
// 使用动态分配或者固定大小的缓冲区
uint8_t txData[5] = {0};
uint8_t rxData[5] = {0};
// 正确复制发送数据
for (size_t i = 0; i < dataLength; i++)
{
txData[i] = data[i];
}
// 或者使用 memcpy(txData, data, dataLength);
// 激活片选
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);
// printf("GPIO_PIN_10 set low\n");
// 添加短暂延时确保片选稳定
// HAL_Delay(1); // 如果需要的话
// 执行双向数据传输
HAL_StatusTypeDef spi_status = HAL_SPI_TransmitReceive(&hspi1, txData, rxData, dataLength, HAL_MAX_DELAY);
// 释放片选
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
// printf("GPIO_PIN_10 set high\n");
// 检查SPI传输状态
if (spi_status == HAL_OK)
{
// printf("Data received\n");
// 正确地将接收数据复制回原数组
for (size_t i = 0; i < dataLength; i++)
{
data[i] = rxData[i];
}
// 或者使用 memcpy(data, rxData, dataLength);
}
}
5. 芯片初始化
根据实际需要启用功能
/**
* @brief 电机初始化
*/
void motor_init(void)
{
printf("Initializing TMC4361A and TMC2160A...\r\n");
// TMC4361A 重置
tmc4361A_writeRegister(0x00, TMC4361A_SW_RESET, 0x52535400);
// TMC4361A SPI设置
tmc4361A_writeRegister(0x00, TMC4361A_SPI_OUT_CONF, 0x4440128C);
tmc4361A_writeRegister(0x00, TMC4361A_STP_LENGTH_ADD, 0x00060004);
// TMC2160A配置
tmc2160A_writeRegister(TMC2160_CHOPCONF, 0x100100C3); // CHOPPER CONFIG
tmc2160A_writeRegister(TMC2160_IHOLD_IRUN, 0x00060402); // I_HOLD I_RUN
tmc2160A_writeRegister(TMC2160_TPOWERDOWN, 0x0000000A); // TPOWERDOWN
tmc2160A_writeRegister(TMC2160_GCONF, 0x00000004); // General config
//tmc2160A_writeRegister(TMC2160_PWMCONF, 0x000404FF); // PWM_CONF
//tmc2160A_writeRegister(TMC2160_COOLCONF, 0x0100A220); // COOLCONF
tmc2160A_writeRegister(TMC2160_TPWMTHRS, 0x000001F4);
//tmc2160A_writeRegister(TMC2160_TCOOLTHRS, 0x000000CB);
//tmc2160A_writeRegister(TMC2160_THIGH, 0x00000032);
// 电流控制
//tmc4361A_writeRegister(0x00, TMC4361A_SCALE_VALUES, 0x0032F064); // SCALE_VALUES
//tmc4361A_writeRegister(0x00, TMC4361A_CURRENT_CONF, 0x00000040); // CURRENT_CONF
// 运动控制参数
tmc4361A_writeRegister(0x00, TMC4361A_AMAX, 0x00000A00); // AMAX
tmc4361A_writeRegister(0x00, TMC4361A_DMAX, 0x00000A00); // DMAX
tmc4361A_writeRegister(0x00, TMC4361A_BOW1, 0x00000500); // BOW1
tmc4361A_writeRegister(0x00, TMC4361A_BOW2, 0x00000A00); // BOW2
tmc4361A_writeRegister(0x00, TMC4361A_BOW3, 0x00000A00); // BOW3
tmc4361A_writeRegister(0x00, TMC4361A_BOW4, 0x00000500); // BOW4
// 初始化电机状态
motor.current_speed = 0;
motor.current_position = 0;
motor.direction = 0;
motor.mode = 0;
printf("Motor initialization completed.\r\n");
}
6. 运动控制
控制电机定速旋转、指定角度旋转、指定圈数旋转、正反转切换功能
/**
* @brief 旋转指定角度
*/
void motor_rotate_angle(int32_t angle, uint8_t direction)
{
// 先停止速度模式(如果正在运行)
if (motor.mode == 1) {
tmc4361A_writeRegister(0x00, TMC4361A_VMAX, 0x00);
tmc4361A_writeRegister(0x00, TMC4361A_RAMPMODE, 0x00000004); // 位置模式
HAL_Delay(1); // 短暂延时确保停止
motor.current_position = tmc4361A_readRegister(0x00, TMC4361A_XACTUAL); // XACTUAL
}
int32_t microsteps = (int32_t)(angle * MICROSTEPS_PER_DEGREE);
if (direction == 1) microsteps = -microsteps;
motor.current_position += microsteps;
motor_set_position_mode();
tmc4361A_writeRegister(0x00, TMC4361A_VMAX, 0x005DC000);
tmc4361A_writeRegister(0x00, TMC4361A_XTARGET, motor.current_position); // 相对位置移动
}
/**
* @brief 旋转指定圈数
*/
void motor_rotate_turns(int32_t turns, uint8_t direction)
{
// 先停止速度模式(如果正在运行)
if (motor.mode == 1) {
tmc4361A_writeRegister(0x00, TMC4361A_RAMPMODE, 0x00000004); // 位置模式
HAL_Delay(1); // 短暂延时确保停止
motor.current_position = tmc4361A_readRegister(0x00, TMC4361A_XACTUAL); // XACTUAL
}
int32_t microsteps = turns * MICROSTEPS_PER_REV;
if (direction == 1) microsteps = -microsteps;
motor.current_position += microsteps;
motor_set_position_mode();
tmc4361A_writeRegister(0x00, TMC4361A_VMAX, 0x005DC000);
tmc4361A_writeRegister(0x00, TMC4361A_XTARGET, motor.current_position); // 相对位置移动
}
/**
* @brief 反转方向(仅速度模式)
*/
void motor_reverse_direction(void)
{
if (motor.mode == 1) { // 只有在速度模式下才能反转
motor.direction = !motor.direction;
motor.current_speed = -motor.current_speed;
tmc4361A_writeRegister(0x00, TMC4361A_VMAX, motor.current_speed);
printf("Direction reversed, new speed: %ld microsteps/s\r\n",
motor.current_speed);
} else {
printf("Error: Reverse only works in speed mode!\r\n");
}
}
四、 实现效果
根据串口输入的指令,电机能够平稳、静音地运动,实现指定速度、角度和圈数运动。

