2022年6月26日日曜日

【Arduino】 NEMA 17 の加減速制御ができた (ステッピングモーター) 【17日目】


NEMA 17 (ステッピングモーター)の加減速制御ができました

モータードライバーが直ぐに結構熱くなるのが懸念点


ソースコード

長いですが、以下に貼り付けます。

「とりあえず動いた」レベルで堅牢性などは不明です。

また、コーディング標準的に Arduino として適切ではない可能性もあります。

加減速から定速に移行する時に弱い衝撃があるので、滑らかにつなげる処理があった方が良いのかも知れません。(難しそう)


※修正 (2022.6.28、v1.2.1)

一部に間違いがあり、rpm が指定の半分になっていました。

赤字の太字で修正します。(4行)


StepperMotor_v1.2.0.ino

// https://www.norwegiancreations.com/2014/12/arduino-tutorial-stepper-motor-with-easydriver/ ← A3967の配線図とサンプルコード

#include "A3967.h"

// ピンアサイン
int m_pinM1 = 2;
int m_pinM2 = 3;
int m_pinDdirection = 4;
int m_pinStep = 5;

// モータードライバークラス
A3967 a3967(A3967::MicloStepTypes::EighthStep, m_pinM1, m_pinM2, m_pinDdirection, m_pinStep);

void setup()
{
    // 初期化
    a3967.Init();

    // シリアルポートの開始
    Serial.begin(9600);

    // 以下、回転動作
    a3967.Step(true, 400, 4);
    delay(1000);

    a3967.Step(false, 300, 3);
    delay(1000);
}

void loop()
{
    Test14();
}

void Test14()
{
    int maxRpm = 600;
    int rotationNumToComplete = 10;
    a3967.Accelerate(true, 0, maxRpm, rotationNumToComplete, A3967::MicloStepTypes::EighthStep);
    a3967.Accelerate(true, maxRpm, maxRpm + 1, rotationNumToComplete + 5);
    //a3967.Step(true, maxRpm, 20);
    a3967.Accelerate(true, maxRpm + 1, maxRpm, rotationNumToComplete + 5);
    a3967.Accelerate(true, maxRpm, 0, rotationNumToComplete - 2);
    delay(1000);
}


A3967.h

#include <Arduino.h>
#include "RotateCalcUtil.h"

#ifndef A3967_h
#define A3967_h

class A3967
{
public:
    // enum
    enum class MicloStepTypes { FullStep, HalfStep, QuarterStep, EighthStep };

    // コンストラクタ
    A3967(MicloStepTypes micloStep, int pinM1, int pinM2, int directionPin, int stepPin);

    // 初期化
    void Init();

    // 回転動作
    void Step(bool isClockwise, int rpm, int rotationNum, A3967::MicloStepTypes micloStepType);
    void Step(bool isClockwise, int rpm, int rotationNum);

    // 加減速動作
    void Accelerate(bool isClockwise, int rpmFrom, int rpmTo, float rotationNumToComplete, A3967::MicloStepTypes micloStepType);
    void Accelerate_01(bool isClockwise, int rpmFrom, int rpmTo, float rotationNumToComplete);
    void Accelerate(bool isClockwise, int rpmFrom, int rpmTo, float rotationNumToComplete);

private:
    // 定数
    const int BASE_STEPS_PER_ROTATION = 200;

    // 処理クラス
    RotateCalcUtil* rotateCalcUtil;

    // メンバー変数
    A3967::MicloStepTypes m_micloStepType = A3967::MicloStepTypes::EighthStep;
    int m_pinM1 = 2;
    int m_pinM2 =3;
    int m_pinDirection = 4;
    int m_pinStep = 5;

    // 1周のステップ数の取得
    int GetStepsPerRotation(A3967::MicloStepTypes micloStepType);

    // マイクロステップモードの書き込み
    void WrtiteMicloStepMode(A3967::MicloStepTypes micloStepType);
};

#endif


A3967.cpp

#include "A3967.h"

#include <Arduino.h>
#include "RotateCalcUtil.h"

/// コンストラクタ
A3967::A3967(A3967::MicloStepTypes micloStepType, int pinM1, int pinM2, int pinDirection, int pinStep)
{
    // 引数の保存
    m_micloStepType = micloStepType;
    m_pinM1 = pinM1;
    m_pinM2 = pinM2;
    m_pinDirection = pinDirection;
    m_pinStep = pinStep;

    // 処理クラスの生成
    rotateCalcUtil = new RotateCalcUtil();
}

