关于TMC系列的电机驱动芯片的使用(基与F405的spi控制)
这个系列的驱动芯片大多都是有两种控制模式的
一种是spi控制,不用发脉冲,直接通过spi发命令给驱动芯片控制电机
一种是通过step/dir引脚控制,这个之前发过一篇2130的文章,但我觉得发脉冲控制的方式不好用,所以不多赘述
使用spi控制最主要是给驱动芯片配置寄存器,电机上电是不会自锁的,得配置好寄存器后正确配置才能锁住
下面是流程
1:先初始化spi通信脚,芯片使能脚,spi片选脚(本文使用的是多个电机)
注意:(在初始化后需要把使能脚上拉,并且置1,在写入寄存器配置后再拉低使能,不然会出现过流的情况,在另一篇文章有说到)
#define SPI1_CS0 PBout(0) //第一个的片选引脚
#define SPI1_CS1 PCout(5) //第二个的片选引脚
#define SPI1_CS2 PCout(4) //第三个的片选引脚
#define MOTOR_EN3 PAout(0)
#define MOTOR_EN2 PAout(1) //电机使能信号2
#define MOTOR_EN1 PAout(2) //电机使能信号1
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);//使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_0 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;/
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
MOTOR_EN1 = 1;
MOTOR_EN2 = 1;
MOTOR_EN3 = 1;
SPI1_CS0 = 1;
SPI1_CS1 = 1;
SPI1_CS2 = 1;
2初始化寄存器
void TMC_5041_Init_aks(Motor_Single_Status *moto,u8 ch)
{
spi_send_int(TMC5160_GCONF,0x4 , ch);
delay_ms(10);
spi_send_int(TMC5160_CHOPCONF, 0x100c3 , ch);
delay_ms(10);
spi_send_int(TMC5160_IHOLD_IRUN,0x60303, ch);
delay_ms(10);
spi_send_int(TMC5160_TPOWERDOWN, 0xa, ch);
delay_ms(10);
spi_send_int(TMC5160_TPWMTHRS, 0x1f4, ch);
delay_ms(10);
spi_send_int(TMC5160_PWMCONF, 0X401C8,ch);
delay_ms(10);
spi_send_int(TMC5160_A1,0x3e8, ch);
delay_ms(10);
spi_send_int(TMC5160_V1,0xc350, ch);
delay_ms(10);
spi_send_int(TMC5160_AMAX,0x1f4, ch);
delay_ms(10);
spi_send_int(TMC5160_VMAX,0x30d40, ch);
delay_ms(10);
spi_send_int(TMC5160_DMAX,0x2bc, ch);
delay_ms(10);
spi_send_int(TMC5160_D1,0x578, ch);
delay_ms(10);
spi_send_int(TMC5160_VSTOP,0xa, ch);//moto->VSTOP
delay_ms(10);
spi_send_int(TMC5160_RAMPMODE,0x0, ch);
delay_ms(10);
spi_send_int(TMC5160_SWMODE, 0x40f, ch);delay_ms(10);
spi_send_int(TMC5160_ENCMODE,0X72c);
delay_ms(10);
spi_send_int(TMC5160_ENCCONST,0XFA0);
delay_ms(10);
delay_ms(300);
switch(ch)
{
case 0:
MOTOR_EN1 = 0;
break;
case 1:
MOTOR_EN2 = 0;
break;
case 2:
MOTOR_EN3 = 0;
break;
}
delay_ms(100);
}//配置是按照推荐配置来的
spi发送函数
多电机的片选,单电机可以去掉
void spi_send_int(u8 addr, int data, u8 ch)
{
int temp;
u8 temp1;
u8 temp2;
switch(ch)
{
case 0:
{
SPI1_CS0 = 0;
}break;
case 1:
{
SPI1_CS1 = 0;
}break;
case 2:
{
SPI1_CS2 = 0;
}break;
}addr = addr + 0x80;
SPI1_ReadWriteByte(&temp2,&addr,1);
for(int i = 0 ; i < 4 ; i++)
{
temp = data & 0xff000000;
temp1 = temp >> 24;
SPI1_ReadWriteByte(&temp2,&temp1,1);
data = data << 8;
}
switch(ch)
{
case 0:
{
SPI1_CS0 = 1;
}break;
case 1:
{
SPI1_CS1 = 1;
}break;
case 2:
{
SPI1_CS2 = 1;
}break;
}
}
spi接收函数
int spi_rec_data(u8 addr, int ch)
{
unsigned char InputBuf[5]={0x00};
unsigned char OutputBuf[5]={0x00};
int data = 0;
switch(ch)
{
case 0:
{
SPI1_CS0 = 0;
}break;
case 1:
{
SPI1_CS1 = 0;
}break;
case 2:
{
SPI1_CS2 = 0;
}break;
}
OutputBuf[0] = addr;
SPI1_ReadWriteByte(InputBuf, OutputBuf ,5);
data = InputBuf[1];
data = data<<8 | InputBuf[2];
data = data<<8 | InputBuf[3];
data = data<<8 | InputBuf[4];
switch(ch)
{
case 0:
{
SPI1_CS0 = 1;
}break;
case 1:
{
SPI1_CS1 = 1;
}break;
case 2:
{
SPI1_CS2 = 1;
}break;
}
return data;
}
spi读写字节
void SPI1_ReadWriteByte(uint8_t *InputBuffer,uint8_t *OutputBuffer,uint8_t NoOfBytes)
{
u32 Byte;
unsigned char Register;
for(Byte=0;Byte<NoOfBytes;Byte++)
{
Register=*OutputBuffer;
SPI_I2S_SendData(SPI1,Register);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
*InputBuffer=SPI_I2S_ReceiveData(SPI1);
OutputBuffer++;
InputBuffer++;
}
}
下面是使用比如绝对运动
//设置为定位模式
spi_send_int(TMC5160_RAMPMODE, 0X0, 轴号);
//设置速度
spi_send_int(TMC5160_VMAX, 0xc350, 轴号);
//设置目标位置
spi_send_int(TMC5160_XTARGET, 0xc800, 轴号);
还有就是读取寄存器的时候需要先读一遍丢弃,再读一遍获取比如
unused_data = spi_rec_data(TMC5160_XTARGET, 1);丢弃
motor1.xtarget = spi_rec_data(TMC5160_XTARGET, 1);存入值
unused_data = spi_rec_data(TMC5160_XACTUAL, 1);丢弃
motor1.xactual = spi_rec_data(TMC5160_XACTUAL, 1);存入值
unused_data = spi_rec_data(TMC5160_VACTUAL, 1);
motor1.vactual = spi_rec_data(TMC5160_VACTUAL, 1);
有一个闭环代码,可以看看,不知道能不能用,可以自己调试一下
unused_data = spi_rec_data(TMC5130_XENC);
motor.xenc = spi_rec_data(TMC5130_XENC);读取编码器位置
En_code = motor.xenc * 32;//根据编码器的返回值计算脉冲,大概调到跟发送的脉冲一样就行,这里用1000线编码器,分辨率设置为4000,在前面的TMC5160_ENCCONST寄存器
one = motor.xactual;
err_code = one - En_code;计算误差
if(err_code > MAX_err || (-1) * err_code > MAX_err)//判断是否需要闭环
{
loop_flag = 2;
}
//判断是否到位
if(motor.ex_place_flag == 1 && loop_flag == 2)开始闭环
{
//将编码器放回的值锁存到实际电机位置,
spi_send_int(TMC5130_XACTUAL, En_code);
delay_ms(100);
unused_data = spi_rec_data(TMC5130_XACTUAL);
two = spi_rec_data(TMC5130_XACTUAL); 查看有没有写进去
// 在运动一个目标位置
delay_ms(100);
spi_send_int(TMC5130_VMAX, 0x0);
//设置为定位模式
spi_send_int(TMC5130_RAMPMODE, 0X0);
//设置速度
spi_send_int(TMC5130_VMAX, 0x1388);//检查这些电机参数的最优配置
//设置目标值
spi_send_int(TMC5130_XTARGET, one);目标位置
loop_flag = 0;
}
发布评论