Release 2017.6.20 / Update 2017.11.3

モータドライバを使う

モータドライバを使ってDCモータを動かしてみました。簡易的なフォローフォーカスが作れないかな、と。

結論として、フォローフォーカスとしては「失敗」なんですけど、環境や使用モータによっては上手く機能すると思うので、参考になるようであれば。

TA7267BP

以前、マルツ電波で見つけたモータドライバ、TA7267BPです。

Arduinoなどのマイコンから2系統の電圧変化を送ることで、モータの回転方向等を制御できます。

確か200円もしなかったので、ついでに購入してそのまま放置してたんですが、先日、ポロっと出てきたので、久しぶりに動かしてみました。そしたら、クリックなしロータリーエンコーダと組み合わせれば、結構細かい制御ができます。

そこで、アルミチャンネル等を加工して固定出来るようにして、動作を確認してみました。

ちなみに、TR7267BPのピン配置は以下のようになっています。

そしてこの電子パーツはもう時代遅れらしく、あまり市場には出回っていないようです。普段、自分が利用する入手先だとここしかありませんでした。

せんごくネット通販

ただし、今回のスケッチでは、Arduinoの2ピンで右回り左回りのHigh・Lowを出しているだけなので、同様な機能のICはいくらでも見つけられるかと。

使用電子パーツ

  • Arduino
  • モータドライバ
  • ロータリーエンコーダ(クリックなし
  • DCモータ
  • コンデンサ(モータ用)

駆動部分はこちらを使いました。ただ、後述通り、あまり抵抗の大きい物体を細かく動かすのには向いてないので、今回の用途としてはあまりオススメできません。参考までに。

回路図

TA7267BPは大きいモータ用らしく、こういった小さいモータにはあまり適さないようです。でも、まあ動きました。

また、モータへ供給する電源は写真だと5Vバッテリーで一緒にしていますが、このギヤボックスのモータは3V定格です。ちゃんと定格に合わせた別電源を接続する事をオススメします。

スケッチ

このプロジェクトファイルをダウンロードするか、下記のウインドウを開いてコピペしてください。

Sample Sketch file – DC motor driver contorl

*このスケッチにはMsTimer2ライブラリが必要です。IDEに無ければインストールしてください。

// DC motor driver control v1.1
// for Arudino Sketch 21.6.2017
// by jumbler (jumbleat.com)

#include <MsTimer2.h>
#define TIM MsTimer2

//defined values
// pins
#define ENCA 3
#define ENCB 4
#define MTRA 5
#define MTRB 6
#define LED  13
//values
#define MOVE_VAL 10
#define REV_RAG  150


//encoder constants
#define ENC_JUDGE 25
#define ECUR B00000011  // enc current position
#define EPRE B00001100  // enc previous position
#define EHOM B00110000  // enc home position
#define ETRG B01000000  // enc has been triggered
#define ECHG B10000000  // encoder count has been charged

const    byte  enc_pins[2] = {ENCA, ENCB};
volatile byte  enc_status;
volatile short enc_count = 0;

//motors
byte motor_pins[2] {MTRA, MTRB};


short ENC_COUNT(int val) {
  static int enc_old;
  if (enc_old != enc_count)
  {
    val += enc_count - enc_old;
    enc_old = enc_count;
    enc_status = enc_status & ~ECHG;
  }
  return val;
}



byte SBIT(byte layer) {
  for (byte i = 0 ; i < 8; i++) if ((layer >> i)&B00000001) return i;
}
byte SBVAL(byte val, byte layer) {
  byte tmp = (val & layer) >> SBIT(layer);
  return tmp;
}



byte ENC_PIN_READ() {
  // Read encoder pins status
  byte enc_cur = (digitalRead(ENCB) << 1) + digitalRead(ENCA);
  // Modify position order
  if (enc_cur < 2) enc_cur = 1 + (enc_cur * -1);

  if (!SBVAL(enc_status, ETRG))
  {
    if (SBVAL(enc_status, EHOM) != enc_cur) enc_status = enc_status | ETRG;
  }

  // apply update to enc_status
  enc_status = (enc_status & B11110000) + ((enc_status & ECUR) << 2) + enc_cur;

  return enc_cur;
}



void ENC_GAUGE() {
  static unsigned short gauge[2];
  byte curr = ENC_PIN_READ();

  // if encoder change has been triggerd
  if (SBVAL(enc_status, ETRG))
  {
    byte prev, dist;
    dist = SBVAL(enc_status, EHOM);

    for (byte i = 0 ; i < (ENC_JUDGE * 1.5) ; i++)
    {
      curr = ENC_PIN_READ();
      prev = SBVAL(enc_status, EPRE);

      // each gauge for "moved" or "not moved"
      bool bias = (curr != dist) ? 1 : 0;
      gauge [bias]++;
      int goal = gauge[1] - gauge[0];

      if (abs(gauge[1] - gauge[0]) > ENC_JUDGE)
      {
        // encoder moved!
        if (goal > 0)
        {
          // increase or decrease
          bool dir = ((curr - dist) > 0) ? 1 : 0;
          if (curr == 0 && dist == 3) dir = 1;
          else if (curr == 3 && dist == 0) dir = 0;

          // add count by the direction
          if (dir) enc_count++;
          else enc_count--;

          // update home position
          enc_status = (enc_status & ~EHOM) + (curr << SBIT(EHOM));
          enc_status = enc_status | ECHG;
        }

        for (byte i = 0 ; i < 2 ; i++) gauge[i] = 0;
        enc_status = enc_status & ~ETRG;
        break;
      }
    }
  }
}


void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(38400);

  for (byte i = 0 ; i < 2 ; i++)
  {
    pinMode(enc_pins[i], INPUT_PULLUP);
    pinMode(motor_pins[i], OUTPUT);
    digitalWrite(LED, 1 - i);
    delay(1000);
  }

  TIM::set(1, ENC_GAUGE);
  TIM::start();
  enc_status = ENC_PIN_READ() << SBIT(EHOM);
}


