分享好人经验:自己写个库,控制TB6560驱动步进电机

更新提示:为 EasyStepper 增加了加速度功能,详情请见: http://www.geek-workshop.com/thread-3366-1-1.html



最近买了两个 42 步进电机和基于 TB6560 的步进电机驱动板,想学习一下步进电机到底是个什么东西。



研究了几天,发现现有库(如 Arduino 自带的 Stepper、Playground 里的 CustomeStepper、EasyDriver 推荐的 AccelStepper)都不是很好用,于是下决心自己写一个,顺便学习一下C++的代码。



先介绍一下这个步进电机驱动板:


IMG_20130124_165201.jpg

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 - 极客工坊

标签: Arduino教程