STM32通过TB6600驱动步进电机实现梯形加减速

目录

1 注意事项

1.1 接线

1.2 电源选择

1.3 支架选择与转向

2 STM32的控制代码

2.1 输入参数

2.2 运行距离的计算

2.3 STM32与TB6600接线

2.4 代码


1 注意事项

1.1 接线

       使用梯形加减速能够使步进电机快速达到运行速度,适用于响应要求高,运行速度快的要求场景,在比赛中,步进电机也是常用的电机种类之一,是开环中运行精度最高的一档。实验中使用的步进电机为42步进电机,接线方式采用共阳极接线,即将TB6600驱动器的脉冲正(PUL+)、方向正(DIR+)、使能正(ENA+)三者接在一起、最后有使能正(ENA+)引出,其余的脉冲负(PUL-)、方向负(DIR-)、使能负(ENA-)各自接到控制信号的引脚上,即可完成步进电机的接线(接线图如下)。

       最后接电源线至TB6600即可,电源线应当注意正负极之分、不可反接,实验中的42步进电机使用的步进电机工作电压为24V、额定电流为

1.2 电源选择

       最后接电源线至TB6600即可,电源线应当注意正负极之分、不可反接,实验中的42步进电机使用的步进电机工作电压为24V、额定电流为1.5A、扭矩0.7nm。42步进电机的工作较小,网上购买的24V电池默认的DC接头都能输出最大3a的电流,可直接接入TB6600的电源接口。

       当使用的步进电机的运行电流超过3A时,普通的DC接头则不再满足使用需求,需要更换为XT60航空公母头接线的方式,更粗壮的电线在保证电池的额定输出电流时,也能保证步进电机的扭矩能正常。当电流过低,电压不足时,电机的扭矩会下降,因此,更换大功率步进电机后,电池也需要更换,电池输出线也要更换。

1.3 支架选择与转向

        步进电机的固定支架,小型的42步进电机,可直接选用购买时的默认支架,当使用体积的庞大,重量大的57(单电机质量5KG)、86(单电机质量10KG)步进电机时,需要考虑支架的强度,且在使用大体积,大质量步进电机时,单靠步进电机的差速无法实现转向,需要引进专门的转向机构。转向机构的选择可参考舵轮的转向结构


2 STM32的控制代码

2.1 输入参数

     stm32端的控制代码输入端较为简单,需要输入的参数有:运行距离、加速度、减速度、稳定运行时的速度,一般来说,比赛中需要变更的只有运行距离即可,通过上位机下发命令,运行指定的距离,为了抵达指定的运行距离而减少丢步。需要考虑步进电机的负载不可过大,步进电机的加速度,减速度和运行速度均不可过大,一旦过大,就会存在严重的丢步,导致运行距离达不到理想的位置。

2.2 运行距离的计算

       使用的步进电机为42步进电机,额定扭矩0.7Nm、步距角1.8度,额定电压24V,额定电流1.5A,假使输入的电压、电流都达到了额定值,负载在步进电机的承受范围内,则可根据代码调整TB6600的细分数,我使用的细分数是1600,即1600个脉冲旋转一圈。在代码中,步进电机的细分数为8细分。其计算值为

脉冲数=(圈数/步距角)*细分数

1600=(360/1.8)*8

当要根据赛场上的实际距离去计算时,则可进行距离的二次计算

设车轮的直径(d)为10cm,则其转一圈的位移为:s=\pi d=31.415926535897cm,即1600个脉冲使一个直径10cm的轮子位移了 31.415926535897cm,即可推算出一个脉冲可使轮子运行的距离为0.0196349540849cm,所以距离所需要的脉冲数最终计算公式为

                                                       脉冲数=(目标距离/单脉冲距离行程)

2.3 STM32与TB6600接线

         TB6600                        STM32F103c8t6

          PUL+

          DIR+                             VCC

          EN+ 

        PUL-                             PA6

        DIR-                              PB13

        EN-                               PB14

2.4 代码

Motor.c

#include "stm32f10x.h"                  // Device header
#include "Motor.h"  
#include "Motor_Init.h"  
#include <math.h>

