步进电机驱动技术1:基于TMC2660的步进电机驱动

步进电机的应用非常广泛,在各种设备中经常会遇到,而步进电机的驱动则是使用步进电机必不可少的部分,可以有多种方式来实现步进电机的驱动,在这里我们来考虑一下基于TMC2660驱动芯片的步进电机驱动。

1、功能概述

TMC2660是德国TRINAMIC公司产的步进电机驱动芯片。TMC2660驱动器提供了业界领先的功能集,包括高分辨率微步、无传感器机械负载测量、负载自适应功率优化和低共振斩波操作。拥有标准SPI和STEP/DIR两种接口模式。集成功率MOSFET处理电机电流高达2.2A。集成的保护和诊断功能支持稳健和可靠的运行。其结构图如下:

TMC2660的参数配置通过SPI接口来实现。TMC2660具有5个配置和控制寄存器,通过SPI端口来访问这些寄存器。这些寄存器的结构定义如下所示:

所有的寄存器都是20位,在最高的2位或3位表示的是寄存器地址也称为操作码。根据具体的寄存器我们选择不同的操作码实现对寄存器的写操作。每一个写操作都会有一个20位的数据返回。而返回数据的内容可以通过修改配置寄存器来定义。具体的格式如下图所示:

2、驱动设计于实现

我们已经了解了TMC2660步进电机驱动芯片的基本技术参数,接下来我们就需要据此来实现TMC2660步进电机驱动芯片的驱动程序的设计与实现。

2.1、对象定义

我们依然是居于对象来实现相关的操作。所以我们首先要定义对象,出于适用性考虑,我们要定义对象的类型并将具体的对象实例化,接下来我们就来抽象对象类型和实例化对象的操作。

2.1.1、对象的抽象

对于一个对象最主要包括属性与操作两方面内容,所以我们先来考虑TMC2660对象具有哪些属性和操作,并抽象出较为通用的TMC2660对象类型。

对于步进电机的驱动都具有哪些属性呢?我们考虑到一台步进电机至少具备启停控制命令、方向控制命令、速度设定以及运行状态等,这些对于每一台步进电机来说,在不同的设置下代表不同的状态,所以我们将其作为其属性来处理。此外,与具体的电机相关的参数如固有步距角、微步设置及当前脉冲频率等。以及与TMC2660相关的状态、寄存器的值、速度规划等都与具体的应用需求相关、用以记录其运行和配置状态,所以我们将其作为属性。

然后再来看一看TMC2660对象需要实现的操作。对于TMC2660对象来说,我们要操作它,需要向其发送和读取数据,需要操作片选信号和使能信号,而这些行为依赖于具体的操作平台,所以我们将其作为对象的操作来设定。TMC2660可以工作在SPI模式或者SD模式,而在SD模式时,存在脉冲和方向的控制,这同样依赖于具体的软硬件操作平台,所以我们也将其作为对象的操作来实现。更具以上的分析我们可以抽象出TMC2660对象类型如下:

/*定义TMC2660对象类型*/
typedef struct TMC2660Object {float microStep; //微步设置float stepAngle; //固有步进角float frequency;    //运行频率uint16_t *pStartStop;   //启停操作命令uint16_t *pDirection;   //方向控制uint16_t *pRotateSet;   //转速设定uint16_t *pMotorState;  //电机状态uint32_t status;        //TMC通讯返回状态uint32_t Register[5];    //寄存器void (*WriteRead)(uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize);void (*ChipSelcet)(TMC2660CSType cs);   //片选信号void (*StartStop)(TMC2660SSType ss);    //启停操作函数void (*Direct)(TMC2660DIRType dir);     //方向操作函数void (*Enable)(TMC2660ENNType enn);     //使能操作函数CurveObjectType curve;          //电机调速曲线
}TMC2660ObjectType;

2.1.2、对象初始化

我们定义了对象类型,可以实现基于对象的操作,但定义的对象变量需要进行初始化才能让不同的对象按照我们的配置的方式去运行。所以在开始对象的使用之前我们先对其进行初始化,具体的初始化函数如下:

