分享好人经验:自己写个库,控制TB6560驱动步进电机
更新提示:为 EasyStepper 增加了加速度功能,详情请见: http://www.geek-workshop.com/thread-3366-1-1.html
最近买了两个 42 步进电机和基于 TB6560 的步进电机驱动板,想学习一下步进电机到底是个什么东西。
研究了几天,发现现有库(如 Arduino 自带的 Stepper、Playground 里的 CustomeStepper、EasyDriver 推荐的 AccelStepper)都不是很好用,于是下决心自己写一个,顺便学习一下C++的代码。
先介绍一下这个步进电机驱动板:
2013-1-24 20:35 上传
(102.27 KB)
最大支持 3A 输出,最多 16 细分,可设置静止电流和衰减。
之所以选择这个驱动板,是因为看到有很多二手的 TB6560 芯片出售,和汽车一样,保有量多的必定是好东西,呵呵。
输出就不说了,都一样。
输入端有3个:CLK、CW、EN,分表代表:
CLK:即 step 端口,给一个脉冲(HIGH-LOW)步进电机动一步;
CW:即 direction 端口,该板子定义为 LOW 为顺时针,HIGH 为逆时针;
EN:即 enable 端口,该板子定义为 LOW 为工作,HIGH 为脱机。
这样类型的接口,好像 Stepper 和 CustomeStepper 都不支持。
AccelStepper 可以用,但我发现由于它引入了加速度的概念(而且不能关闭),导致在多圈旋转时会出现不同步,具体问题懒得看,有兴趣的同学可以看看:http://www.open.com.au/mikem/arduino/AccelStepper/index.html 。
其实,从原理上分析,让这个 TB6560 工作起来很简单,只要给脉冲就行了,如:
void loop()
{
digitalWrite(STEP_PIN, HIGH);
delay(1);
digitalWrite(STEP_PIN, LOW);
delay(10); // 修改这个参数可以控制速度
}
但问题是:如果使用 delay 方式,将导致不能同时使用多个步进电机,而且,对系统的其他代码也会有影响。
阅读了 CustomeStepper 的代码后,我的 EasyStepper 就来了。。。。
先看 EasyStepper.h 文件:
#ifndef EasyStepper_h
#define EasyStepper_h
#include <stdlib.h>
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#include <wiring.h>
#endif
// These defs cause trouble on some versions of Arduino
#undef round
/**
* author [email]shangcm@gmail.com[/email]
*/
class EasyStepper
{
public:
/**
* to create the EasyStepper
* parameters:
* stepPin the step pin
* directionPin the direction pin
* enablePin the enable pin
* directionPinsInverted if the value is true then invert HIGH/LOW value for direction pin
* enablePinsInverted if the value is true then invert HIGH/LOW value for enable pin
*/
EasyStepper(byte stepPin, byte directionPin, byte enablePin, bool directionPinsInverted, bool enablePinsInverted);
/**
* to startup the EasyStepper
*/
void startup();
/**
* to shutdown the EasyStepper, will release the enable pin to save power
*/
void shutdown();
/**
* to rotate steps from current position with speed
* speed the speed for rotation, the unit is steps per second
*/
void rotate(float speed, int steps);
/**
* to stop the stepper, the motor will stay at current position
*/
void stop();
/**
* run, you must call this as frequently as possible, but at least once per minimum step interval,
* preferably in your main loop.
*/
void run();
/**
* if return true means the action is done
*/
boolean isDone();
/**
* enable the debug mode?
*/
void debugMode(boolean enabled);
protected:
//
private:
boolean debugModeFlag;
byte stepPin;
byte directionPin;
byte enablePin;
bool directionPinsInverted;
bool enablePinsInverted;
int stepsToGo;
int stepsGone;
float stepTime;
unsigned long nextTimestamp;
boolean done;
void step();
};
#endif
再来看 EasyStepper.cpp,代码里面有注释,应该容易理解的:
#include "EasyStepper.h"
EasyStepper::EasyStepper(byte stepPin, byte directionPin, byte enablePin, bool directionPinsInverted, bool enablePinsInverted)
{
// save the parameters
this->stepPin = stepPin;
this->directionPin = directionPin;
this->enablePin = enablePin;
this->directionPinsInverted = directionPinsInverted;
this->enablePinsInverted = enablePinsInverted;
}
void EasyStepper::startup()
{
// set the pin mode
pinMode(this->stepPin, OUTPUT);
pinMode(this->directionPin, OUTPUT);
pinMode(this->enablePin, OUTPUT);
// enable the stepper
digitalWrite(enablePin, HIGH ^ this->enablePinsInverted);
// initialize the done to true
this->done = true;
}
void EasyStepper::shutdown()
{
// disable the stepper
digitalWrite(enablePin, LOW ^ this->enablePinsInverted);
}
void EasyStepper::debugMode(boolean enabled)
{
this->debugModeFlag = enabled;
}
void EasyStepper::rotate(float speed, int steps)
{
// ignore the zero value
if (speed != 0 && steps != 0)
{
if (steps > 0)
{
// CW
digitalWrite(directionPin, HIGH ^ this->directionPinsInverted);
if (this->debugModeFlag)
{
Serial.println("CW");
}
}
else if (steps < 0)
{
// CCW
digitalWrite(directionPin, LOW ^ this->directionPinsInverted);
if (this->debugModeFlag)
{
Serial.println("CCW");
}
}
this->done = false;
// the steps to go
this->stepsToGo = abs(steps);
// the steps gone
this->stepsGone = 0;
// change the speed to stepTime, micro seconds per step
this->stepTime = 1000.0 * 1000.0 / abs(speed);
// current timestamp
unsigned long time = micros();
this->nextTimestamp = time + this->stepTime;
if (this->debugModeFlag)
{
Serial.print("stepsToGo=");
Serial.print(this->stepsToGo);
Serial.print(", stepTime=");
Serial.print(this->stepTime);
Serial.print(", currentTimestamp=");
Serial.print(time);
Serial.print(", nextTimestamp=");
Serial.println(this->nextTimestamp);
}
// call the step method to rotate the motor
this->step();
}
}
void EasyStepper::stop()
{
this->stepsToGo = 0;
this->done = true;
}
void EasyStepper::run()
{
// the current timestamp
unsigned long time = micros();
if (time >= this->nextTimestamp && !this->done)
{
this->step();
}
if (this->debugModeFlag)
{
Serial.print("currentTimestamp=");
Serial.println(time);
}
}
void EasyStepper::step()
{
// are there some steps to rotate?
if (this->stepsToGo > this->stepsGone)
{
// HIGH value
digitalWrite(stepPin, HIGH);
// delay
delayMicroseconds(2);
// LOW value
digitalWrite(stepPin, LOW);
// increase the stepsGone
this->stepsGone++;
// current timestamp
unsigned long time = micros();
this->nextTimestamp = time + this->stepTime;
if (this->debugModeFlag)
{
Serial.print("stepsGone=");
Serial.print(stepsGone);
Serial.print(", currentTimestamp=");
Serial.print(time);
Serial.print(", nextTimestamp=");
Serial.println(this->nextTimestamp);
}
}
else
{
this->done = true;
}
}
boolean EasyStepper::isDone()
{
return this->done;
}
最后来看一个测试程序,逻辑很简单,来回转 10 次,然后停:
#include <EasyStepper.h>
// define the pins
#define STEP_PIN 34
#define DIR_PIN 35
#define EN_PIN 36
// define the inverted
#define DIR_PIN_INVERTED true
#define EN_PIN_INVERTED true
// the EasyStepper instance
EasyStepper stepper1(STEP_PIN, DIR_PIN, EN_PIN, DIR_PIN_INVERTED, EN_PIN_INVERTED);
// the stepps to rotate
int stepps = 200;
// run times
int times = 0;
void setup()
{
Serial.begin(9600);
stepper1.debugMode(false);
stepper1.startup();
stepper1.rotate(400.0, stepps);
}
void loop()
{
if (times < 10)
{
stepper1.run();
if (stepper1.isDone())
{
// go back
stepps *= -1;
stepper1.rotate(400.0, stepps);
times++;
}
}
}
好人是个 Java 程序员,写 C++ 太吃力了,程序里面应该有 bug 或可以优化的地方,还请大家来指导,谢谢。。。。
via - 极客工坊