/// 初期化
void A3967::Init()
{
    // ピンモードの設定
    pinMode(m_pinM1, OUTPUT);
    pinMode(m_pinM2, OUTPUT);
    pinMode(m_pinDirection, OUTPUT);
    pinMode(m_pinStep, OUTPUT);

    // マイクロステップモードの書き込み
    A3967::WrtiteMicloStepMode(m_micloStepType);
}

/// 回転動作
void A3967::Step(bool isClockwise, int rpm, int rotationNum, A3967::MicloStepTypes micloStepType)
{
    // 引数の保存
    m_micloStepType = micloStepType;

    // マイクロステップモードの書き込み
    A3967::WrtiteMicloStepMode(m_micloStepType);

    // 実際の回転動作
    A3967::Step(isClockwise, rpm, rotationNum);
}
void A3967::Step(bool isClockwise, int rpm, int rotationNum)
{
    // 1周のステップ数の取得
    int stepsPerRotation = A3967::GetStepsPerRotation(m_micloStepType);

    // 必要なステップ数
    int steps = stepsPerRotation * rotationNum;

    // 1ステップの時間の計算
    int microSecPerStep = rotateCalcUtil->CalcMicroSecPerStep(rpm, stepsPerRotation);
            
    //Serial.print("Step.microSecPerStep : ");
    //Serial.println(microSecPerStep);

    // 回転方向の指定
    if(isClockwise == true) { digitalWrite(m_pinDirection, HIGH); }
    else  { digitalWrite(m_pinDirection, LOW); }

    // 回転動作
    int microSecPerStepA;
    for(int i = 0; i < steps; i++)
    {
        //@@@@
        int microSecPerStepA = rotateCalcUtil->CalcMicroSecPerStep(rpm, stepsPerRotation);

        // 書き込み
        digitalWrite(m_pinStep, HIGH);
        //delayMicroseconds(microSecPerStep);
        delayMicroseconds(microSecPerStep / 2);
        digitalWrite(m_pinStep, LOW);
        //delayMicroseconds(microSecPerStep);
        delayMicroseconds(microSecPerStep / 2);
    }
}

/// 加減速動作
void A3967::Accelerate(bool isClockwise, int rpmFrom, int rpmTo, float rotationNumToComplete, A3967::MicloStepTypes micloStepType)
{
    // 引数の保存
    m_micloStepType = micloStepType;

    // マイクロステップモードの書き込み
    A3967::WrtiteMicloStepMode(m_micloStepType);

    // 実際の加減速動作
    A3967::Accelerate(isClockwise, rpmFrom, rpmTo, rotationNumToComplete);
}
void A3967::Accelerate_01(bool isClockwise, int rpmFrom, int rpmTo, float rotationNumToComplete)
{
    // 1周のステップ数の取得
    int stepsPerRotation = A3967::GetStepsPerRotation(m_micloStepType);

    // 必要なステップ数
    int steps = stepsPerRotation * rotationNumToComplete;

    // 1ステップの時間の計算(開始時と終了時)
    int microSecPerStep_From = rotateCalcUtil->CalcMicroSecPerStep(rpmFrom, stepsPerRotation);
    int microSecPerStep_To = rotateCalcUtil->CalcMicroSecPerStep(rpmTo, stepsPerRotation);

    // 差分の計算
    long microSecPerStep_Delta = 0;
    {
        int diff = microSecPerStep_To - microSecPerStep_From;
        microSecPerStep_Delta = (diff * (long)1000) / (long)steps;
    }

    // 回転方向の指定
    if(isClockwise == true) { digitalWrite(m_pinDirection, HIGH); }
    else  { digitalWrite(m_pinDirection, LOW); }

    // 加減速動作
    int microSecPerStep = microSecPerStep_From;
    long accumulation = 0;
    long temp = 0;
    for(int i = 0; i < steps; i++)
    {
        digitalWrite(m_pinStep, HIGH);
        delayMicroseconds(microSecPerStep);
        digitalWrite(m_pinStep, LOW);
        delayMicroseconds(microSecPerStep);

        // 差分の確認と適用
        accumulation += microSecPerStep_Delta;
        if(accumulation < (long)-1000 || (long)1000 < accumulation)
        {
            temp = accumulation / (long)1000;
            microSecPerStep += temp;
            accumulation -= temp * (long)1000;
        }
    }
}
void A3967::Accelerate(bool isClockwise, int rpmFrom, int rpmTo, float rotationNumToComplete)
{
    // 1周のステップ数の取得
    int stepsPerRotation = A3967::GetStepsPerRotation(m_micloStepType);

    // 必要なステップ数
    int steps = stepsPerRotation * rotationNumToComplete;

    // 差分の計算
    long rp1000000m_Delta = (long)0;
    {
        int diff = rpmTo - rpmFrom;
        rp1000000m_Delta = ((long)diff * (long)1000000) / ((long)steps - (long)1);
    }

    // 回転方向の指定
    if(isClockwise == true) { digitalWrite(m_pinDirection, HIGH); }
    else  { digitalWrite(m_pinDirection, LOW); }

    // 加減速動作
    int microSecPerStep;
    int rpm = rpmFrom;
    long accumulation = 0;
    long temp = 0;
    for(int i = 0; i < steps; i++)
    {
        // 差分の確認と適用
        if(i != 0)
        {
            accumulation += rp1000000m_Delta;
            if(accumulation < (long)-1000000 || (long)1000000 < accumulation)
            {
                temp = accumulation / (long)1000000;
                rpm += temp;
                accumulation -= temp * (long)1000000;
            }
        }

        // 1ステップの時間の計算
        microSecPerStep = rotateCalcUtil->CalcMicroSecPerStep(rpm, stepsPerRotation);

        //Serial.print("Accelerate.microSecPerStep : ");
        //Serial.println(microSecPerStep);

        // 書き込み
        digitalWrite(m_pinStep, HIGH);
        //delayMicroseconds(microSecPerStep);
        delayMicroseconds(microSecPerStep / 2);
        digitalWrite(m_pinStep, LOW);
        //delayMicroseconds(microSecPerStep);
        delayMicroseconds(microSecPerStep / 2);
    }
}

