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进行控制。输出固定频率时候步进电机就匀速转动,频率越快步进电机转速就越快。
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模式控制则由芯片把我们输出这种脉冲时序。
STEP | A | B | A ‾ \overline{A} A | B ‾ \overline{B} B |
---|---|---|---|---|
1 | 1 | 1 | 0 | 0 |
2 | 0 | 1 | 1 | 0 |
3 | 0 | 0 | 1 | 1 |
4 | 1 | 0 | 0 | 1 |
5 | 1 | 1 | 0 | 0 |
对比这里双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芯片不支持驱动单极性步进电机?