2012年5月8日火曜日

Motor Shield

 前回の Wireless SD Shield を用いたお試しで、XBeeとBluetoothでのワイヤレス接続は出来るようになりました。XBee WiFi による無線LAN接続がまだ成功していませんが、そっちは放おって置いて他のShieldへ浮気です。
 今回のおもちゃは Motor Shield R3 です。基本的にはArduino の PWM(アナログ)出力端子にモータードライバICが接続されたものです。デジタル出力端子を使って方向とブレーキ信号を制御します。実家に戻れば東芝のTA72とかTA84とかのHブリッジドライバICの在庫があるので自作する手もあるんですが、なんとなくコレクション的に純正のMotor Shieldを買ってしまいました。
 仕様または回路図(あるいは実物のIC)を見ると STマイクロのL298 というドライバを使っているのがわかります。
 Motor Shield には 外部電源とモーター接続用のターミナル端子6口の他に電流センサ(アナログ入力)の機能などもあるようです。2chのDCモーターの制御ができ、Arduinoの D3とD11のPWMでそれぞれのモーターのパワーを、D12、D13のディジタル出力で回転方向を制御します。
 まずは、モータ用に別電源を使うようにするためにパターンカットをしました。このMotor Shield R3 はデフォルトの状態だと Vin からモータ電源を引いてきます。ターミナルに外部電源を接続して利用するためには基板裏側の「Vin Connect」というパターンをカットする必要があります。


 上の画像の部分のパターンをカットするんですが、はじめここのパターンカットが中途半端な状態でモータが動いたり動かなかったりという状態になってしまいました。外部端子からの電源とVinからの電源がぶつかっていました。改めてきちんとカットすることで正常に動くようになりました。

 Motor Shield の外観はこんな感じです。


 Motor Shield なのでモーターを動かさないと意味が無いので、今回は、たまたま手持ちのあった タミヤのリモコンロボットセットの一部を使いました。とりあえず、こんなような感じで接続して、


  今は電源は有線で供給しているし、基板も車体に載せていないので、車体をひっくり返して車輪の回転を確認します。


 作りかけの中途半端な状態ですが、ソースはこんな感じ。

// Motor Shield Rev.3 のピン設定。
#define PIN_CH1_PWM 3
#define PIN_CH1_DIR 12
#define PIN_CH1_BRK 9
#define PIN_CH2_PWM 11
#define PIN_CH2_DIR 13
#define PIN_CH2_BRK 8

// 各モータの配置・配線で正逆転を適宜変更。
#define CH1_CW HIGH
#define CH1_CCW LOW
#define CH2_CW LOW
#define CH2_CCW HIGH

#define DIR_CW 0
#define DIR_CCW 1

static char CHAR_CR = 0x0d;
static char CHAR_LF = 0x0a;

char rcvChar;
char rcvBuf[64];
int rcvCnt;
boolean isRcvCmd;
int spd1, spd2;

// 回転方向(出力ピン)の設定。
// ch - 設定チャネル 1 or 2
// dir - 回転方向 DIR_CWで正転、DIR_CCWで逆転
// 戻り値 なし。
void setDirection(int ch, int dir)
{
if(ch == 1)
{
if(dir == DIR_CW)
{
digitalWrite(PIN_CH1_DIR, CH1_CW);
}
else if (dir == DIR_CCW)
{
digitalWrite(PIN_CH1_DIR, CH1_CCW);
}
}
else if (ch == 2)
{
if (dir == DIR_CW)
{
digitalWrite(PIN_CH2_DIR, CH2_CW);
}
else if (dir == DIR_CCW)
{
digitalWrite(PIN_CH2_DIR, CH2_CCW);
}
}
}

// 各チャネルのモータ出力(スピード)設定。
// ch - 設定チャネル 1 or 2
// spd - 設定出力。0~255
// 戻り値 なし。
boolean setMotorSpeed(int ch, int spd)
{
if ((ch != 1) && (ch != 2)) return false;
if (spd > 255) spd = 255;
if (spd < -255) spd = -255;

if (spd < 0)
{
spd = -1 * spd;
setDirection(ch, DIR_CCW);
}
else
{
setDirection(ch, DIR_CW);
}

if (ch == 1)
{
analogWrite(PIN_CH1_PWM, spd);
}
else if (ch == 2)
{
analogWrite(PIN_CH2_PWM, spd);
}

return true;
}

// モータ出力 ch1,2設定。
// ch1spd - ch1の出力。0~255
// ch2spd - ch2の出力。0~255
// 戻り値 なし。
void setMotorSpeed2(int ch1spd, int ch2spd)
{
setMotorSpeed(1, ch1spd);
setMotorSpeed(2, ch2spd);
}

// モータ設定の初期化。
// 引数 なし。
// 戻り値 なし。
void initMotor()
{
pinMode(PIN_CH1_PWM, OUTPUT);
pinMode(PIN_CH1_DIR, OUTPUT);
pinMode(PIN_CH1_BRK, OUTPUT);
pinMode(PIN_CH2_PWM, OUTPUT);
pinMode(PIN_CH2_DIR, OUTPUT);
pinMode(PIN_CH2_BRK, OUTPUT);

setMotorSpeed2(0, 0);

digitalWrite(PIN_CH1_BRK, LOW);
digitalWrite(PIN_CH2_BRK, LOW);
}


// 初期設定。
void setup()
{
initMotor();

spd1 = 0;
spd2 = 0;
setMotorSpeed2(spd1, spd2);

Serial.begin(9600);
}

// メインループ。
void loop()
{
// シリアル受信処理。受信バッファにデータがあれば、
if (Serial.available() > 0)
{
/// 1文字読み込み。
rcvChar = Serial.read();
if ((rcvChar == CHAR_CR) || (rcvChar == CHAR_LF))
{
/// 改行(終端)文字の場合は、
if (rcvCnt > 0)
{
//// 既に1文字以上受診していれば、文字列終端(null)を
//// 設定して、コマンド受信フラグを立てる。
rcvBuf[rcvCnt] = '\0';
isRcvCmd = true;
rcvCnt = 0;
}
}
else
{
/// 改行文字でない場合は配列に順次取り込み。
rcvBuf[rcvCnt] = rcvChar;
rcvCnt++;
}
}

// コマンド解析処理。
if (isRcvCmd == true)
{
Serial.println(rcvBuf);
isRcvCmd = false;

if (rcvBuf[0] == '0')
{
digitalWrite(PIN_CH1_BRK, HIGH);
digitalWrite(PIN_CH2_BRK, HIGH);
spd1 = 0;
spd2 = 0;
setMotorSpeed2(spd1, spd2);
delay(200);
digitalWrite(PIN_CH1_BRK, LOW);
digitalWrite(PIN_CH2_BRK, LOW);
}
else if (rcvBuf[0] == '+')
{
spd1 += 25;
spd2 += 25;
if (spd1 > 255) spd1 = 255;
if (spd2 > 255) spd2 = 255;
setMotorSpeed2(spd1, spd2);
}
else if (rcvBuf[0] == '-')
{
spd1 -= 25;
spd2 -= 25;
if (spd1 < -255) spd1 = -255;
if (spd2 < -255) spd2 = -255;
setMotorSpeed2(spd1, spd2);
}

Serial.print(spd1, DEC);
Serial.print(",");
Serial.println(spd2, DEC);
}
}