Funpack4-3: 基于TMC4361和TMC2160的静音电机驱动系统
该项目使用了TMC4361、TMC2160、nucleo-h533 开发板、42步进电机,实现了静音电机驱动系统的设计,它的主要功能为:初始化tmc寄存器,通过串口与用户交互,根据用户命令控制电机。
标签
Funpack活动
开发板
电机
TMC4361
TMC2160
clr
更新2026-02-05
44

一、 项目介绍

在精密仪器、自动化、机器人以及智能家居等领域对电机驱动系统的性能要求日益提高,传统的步进电机驱动方案普遍存在噪音大、振动强、功耗高、低速运行不平滑等问题,这在许多对静音和舒适性有高要求的应用场景中成为了关键短板。本项目设计并实现一套高性能、超静音的步进电机驱动系统。使用nuleo-h533开发板作为主控板,结合TMC4361运动控制芯片和TMC2160电机驱动芯片,通过软硬件协同优化,彻底挖掘步进电机的潜力,实现超强静音性能和平滑度

 

TMC4361 集成了位置、速度、加速度闭环控制。支持S形曲线、梯形曲线等多种速度剖面,有效减小启停阶段的冲击和振动。

TMC2160 具有StealthChop2 驱动技术,通过在超声波范围内进行平稳的电流控制,从根本上消除了电机可闻噪音。

项目目标:

  1. 正确连接系统。
  2. 合理配置寄存器,实现静音电机驱动。
  3. 实现串口交互系统,控制电机运行。
  4. 实现精准的运动控制性能


二、 设计思路

根据官方数据手册正确连接mcutmc4361tmc2160,需要注意mcu需要给tmc4361时钟信号


image.png


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


程序流程图:

untitled_diagram.png

三、 实现过程

1. 正确连接SPI接口

根据推荐的方式,将MCU SPI连接到TMC4361,TMC4361再连接TMC2160

image.png

根据 BOB 开发板走线复刻的兼容arduino的拓展板,方便连接电机电源和电机,可选 3.3V 和 5V IO。PCB_PCB1_2026-01-08.png



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");
    }
}


四、 实现效果

根据串口输入的指令,电机能够平稳、静音地运动,实现指定速度、角度和圈数运动。

image.png

image.png







附件下载
TMCStepperMotor.zip
工程文件
ProDoc_Board1_2026-01-08 (1).epro2
拓展板工程
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号