speedRampData srd= {STOP,0,0,0,0,0,0}; //加减速变量    
uint8_t  motor_sta        = 0;//电机状态


/*
step   移动步数(正数为正转,负数为逆时针)
accel  加速度,实际值为accel*0.1*rad/sec^2  10倍并且2个脉冲算一个完整的周期
decel  减速度,实际值为decel*0.1*rad/sec^2
speed  最大速度,实际值为speed*0.1*rad/sec
 */
void MOTOR_Move(int32_t step, uint32_t accel, uint32_t decel, uint32_t speed)
{
		
    uint16_t tim_count; 																										 //存放中断时刻的计数值
    unsigned int max_s_lim;                                     					 	 //达到最大速度时的步数    
    unsigned int accel_lim;																									 //必须开始减速的步数(如果还没有加速度到最大速度时)

		if(motor_sta!= STOP)  																									 //只允许步进电机在停止的时候才继续
			return;			
    if(step < 0)   																													 //逆时针
			{
				GPIO_SetBits(GPIOB,GPIO_Pin_13);																		 //PA13为方向引脚,输出高电平
				step = -step;      
			}		
		else   																																	 //顺时针
			{
				GPIO_ResetBits(GPIOB,GPIO_Pin_13);																	 //PA13为方向引脚,输出低电平		
			}        
    if(step == 1)   																											   // 如果只移动一步
    {       
      srd.accel_count = -1; 																								 // 只移动一步
        
      srd.run_state = DECEL;																								 // 减速状态
        
      srd.step_delay = 1000;																								 // 短延时

     }
    
    else if(step != 0)  																			 								// 步数不为零才移动
    {					
				srd.min_delay = (int32_t)(A_T_x10/speed);															// 设置最大速度极限, 计算min_delay用于定时器的计数器的值min_delay = (alpha / tt)/ w   
				srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10);		// 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.01rad/sec^2
																																							// step_delay = 1/tt * sqrt(2*alpha/accel)
																																							// step_delay = ( tfreq*0.69/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100 
			  max_s_lim = (uint32_t)(speed*speed/(A_x200*accel/10));								//计算多少步之后达到最大速度的限制 max_s_lim = speed^2 / (2*alpha*accel)
    
				if(max_s_lim == 0)																										//如果达到最大速度小于0.5步,我们将四舍五入为0,但实际我们必须移动至少一步才能达到想要的速度 
				{
					max_s_lim = 1;
				}    
				accel_lim = (uint32_t)(step*decel/(accel+decel)); 										// 计算多少步之后我们必须开始减速,n1 = (n1+n2)decel / (accel + decel)
   
				if(accel_lim == 0) 																										// 我们必须加速至少1步才能开始减速
				{
					accel_lim = 1;
				}
   
				if(accel_lim <= max_s_lim)																						//加速阶段到不了最大速度就得减速。。。使用限制条件我们可以计算出减速阶段步数 
				{
					srd.decel_val = accel_lim - step;																		//减速段的步数
				}
				else
				{
					srd.decel_val = -(max_s_lim*accel/decel);														//减速段的步数 
				}
   
				if(srd.decel_val == 0) 																								// 不足一步 按一步处理 
				{
					srd.decel_val = -1;
				}    
				srd.decel_start = step + srd.decel_val;																//计算开始减速时的步数
 
    
				if(srd.step_delay <= srd.min_delay)																		// 如果一开始c0的速度比匀速段速度还大,就不需要进行加速运动,直接进入匀速
				{
					srd.step_delay = srd.min_delay;
					srd.run_state = RUN;
				}
				else
				{
					srd.run_state = ACCEL;
				}
    
				srd.accel_count = 0;																									// 复位加速度计数值
		
			}
			motor_sta = 1;  																												// 电机为运动状态
			tim_count = TIM_GetCounter(TIM3);																				//获取计数值
			TIM_SetCompare1(TIM3,tim_count+srd.step_delay/2);												//设置定时器比较值 
			TIM_ITConfig(TIM3,TIM_IT_CC1,ENABLE);																		//使能定时器通道 
			TIM_CCxCmd(TIM3,TIM_Channel_1,TIM_CCx_Enable);
			TIM_Cmd(TIM3, ENABLE);																									//开启定时器
}
 