//-- 以下、privateメソッド

/// 1周のステップ数の取得
int A3967::GetStepsPerRotation(A3967::MicloStepTypes micloStepType)
{
    // 1回転のステップ数(ベース)
    int stepsPerRotation = A3967::BASE_STEPS_PER_ROTATION;

    // マイクロステップの場合は係数を適用
    if (micloStepType == A3967::MicloStepTypes::HalfStep)
    {
        stepsPerRotation = stepsPerRotation * 2;
    }
    else if (micloStepType == A3967::MicloStepTypes::QuarterStep)
    {
        stepsPerRotation = stepsPerRotation * 4;
    }
    else if (micloStepType == A3967::MicloStepTypes::EighthStep)
    {
        stepsPerRotation = stepsPerRotation * 8;
    }

    // 返す
    return stepsPerRotation;
}

/// マイクロステップモードの書き込み
void A3967::WrtiteMicloStepMode(A3967::MicloStepTypes micloStepType)
{
    if(m_micloStepType == A3967::MicloStepTypes::FullStep)
    {
        digitalWrite(m_pinM1, LOW);
        digitalWrite(m_pinM2, LOW);
    }
    else if(m_micloStepType == A3967::MicloStepTypes::HalfStep)
    {
        digitalWrite(m_pinM1, HIGH);
        digitalWrite(m_pinM2, LOW);
    }
    else if(m_micloStepType == A3967::MicloStepTypes::QuarterStep)
    {
        digitalWrite(m_pinM1, LOW);
        digitalWrite(m_pinM2, HIGH);
    }
    else
    {
        digitalWrite(m_pinM1, HIGH);
        digitalWrite(m_pinM2, HIGH);
    }
}


RotateCalcUtil.h

#ifndef RotateCalcUtil_h
#define RotateCalcUtil_h

class RotateCalcUtil
{
public:
    // コンストラクタ
    RotateCalcUtil();

    // 1ステップの時間の計算
    int CalcMicroSecPerStep(int rpm, int stepsPerRotation);

private:
};

#endif


RotateCalcUtil.cpp

#include "RotateCalcUtil.h"

#include <Arduino.h>

/// コンストラクタ
RotateCalcUtil::RotateCalcUtil()
{
    // 何もしない
}

/// 1ステップの時間の計算
int RotateCalcUtil::CalcMicroSecPerStep(int rpm, int stepsPerRotation)
{
    if(rpm < 10) { rpm = 10; }
    long rp1000s = (rpm * (long)1000) / (long)60;               // 1000秒あたりの回転数
    long stepsPer1000Sec = rp1000s * stepsPerRotation;          // 1000秒あたりのステップ数
    long microSecPerStep = (long)1000000000 / stepsPer1000Sec;  // 1ステップあたりのマイクロ秒
    int ret = (int)microSecPerStep;                             // 型変換
    return ret;
}




0 件のコメント:

コメントを投稿