/*初始化TMC2660对象*/
void Tmc2660Initialization(TMC2660ObjectType *tmc,       //待初始化的TMC对象变量TMC2660SdoffType interface,      //驱动接口类型TMC2660MicroStepType microStep,  //微步设置uint16_t Power,                  //电流量程uint16_t stepAngle,                  //固有步进角uint16_t *pStartStop,            //启停操作命令uint16_t *pDirection,            //方向控制uint16_t *pRotateSet,            //转速设定uint16_t *pMotorState,           //电机状态TMC2660WriteReadType writeRead,  //读写函数指针TMC2660ChipSelcetType cs,        //片选操作函数指针TMC2660StartStopType startStop,  //启停操作函数指针TMC2660DirectType direct,        //方向设置函数指针TMC2660EnableType enable         //使能控制函数指针)
{uint32_t MicroStep[9]={0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00};uint16_t MicroStepNum[9]={1,2,4,8,16,32,64,128,256};if((tmc==NULL)||(writeRead==NULL)||(cs==NULL)||(enable==NULL)){return;}tmc->WriteRead=writeRead;tmc->ChipSelcet=cs;tmc->StartStop=startStop;tmc->Direct=direct;tmc->Enable=enable;tmc->pStartStop=pStartStop;tmc->pDirection=pDirection;tmc->pRotateSet=pRotateSet;tmc->pMotorState=pMotorState;tmc->microStep=MicroStepNum[microStep];tmc->stepAngle=(float)stepAngle/10.0;tmc->curve.stepSpeed=0.02;tmc->curve.currentSpeed=0;tmc->curve.startSpeed=0;tmc->curve.speedMax=300;tmc->curve.speedMin=1.0;tmc->curve.curveMode=CURVE_SPTA;tmc->curve.flexible=10.0;tmc->Register[Reg_DRVCTRL]=DRVCTRL;tmc->Register[Reg_CHOPCONF]=CHOPCONF;tmc->Register[Reg_SMARTEN]=SMARTEN;tmc->Register[Reg_SGCSCONF]=SGCSCONF;tmc->Register[Reg_DRVCONF]=DRVCONF;tmc->Register[Reg_CHOPCONF]=tmc->Register[Reg_CHOPCONF]|0x1B1;tmc->Register[Reg_SMARTEN]=tmc->Register[Reg_SMARTEN]|0x202;tmc->Register[Reg_SGCSCONF]=tmc->Register[Reg_SGCSCONF]|0x10000;WriteReadTmc2660Register(tmc,Reg_CHOPCONF);WriteReadTmc2660Register(tmc,Reg_SGCSCONF);if(interface==TMC2660_SPI){tmc->Register[Reg_DRVCONF]=tmc->Register[Reg_DRVCONF]|0xA190;WriteReadTmc2660Register(tmc,Reg_DRVCONF);}else{tmc->Register[Reg_DRVCONF]=tmc->Register[Reg_DRVCONF]|0xA140;WriteReadTmc2660Register(tmc,Reg_DRVCONF);tmc->Register[Reg_DRVCTRL]=tmc->Register[Reg_DRVCTRL]|0x100|MicroStep[microStep];WriteReadTmc2660Register(tmc,Reg_DRVCTRL);}WriteReadTmc2660Register(tmc,Reg_SMARTEN);SetMotorPower(tmc,Power);
}

2.2、对象操作

接下来我们考虑对TMC2660进行的操作问题。我们已经知道TMC2660拥有5个寄存器,而对TMC2660的各种配置都是通过这5个寄存器来实现的。即使使用SD模式来实现电机驱动也是通过寄存器配置才能实现,所以对TMC2660基本的操作则是读写TMC2660寄存器。至于SD模式下,输入脉冲和方向信号依赖于具体平台,我们已将其定义为对象的回调函数。

/*读写寄存器*/
static void WriteReadTmc2660Register(TMC2660ObjectType *tmc,TMC2660RegType reg)
{uint8_t wData[3];uint8_t rData[3];uint32_t status=0;uint32_t regValue;tmc->ChipSelcet(TMC2660CS_Enable);regValue=tmc->Register[reg]&0xFFFFF;wData[0]=(uint8_t)(regValue>>16);wData[1]=(uint8_t)(regValue>>8);wData[2]=(uint8_t)regValue;tmc->WriteRead(wData,3,rData,3);status=rData[0];status=(status<<8)+rData[1];status=(status<<8)+rData[2];tmc->status= status;tmc->ChipSelcet(TMC2660CS_Disable);
}

3、驱动的应用

我们已经设计并实现了TMC2660步进电机驱动芯片的驱动程序,接下来我们实现一个实例来验证这一驱动设定是否符合要求。

3.1、声明并初始化对象

在开始一切操作之前,首先我们需要一个对象。前面的设计中,我们已经定义了一个TMC2660对象类型,所以我们使用它定义一个对象变量。

TMC2660ObjectType tmc;

定义了tmc对象变量之后,还没有办法使用,因为我们需要对其进行初始化。前面我们已经设计了对象初始化函数,我们需要使用它来初始化tmc对象变量。初始化函数需要如下参数:

TMC2660ObjectType *tmc,          //待初始化的TMC对象变量

TMC2660SdoffType interface,      //驱动接口类型

TMC2660MicroStepType microStep,  //微步设置

uint16_t Power,                  //电流量程

uint16_t stepAngle,               //固有步进角

uint16_t *pStartStop,            //启停操作命令

uint16_t *pDirection,            //方向控制

uint16_t *pRotateSet,            //转速设定

uint16_t *pMotorState,           //电机状态

TMC2660WriteReadType writeRead,  //读写函数指针

TMC2660ChipSelcetType cs,        //片选操作函数指针

TMC2660StartStopType startStop,  //启停操作函数指针

TMC2660DirectType direct,        //方向设置函数指针

TMC2660EnableType enable         //使能控制函数指针

在这些参数中,操作变量将具体的变量指针传入即可,而其它参数如接口类型,步距角等则根据具体的应用情况输入即可。需要注意的是,5歌操作函数指针,其函数原型定义如下:

typedef void (*TMC2660WriteReadType)(uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize);
typedef void (*TMC2660ChipSelcetType)(TMC2660CSType cs);     //片选信号
typedef void (*TMC2660StartStopType)(TMC2660SSType ss);        //启停操作函数
typedef void (*TMC2660DirectType)(TMC2660DIRType dir);     //方向操作函数
typedef void (*TMC2660EnableType)(TMC2660ENNType enn);     //使能操作函数

这些操作函数依赖于具体的操作平台,我们采用的是已于STM32F103CBT6和HAL库函数的操作平台,所以根据函数原型定义来收集这些函数如下:

/*TMC2660片选操作函数*/
static void TMC2660ChipSelcet(TMC2660CSType cs)
{if(cs==TMC2660CS_Enable){TMC_CSN_ENABLE();}else{TMC_CSN_DISABLE();}
}/*启停操作函数*/
static void MotorStartStop(TMC2660SSType ss)
{if(ss==TMC2660SS_Start){if(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3) != HAL_OK){}}else{if(HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3) != HAL_OK){}}
}/*方向操作函数*/
static void MotorDirect(TMC2660DIRType dir)
{if(dir==TMC2660DIR_CCW){TMC_DIR_DISABLE();}else{TMC_DIR_ENABLE();}
}/*使能操作函数*/
static void TMC2660Enable(TMC2660ENNType enn)
{if(enn==TMC2660ENN_Enable){TMC_ENN_ENABLE();}else{TMC_ENN_DISABLE();}
}/*通过SPI2端口读写数据*/
static void WriteReadBySPI2(uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize)
{HAL_SPI_TransmitReceive (&hspi2, wData, rData, wSize, 1000);
}

