大连好人 发表于 2013-1-24 17:20:33

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

本帖最后由 大连好人 于 2013-2-6 17:20 编辑

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

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

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

先介绍一下这个步进电机驱动板(这个算广告吗? http://item.taobao.com/item.htm?id=12484846976 ):



最大支持 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 protected]
*/
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 或可以优化的地方,还请大家来指导,谢谢。。。。

Malc 发表于 2013-1-24 17:49:00

呵呵 这个easystepper 对付300rpm以下的应该还不错,再高估计就丢步了
该有的基本都有了吧
步进电机最好还是有加减速控制的好,accelstepper用过,貌似有速度限制,到了一定速度后上不去,作者说4000HZ以上的频率就不可靠了
另,tb6050最高输入15khz,8细分下,步进只能达到562rpm。。。

大连好人 发表于 2013-1-24 20:34:51

Malc 发表于 2013-1-24 17:49 static/image/common/back.gif
呵呵 这个easystepper 对付300rpm以下的应该还不错,再高估计就丢步了
该有的基本都有了吧
步进电机最好还 ...

感谢点评。

300 rpm 的意思是一秒钟 5 转?嗯,对我能想到的项目是足够快了。

AccelStepper 也许是我不会用吧,总是不同步,就先用自己这个对付着了。。。。

Malc 发表于 2013-1-24 21:15:56

大连好人 发表于 2013-1-24 20:34 static/image/common/back.gif
感谢点评。

300 rpm 的意思是一秒钟 5 转?嗯,对我能想到的项目是足够快了。


accelstepper由于加减速,多电机联动是会有不同步的
你可以测试一下这段代码最高能运行多少转

大连好人 发表于 2013-1-25 09:19:21

Malc 发表于 2013-1-24 21:15 static/image/common/back.gif
accelstepper由于加减速,多电机联动是会有不同步的
你可以测试一下这段代码最高能运行多少转

能详细和我说说为什么驱动步进电机最好是有加速度功能吗?如果有必要,我把我的程序也加上加速度。

另外,我说的 AccelStepper 不同步问题,不是指多个电机不同步,而是一个电机的问题。
例如,转一圈没问题,当需要转两圈以上时,就会出现停滞现象(每次都出现),即,运动完毕后不能回到出发点。不知道是为什么,我觉得和那个加速度有关系。

yyc11235813 发表于 2013-1-25 09:38:03

看看······················:)

Malc 发表于 2013-1-26 10:52:23

大连好人 发表于 2013-1-25 09:19 static/image/common/back.gif
能详细和我说说为什么驱动步进电机最好是有加速度功能吗?如果有必要,我把我的程序也加上加速度。

另 ...

步进电机的特性,高速时力矩会变小,如果突然高速启动或突然刹车,会造成丢步的
所以要用加减速控制,如果速度比较低,那就可以不用了
accelStepper我也用过,没发现它有这个问题

stanley 发表于 2013-1-26 15:23:38

:)謝謝.可給我關於accelStepper的連結嗎?

MicroCao 发表于 2013-1-27 21:56:42

期待完善更新

大连好人 发表于 2013-1-28 10:25:28

stanley 发表于 2013-1-26 15:23 static/image/common/back.gif
謝謝.可給我關於accelStepper的連結嗎?

帖子里面有啊: http://www.open.com.au/mikem/arduino/AccelStepper/index.html

大连好人 发表于 2013-2-6 17:20:25

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

大连好人 发表于 2013-2-6 17:22:52

Malc 发表于 2013-1-26 10:52 static/image/common/back.gif
步进电机的特性,高速时力矩会变小,如果突然高速启动或突然刹车,会造成丢步的
所以要用加减速控制,如 ...

改进了一下,请您给 review 一下? http://www.geek-workshop.com/thread-3366-1-1.html

大连好人 发表于 2013-2-6 17:24:03

MicroCao 发表于 2013-1-27 21:56 static/image/common/back.gif
期待完善更新

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

macwrit 发表于 2013-3-22 00:22:56

正好需要。好东西,支持。

Cola_DOG 发表于 2013-4-17 15:37:26

请问好人,对于arduino来说 TB6560上的 EN+/- CW+/- 和CLK+/-这六根线应该怎么接,急求?谢谢!
页: [1] 2 3
查看完整版本: 分享好人经验:自己写个库,控制TB6560驱动步进电机