重要的内容写在前面:
- 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。
- 个人把这个教程学完之后,整体感觉是很好的,如果有条件的可以先学习一些相关课程,学起来会更加轻松,相关课程有数字电路(强烈推荐先学数电,不然可能会有一些地方理解起来很困难)、模拟电路等,然后就是C++(注意C++是必学的)。
- 文章中的代码都是跟着老师边学边敲的,不过比起老师的版本我还把注释写得详细了些,并且个人认为重要的地方都有详细的分析。
- 一些函数的介绍有参考太极创客官网给出的中文翻译,为了便于现查现用,把个人认为重要的部分粘贴了过来并做了一些修改。
- 如有错漏欢迎指正。
视频链接:4-1-1 电机基本结构与工作原理_哔哩哔哩_bilibili
太极创客官网:太极创客 – Arduino, ESP8266物联网的应用、开发和学习资料
二、Arduino驱动直流电机
1、直流电机的转向与转速控制
(1)直流电机通常有两个引脚,分别为电机的正负两极。给电机两极施加一个正向直流电压,将会有正向直流电流流过电机,在正向电流的驱动下直流电机的转子做正向旋转;给电机两极施加一个反向直流电压,将会有反向直流电流流过电机,在反向电流的驱动下直流电机的转子做反向旋转。
(2)可使用H桥电路控制电机的旋转方向,如下图所示,H桥电路有4个开关,有两种闭合开关的方式,它们将会使电机承受不同方向的直流电压,从而使转子往不同方向旋转。
(3)借助PWM调制,可以控制电机的转速,其中一种比较常用的方法是控制H桥电路开关的闭合时间,这样,流过电机的电流就会变成占空比不为100%的脉冲电流,而由于存在PWM调制,不同占空比的脉冲电流将等效于相应数值的模拟恒定电流,不同数值的模拟恒定电流将会驱动电机以不同的转速工作。
2、电机控制模块
(1)要想使用Arduino控制直流电机,可以用元器件搭建H桥电路,Arduino负责控制四个开关的闭合,不过这种方法虽然看起来简单,但实际操作起来相对复杂,对此可以使用电机控制模块直接控制直流电机,这样就不需要自行搭建H桥电路了,而且也不需要手搓PWM调制的代码。
(2)这里使用的是XY-2.5AD电机控制模块控制130型号的有刷直流电机(工作电压为1-6V),如下图所示,该模块的芯片内部集成了两个H桥电路,所以可以同时控制两个130型号的有刷直流电机。
①电机控制模块左上角的两个蓝色接口用于连接电机直流电源(2-10V),这里需要注意的是,不能使用Arduino开发板的5V(以及3.3V)引脚为电机控制模块供电,因为130型号的电机其工作功率已经远远超出Arduino开发板所能承受的范围。
②电机控制模块有5个控制引脚,其中一个接GND,另外4个可接Arduino的PWM输出引脚,其中IN1、IN2控制直流电机A,IN3、IN4控制直流电机B,悬空(未连接高电平或低电平)则等效于低电平。下表示出了4个输入引脚的功能,其中“1”代表高电平,“0”代表低电平,“PWM”代表PWM信号。
直流电机 | 旋转方式 | IN1 | IN2 | IN3 | IN4 |
MOTOR-A | 正转(调速) | 1/PWM | 0 | ||
反转(调速) | 0 | 1/PWM | |||
待机 | 0 | 0 | |||
刹车 | 1 | 1 | |||
MOTOR-B | 正转(调速) | 1/PWM | 0 | ||
反转(调速) | 0 | 1/PWM | |||
待机 | 0 | 0 | |||
刹车 | 1 | 1 |
(3)通过串口控制电机:
//XY-2.5AD 连接Arduino引脚的编号
int IN1 = 3, IN2 = 5, IN3 = 6, IN4 = 9;
int pinNum; //存储当前控制的引脚号
int ctrlVal; //存储电机运行控制参数
void setup()
{
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
Serial.begin(9600);
Serial.println("++++++++++++++++++++++++++++++");
Serial.println("+ Taichi-Maker XY-2.5AD Demo +");
Serial.println("+ www.taichi-maker.com +");
Serial.println("++++++++++++++++++++++++++++++");
}
void loop()
{
if (Serial.available()) //检查串口缓存是否有数据等待传输
{
char cmd = Serial.read(); //获取电机指令中的编号信息
switch(cmd)
{
case 'p': //设置需要控制的引脚编号
pinNum = Serial.parseInt();
Serial.print("Pin Number ");Serial.print(pinNum);Serial.print(" ,");break;
case 'a': //模拟模式控制引脚
ctrlVal = Serial.parseInt();
analogWrite(pinNum, ctrlVal);
Serial.print("Set Value ");Serial.print(ctrlVal);Serial.println(".");break;
case 'd': //数字模式控制引脚
ctrlVal = Serial.parseInt();
digitalWrite(pinNum, ctrlVal);
Serial.print("Set Value ");Serial.print(ctrlVal);Serial.println(".");break;
default: //未知指令
Serial.println("Unknown Command");break;
}
}
}
①通过串口向Arduino发送内容“p3d1p5d0”,Arduino会将引脚3设置为输出高电平,将引脚5设置为输出低电平,这样,电机A就会进行最高速的正向旋转。
②通过串口向Arduino发送内容“p6a200p9d0”,Arduino会将引脚6设置为输出占空比约为78.5%(200/255×100%)的脉冲信号,将引脚9设置为输出低电平,这样,电机B就会进行正向旋转,但速度并不是最大值。
三、Arduino驱动步进电机
1、电机驱动板
(1)仍旧以NEMA 17步进电机为例,Arduino虽然可以直接控制NEMA 17步进电机,但在设计程序时会比较复杂,为此可在二者之间添加一个A4988电机驱动板,Arduino通过控制A4988电机驱动板从而间接控制NEMA 17步进电机。
(2)A4988电机驱动板的引脚分布如下图所示。
①VMOT——电机电源正极(可用电源电压为8V-35V),此引脚用于连接为电机供电的电源。
②GND——右上的GND是电机电源接地引脚,右下的GND是逻辑电源接地引脚。
③1A、1B、2B、2A——电机绕组(线圈组)1、2的控制引脚。
④VDD——逻辑电源正极(3-5.5V),此引脚用于为A4988电机驱动板供电。
⑤ENABLE——使能引脚(低电平有效),当此引脚为低电平时,A4988才能进行电机驱动工作,当该引脚为高电平时,A4988将不会进行电机驱动工作(如果该引脚悬空,则A4988默认为使能状态,即该引脚没有连接任何电平时,A4988可以正常工作)。
⑥MS1、MS2、MS3——驱动模式引脚,这三个引脚控制A4988微步细分驱动模式,通过这三个引脚的逻辑电平,可以调整A4988驱动电机模式为全、半、1/4、1/8及1/16步进模式。上图中右侧的表格里有具体如何调节这三个引脚电平以及A4988在不同的电平组合下的驱动模式,表格中“L”代表低电平,“H”代表高电平,“FULL”为全步进,“HALF”为半步进,“QUATER”为1/4步进,“EIGHTH”为1/8步进,“SIXTEENTH”为1/16步进。
⑦STEP——步进引脚,该引脚接收脉冲控制信号,A4988根据驱动模式引脚控制电机运转,在全步进模式下,一个脉冲驱动转子旋转“一步”,其它驱动模式以此类推。
⑧DIR——电机旋转方向控制引脚,当该引脚为低电平时,A4988驱动电机转子往顺时针方向旋转,当该引脚为高电平时,A4988驱动电机转子往逆时针方向旋转。
⑨SLEEP——睡眠引脚(低电平有效),当此引脚为低电平时,A4988进入休眠状态(减小电能消耗),当该引脚为高电平时,A4988将不会进行电机驱动工作,当该引脚为高电平时,A4988才能进行电机驱动工作(如果无需使用休眠功能,可以将该引脚与RESET引脚连接)。
⑩RESET——复位引脚(低电平有效),当此引脚为低电平时,A4988复位至初始状态,当该引脚为高电平时,A4988正常工作(如果该引脚悬空,则A4988默认不进行复位)。
(3)下图所示的是Arduino通过A4988电机驱动板控制NEMA电机的简化版电路连接。
①MS1、MS2、MS3全部悬空,意味着A4988只能选择全步进模式驱动电机;另外,A4988电机电源引脚上连接了一个100uF的电解电容(电解电容引脚有正负极之分,电容正极引脚应接在A4988电机电源正极引脚,相反的电解电容负极引脚接在A4988电机电源接地引脚),该电容可以起到A4988驱动板电源保护的作用,如果没有100uF的电解电容,可以使用任何大于47uF的电解电容来替换,同时尽量将该电容安装在靠近VMOT和GND引脚。
②在正式开始开发之前,必须需要先对A4988进行调节,在A4988上有一个金属旋钮,它实质上是一个电位器,要将其电位(GND为零电势)调节至,,其中是步进电机工作时线圈允许流过的最大电流,是A4988模块上的电阻值。(在调节过程中A4988的VDD引脚需要通电)
③将下面的程序下载到Arduino中,可以发现电机的现象与程序注释相符合。
const int dirPin = 2; //连接A4988的方向引脚
const int stepPin = 3; //连接A4988的步进引脚
const int STEPS_PER_REV = 200; //电机旋转一周的步数(200转=1周)
void setup()
{
// Arduino控制A4988步进和方向的引脚为输出模式
pinMode(stepPin,OUTPUT);
pinMode(dirPin,OUTPUT);
}
void loop()
{
digitalWrite(dirPin,LOW); //设置电机顺时针旋转
//电机慢速顺时针旋转一圈
for(int x = 0; x < STEPS_PER_REV; x++) //每次循环都会往STEP引脚输出一个脉冲(即电机旋转“一步”)
{
digitalWrite(stepPin,HIGH);
delayMicroseconds(2000); //等待2000us
digitalWrite(stepPin,LOW);
delayMicroseconds(2000); //等待2000us
}
delay(1000); //等待一秒
digitalWrite(dirPin,HIGH); //设置电机逆时针旋转
//电机快速逆时针旋转两圈
for(int x = 0; x < (STEPS_PER_REV * 2); x++) //每次循环都会往STEP引脚输出一个脉冲(即电机旋转“一步”)
{
digitalWrite(stepPin,HIGH);
delayMicroseconds(1000); //等待1000us
digitalWrite(stepPin,LOW);
delayMicroseconds(1000); //等待1000us
}
delay(1000); //等待一秒
}
(4)下图所示的是Arduino通过A4988电机驱动板控制NEMA电机的完整版电路连接。
①将下面的程序下载到Arduino中,然后进行人工调试。
[1]全局变量部分:
//A4988引脚连接Arduino引脚的编号
const int dirPin = 2; // Direction
const int stepPin = 3; // Step
const int sleepPin = 4; // Sleep
const int resetPin = 5; // Reset
const int ms3Pin = 6; // Ms3
const int ms2Pin = 7; // Ms2
const int ms1Pin = 8; // Ms1
const int enPin = 9; // Enable
const int STEPS_PER_REV = 200; //步进电机旋转一周步数
char cmd; //存储用户指令字符
int data; //存储用户指令数据
int motorT = 2000; //电机转动的半周期(每隔2000us×2转动一次)
[2]初始化部分:
void setup()
{
//设置引脚模式
pinMode(stepPin,OUTPUT);pinMode(dirPin,OUTPUT);pinMode(sleepPin,OUTPUT);
pinMode(resetPin,OUTPUT);pinMode(enPin,OUTPUT);
pinMode(ms3Pin,OUTPUT);pinMode(ms2Pin,OUTPUT);pinMode(ms1Pin,OUTPUT);
//初始化引脚状态
digitalWrite(sleepPin,HIGH);digitalWrite(resetPin,HIGH);digitalWrite(enPin,LOW);
//初始化电机步进模式为全步进
digitalWrite(ms1Pin, LOW);digitalWrite(ms2Pin, LOW);digitalWrite(ms3Pin, LOW);
Serial.begin(9600);
Serial.println("++++++++++++++++++++++++++++++++++");
Serial.println("+ Taichi-Maker A4988 Steper Demo +");
Serial.println("+ www.taichi-maker.com +");
Serial.println("++++++++++++++++++++++++++++++++++");
Serial.println("");
Serial.println("Please input motor command:");
}
[3]主循环部分:
void loop()
{
if (Serial.available())
{
cmd = Serial.read();
Serial.print("cmd = ");
Serial.print(cmd);
Serial.print(" , ");
data = Serial.parseInt();
Serial.print("data = ");
Serial.print(data);
Serial.println("");
runUsrCmd(); //用于运行用户指令
}
}
[4]用户指令处理函数:
void runUsrCmd()
{
switch(cmd){
case 'x': //设置步进电机旋转(顺时针/逆时针)
Serial.print("Set Rotation To ");
if (data == 0){
digitalWrite(dirPin,0);Serial.println("Clockwise.");//电机顺时针转动
}
else {
digitalWrite(dirPin,1);Serial.println("Counter Clockwise.");//电机逆时针转动
}break;
case 'g': //设置A4988的enable功能
Serial.print("Set Motor To ");
if (data == 0){
digitalWrite(enPin, 1);Serial.println("Disable."); //失能A4988
}
else {
digitalWrite(enPin, 0);Serial.println("Enable."); //使能A4988(默认)
}break;
case 'm': //设置A4988的sleep功能
Serial.print("Set Motor To ");
if (data == 0){
digitalWrite(sleepPin, 0);Serial.println("Sleep."); //休眠模式
}
else {
digitalWrite(sleepPin, 1);Serial.println("Awake."); //正常工作模式(默认)
}break;
case 'b': //设置步进的驱动模式(全步进、半步进、1/4步进、1/8步进、1/16步进)
if (data == 1 || data == 2 || data == 4 || data == 8 || data == 16){
Serial.print("Set Motor Step Control To ");
setStepMode(data); //根据指令数据设置步进模式
}
else {
Serial.println("Wrong Step Mode Cmd!");
}break;
case 'z': //步进电机转动指定的次数
runStepper(motorT, data);break;
case 'd': //设置步进电机的运行速度(转动(半)周期)
motorT = data;Serial.print("Set Motor T/2 To ");Serial.println(data);break;
default: //未知指令
Serial.println("Unknown Command");
}
}
[5]步进电机运行函数:
void runStepper (int rotationT, int stepNum)
{
for(int x = 0; x < stepNum; x++) //一次循环输出一个脉冲,电机转动一次
{
digitalWrite(stepPin,HIGH);
delayMicroseconds(rotationT); //等待一个半周期
digitalWrite(stepPin,LOW);
delayMicroseconds(rotationT); //等待一个半周期
}
}
[6]步进模式设置函数:
void setStepMode(int modeNum)
{
switch(modeNum)
{
case 1: //全步进
digitalWrite(ms1Pin, LOW); digitalWrite(ms2Pin, LOW);
digitalWrite(ms3Pin, LOW);
Serial.println(F("Stepping Mode: Full"));break;
case 2: //半步进
digitalWrite(ms1Pin, HIGH); digitalWrite(ms2Pin, LOW);
digitalWrite(ms3Pin, LOW);
Serial.println(F("Stepping Mode: 1/2"));break;
case 4: //1/4步进
digitalWrite(ms1Pin, LOW); digitalWrite(ms2Pin, HIGH);
digitalWrite(ms3Pin, LOW);
Serial.println(F("Stepping Mode: 1/4"));break;
case 8: //1/8步进
digitalWrite(ms1Pin, HIGH); digitalWrite(ms2Pin, HIGH);
digitalWrite(ms3Pin, LOW);
Serial.println(F("Stepping Mode: 1/8"));break;
case 16: //1/16步进
digitalWrite(ms1Pin, HIGH); digitalWrite(ms2Pin, HIGH);
digitalWrite(ms3Pin, HIGH);
Serial.println(F("Stepping Mode: 1/16")); break;
}
}
②首先通过串口向Arduino发送内容“b1”,设置步进电机的驱动方式为全步进;然后通过串口向Arduino发送内容“x0”,设置步进电机的旋转方向为顺时针方向;接着通过串口向Arduino发送内容“d2000”,设置步进电机的转动周期为2000×2us,至此完成电机的初始设置。通过串口向Arduino发送内容“z100”,步进电机往顺时针方向旋转100步,总耗时约为2000×2×100us。
③按照上面的调试过程,结合程序注释可进行更全面的调试,这里不再赘述。
2、Stepper库
(1)Stepper库是Arduino IDE提供的标准库,可用于控制电机,比如上例的NEMA 17(不过对于NEMA 17,使用Stepper库控制它会有局限性)。除了NEMA 17外,Stepper库还可以控制28BYJ-48单极性步进电机,它是外径28毫米四相八拍式永磁减速型步进电机,后面将以28BYJ-48单极性步进电机为例对Stepper库的使用进行演示。
(2)28BYJ-48步进电机的介绍:
①28BYJ-48的内部结构示意图如下所示。里圈上面有6个齿,分别标注为0~5,这个就是电机的转子,转子的每个齿上都带有永久的磁性,是一块永磁体;与里圈相对于的外圈就是定子,它是跟电机的外壳固定在一起的,它上面有8个齿,而每个齿上都缠上了一个线圈绕组,正对着的2个齿上的绕组又是串联在一起的,也就是说正对着的2个绕组总是会同时导通或关断的,如此就形成了四相,在图中分别标注为 A-B-C-D。
②下图所示的是28BYJ-48步进电机的拆解图,从图中可以看到,位于最中心的那个只有九个小齿的齿轮才是步进电机的转子输出,小齿轮旋转带动大齿轮旋转,这种带动逐层传递,最终的结果就是转子转动64圈(实际上是63.65圈),电机的输出轴才转动1圈,由此得出28BYJ-48步进电机的减速比是64:1。
③28BYJ-48步进电机的相数为4,但实际上它和三相电机的工作原理差不多,在全步进模式下,它也是轮流给能独立通电的线圈组通电,由此促使电机转子转动。
[1]假定电机的起始状态就如上面的内部结构示意图所示,逆时针方向转动,起始时是B相绕组的开关闭合,B相绕组导通,那么导通电流就会在正上和正下两个定子齿上产生磁性,这两个定子齿上的磁性就会对转子上的0和3号齿产生最强的吸引力,就会如图所示的那样,转子的0号齿在正上、3号齿在正下而处于平衡状态,此时转子的1号齿与右上的定子齿(也就是C相)的一个绕组呈现一个很小的夹角,2号齿与右边的定子齿(也就是D相)绕组呈现一个稍微大一点的夹角,很明显这个夹角是1号齿和C绕组夹角的2倍,同理,左侧的情况也是一样的。
[2]接下来,把B相绕组断开,而使C相绕组导通,那么很明显,右上的定子齿将对转子1号齿产生最大的吸引力,而左下的定子齿将对转子4号齿,产生最大的吸引力,在这个吸引力的作用下,转子1、4号齿将对齐到右上和左下的定子齿上而保持平衡,如此,转子就转过了起始状态时1号齿和C相绕组那个夹角的角度。
[3]再接下来,断开C相绕组,导通D相绕组,过程与上述的情况完全相同,最终将使转子2、5号齿与定子D相绕组对齐,转子又转过了上述同样的角度。
[4]当A相绕组再次导通,转子的0、3号齿将由原来的对齐到上下2个定子齿,而变为了对齐到左上和右下的两个定子齿上,即转子转过了一个定子齿的角度。
[5]每为一组线圈通电,转子转动一次,把这称为完成了一个节拍的操作,那么轮流为四组线圈通电就是完成了一个四节拍操作,相应地,转子将转过一个定子齿的角度,如果完成8个四节拍操作,转子将转过完整的一圈,上述这种工作模式就是步进电机的单四拍模式——单相绕组通电四节拍。
[6]在单四拍的每两个节拍之间再插入一个双绕组导通的中间节拍,可以组成八拍模式(实际上就是半步进模式),比如,在从B相导通到C向导通的过程中,加入一个B相和C相同时导通的节拍,这个时候,由于B、C两个绕组的定子齿对它们附近的转子齿同时产生相同的吸引力,这将导致这两个转子齿的中心线运动到B、C两个绕组的中心线上,也就是新插入的这个节拍使转子转过了上述单四拍模式中步进角度的一半,即5.625度,这样一来,就使转动精度增加了一倍,而转子转动一圈则需要8*8=64拍了;另外,新增加的这个中间节拍,还会在原来单四拍的两个节拍引力之间又加了一把引力,从而可以大大增加电机的整体扭力输出,使电机更“有劲”了。
[7]除了上述的单四拍和八拍的工作模式外,还有一个双四拍的工作模式——双绕组通电四节拍,其实就是把八拍模式中的两个绕组同时通电的那四拍单独拿出来,而舍弃掉单绕组通电的那四拍而已。
(3)Stepper库函数:
①Stepper.step(steps):如果steps为正,步进电机Stepper将做顺时针旋转,步数为steps,如果steps为负;步进电机Stepper将做逆时针旋转,步数为steps的绝对值;另外,Arduino在运行此函数时,将会等待电机执行完运行要求以后才会执行下一条语句。
②Stepper.setSpeed(rpm):设置步进电机运行速度为prm(单位为每分钟转数,参数类型为长整型)。
(4)为了便于Arduino程序控制28BYJ-48步进电机,可在二者之间添加一个ULN2003电机驱动板,Arduino程序通过控制ULN2003电机驱动板间接控制28BYJ-48步进电机。
①按照下图所示将电路连接好,其中步进电机接口用于连接28BYJ-48步进电机的五根引线(四相分别使用一根引线,GND使用一根引线)。
②将下面的程序下载到开发板中,可以看到首先步进电机将以非常慢的速度旋转,接下来电机将以较为缓慢的速度顺时针旋转半圈,然后会以较快的速度逆时针旋转半圈。
#include <Stepper.h>
const int STEPS_PER_ROTOR_REV = 32; //电机转子旋转一周需要的步数
const int GEAR_REDUCTION = 64; //减速比(输出轴旋转一周需要转子的旋转圈数)
//电机外部输出轴旋转一周步数(2048)
const float STEPS_PER_OUT_REV = STEPS_PER_ROTOR_REV * GEAR_REDUCTION;
int StepsRequired; //记录电机旋转步数
Stepper steppermotor(STEPS_PER_ROTOR_REV, 8, 10, 9, 11); //建立步进电机对象
//电机转子旋转一周需要的步数为32,电机控制引脚连接Arduino引脚的编号为8、9、10、11
void setup()
{
//setup函数内无内容
}
void loop()
{
StepsRequired = 4;
//极慢转动4步用于观察ULN2003电机驱动板LED变化
steppermotor.setSpeed(1); //设置电机的运转速度为1
steppermotor.step(StepsRequired); //转子旋转4步
delay(1000);
//慢速顺时针旋转一圈
StepsRequired = STEPS_PER_OUT_REV;
steppermotor.setSpeed(500); //设置电机的运转速度为500
steppermotor.step(StepsRequired); //输出轴旋转一周
delay(1000);
//快速逆时针旋转一圈
StepsRequired = -STEPS_PER_OUT_REV;
steppermotor.setSpeed(800); //设置电机的运转速度为800
steppermotor.step(StepsRequired); //输出轴旋转一周
delay(2000);
}
③建立步进电机对象Stepper时,构造函数的后两个参数是可选参数,有的电机需要连接四个控制引脚,而有的电机只需要连接两个控制引脚;另外,构造函数参数中的转子旋转一周需要的步数决定了电机采取四拍模式还是八拍模式进行运转。
3、AccelStepper库
(1)AccelStepper是一款功能强大,简单易用的控制步进电机的Arduino第三方库,目前Arduino内置的stepper库只能同时控制一台步进电机(因为step函数需要等待电机旋转结束才结束调用,在此期间Arduino程序会“卡死”在step函数中),如果需要控制两台及以上的的步进电机,那么AccelStepper库是一个非常好的选择。后面将继续以28BYJ-48单极性步进电机为例对AccelStepper库的使用进行演示。
(2)在全步进模式下,以28BYJ-48单极性步进电机的输出轴旋转一圈需要2048步,将这2048步等比映射到一圈360°上,得到的数值0-2048对应2048步每一步的位置,其中数值0对应的是电机通电后的初始位置,以此类推,如下图所示,这个数值将会作为部分AccelStepper库函数的参数或者返回值。(半步进模式以及其它模式同理,都是把转动一圈的步数等比映射到一圈360°上)
(3)AccelStepper库函数:
①AccelStepper.setMaxSpeed(speed):设置步进电机的最大运行速度。
②AccelStepper.setAcceleration(speed):设置步进电机的加速度。
③AccelStepper.setSpeed(speed):设置步进电机匀速模式下的运行速度。
④AccelStepper.targetPosition():获取步进电机的运行目标位置。
⑤AccelStepper.currentPosition():获取步进电机的当前位置。
⑥AccelStepper.setCurrentPosition(angle-step):复位步进电机的初始位置,即将步进电机的当前位置对应的数值设置为angle-step,以此类推,整个参照体系随之发生改变。
⑦AccelStepper.move(angle-step):设置步进电机运动的相对目标位置(即以当前位置为参照点向目标位置运动),angle-step的正负决定了电机的旋转方向。
⑧AccelStepper.moveTo(angle-step):设置步进电机运动的绝对目标位置(以数值0对应的位置为参照点向目标位置运动)。
⑨AccelStepper.run():设置步进电机的运行模式为先加速后减速模式,该模式下步进电机并不会持续运动,它会一直朝着目标位置运动,到达目标位置后会停止运动,只有目标位置更改后,步进电机才会继续运动,步进电机每次运动都由静止开始按加速度匀加速至最大运行速度,在即将到达目标位置时按加速度匀减速至静止。
⑩AccelStepper.runSpeed():设置步进电机的运行模式为匀速模式,且控制步进电机不断按setSpeed设置的速度运行。
⑪runToNewPosition(angle-step):电机运行到用户指定位置值,目标位置为绝对位置,电机没有到达目标位置前,Arduino将不会继续执行后续程序内容。
(4)按照下图所示将电路连接好。
①示例程序1:
#include "AccelStepper.h"
#define FULLSTEP 4 //全步进方式对应的参数(半步进方式下输出轴转动一周需4096步)
#define HALFSTEP 8 //半步进方式对应的参数(全步进方式下输出轴转动一周需2048步)
#define motor1Pin1 8 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN1
#define motor1Pin2 9 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN2
#define motor1Pin3 10 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN3
#define motor1Pin4 11 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN4
#define motor2Pin1 4 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN1
#define motor2Pin2 5 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN2
#define motor2Pin3 6 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN3
#define motor2Pin4 7 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN4
//定义两个步进电机对象,1号电机设置为半步进运行(HALFSTEP),2号电机设置为全步进运行(FULLSTEP)
AccelStepper stepper1(HALFSTEP, motor1Pin1, motor1Pin3, motor1Pin2, motor1Pin4);
AccelStepper stepper2(FULLSTEP, motor2Pin1, motor2Pin3, motor2Pin2, motor2Pin4);
void setup()
{
stepper1.setMaxSpeed(500.0); //1号电机的最大速度为500
stepper1.setAcceleration(50.0); //1号电机的加速度为50.0
stepper2.setMaxSpeed(500.0); //2号电机的最大速度为500
stepper2.setAcceleration(50.0); //2号电机的加速度为50.0
}
void loop()
{
if (stepper1.currentPosition() == 0 && stepper2.currentPosition() == 0)
{ //两个电机输出轴的绝对位置均处于0才能执行下面两条语句
stepper1.moveTo(2048); //1号电机转动半周(绝对位置变化:0→2048)
stepper2.moveTo(2048); //2号电机转动一周(绝对位置变化:0→2048)
}
else if (stepper1.currentPosition() == 2048 && stepper2.currentPosition() == 2048)
{ //两个电机输出轴的绝对位置均处于2048才能执行下面两条语句
stepper1.moveTo(0); //1号电机转动半周(绝对位置变化:2048→0)
stepper2.moveTo(0); //2号电机转动一周(绝对位置变化:2048→0)
}
stepper1.run(); //1号电机运行(先加速后减速模式)
stepper2.run(); //2号电机运行(先加速后减速模式)
}
②示例程序2:
[1]全局变量、宏定义及初始化部分:
#include "AccelStepper.h"
#define FULLSTEP 4 //全步进方式对应的参数(半步进方式下输出轴转动一周需4096步)
#define HALFSTEP 8 //半步进方式对应的参数(全步进方式下输出轴转动一周需2048步)
#define motor1Pin1 8 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN1
#define motor1Pin2 9 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN2
#define motor1Pin3 10 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN3
#define motor1Pin4 11 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN4
#define motor2Pin1 4 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN1
#define motor2Pin2 5 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN2
#define motor2Pin3 6 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN3
#define motor2Pin4 7 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN4
//定义两个步进电机对象,1号电机设置为半步进运行(HALFSTEP——八拍工作模式),2号电机设置为全步进运行(FULLSTEP——四拍工作模式)
AccelStepper stepper1(HALFSTEP, motor1Pin1, motor1Pin3, motor1Pin2, motor1Pin4);
AccelStepper stepper2(FULLSTEP, motor2Pin1, motor2Pin3, motor2Pin2, motor2Pin4);
char cmd;
int data;
int stepperNum; //指示当前受控制的电机是一个(以及具体是哪一个)或是两个
void setup()
{
stepper1.setMaxSpeed(500.0); //1号电机的最大速度为500
stepper1.setAcceleration(50.0); //1号电机的加速度为50.0
stepper2.setMaxSpeed(500.0); //2号电机的最大速度为500
stepper2.setAcceleration(50.0); //2号电机的加速度为50.0
Serial.begin(9600);
Serial.println(F("++++++++++++++++++++++++++++++++++"));
Serial.println(F("+ Taichi-Maker AccelStepper Demo +"));
Serial.println(F("+ www.taichi-maker.com +"));
Serial.println(F("++++++++++++++++++++++++++++++++++"));
Serial.println(F(""));
Serial.println(F("Please input motor command:"));
}
[2]主循环部分:
void loop()
{
if (Serial.available())
{
cmd = Serial.read(); //获取电机指令中指令信息
Serial.print(F("cmd = "));
Serial.print(cmd);
Serial.print(F(" , "));
data = Serial.parseInt(); //获取电机指令中参数信息
Serial.print(F("data = "));
Serial.print(data);
Serial.println(F(""));
runUsrCmd(); //指令处理
}
stepper1.run(); //1号电机运行(先加速后减速模式)
stepper2.run(); //2号电机运行(先加速后减速模式)
}
[3]用户指令处理部分:
void runUsrCmd()
{
switch(cmd){
case 'o': //获取当前电机输出轴位置
Serial.print(F("stepper1 Position: "));
Serial.println(stepper1.currentPosition()); //获取当前电机1的输出轴位置
Serial.print(F("stepper2 Position: "));
Serial.println(stepper2.currentPosition()); //获取当前电机2的输出轴位置
Serial.print(F("Current Running Motor: "));
//输出当前受控制的电机编号
if (stepperNum == 1 || stepperNum == 2){ //只有一个电机受控制
Serial.print(F("Motor# "));Serial.println(stepperNum);
}
else if (stepperNum == 0 ){ //两个电机同时受控制
Serial.println(F("Both Motors"));
}break;
case 'v': //使电机运行到用户指定的坐标位置
if (stepperNum == 1){
Serial.print(F("Motor1 'moveTo' "));Serial.println(data);
stepper1.moveTo(data); //控制电机1运行到data对应的绝对位置
}
else if (stepperNum == 2){
Serial.print(F("Motor2 'moveTo' "));Serial.println(data);
stepper2.moveTo(data); //控制电机2运行到data对应的绝对位置
}
else if (stepperNum == 0){ //两个电机可以同时运行
Serial.print(F("Both Motors 'moveTo' "));Serial.println(data);
stepper1.moveTo(data); //控制电机1运行到data对应的绝对位置
stepper2.moveTo(data); //控制电机2运行到data对应的绝对位置
}break;
case 'm': //使电机运行相应步数(可顺时针也可逆时针)
if (stepperNum == 1){
Serial.print(F("Motor1 'move' "));Serial.println(data);
stepper1.move(data); //以当前位置为参照点,控制电机1运行到data对应的相对位置
}
else if (stepperNum == 2){
Serial.print(F("Motor2 'move' "));Serial.println(data);
stepper2.move(data); //以当前位置为参照点,控制电机2运行到data对应的相对位置
}
else if (stepperNum == 0){ //两个电机可以同时运行
Serial.print(F("Both Motors 'move' "));Serial.println(data);
stepper1.move(data); //以当前位置为参照点,控制电机1运行到data对应的相对位置
stepper2.move(data); //以当前位置为参照点,控制电机2运行到data对应的相对位置
}break;
case 'r': //让电机运行到用户指定的位置
if (stepperNum == 1){
Serial.print(F("Motor1 'runToNewPosition' "));Serial.println(data);
stepper1.runToNewPosition(data); //控制电机1运行到data对应的绝对位置
}
else if (stepperNum == 2){
Serial.print(F("Motor2 'runToNewPosition' "));Serial.println(data);
stepper2.runToNewPosition(data); //控制电机2运行到data对应的绝对位置
}
else if (stepperNum == 0){ //电机1运行结束后,电机2才能开始运行
Serial.print(F("Both Motors 'runToNewPosition' "));Serial.println(data);
stepper1.runToNewPosition(data); //控制电机1运行到data对应的绝对位置
stepper2.runToNewPosition(data); //控制电机2运行到data对应的绝对位置
}break;
case 's': //设置当前位置为用户指定位置值
if (stepperNum == 1){
Serial.print(F("Set stepper1 Current Position to "));
Serial.println(data); //将电机1输出轴当前位置的数值设置为data
stepper1.setCurrentPosition(data);
}
else if (stepperNum == 2){
Serial.print(F("Set stepper2 Current Position to "));
Serial.println(data); //将电机2输出轴当前位置的数值设置为data
stepper2.setCurrentPosition(data);
}
else if (stepperNum == 0){
Serial.print(F("Set both steppers' Current Position to "));Serial.println(data);
stepper1.setCurrentPosition(data); //将电机1输出轴当前位置的数值设置为data
stepper2.setCurrentPosition(data); //将电机2输出轴当前位置的数值设置为data
}break;
case 'a': //设置电机在先加速后减速模式下运行的加速度
if (stepperNum == 1){
Serial.print(F("Motor1 'setAcceleration' "));Serial.println(data);
stepper1.setAcceleration(data); //将电机1的加速度设置为data
}
else if (stepperNum == 2){
Serial.print(F("Motor2 'setAcceleration' "));Serial.println(data);
stepper2.setAcceleration(data); //将电机2的加速度设置为data
}
else if (stepperNum == 0){
Serial.print(F("Both Motors 'setAcceleration' "));Serial.println(data);
stepper1.setAcceleration(data); //将电机1的加速度设置为data
stepper2.setAcceleration(data); //将电机2的加速度设置为data
}break;
case 'x': //设置电机在先加速后减速模式下运行的最大速度
if (stepperNum == 1){
Serial.print(F("Motor1 'setMaxSpeed' "));Serial.println(data);
stepper1.setMaxSpeed(data); //将电机1的最大速度设置为data
}
else if (stepperNum == 2){
Serial.print(F("Motor2 'setMaxSpeed' "));Serial.println(data);
stepper2.setMaxSpeed(data); //将电机2的最大速度设置为data
}
else if (stepperNum == 0){
Serial.print(F("Both Motors 'setMaxSpeed' "));Serial.println(data);
stepper1.setMaxSpeed(data); //将电机1的最大速度设置为data
stepper2.setMaxSpeed(data); //将电机2的最大速度设置为data
}break;
case 'd': //用户通过此指令可指定哪一个电机进行工作
if (data == 1 || data == 2){ //指定仅控制电机1或电机2
stepperNum = data;
Serial.print(F("Running Motor "));Serial.println(stepperNum);
}
else if (data == 0 ){ //同时控制两个电机工作
stepperNum = data;
Serial.println(F("Running Both Motors "));
}
else { //参数有误
Serial.print(F("Motor Number Wrong."));
}break;
default: //未知指令
Serial.println(F("Unknown Command"));
}
}
③示例程序3:
#include "AccelStepper.h"
#define FULLSTEP 4 //全步进方式对应的参数(半步进方式下输出轴转动一周需4096步)
#define HALFSTEP 8 //半步进方式对应的参数(全步进方式下输出轴转动一周需2048步)
#define motor1Pin1 8 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN1
#define motor1Pin2 9 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN2
#define motor1Pin3 10 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN3
#define motor1Pin4 11 //一号28BYJ-48连接的ULN2003电机驱动板引脚IN4
#define motor2Pin1 4 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN1
#define motor2Pin2 5 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN2
#define motor2Pin3 6 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN3
#define motor2Pin4 7 //二号28BYJ-48连接的ULN2003电机驱动板引脚IN4
//定义两个步进电机对象,均设置为全步进运行(FULLSTEP)
AccelStepper stepper1(FULLSTEP, motor1Pin1, motor1Pin3, motor1Pin2, motor1Pin4);
AccelStepper stepper2(FULLSTEP, motor2Pin1, motor2Pin3, motor2Pin2, motor2Pin4);
void setup()
{
stepper1.setMaxSpeed(500); //设置电机1的最大速度为500
stepper1.setSpeed(300); //设置电机1匀速模式下的运行速度为300
stepper2.setMaxSpeed(500); //设置电机2的最大速度为500
stepper2.setSpeed(300); //设置电机2匀速模式下的运行速度为300
Serial.begin(9600);
Serial.println(F("++++++++++++++++++++++++++++++++++"));
Serial.println(F("+ Taichi-Maker AccelStepper Demo +"));
Serial.println(F("+ www.taichi-maker.com +"));
Serial.println(F("++++++++++++++++++++++++++++++++++"));
Serial.println(F(""));
Serial.println(F("Please input motor command:"));
}
void loop()
{
if (Serial.available())
{
int data = Serial.parseInt();
Serial.print("Motor 'setSpeed' ");Serial.println(data);
stepper1.setSpeed(data); //更改电机1匀速模式下的运行速度
stepper2.setSpeed(data); //更改电机2匀速模式下的运行速度
}
stepper1.runSpeed(); //电机1以匀速模式运行
stepper2.runSpeed(); //电机2以匀速模式运行
}