void loop() {
  static int val;
  static unsigned long time_move;

  if (SBVAL(enc_status, ECHG))  // if encoder has changed
  {
    static int  val_ref = val;
    static bool dir_ref = 0;

    digitalWrite(LED, HIGH);

    // get encoder's increase or decrease
    val = ENC_COUNT(val);

    // move Motor
    bool motor_dir = (val_ref > val) ? 1 : 0;
    digitalWrite(motor_pins[!motor_dir], LOW);
    digitalWrite(motor_pins[motor_dir], HIGH);

    // refresh time value
    if (millis() >= time_move) time_move = millis();

    // if rotation has reversed
    if (motor_dir != dir_ref)
    {
      time_move = millis() + REV_RAG;  // add times for reversed idling
      dir_ref   = motor_dir;           // update present direction
    }

    // set moving duration
    time_move += MOVE_VAL * abs(val - val_ref);

    // set present val to reference old
    val_ref = val;

  } else {
    if (millis() > time_move) // set move action off
    {
      digitalWrite(LED, LOW);
      for (byte i = 0 ; i < 2 ; i++) digitalWrite(motor_pins[i], LOW);
    }
  }
}

ちょっと長いですが、ほぼ、エンコーダ読み取り関係です(こちらのコードをまんま流用)。やっているのは、「ロータリーエンコーダの増減があれば、少し(10ms)その方向にモータを動かす」だけです。あまり早いと取りこぼしはありますが、それでも、かなり細かい動きが出来ます。

定数の詳細

スケッチ内の定数についていくつか補足しておきます。

#MOVE_VAL

モータの動く時間で、そのままms(ミリ秒)です。エンコーダの1目盛り分での動きをもっと細かくしたければ値を減らし、逆なら増やします。

#REV_RAG

使ったギヤボックスは遊びがあるので、回転方向が反転すると、その分だけ空回りします。それを補うため、回転が反転した場合、余分に動く秒数(ms)を設定しています。いらなければ0にします。

失敗のワケ

動きは良かったんですが、このギヤボックス、結構ガタや遊びがあって、大事なところで滑って空回りしてしまいます。要は剛性・トルク不足です。

 

使おうとしていたレンズ「SIGMA 30mm1.4F」はかなり年季が入っていて、フォーカスリングが硬いです。なので、この程度のトルクではうまく機能しませんでした。アルミフレーム工作の精度が低いからかも知れませんが。

ただ、リングが軽いレンズならそれなりに動いたので、場合によっては使えるな、と。あるいは、もっと負荷の低い、別の用途だったら充分機能するのでは、と思います。

参考サイト

panchiga’s blog – DCモーターを制御する

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください