TMC2660驱动及调试记录

TMC2660

一款优秀的电机驱动芯片,驱动简单。
理论就看这篇:TMC260/TMC2660/TMC262步进电机驱动
或者直接看手册,手册也不复杂。

  • 使用SPI通信,通过SPI配置参数。
  • 支持直接使用SPI和Step/Dir方式控制两种控制步进电机的方式。

TMC2660驱动

1、原理图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
需要驱动两个步进电机,两个步进电机连接一样,使用同一个SPI接口通信 —— SPI2

2、编写驱动

2.1 SPI驱动

SPI驱动直接STM32CubeMX配置即可,容易得很。
需要注意的是查看手册SPI通信时序就知道,时钟极性选择SPI_POLARITY_HIGH,时钟相位选择SPI_PHASE_2EDGE

void MX_SPI2_Init(void)
{
  ...
  hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  ...
}

2.2 TMC2660驱动代码

bsp_tmc2660.h:

#ifndef _BSP_TMC2660_H_
#define _BSP_TMC2660_H_

// #include <stdint.h>
typedef unsigned char uint8_t;

#define TMC2660_M1 0
#define TMC2660_M2 1
typedef uint8_t enTCM2660Def;

#define TMC2660_Disable  1
#define TMC2660_Enable   0
typedef uint8_t enTCM2660EnDisableDef;

#define TMC2660_CW   0
#define TMC2660_CCW  1
typedef uint8_t enTMC2660DirDef;

/* 细分步数 register value 用于Step/DIR模式 */
#define	TMC2660_MICROSTEP_256		0x00
#define	TMC2660_MICROSTEP_128		0x01
#define	TMC2660_MICROSTEP_64		0x02
#define	TMC2660_MICROSTEP_32		0x03
#define	TMC2660_MICROSTEP_16		0x04
#define	TMC2660_MICROSTEP_8			0x05
#define	TMC2660_MICROSTEP_4			0x06
#define	TMC2660_MICROSTEP_2			0x07    /* Half Step */
#define	TMC2660_MICROSTEP_1			0x08    /* Full Step */
typedef uint8_t microstep_t;

#define TMC2660_SG2_THRESHOLD_VAL (val)  (val << 8)

void BSP_TMC2660_Init(void);
void BSP_TMC2660_DirectSet(enTCM2660Def t, enTMC2660DirDef dir);
void BSP_TMC2660_Enable(enTCM2660Def t, enTCM2660EnDisableDef state);
microstep_t BSP_TMC2660_MicrostepSet(enTCM2660Def t, microstep_t microstep);
uint8_t  BSP_TMC2660_TorqueSet(enTCM2660Def t, uint8_t torque);

#endif /* _BSP_TMC2660_H_ */

bsp_tmc2660.c:

#include <stdio.h>
#include "spi.h"
#include "bsp_tmc2660.h"
#include "bsp_cfg.h"


/* tmc2660 register */
#define		REG_DRVCTRL			0x00000000
#define		REG_CHOPCONF		0x00080000
#define		REG_SMARTEN			0x000A0000
#define		REG_SGCSCONF		0x000C0000
#define		REG_DRVCONF			0x000E0000

#define DRVCTRL_SPI_PHA    (1 << 17)
/* 
 * 使用SPI模式需要设置DRVCONF寄存器的SDOFF位(bit7),
 *  0: Enable STEP and DIR interface. 
 *   1: Disable STEP and DIR interface. SPI interface is used 
 *   to move motor. 
*/

/* 默认配置 */
#define		SCG_DEFAULT			0x10000

/* spi chip select */
#define		TMC2660_M1_PORT_CS		GPIOB
#define		TMC2660_M1_GPIO_CS		GPIO_PIN_12		

/* Step motor enable */
#define		TMC2660_M1_PORT_EN		GPIOA			
#define		TMC2660_M1_GPIO_EN		GPIO_PIN_8

/* step pulse */
#define		TMC2660_M1_PORT_STEP	GPIOC			
#define		TMC2660_M1_GPIO_STEP	GPIO_PIN_8

/* direction */
#define		TMC2660_M1_PORT_DIR	    GPIOC			
#define		TMC2660_M1_GPIO_DIR	    GPIO_PIN_7	

/* spi chip select */
#define		TMC2660_M2_PORT_CS		GPIOC
#define		TMC2660_M2_GPIO_CS		GPIO_PIN_6		

/* Step motor enable */
#define		TMC2660_M2_PORT_EN		GPIOD			
#define		TMC2660_M2_GPIO_EN		GPIO_PIN_2

/* step pulse */
#define		TMC2660_M2_PORT_STEP	GPIOB			
#define		TMC2660_M2_GPIO_STEP	GPIO_PIN_9

/* direction */
#define		TMC2660_M2_PORT_DIR	    GPIOC			
#define		TMC2660_M2_GPIO_DIR	    GPIO_PIN_10	

static uint32_t tmc2660_SPI_Xfer(uint32_t wdata)
{
    uint8_t wbuf[3] = {0};
    uint8_t rbuf[3] = {0};

    wbuf[0] = (wdata>>16) & 0xff;
    wbuf[1] = (wdata>>8) & 0xff;
    wbuf[2] = (wdata&0xff);

    HAL_SPI_TransmitReceive(&hspi2, (uint8_t *)&wbuf, rbuf, 3, HAL_MAX_DELAY);
    uint32_t ret = (rbuf[0]<<16 | rbuf[1]<<8 | rbuf[2]);
    return ret;
}

#define LEVEL_SW(level)    ((level) > 0 ? GPIO_PIN_SET : GPIO_PIN_RESET)

static void tmc2660_SPI_CS(enTCM2660Def t, uint8_t level)
{
    if (t == TMC2660_M1)
    {
        HAL_GPIO_WritePin(TMC2660_M2_PORT_CS, TMC2660_M2_GPIO_CS, LEVEL_SW(1));  /* cancel M2 chip select */
        HAL_GPIO_WritePin(TMC2660_M1_PORT_CS, TMC2660_M1_GPIO_CS, LEVEL_SW(level));
    }
    else if (t == TMC2660_M2)
    {
        HAL_GPIO_WritePin(TMC2660_M1_PORT_CS, TMC2660_M1_GPIO_CS, LEVEL_SW(1));  /* cancel M1 chip select */
        HAL_GPIO_WritePin(TMC2660_M2_PORT_CS, TMC2660_M2_GPIO_CS, LEVEL_SW(level));
    }
}


static void tmc2660Enable(enTCM2660Def t, uint8_t level)
{
    if (t == TMC2660_M1)
    {
        HAL_GPIO_WritePin(TMC2660_M1_PORT_EN, TMC2660_M1_GPIO_EN, LEVEL_SW(level));
    }
    else
    {
        HAL_GPIO_WritePin(TMC2660_M2_PORT_EN, TMC2660_M2_GPIO_EN, LEVEL_SW(level));
    }
}

