基于STM32F103C8T6的57步进电机的控制方案
最近由于使用需要,需要用拥有较大扭矩的步进电机,逛了一圈某宝挑选了一款扭矩可达2.5N·m的57步进电机,自己琢磨了挺久,写下该文章作为笔记来积累。本人菜鸡,用的方法也是基本方法,望大佬轻喷。
1.硬件选型部分:
步进电机选型:
大扭矩的一款57步进电机
驱动选型:
TB6600驱动模块最高可达32细分
主控选型:
stm32f103c8t6最小系统开发板
电源选型:
150W24V开关电源
2.步进电机转速控制思路:
2.1步进电机工作原理简解
步进电机之所以会进行转动,实际上是通过对于其内部电磁铁进行通断电从而吸引或排斥转子进行转动,而通过使用驱动对步进电机进行控制,可以使得在原有步进电机的固定步距的基础上,再进行细分。
如:一般57步进电机的步距角为1.8°,即当给步进电机一个脉冲信号时,步进电机就会旋转1.8°(值得一提的是这跟舵机的控制有一定的相似之处,只不过舵机的控制对脉冲占空比有要求,而步进电机的的转动只与脉冲个数有关,而与脉冲的占空比没有关系)但当使用了驱动器时可以使得步距角减小,假设设置驱动采用16细分时,可以理解成电机收获一个有效脉冲时转动了1.8°/16。
2.2步进电机工作特定转速下的对应脉冲频率计算
了解了步进电机的工作原理后,我们知道了,步进电机若要在特定的时间内的转动一个特定的角度,就必须配合脉冲信号。
因此,在不进行细分条件下,假设步距角为DEG(单位:°),转速要求为n(单位:r/s),由此可以得到转一圈所需脉冲个数为:360/DEG,则在规定转速下,一秒内需要的脉冲个数(即脉冲频率)为:360/DEG*n(单位:HZ)。
若细分系数为STEP,结合之前分析,可以讲此时的步距角等效为:DEG/STEP,因此我们可以得出在进行了细分后的一秒的脉冲个数(脉冲频率)为:360*STEP/DEG *n(单位:HZ)。
举例分析:
已知步进电机的步距角为1.8°,现配合TB6600驱动进行16细分,要求转速为1r/s,由上面公式可以得到脉冲频率f=360 * 16/1.8*1=3200HZ
3.stm32对步进电机的控制思路:
以上面的例子为例,我们需要得到一个3200HZ的脉冲信号,这对于stm32单片机而言并不难,stm32可以通过设置对应通用定时器或者高级定时器的特定通道实现一定频率的脉冲的输出。
在开发板上,定时器的时钟频率为72MHZ,我们知道单片机的引脚输出PWM的频率的计算公式为72M/[(psc+1)*(arr+1)],在转速为1r/s的基础上,可以得出(psc+1) * (arr+1)=72M/3200=22500;不妨将psc值设为449,arr值设为49。
若希望能实现变速调节,只需改变arr或者psc的值即可。此处值得一提的是,在对于IO引脚设置完脉冲频率后,不要忘记对于占空比进行设置,不然无法称之为脉冲信号,而占空比的设置基于你设置的PWM的输出模式,但不论是TIM_OCMode_PWM1还是TIM_OCMode_PWM2,只需知道相应的计算公式即可,如果只是希望单纯的实现步进电机的转速控制,占空比的影响不是很大。
PWM配置带码呈现:
void PWM_Init_TIM1(uint16_t arr,uint16_t psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_GPIOA,ENABLE);
//PA11 作为PWM的输出
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=PUL_Pin;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_TimeBaseInitStructure.TIM_Period=arr;
TIM_TimeBaseInitStructure.TIM_Prescaler=psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision=0;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure);
//PWM模式1:TIM1->CNT < CCR时为高
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse=0;
TIM_OC4Init(TIM1,&TIM_OCInitStructure);
//MOE 主输出使能
TIM_CtrlPWMOutputs(TIM1,ENABLE);
//预装载使能
TIM_OC4PreloadConfig(TIM1,TIM_OCPreload_Enable);
//使能TIMx在ARR上的预装载寄存器
TIM_ARRPreloadConfig(TIM1, ENABLE);
//相应中断的处理
TIM_ClearFlag(TIM1, TIM_FLAG_Update);
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM1,ENABLE);
}
若希望能对转角进行控制,可以设置TIM1的溢出中断,设置一个计数变量,当变量到达一定值(经过了一定数量的脉冲信号后)失能PWM输出。
4.硬件接线图:
TB660驱动器的引脚说明及使用方法:
ENA+,ENA-:使能信号,可用于电机启停
DIR+,DIR-:方向信号,可控制电机正反转
PUL+,PUL-:脉冲控制信号,两者之中一者接单片机地,一者接脉冲信号输出引脚。
本文采用了共阴极接法,当ENA+为高电平时,电机会停转,当ENA+为低电平时,电机可进行工作。(不要被上图高电平有效误导)
ENA+ ------- PB13(通用IO)
DIR+ ------- PB14(通用IO)
PUL+ ------- PA11(TIM1CH4)
ENA-,DIR-,PUL- ---------开发板GND或设置相应IO引脚为低电平。
5.实验验证:
通过上学期学的QT的编程应用,结合MODBUS-CRC16查表法的通讯协议,建立了上位机(PC)与下位机的通讯,实现了通过上位机对于步进电机的转速以及转向控制。
实验发现,该电机的转速存在一定的上限,问了卖家才知道,调速范围大概在0-200圈/分钟。
使用时应注意接线正确。
下位机部分通讯代码:
void USART1_IRQHandler()
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收到数据
{
//帧头(地址位):m 功能位 :s x 两位速度 一位方向 两位校验
static uint8_t Receive_Buffer[7];
static uint8_t num=0;
static uint8_t crc_low,crc_high;
uint8_t data;
uint8_t speed_value,direction_value;
data=USART_ReceiveData(USART1);
Receive_Buffer[num]=data;//接受到的数据
num++;
if(Receive_Buffer[0]!='m')
{
num=0;//如果第一位接受到的数据不是'm'重新开始接受数据
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
return;
}
if(num==7)
{
num=0;
crc_low=(N_CRC16(Receive_Buffer,5)&0xff00)>>8;//low
crc_high=N_CRC16(Receive_Buffer,5)&0xff;//high
if(Receive_Buffer[1]=='s'&&crc_low==Receive_Buffer[5]&&crc_high==Receive_Buffer[6])
{
speed_value=(Receive_Buffer[2]-0x30)*10+(Receive_Buffer[3]-0x30)*1;
direction_value=(Receive_Buffer[4]-0x30)*1;
if(direction_value!=2)
{
Speed_Set(speed_value,direction_value);
memset(Receive_Buffer,0,sizeof(Receive_Buffer));//清空数组为下一次接受做好准备
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
else
{
Step_Motor_Stop();
memset(Receive_Buffer,0,sizeof(Receive_Buffer));//清空数组为下一次接受做好准备
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
else
{
memset(Receive_Buffer,0,sizeof(Receive_Buffer));//清空数组为下一次接受做好准备
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
}
USART_ClearITPendingBit(USART3,USART_IT_RXNE);
}
6.总结:
作为一名小白第一次写博客,步进电机也是第一次用,花了一个晚上的时间也才研究到这种水平,望各位大佬见谅,不惜勿喷,如若存在错误之处还请指出。代码实在太过粗糙工程文件就不放出来了。