前言:在这篇博客中,我将分享一下使用 STM32F103ZET6 的 PWM 功能来有效地控制 57 步进电机的启动和旋转方向。
通过本文我们会学习到:使用STM32的通用定时器定时,控制步进电机正反转和启停。并学习步进电机和步进电机驱动器的基本使用方法,学习使用PWM来控制步进电机。
一、实验准备
1.器材准备
一块stm32f103zet6开发板、12~48V直流电源、步进电机驱动器、57步进电机。
2.器材简介
(1)stm32f103zet6
用STM32f103x系列的开发板都可行,不过代码可能要改一改。
(2)57步进电机
57 步进电机是一种常见的步进电机类型,其“57”通常指的是电机的法兰尺寸,即电机机身的边长或直径为 57 毫米。
57 步进电机具有以下一些特点和参数:
- 步距角:通常为 1.8°,这意味着电机每接收一个脉冲信号,就会转动 1.8°,一般需要 200 个脉冲才能完成一次完整的 360°旋转。不过也有一些 57 步进电机的步距角为 0.9°。
- 额定电流:常见的额定电流在 2A 到 5A 之间,具体数值取决于电机型号和应用要求。
- 额定电压:一般在 24V 到 48V 范围内,需要注意的是,电压越大,扭矩越大,但超过额定电压容易烧毁电机。
- 静态力矩(保持转矩):电机在静止不动时能够抵抗外力的能力。不同型号的 57 步进电机静态力矩有所差异,可能在 0.8Nm 到 3Nm 之间。通常机身越长,扭矩越大。
- 细分步数:通过驱动器可以将每步的角度进一步细分,以提高电机的运动平滑度和精度,常见的细分步数有 16、32、64 等。
- 相数:一般为两相。
在实际使用 57 步进电机时,需要根据具体的应用需求和系统要求,选择合适的电机型号,并配合相应的驱动器和控制系统,以实现精确的位置控制和运动控制。同时,还需注意电机的接线正确性、电源的稳定性以及工作环境的要求等。
(3)步进电机驱动器
步进电机驱动器是一种用于控制步进电机运行的设备。它能够将控制器发出的脉冲信号转换为步进电机的角位移,实现对电机的精确控制。
通常,步进电机驱动器具有以下特点和功能:
- 细分设置:通过拨码开关或其他方式,可以选择不同的细分模式,如整步、半步、1/4 步、1/8 步、1/16 步、1/32 步等。细分越高,电机的运行越平稳、精度越高,但速度可能会相对降低。
- 电气特性:包括适用的电压范围、额定电流等。一般来说,需要根据步进电机的参数选择合适的驱动器,以确保电机能够正常工作且不过载。
- 接口:具有与控制器和步进电机连接的接口。常见的接口信号包括脉冲信号(PUL)、方向信号(DIR)和使能信号(ENA)等。
- 电流控制:可调节输出给步进电机的电流大小,以适应不同扭矩需求的电机或应用场景。
- 故障指示:部分驱动器带有故障指示灯,如过流、过热或欠压等故障发生时会亮起,以便及时发现和排除问题。
例如,TB6600 驱动器是一款常见的步进电机驱动器,它的右边分为两个区域。
:
- signal 区域:用于与开发板连接,以控制电机的驱动。其中 ENA 接口(使能信号)有效时,驱动器将自动切断电机绕组电流,使电机处于自由状态(无保持转矩);不连接时默认为无效状态,电机绕组通以电流可正常工作。DIR 接口(方向信号)用于控制电机旋转方向,信号有效时电机顺时针旋转,无效时逆时针旋转。PUL 接口(脉冲信号)接收控制器发出的脉冲,驱动器每接受一个脉冲信号,就驱动步进电机旋转一个步距角,对于最佳输入要求,此信号占空比最好为 1:1,脉冲信号的频率不大于 100kHz。
- high voltage 区域:用于与步进电机连接。
步进电机驱动器的细分是指将电机固有步距角进一步细分成更小的角度。
以常见的两相步进电机(步距角为1.8°)为例,在没有细分的情况下(1细分),驱动器接收一个脉冲信号,电机就走1.8度。走完360度需要接收360÷1.8 = 200个脉冲。
如果驱动器设定为4细分,那么电机的步距角则为1.8°÷4 = 0.45°。即驱动器每接收一个脉冲,电机转动0.45°,此时走完360度需要的脉冲数为360÷0.45 = 800个。
细分的作用主要是改善电机的运行性能,使电机转动更平稳、减少振动和噪音。在一些对精度要求较高的应用场合,细分可以提高控制精度。
常见的细分倍数有1/2、1/4、1/8、1/16、1/32、1/64 或1/5、1/10、1/20等。细分后电机步距角的计算公式为:步距角 = 电机固有步距角÷细分数。
需要注意的是,细分倍数并不是越高越好。过高的细分倍数可能会导致一些问题,例如对驱动器的技术要求和工艺要求更高,成本增加;而且细分倍数过高时,电机的定位精度可能并不能实际提高,因为电机本身可能无法分辨过于微小的角度变化。在实际使用时,应根据具体的应用需求和电机性能来选择合适的细分倍数。同时,细分驱动器要精确控制电机的相电流,所以对驱动器要有相当高的技术要求和工艺要求。
3.接线
提醒:接线时请务必注意安全。
(1)电机与步进电机驱动器
在步进电机驱动器上,A+A-是一相,B+B-是另一相。按照电机上的标识来连接电机驱动器。如果没有标识的话,可以用万用表测,另外再简单一点的方法是让两根线碰在一起,再转动电机,若比较难转动,那这两根是一组的。举个例子:我的电机也是没有标识的,测出来后是黑绿一组,红蓝一组,然后要么电机上的黑绿一组接电机驱动器上的A+ A-,红蓝一组接电机驱动器上的B+ B-,要么电机上的黑绿一组接电机驱动器上的B+ B-,红蓝一组接电机驱动器上的A+ A-。其中什么电机上的黑线接A+或者A-都可以,主要是电机上的一相接对电机驱动器上的一相就行。
(2)步进电机驱动器与12~48V直流电源
VCC(驱动器)---VCC(12~48V直流电源)
GND(驱动器)---GND(12~48V直流电源)
(3)步进电机驱动器与stm32f103zet6
a.共阳极接法
接法:将电机驱动器的ENA+,DIR+,PUL+接到开发板的电源上,最好是5V ,如果大于5V,需要外部另加限流电阻,否则有可能烧毁驱动器控制接口的电路;ENA-,DIR-,PUL-接开发板相应的控制引脚。
b.共阴极接法
接法:将电机驱动器的ENA-,DIR-,PUL-接地;ENA+,DIR+,PUL+接开发板相应的控制引脚。
我这里用的是共阴极接法。
二、代码
主函数主要包含:
#include "key.h"//通过按键对电机进行操作
#include "motor.h"//包含电机的启动关闭和改变旋转方向的函数
#include "pwm.h"//提供脉冲
【1】程序简介
工程名称:STM32F103ZET6输出PWM驱动57步进电机
实验平台:正点原子战舰STM32F103ZET6开发板
代码参考:正点原子标准库
【2】实验操作:
下载本程序,复位开发板即可。
【3】注意事项:
步进电机驱动器使用32细分。
代码仅供参考:
pwm.c:
#include "pwm.h"
/*
arr:自动重装值
psc:时钟预分频数
脉冲频率:f=72M/(arr+1)(psc+1) 最好不要超过100kHZ!
这里使用的是定时器3的通道一!
PA6作为脉冲引脚
*/
void PWM_Init(u16 arr,u16 psc)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=arr;
TIM_TimeBaseInitStruct.TIM_Prescaler=psc;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse=arr/2;
TIM_OC1Init(TIM3,&TIM_OCInitStruct);
TIM_Cmd(TIM3,ENABLE);
}
pwm.h:
#ifndef __PWM_H
#define __PWM_H
#include "sys.h"
void PWM_Init(u16 arr,u16 psc);
#endif
motor.c:
#include "motor.h"
#include "key.h"
/*
ENA+---PA1
DIR+---PA0
*/
extern u8 KeyNum0;//定义全局变量
extern u8 KeyNum1;
void MOTOR_Init(void)//ENA+,DIR+引脚初始化
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
void MotorOpen(void)//电机启动
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void MotorClose(void)//电机关闭
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void MotorForward(void)//电机顺时针旋转
{
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}
void MotorReverse(void)//电机逆时针旋转
{
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
void MotorSwitch(void)//控制电机开与关---KEY1
{
KeyNum1=Key1_GetNum();
if(KeyNum1==1)
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1))
{
MotorOpen();
}
else
{
MotorClose();
}
}
}
void MotorDirection(void)//控制电机旋转方向---KEY0
{
KeyNum0=Key0_GetNum();
if(KeyNum0==1)
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_0))
{
MotorForward();
}
else
{
MotorReverse();
}
}
}
motor.h:
#ifndef __MOTOR_H
#define __MOTOR_H
#include "sys.h"
void MOTOR_Init(void);
void MotorOpen(void);
void MotorClose(void);
void MotorForward(void);
void MotorReverse(void);
void MotorSwitch(void);
void MotorDirection(void);
#endif
key.c:
#include "key.h"
#include "delay.h"
void KEY0_Init(void)//KEY0---PE4
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStruct);
}
void KEY1_Init(void)//KEY1---PE3
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStruct);
}
void KEY2_Init(void)//KEY2---PE2
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStruct);
}
void WK_UP_Init(void)//WK_UP---PA0
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}
u8 Key0_GetNum(void)
{
u8 KeyNum=0;
if(KEY0==0)
{
delay_ms(20);
while(KEY0==0);
delay_ms(20);
KeyNum=1;
}
return KeyNum;
}
u8 Key1_GetNum(void)
{
u8 KeyNum=0;
if(KEY1==0)
{
delay_ms(20);
while(KEY1==0);
delay_ms(20);
KeyNum=1;
}
return KeyNum;
}
u8 Key2_GetNum(void)
{
u8 KeyNum = 0;
if (KEY2 == 0)
{
delay_ms(20);
while (KEY2 == 0);
delay_ms(20);
KeyNum = 1;
}
return KeyNum;
}
u8 WK_UP_GetNum(void)
{
u8 KeyNum=0;
if(WK_UP==1)
{
delay_ms(20);
while(WK_UP==1);
delay_ms(20);
KeyNum=1;
}
return KeyNum;
}
key.h:
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
void KEY0_Init(void);
void KEY1_Init(void);
void KEY2_Init(void);
void WK_UP_Init(void);
u8 Key0_GetNum(void);
u8 Key1_GetNum(void);
u8 Key2_GetNum(void);
u8 WK_UP_GetNum(void);
#endif
main.c:
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "key.h"
#include "motor.h"
#include "pwm.h"
/*****************************************************************************************
引脚分配: 功能实现:
PWM---PA6(PUL+) 电机启动,方向沿顺时针旋转,按下KEY0,
DIR---PA0(DIR+) 电机逆时针旋转,按下KEY1,电机停止转动
ENA---PA1(ENA+)
KEY0--PE4(控制方向)
KEY1--PE3(控制开关)
*****************************************************************************************/
u8 KeyNum0;
u8 KeyNum1;
int main(void)
{
delay_init();
KEY0_Init();
KEY1_Init();
MOTOR_Init();
PWM_Init(1000-1,72-1);
while(1)
{
MotorSwitch();
MotorDirection();
}
}