至此,我们已经明白了初始化函数所需要的全部参数,我们可以使用改初始换函数初始化tmc对象变量如下:

/*初始化TMC2660对象*/Tmc2660Initialization(&tmc,TMC2660_SD,MicroStep_256,aPara.phyPara.sm42PowerRange,aPara.phyPara.sm42StepAngle,&aPara.phyPara.sm42StartStop,&aPara.phyPara.sm42Direction,&aPara.phyPara.sm42RotateSet,&aPara.phyPara.sm42RunStatus,WriteReadBySPI2,TMC2660ChipSelcet,MotorStartStop,MotorDirect,TMC2660Enable);

3.2、基于对象进行操作

初始化之后,我们就可以使用该对象来事项我们想要的操作了。我们设计一个应用函数调用相关驱动实现操作,并判断速度的设定是否改变来决定是否调整电机的运行速度。

/* 步进电机驱动控制处理函数 */
void SM42Tmc2660Driver(void)
{float temp=0;if(aPara.phyPara.sm42RotateSet<=0){//return;aPara.phyPara.sm42StartStop=0;}Tmc2660ControlBySD(&tmc);SpeedSet(tmc.frequency);//计算转速temp=tmc.frequency*((float)aPara.phyPara.sm42StepAngle);temp=temp/((float)aPara.phyPara.sm42MicroStep);aPara.phyPara.sm42RotateSpeed=(uint16_t)(temp*100/30.0);
}/* 速度调整函数 */
static void SpeedSet(float freq)
{uint16_t period=0;float temp=24000000;if(freq>0){temp=temp/freq;period=(uint16_t)temp;if((2<=period)&&(period<65535)){__HAL_TIM_SET_AUTORELOAD(&htim1,period-1);__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,period/2-1);}}else{__HAL_TIM_SET_AUTORELOAD(&htim1,0);__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_3,0);}
}

4、应用总结