void speed_decision()                                                         //中断执行函数
{
	__IO uint32_t tim_count=0;
	__IO uint32_t tmp = 0;  
  uint16_t new_step_delay=0;                                                  // 保存新(下)一个延时周期  
  __IO static uint16_t last_accel_delay=0;                                    // 加速过程中最后一次延时(脉冲周期). 
  __IO static uint32_t step_count = 0; 																			  // 总移动步数计数器  
  __IO static int32_t rest = 0;																								// 记录new_step_delay中的余数,提高下一步计算的精度  
  __IO static uint8_t i=0;																										//定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲
 
  if (TIM_GetITStatus(TIM3, TIM_IT_CC1)== SET)
  {	  
		
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);																	// 清楚定时器中断		
	  tim_count = TIM_GetCounter(TIM3);																					//获取计数值
		tmp = tim_count+srd.step_delay/2;
	  TIM_SetCompare1(TIM3,tmp);																								// 设置比较值
		i++; 
		if(i==2)																																	//中断两次为一个脉冲
		{
			i=0; 
			switch(srd.run_state)
			{
				case STOP:																														//停止状态
					step_count = 0;
					rest = 0;
					
					TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
				  TIM_CCxCmd(TIM3,TIM_Channel_1,TIM_CCx_Disable);
				  TIM_Cmd(TIM3, DISABLE);					                                  //单个电机可以关闭定时器,多个电机只关闭通道即可
					motor_sta = 0;  
					break;
				
				case ACCEL:																														//加速状态
				step_count++;
				srd.accel_count++;
				new_step_delay = srd.step_delay - (((2 *srd.step_delay) + rest)/(4 * srd.accel_count + 1));//计算新(下)一步脉冲周期(时间间隔)
				rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);					// 计算余数,下次计算补上余数,减少误差
				
					if(step_count >= srd.decel_start) 																	//检查是够应该开始减速
					{
						srd.accel_count = srd.decel_val;																	//加速计数值为减速阶段计数值的初始值
						srd.run_state = DECEL;																						//下个脉冲进入减速阶段 
					}
					
					else if(new_step_delay <= srd.min_delay)														//检查是否到达期望的最大速度
					{
						last_accel_delay = new_step_delay;																//保存加速过程中最后一次延时(脉冲周期)
						new_step_delay = srd.min_delay;   																// 使用min_delay(对应最大速度speed) 
						rest = 0;            																							//清零余值               
						srd.run_state = RUN;																							//设置为匀速运行状态 
					}
					break;
					
				case RUN:
          step_count++;  																											// 步数加1				  
          new_step_delay = srd.min_delay;   																  // 使用min_delay(对应最大速度speed)				 
          if(step_count >= srd.decel_start)   																// 需要开始减速
					{
            srd.accel_count = srd.decel_val;  																// 减速步数做为加速计数值
            new_step_delay = last_accel_delay;																// 加阶段最后的延时做为减速阶段的起始延时(脉冲周期)
            srd.run_state = DECEL;           																  // 状态改变为减速
          }
          break;
					
				case DECEL:
          step_count++;  																											// 步数加1

          srd.accel_count++; 																									// 是个负数
          new_step_delay = srd.step_delay - (((2 * srd.step_delay) + rest)/(4 * srd.accel_count + 1)); //计算新(下)一步脉冲周期(时间间隔)
          rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);				// 计算余数,下次计算补上余数,减少误差
          if(srd.accel_count >= 0) 																						//检查是否为最后一步  是个负数所以要判断 大于等于零时 应该就是减速结束
          {
            srd.run_state = STOP;
          }
          break;
			}
			 srd.step_delay = new_step_delay; 																			// 为下个(新的)延时(脉冲周期)赋值
		}
	}
}
	

void TIM3_IRQHandler(void)
{
	speed_decision();
}



Motor.h

#ifndef __Motor_H
#define __Motor_H
#include "stm32f10x.h"                  // Device header
#include "math.h"


