目次 [ Contents ]
モータドライバを使って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モーターを制御する