在本篇中,我们设计并实现了TMC2660的驱动程序,并基于驱动程序设计了一个验证程序,测试结果良好。事实上,该驱动已经使用到我们的多个项目之中,运行效果目前还是不错的。

在使用驱动程序时需要注意,片选信号并非必须实现。因为有些时候我们可能需要在硬件上直接将其选中,此时添加片选操作函数是没有什么意义的,我们可以在初始化时传入NULL来完成。

在配置TMC2660的寄存器时,一定要仔细根据自己的应用需求来配置,如电流保护、波形输出等这些参数的配置对力矩以及电机的运行噪声有很大关系,所以需要特别注意。

欢迎关注:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499333.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外设驱动库开发笔记26:nRF24L01无线通讯驱动

现在无线在我们的生活中无处不在。而我们开发的物联网产品也大量使用无线通讯。在这一篇文章中&#xff0c;我们将讨论nRF24L01无线通讯模块驱动程序的开发与实现。 1、功能概述 nRF24L01是一款工作在2.4~2.5GHz世界通用ISM 频段的单片无线收发器芯片无线收发器包括&#xff…

外设驱动库开发笔记27:ESP8266无线通讯驱动

我们的物联网产品所使用的平台都支持无线通讯&#xff0c;而且无线通讯本身更的成本较低&#xff0c;受到大家的欢迎。在本篇文章中&#xff0c;我们将详细讨论并实现ESP8266无线通讯模块的驱动。 1、功能概述 ESP8266是由乐鑫公司出品的一款物联网芯片&#xff0c;因为价格较…

外设驱动库开发笔记28:W5500以太网控制器

以太网通讯是一种被广泛使用的数据通讯方式。在嵌入式应用中也经常使用&#xff0c;但协议栈的实现并不是一件容易的事。不过有些以太网控制器就带有协议栈&#xff0c;如W5500。在本篇中我们将讨论如何设计并实现W5500以太网控制器的驱动。 1、功能概述 W5500是WIZnet开发的…

外设驱动库开发笔记29:DS17887实时时钟驱动

一些时候&#xff0c;在我们的嵌入式产品中需要记录时间&#xff0c;所以我们就需要获取时钟&#xff0c;当然实现的方式多种多样&#xff0c;有的MCU本身就有这一功能&#xff0c;不过精度较低。当我们的应用要求较高时就需要使用专门的实时时钟芯片&#xff0c;如DS17887。在…

外设驱动库开发笔记30:宇电AI-BUS通讯驱动

嵌入式系统通常都会与外部设备进行通讯&#xff0c;这就涉及到通讯协议的问题。这些通讯协议有的是标准协议有的厂家自定义的协议&#xff0c;如宇电的AI-BUS。在本篇中&#xff0c;我们将讨论AI-BUS的驱动&#xff0c;以便于与宇电设备的通讯。 1、功能概述 宇电的设备使用基…

步进电机驱动技术3:基于ULN2003的步进电机驱动

在我们的项目中&#xff0c;经常使用到低电压小功率的步进电机&#xff0c;此类步进电机若采用驱动器控制不断成本高也过于复杂&#xff0c;我们可以直接使用场效应管或者达林顿管来实现对其的驱动。在本篇中&#xff0c;我们就来讨论一下基于ULN2003A达林顿管实现对步进电机的…

通讯接口应用笔记2:MAX3160实现多协议通讯

在一些应用需求中&#xff0c;我们需要对外部提供串行通讯端口&#xff0c;但这些端口所通讯的目标设备各有不同&#xff0c;接口协议也有RS232以及RS485和RS422多种。面对这种情况&#xff0c;我们当然可以同时设计多个串口以适应不同需要&#xff0c;但无疑对硬件资源是一种浪…

电机速度曲线规划1:梯形速度曲线设计与实现

电机驱动是很常见的应用&#xff0c;在很多系统中我们都会碰到需要改变电机的速度以实现相应的控制功能&#xff0c;这就涉及到电机速度曲线规划的问题。在这篇中我们就来简单讨论一下电机的梯形曲线规划的问题。 1、基本原理 梯形速度曲线控制算法是工业控制领域应用最为广泛…

文件系统应用笔记之一:FatFS在STM32F4上的移植

在实现如U盘文件读写&#xff0c;SD卡的文件读写等工作时&#xff0c;我们往往需要一个文件系统来支持我们的工作。特别在一些MCU应用中&#xff0c;文件系统的加入能明显改善系统交互的友好性。在这一篇中&#xff0c;我们就来讨论FatFS文件系统在STM32F4上的移植和应用。 1、…

通讯接口应用笔记3:使用W5500实现Modbus TCP服务器

