使用硬件型号为:STM32F103C8T6最小系统板、A4988模块、42步进电机(42BYGH39)

1.1 A4988介绍


引脚 功能说明 接线
EN 使能端,低电平有效 接GND或单片机IOx1
MS1/2/3 步进模式选择 不接或单片机IOx3
SLP 休眠,高电平有效 短接RST
STEP 输入脉冲,一个脉冲转一下 单片机IOx1
DIR 方向位,0/1各代表一方向 单片机IOx1
VMOT/GND 电源接口,直流供电8~35V,最大2A 12/24V电源
1A/1B/2A/2B 步进电机接线 步进电机
VDD/GND 接单片机3.3V和GND 单片机电源

1.2 接线方式

STM32与USB转TTL

STM32 USB转TTL
3.3V/5V 3.3/5V
GND GND
PA9 RXD
PA10 TXD

STM32与A4988

STM32 A4988
3.3V/5V VDD
GND GND
PB6 STEP(motor.h定义)
PB7 DIR (motor.h定义)
EN接GND
SLP接RST

A4988与步进电机

A4988 步进电机
VMOT 12V电源+
GND 12V电源-
B2 B-
A2 B+
A1 A+
B1 A-

需要注意步进电机接线相序

A4988标注的1A、1B、2A、2B,数字代表相、ab代表正负。

步进电机标注的A+、A-、B+、B-,其中AB代表相,±代表正负。

因此接线对应方式应该是:A+和A-对应1A和1B、B+和B-对应2A和2B。比如我的步进电机为黑色A+、绿色A-、红色B+、蓝色B-,那么对应A4988的B2 A2 A1 B1,电机接线顺序就是B- B+ A+ A-(蓝-红-黑-绿)。

如果接错可能会出现电机反转,或只振动不旋转的现象。

1.2 程序设计

本程序为串口控制步进电机,改编自开发板的串口例程,另外编写了步进电机的驱动函数。

GPIO中间的延时表示速度,delay_ms(2)约0.8s每圈,delay_ms(1)约0.4s每圈。

默认情况下(全步进)一个STEP脉冲步进电机转90°。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//motor.h
#ifndef __MOTOR_H
#define __MOTOR_H
#include "delay.h"

//PF0-7,12-15

#define Motor_GPIO GPIOF //PF
#define Motor_RCC RCC_APB2Periph_GPIOF
//第一个步进电机A4988的接线
#define Motor1_STEP GPIO_Pin_1 //STEP - PF1
#define Motor1_DIR GPIO_Pin_2 //DIR - PF2
//第二个步进电机A4988的接线 //PB
#define Motor2_STEP GPIO_Pin_3 //STEP - PF3
#define Motor2_DIR GPIO_Pin_4 //DIR - PF4

void MOTOR_Init(void);
void motor(unsigned int motor1_dir, unsigned int motor1_step, unsigned int motor2_dir, unsigned int motor2_step);



#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//motor.c
#include "motor.h"
/*GPIO_motornum和GPIOx用于选择电机,GPIO_direction用于选择电机方向,dir:0为逆1为正,k为90°的倍数*/
// GPIO

void MOTOR_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(Motor_RCC,ENABLE);

//Motor初始化
GPIO_InitStructure.GPIO_Pin = Motor1_STEP|Motor1_DIR|Motor2_STEP|Motor2_DIR;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(Motor_GPIO,&GPIO_InitStructure); // 初始化GPIOB
GPIO_ResetBits(Motor_GPIO,Motor1_STEP); //初始化GPIOB_6输出低电平
GPIO_ResetBits(Motor_GPIO,Motor1_DIR); //初始化GPIOB_7输出低电平
GPIO_ResetBits(Motor_GPIO,Motor2_STEP); //初始化GPIOB_8输出低电平
GPIO_ResetBits(Motor_GPIO,Motor2_DIR); //初始化GPIOB_9输出低电平
}