typedef struct
{
	 uint8_t run_state;    //电机旋转状态
	 uint8_t dir ;         //电机旋转方向
	 int step_delay;   //下个脉冲周期(时间间隔)启动时为加速度
	 int decel_start;  //启动减速位置
	 int decel_val;    //减速阶段步数
	 int min_delay;    //最小脉冲周期(最大速度,即匀速段的速度)
	 int accel_count;  //加速阶段计数值
}speedRampData;


#define TRUE     1
#define FALSE    0


/*电机速度决策中的四个状态*/
#define STOP              0 // 停止状态
#define ACCEL             1 // 加速状态
#define DECEL             2 // 减速状态
#define RUN               3 // 匀速状态


#define TIM_PRESCALER      32
#define T1_FREQ            (SystemCoreClock/(TIM_PRESCALER+1))     //定时器频率

/*电机单圈参数*/
#define STEP_ANGLE				1.8									//步进电机的步距角 单位:度
#define FSPR              200        //步进电机单圈步数

#define MICRO_STEP        8         				//细分器细分数 
#define SPR               (FSPR*MICRO_STEP)  //16细分的步数

//数学常数,用于MSD_MOVE函数的简化计算
#define ALPHA             ((float)(2*3.14159/SPR))       // α= 2*pi/spr    
#define A_T_x10           ((float)(10*ALPHA*T1_FREQ))
#define T1_FREQ_148       ((float)((T1_FREQ*0.676)/10)) // 0.69为误差修正值(计算过程,文档中有写)
#define A_SQ              ((float)(2*100000*ALPHA)) 
#define A_x200            ((float)(200*ALPHA))
    

void MOTOR_Move(int32_t step, uint32_t accel, uint32_t decel, uint32_t speed);				
extern void speed_decision(void);	

					
#endif





          

                          
 
 

                    

Motor_Init.c

#include "stm32f10x.h"                  // Device header
#include "Motor_Init.h"  
#include "Motor.h"


//GPIO口初始化
void MOTOR_GPIO_Init(void) 
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 				//开启PA端口时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);					//开启PB端口时钟
	
	/* 步进电机驱动器:脉冲输出PA6 */
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;									//复用输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	 /* 步进电机驱动器:方向控制 PB13*/
	 /* 步进电机驱动器:使能控制PB14 */
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

//定时器初始化
void MOTOR_TIM_Init(void)
{
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	TIM_InternalClockConfig(TIM3);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟分频因子,数字滤波采样频率的参数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 65536-1;									//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = TIM_PRESCALER;				//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; 						//高级定时器功能
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;	
	TIM_OCStructInit(&TIM_OCInitStructure);	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;  						//翻转模式
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;															//CCR 比较值
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Disable);    						//关闭预装载
	TIM_CCxCmd(TIM3,TIM_Channel_1,TIM_CCx_Disable);
	
	
	TIM_ClearFlag(TIM3,TIM_FLAG_CC1);																//清除定时器3的通道1的标志位
	TIM_ITConfig(TIM3,TIM_IT_CC1,ENABLE);														//开启定时器3的通道1中断

	
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);									//分组
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStructure);
	

}




Motor_Init.h

#ifndef __Motor_Init_H
#define __Motor_Init_H
#include "stm32f10x.h"                  // Device header
#include "math.h"
#include "Motor.h"  



//输出比较模式周期设置为0xFFFF
#define TIM_PERIOD                   0xFFFF

void MOTOR_GPIO_Init(void);
void MOTOR_TIM_Init(void);


#endif

main.c


#include "stm32f10x.h"                  // Device header
#include "Delay.h"

#include "Motor.h"  
#include "Motor_Init.h"  


uint32_t set_speed  = 1000;         // 最大速度设置 单位为0.1rad/sec
uint32_t step_accel =25;         // 加速度 单位为0.1rad/sec^2
uint32_t step_decel = 25;         // 减速度 单位为0.1rad/sec^2
uint32_t step=1600;



//extern	uint8_t	motor_sta		;	

int main(void)
{

	MOTOR_GPIO_Init();
	MOTOR_TIM_Init();

	MOTOR_Move(step*20,step_accel,step_decel,set_speed);       //需要时调用
	
	while (1)
	{
		
		
	
	}
}


















  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值