目次 [ 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に無ければインストールしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
// 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モーターを制御する