void motor(unsigned int motor1_dir, unsigned int motor1_step, unsigned int motor2_dir, unsigned int motor2_step)
{
unsigned int i;

switch(motor1_dir)
{
case 0 : GPIO_SetBits(Motor_GPIO,Motor1_DIR); break;
case 1 : GPIO_ResetBits(Motor_GPIO,Motor1_DIR); break;
default : break;
}
switch(motor2_dir)
{
case 0 : GPIO_SetBits(Motor_GPIO,Motor2_DIR); break;
case 1 : GPIO_ResetBits(Motor_GPIO,Motor2_DIR); break;
default : break;
}
/*
GPIO_SetBits(Motor_GPIO,Motor1_STEP);
GPIO_SetBits(Motor_GPIO,Motor2_STEP);
delay_ms(2); //周期1.3ms
GPIO_ResetBits(Motor_GPIO,Motor1_STEP);
GPIO_ResetBits(Motor_GPIO,Motor2_STEP);
delay_ms(2);
*/
for(i = 0;i < motor1_step || i < motor2_step; i++)
{
if(i<motor1_step)
{
GPIO_SetBits(Motor_GPIO,Motor1_STEP);
delay_ms(2); //周期1.3ms
GPIO_ResetBits(Motor_GPIO,Motor1_STEP);
delay_ms(2);
}
if(i<motor2_step)
{
GPIO_SetBits(Motor_GPIO,Motor2_STEP);
delay_ms(2); //周期1.3ms
GPIO_ResetBits(Motor_GPIO,Motor2_STEP);
delay_ms(2);
}
}

//delay_ms(2); //延时一会
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//main.c
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "mbotLinuxUsart.h"//引用该头文件是使用,通信协议的前提
#include "motor.h"

#define IMAGE_WIDTH 640/2
#define IMAGE_HEIGHT 480/2

//测试发送变量
short testSend1 =1111;
short testSend2 =2222;
short testSend3 =3333;
unsigned char testSend4 = 0x05;

//测试接收变量
int testRece1 =400;
int testRece2 =300;
unsigned char testRece3 = 0x00;


int main(void)
{
//=======================================变量定义=====================================================
u8 dir1;
u8 dir2;
u16 step1;
u16 step2;
//======================================硬件初始化====================================================
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组2
uart_init(115200); //串口初始化为115200
MOTOR_Init(); //初始化A4988驱动

//=======================================循环程序=====================================================
while(1)
{
//将需要发送到ROS的数据,从该函数发出,前三个数据范围(-32768 - +32767),第四个数据的范围(0 - 255)
usartSendData(testSend1,testSend2,testSend3,testSend4);
if(testRece1>IMAGE_WIDTH)
{
dir1=1;
step1=testRece1-IMAGE_WIDTH;
}
else
{
dir1=0;
step1=IMAGE_WIDTH-testRece1;
}
if(testRece2>IMAGE_HEIGHT)
{
dir2=0;
step2=testRece2-IMAGE_HEIGHT;
}
else
{
dir2=1;
step2=IMAGE_HEIGHT-testRece2;
}
motor(dir1,step1,dir2,step2);
//必须的延时
delay_ms(13);
}
}

//====================================串口中断服务程序=================================================
void USART1_IRQHandler()
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//首先清除中断标志位
//从ROS接收到的数据,存放到下面三个变量中
usartReceiveOneData(&testRece1,&testRece2,&testRece3);
}
}
//===========================================END=======================================================

1.3 实验

STM32从ROS系统获取图像中的目标中心坐标。
receiveData1存放目标x值,receiveData2存放目标y值。

由于ROS系统持续发送坐标值,因此STM32实时接受数据进行控制。A4988采用16细分,每次驱动(50循环)步进5.625°,即每个循环0.1°,可考虑根据目标偏移程度设置每次指令步进大小。例如每差1坐标,每次指令加一个循环0.1°。