static void tmc2660GPIOConfig(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
   
      /*Configure GPIO pin : TMC2660_M1 */
    GPIO_InitStruct.Pin = TMC2660_M1_GPIO_CS;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(TMC2660_M1_PORT_CS, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = TMC2660_M1_GPIO_EN;
    HAL_GPIO_Init(TMC2660_M1_PORT_EN, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = TMC2660_M1_GPIO_DIR;
    HAL_GPIO_Init(TMC2660_M1_PORT_DIR, &GPIO_InitStruct);   

    /*Configure GPIO pin : TMC2660_M2 */
    GPIO_InitStruct.Pin = TMC2660_M2_GPIO_CS;
    HAL_GPIO_Init(TMC2660_M2_PORT_CS, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = TMC2660_M2_GPIO_EN;
    HAL_GPIO_Init(TMC2660_M2_PORT_EN, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = TMC2660_M2_GPIO_DIR;
    HAL_GPIO_Init(TMC2660_M2_PORT_DIR, &GPIO_InitStruct);

    HAL_GPIO_WritePin(TMC2660_M1_PORT_CS, TMC2660_M1_GPIO_CS, LEVEL_SW(1));
    HAL_GPIO_WritePin(TMC2660_M1_PORT_EN, TMC2660_M1_GPIO_EN, LEVEL_SW(1));
    HAL_GPIO_WritePin(TMC2660_M1_PORT_DIR, TMC2660_M1_GPIO_DIR, LEVEL_SW(0));

    HAL_GPIO_WritePin(TMC2660_M2_PORT_CS, TMC2660_M2_GPIO_CS, LEVEL_SW(1));
    HAL_GPIO_WritePin(TMC2660_M2_PORT_EN, TMC2660_M2_GPIO_EN, LEVEL_SW(1));
    HAL_GPIO_WritePin(TMC2660_M2_PORT_DIR, TMC2660_M2_GPIO_DIR, LEVEL_SW(0));
}

#define BSP_TMC2660_MODE_SPI  1
#define BSP_TMC2660_MODE_STEP_DIR 0

#define BSP_TMC2660_MODE  BSP_TMC2660_MODE_SPI

static void tmc2660ParamInit(void)
{
    uint32_t ret;
//Device Initialization from Datasheet
/* 1- spi, 0 - step/dir */
#if BSP_TMC2660_MODE == BSP_TMC2660_MODE_SPI  /* SPI Mode */
#define		INIT_DRVCTRL		0x00000000
#define		INIT_CHOPCONF		0x000901B4
#define		INIT_SMARTEN		0x000A8202
#define		INIT_SGCSCONF		0x000D0010
#define		INIT_DRVCONF		0x000E0090
#elif BSP_TMC2660_MODE == BSP_TMC2660_MODE_STEP_DIR  /* Step/DIR Mode*/
#define		INIT_DRVCTRL		0x00000000
#define		INIT_CHOPCONF		0x000901B4
#define		INIT_SMARTEN		0x000A8202
#define		INIT_SGCSCONF		0x000D0010//0x000D001F
#define		INIT_DRVCONF		0x000EF010
#endif 

    tmc2660_SPI_CS(TMC2660_M2, 0);
    ret = tmc2660_SPI_Xfer(REG_DRVCTRL | INIT_DRVCTRL | TMC2660_MICROSTEP_32);
    tmc2660_SPI_CS(TMC2660_M2, 1);
    printf("reg %08lx\r\n", ret);

    tmc2660_SPI_CS(TMC2660_M2, 0);
	ret = tmc2660_SPI_Xfer(REG_CHOPCONF | INIT_CHOPCONF);
    tmc2660_SPI_CS(TMC2660_M2, 1);
    printf("reg %08lx\r\n", ret);

    tmc2660_SPI_CS(TMC2660_M2, 0);
	ret = tmc2660_SPI_Xfer(REG_SMARTEN | INIT_SMARTEN);
    tmc2660_SPI_CS(TMC2660_M2, 1);
    printf("reg %08lx\r\n", ret);

    tmc2660_SPI_CS(TMC2660_M2, 0);
	ret = tmc2660_SPI_Xfer(REG_SGCSCONF | INIT_SGCSCONF);
    tmc2660_SPI_CS(TMC2660_M2, 1);
    printf("reg %08lx\r\n", ret);

    tmc2660_SPI_CS(TMC2660_M2, 0);
    ret = tmc2660_SPI_Xfer(REG_DRVCONF | INIT_DRVCONF);
    tmc2660_SPI_CS(TMC2660_M2, 1);
    printf("reg %08lx\r\n", ret);
}

void BSP_TMC2660_Init(void)
{
    MX_SPI2_Init();

    tmc2660GPIOConfig();

    tmc2660ParamInit();
    
    BSP_TMC2660_Enable(TMC2660_M1, TMC2660_Disable);
    BSP_TMC2660_Enable(TMC2660_M2, TMC2660_Enable);
}

/**
 * @brief:  Set Motor direction(设置电机方向)
 * @retval:  
 * @note:  
 */
void BSP_TMC2660_DirectSet(enTCM2660Def t, enTCM2660EnDisableDef dir)
{
    if (t == TMC2660_M1)
    {
        HAL_GPIO_WritePin(TMC2660_M1_PORT_DIR, TMC2660_M1_GPIO_DIR, LEVEL_SW(dir));
    }
    else if (t == TMC2660_M2)
    {
        HAL_GPIO_WritePin(TMC2660_M2_PORT_DIR, TMC2660_M2_GPIO_DIR, LEVEL_SW(dir));
    }
}

void BSP_TMC2660_Enable(enTCM2660Def t, enTCM2660EnDisableDef state)
{
    if (t == TMC2660_M1)
    {
        HAL_GPIO_WritePin(TMC2660_M1_PORT_EN, TMC2660_M1_GPIO_EN, LEVEL_SW(state));
    }
    else if (t == TMC2660_M2)
    {
        HAL_GPIO_WritePin(TMC2660_M2_PORT_EN, TMC2660_M2_GPIO_EN, LEVEL_SW(state));
    }
}

/**
 * @brief:  Set Motor microstep (设置tmc2660细分步数)
 * @retval: return microstep 
 * @note:  
 */
microstep_t BSP_TMC2660_MicrostepSet(enTCM2660Def t, microstep_t microstep)
{
	uint32_t cmd = 0;

    if (microstep > TMC2660_MICROSTEP_1 || microstep  < TMC2660_MICROSTEP_256)
    {
        microstep = TMC2660_MICROSTEP_16;
    }

	cmd = REG_DRVCTRL | INIT_DRVCTRL | microstep;    

    tmc2660_SPI_CS(t, 0);
    uint32_t ret = tmc2660_SPI_Xfer(cmd);
    tmc2660_SPI_CS(t, 1);

    return microstep;
}

/**
 * @brief:  Set Motor microstep (设置tmc2660电流缩放)
 * @retval: return scale 
 * @note:  scaleCurrent = scale / 32 * Current
 */
uint8_t BSP_TMC2660_CurrentScaleSet(enTCM2660Def t, uint8_t scale)
{
	uint32_t cmd = 0;

    if (scale > 31 || scale  < 0)
    {
        scale = 16;
    }

	cmd = REG_SGCSCONF | INIT_SGCSCONF | scale;

    tmc2660_SPI_CS(t, 0);
    uint32_t ret = tmc2660_SPI_Xfer(cmd);
    tmc2660_SPI_CS(t, 1);

    return scale;
}


  • BSP_TMC2660_MODE通过宏选择SPI模式还是STEP/DIR模式控制,通过设置REG_DRVCONF寄存器bit7来选择,但bit7为0时使用STEP/DIR模式为1时使用SPI模式
2.2.3 使用STEP/DIR方式驱动

STEP/DIR方式驱动很简单,给STEP引脚输入PWM信号即可,可以使用定时器输出PWM。
当使用PWM方式驱动时,由芯片产生驱动步进电机的时序。
这里使用定时器比较输出翻转模式输出PWM进行控制。输出固定频率时候步进电机就匀速转动,频率越快步进电机转速就越快。

2.2.4 使用SPI驱动

查看手册可以知道DRVCTRL寄存器在SPI模式时用于控制步进电机。
在这里插入图片描述
在这里插入图片描述

  • 寄存器的bit17和bit8分别用于控制OA1-OA2、OB1-OB2的电流方向
  • bit16 - bit9用于控制通过OA1-OA2电流的大小。
  • bit7 - bit0 用于控制通过OB1-OB2电流的大小。

在这里插入图片描述

  • A ~ OA1
  • A ‾ \overline{A} A ~ OA2
  • B ~ OB1
  • B ‾ \overline{B} B ~ OB2

具体如何控制还需要了解步进电机控制原理:两相步进电机的控制及其实现
查看步进电机规格书里面也写了驱动时序 : 只要按照这个时序输出脉冲就可以控制。如果用STEP/DIR模式控制则由芯片把我们输出这种脉冲时序。
在这里插入图片描述

STEPAB A ‾ \overline{A} A B ‾ \overline{B} B
11100
20110
30011
41001
51100

* 第1节拍,OA 1 OB1 通电,也就是输出高电平;OA2、OB2输出低电平
对比这里双4拍可知步进电机的时序和这个是一样的。

使用SPI模式控制

/**
 * @brief:  SPI Mode To ctrl stepper motor
 * @retval:  
 * @note:  0
  */
void BSP_TMC2660_SPIMoveStep(enTCM2660Def t)
{
    static uint8_t index = 0;
    uint32_t beats[4] = {0x01f0f8, 0x03f0f8, 0x03f1f8, 0x01f1f8};
    // uint32_t beats[4] = {0x00f87c, 0x002f87c, 0x002f97c, 0x00f97c};
    uint32_t cmd;
    tmc2660Enable(t, 0);

    cmd = REG_DRVCTRL | beats[index];

    tmc2660_SPI_CS(t, 0);
    tmc2660_SPI_Xfer(cmd);
    tmc2660_SPI_CS(t, 1);

    index = (++index) % 4;
}
  • 第1节拍,向寄存器写入0x01f0f8,bit17和bit8是0,电流方向OA1 —> OA2、OB1 —>OB2,也就是X+Y+,对应规格书 时序 1 1 0 0
  • 第2节拍,向寄存器写入0x03f0f8,bit17为1,bit8为0,电流方向OA2 —> OA1、OB1 —>OB2,也就是X-Y+,对应规格书 时序 0 1 1 0
  • 第3节拍,向寄存器写入0x03f1f8,bit17为1,bit8为1,所以电流方向OA2 —> OA1、OB2 —>OB1,也就是X-Y-,对应规格书 时序 0 0 1 1,
  • 第4节拍,向寄存器写入0x01f1f8,bit17为0,bit8为1,所以电流方向OA1 —> OA2、OB2 —>OB1,也就是X+Y-,对应规格书 时序 1 0 0 1,

测试是可以运行的,说明SPI模式驱动步进电机完成。

问题

驱动很简单,然而按照上面驱动调试了好久电机都驱动不了。
问题就出在了电机连接线是6线,相比一般的4线驱动电机还多了ACOM线和BCOM线。
没有接电机的时候,示波器测试脉冲信号输出都是正常的,一旦接上这6线电机,再次测量脉冲信号就异常了,
在这里插入图片描述
正常波形应该是这样的:
在这里插入图片描述
更换了一个4线的电机,测试发现可以驱动,这证明驱动是没问题了。
对比两个电机就是多了ACOM和BCOM线,猜测可能是ACOM和BCOM影响的所以驱动不了,去掉ACOM和BCOM连接后,测试果然电机成功驱动。

推测原因:电机连接了ACOM和BCOM线,属于单极性步进电机?TMC2660芯片不支持驱动单极性步进电机?

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欲盖弥彰1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值