前面我们设计实现了W5500的驱动程序&#xff0c;也讲解了驱动的使用方式。在最近一次的项目应用中&#xff0c;正好有一个使用W5500实现TCP通讯的需求&#xff0c;所以我们就使用该驱动程序轻松实现。这一篇中我们就来说一说基于我们W5500通讯驱动程序实现TCP通讯的过程。 1、…

电机速度曲线规划2:S形速度曲线设计与实现

电机驱动是很常见的应用&#xff0c;在很多系统中我们都会碰到需要改变电机的速度以实现相应的控制功能&#xff0c;这就涉及到电机速度曲线规划的问题。在这篇中我们就来简单讨论一下电机的S型曲线规划的问题。 1、基本原理 S型速度曲线控制算法是工业控制领域另一种常用的加…

USB应用开发笔记之一:STM32上实现USB主机读写U盘

在项目应用中&#xff0c;经常会有对外交换数据的需求。USB接口读写U盘无疑是一种颇为方便的选择。在这一篇中&#xff0c;我们就来讨论如何在STM32上实现USB主机读写U盘文件的方法。 1、应用概述 在我们的产品上有这样一个需求&#xff0c;希望通过大容量的U盘存取数据。我们…

Modbus协议栈综合实例设计

自我们开源了我们的Modbus协议栈之后&#xff0c;就一直有朋友来信说希望提供示例。这次我们整理了几个例子以供参考。 1、应用实例规划 在这次的实例中&#xff0c;我们使用的目标板拥有一个以太网接口、一个RS232串行接口和一个RS485串行接口&#xff0c;所以我们规划实现&a…

ThreadX应用开发笔记之二:移植ThreadX到STM32H7平台

前面我们将ThreadX成功移植到了STM32F4平台&#xff0c;但这只是我们的部分应用。我们希望将ThreadX的优势发挥到我们的更多应用中&#xff0c;所以在这一篇中我们就来实现将ThreadX移植到STM32H7平台中。 1、前期准备 在开始将ThreadX移植到STM32H7平台之前&#xff0c;我们需…

外设驱动库开发笔记31:S-Modlue远红外气体传感器驱动

在气体分析类产品中&#xff0c;我们经常会用到远红外气体传感器。我们就在碳氢类气体成分分析中使用了S-Modlue远红外气体传感器。接下来&#xff0c;我们将讨论S-Modlue远红外气体传感器驱动的设计与实现。 1、功能概述 S-MODULE EVO 使用非分散红外检测技术NDIR&#xff0c…

外设驱动库开发笔记32:HLPM025K3 PM2.5传感器驱动

现在人们对大气环境及室内环境都比较关注。PM2.5在生活中也是常见的词汇。在有些产品中就要求检测PM2.5的数值。检测PM2.5的手段多种多样&#xff0c;在要求不高时我们通常可以采用激光模块。在这一篇中&#xff0c;我们将讨论HLPM025K3 PM2.5传感器驱动的设计与实现。 1、功能…

外设驱动库开发笔记33:LCD1602液晶显示屏驱动

LCD1602是一种工业字符型液晶&#xff0c;能够同时显示16x02即32个字符。LCD1602液晶显示的原理是利用液晶的物理特性&#xff0c;通过电压对其显示区域进行控制&#xff0c;即可以显示出图形。在这一章我们就来讨论LCD1602液晶显示屏驱动的设计与实现。 1、功能概述 LCD1602液…

滤波器开发之四:基于算术平均的中值滤波器

在信号采集系统中&#xff0c;除了我们感兴趣的数据外&#xff0c;难免会有一些来自于环境的干扰信号。但我们总希望我们得到的数据是纯净而真实的&#xff0c;为了达到这个目标&#xff0c;我们不得不想办法去除这些干扰信号&#xff0c;于是滤波器就成为我们必不可少的帮手。…

LwIP应用开发笔记之十一:LwIP带操作系统UDP服务器

我们已经实现了在FreeRTOS系统上的LwIP的移植工作&#xff0c;但只是简单的在系统平台上跑了起来。我们还希望能做更多的事情&#xff0c;这一节我们就在FreeRTOS系统上实现基于LwIP的UDP服务器。 1、UDP协议简述 UDP协议全称是用户数据报协议&#xff0c;在网络中它与TCP协议…

滤波器开发之五:基于算术平均的限幅滤波器

通过AD采集数据时&#xff0c;我们总是希望采集到的数据是纯净而真实的&#xff0c;而实际上环境中存在太多的干扰信号&#xff0c;为了让我们得到的数据尽可能地接近实际值&#xff0c;我们需要降低这些干扰信号的影响。所以软件实现的数字滤波器应运而生&#xff0c;这一篇我…