Raspberry Pi Pico Wによるステッピングモータ駆動の実験(Arduino IDE使用)


Raspberry Pi Pico Wを使用してステッピングモータを駆動する実験をしました。プログラミングツールとしてはArduino IDEを使用しました。Arduino言語にはステッピングモータを駆動する関数としてStepperライブラリがありますが、最初は使用しないでプログラムを作成しました。また、Stepperライブラリーを使用した場合のプログラムも作成しました。

使用ステッピングモータ

ステッピングモータは28BYJ-48とそのドライバーボードのセットを使用しました。このステッピングモータとドライバーボードは複数のネット通販から購入することが出来ます。とにかく価格が安いので使用してみました。

28BYJ-48ステッピングモータとドライバーボード
28BYJ-48ステッピングモータ裏面
ドライバーボード

今回使用したステッピングモータの主な仕様は表1の通りです。このモータは1/64のギアで減速されていて、1-2相励磁の場合にステップ角は5.625°/64なので4096ステップでモータ軸が1回転します。最大自起動周波数が500ppsなので、500パルス/sec以下で起動する必要があります。起動後は徐々に加速を行った場合に、最大で1000パルス/secまでは仕様上は可能と思われます。

項目仕様
相数4相
励磁方式1-2相励磁方式
ステップ角5.625°/64   (減速比1/64)
電圧5VDC
相抵抗22Ω ± 7% 25°C
最大応答周波数1000pps
最大自起動周波数500pps
引き込みトルク800gf.cm / 5VDC 400pps
表1.28BYJ-48ステッピングモータの主な仕様

ステッピングモータとドライバーボードの回路

ステッピングモータ28BYJ-48に関しては仕様書が有りましたが、ドライバーボードに関しては資料が無かったので、回路をテスターを使用して調べました。図1に調べたステッピングモータ28BYJ-48とドライバーボードの接続回路を示します。

図1、ステッピングモータとドライバーボード回路図

ドライバーボード内のULN2003ANはダーリントントランジスタアレーで定格で500mAの電流を出力することが出来ます。ステッピングモータの赤線がコモン線で5Vに接続されています。外部接続コネクタとステッピングモータコイル線の関係は以下の通りです。
IN1をHIGH(3.3V)とした時にステッピングモータの赤線と青線に接続されたコイルに電流が流れます。
IN2をHIGH(3.3V)とした時にステッピングモータの赤線と桃線に接続されたコイルに電流が流れます。
IN3をHIGH(3.3V)とした時にステッピングモータの赤線と黄線に接続されたコイルに電流が流れます。
IN4をHIGH(3.3V)とした時にステッピングモータの赤線と橙線に接続されたコイルに電流が流れます。

ステッピングモータの駆動に関して

今回使用したステッピングモータ28BYJ-48は1-2相励磁の場合、8ステップでモータのコイルの励磁を切り替えて駆動します。4096ステップでモータが1回転します。ドライバーボードの外部接続コネクタの信号ピン(IN1~IN4)から見た励磁の順番を28BYJ-48の仕様書から以下に作成しました。本表から分かるように1相励磁と2相励磁を繰り返すので1-2相励磁と呼びます。この順にIN1~IN4の信号線を制御すればステッピングモータを回転させることが出来ます。

ボードピンモータ線ステップ1ステップ2ステップ3ステップ4ステップ5ステップ6ステップ7ステップ8
IN1LOWLOWLOWLOWLOWHIGHHIGHHIGH
IN2LOWLOWLOWHIGHHIGHHIGHLOWLOW
IN3LOWHIGHHIGHHIGHLOWLOWLOWLOW
IN4HIGHHIGHLOWLOWKIWLOWLOWHIGH
表2.励磁順序(反時計方向回り)

テスト装置回路図

マイコンとして価格の安いRaspberry Pi Pico Wを使用しました。ステッピングモータのドライバーボードの外部接続コネクタのIN1~IN4をRaspberry Pi Pico WのGP0~GP3に接続しました。またGP15にモータ回転スタート用のスイッチを接続しました。
図2にテスト装置回路図を示します。

図2、テスト装置回路図
図3、テスト装置写真

プログラムNo.1 (Stepperライブラリ使用無)

Raspberry Pi Pico Wのプログラム開発ツールとして、Arduino IDEを使用しました。
Arduino言語にはステッピングモータを制御するStepperライブラリーがありますが1-2相励磁方式に対応してないので、Stepperライブラリを使用しないで1-2相励磁駆動のプログラムを作成しました。
このプログラムはGP15に接続されたスイッチをONすると、ステッピングモータが1024ステップ(90°)回転します。
全プログラムを4個に分割して説明します。

1.初期処理 setup()関数

setup()関数で各種初期設定をしています。
int motor_stepはステッピングモータのステップ値(表2のステップ1~ステップ8)を1~8の数値で記憶する変数です。
pinMode(15, INPUT_PULLUP)関数で起動用のスイッチをPG15に割り当てています。
pinMode()関数でステッピングモータのドライバーボードの入力信号IN1~IN4をGP0~GP3に割り当てています。
stepMoterOn()関数でステッピングモータの初期励磁をしています。最初はどのステップに居るのか分からないのですが、とりあえずステップ1で励磁しています。stepMoterOn()関数に関しては後項で説明します。

//------Raspberry Pi Pico W ステッピングモータ駆動テストNo.1-------------
int motor_step = 1;           //ステップモータのステップ値(1~8)

void setup() {
  pinMode(15, INPUT_PULLUP);  //GP15を入力端子に設定(スイッチ接続)

  pinMode(0, OUTPUT);         //ステッピングモータ(IN1 青線)
  pinMode(1, OUTPUT);         //ステッピングモータ(IN2 桃線)
  pinMode(2, OUTPUT);         //ステッピングモータ(IN3 黄線)
  pinMode(3, OUTPUT);         //ステッピングモータ(IN4 橙線)

  //ステッピングモータの初期励磁(ステップ1の位置で励磁)
  motor_step = stepMoterOn(0, 1, 2, 3);
}

2.メイン処理 loop()関数

loop()関数で実際のステッピングモータの動作を行います。
GP15に接続されたスイッチが押されるまで待ちます。
ステッピングモータを1024ステップ回します。モータは4096ステップで1回転なので90°回転します。
step_1_rot()関数はステッピングモータを1ステップ駆動する関数で、現時点のステップ位置(1~8)を基に次のステップの励磁を導き出してI/Oに出力します。step_1_rot()関数に関しては後項で説明します。
delay(1)関数で1msec待ちをして、次のステップ送りまでの時間を取ります。このdelay()関数でモータの回転速度を決めます。さらに速く回すためにはμsec単位のdelayMicroseconds()関数を使用します。

void loop() {
  while (true){
    //スイッチ読込(PG15 LOW判断)
    if (digitalRead(15) == LOW){
      break;
    }
  }

  //1024ステップ回転(1/4回転)
  for (int i = 0; i < 1024; i++){
    motor_step = step_1_rot(motor_step, true, 0, 1, 2, 3);   //CW方向1ステップ回転
    delay(1);                           //1msec待ち
  } 
}

3.初期位置励磁 stepMoterOn()関数

ステッピングモータを初期励磁としてステップ1の位置に励磁する関数です。GP0:LOW, GP1:LOW, GP2:LOW, GP3:HIGHとして表2のステップ1の励磁形でモータコイルを励磁します。

//------ステッピングモータの初期位置励磁
//引数
//gp_1: 青線用i/o番号
//gp_2: 桃線用i/o番号
//gp_3: 黄線用i/o番号
//gp_4: 橙線用i/o番号
//リターン値: 現状のモータステップ位置番号(1~8)
int stepMoterOn(int gp_1, int gp_2, int gp_3, int gp_4)
{
  //ステップ1の位置で励磁
  digitalWrite(gp_1, LOW);    //GP0 LOW
  digitalWrite(gp_2, LOW);    //GP1 LOW
  digitalWrite(gp_3, LOW);    //GP2 LOW
  digitalWrite(gp_4, HIGH);   //GP3 HIGH

  return 1;                   //ステップ1をリターン
}

4.モータ1ステップ駆動 step_1_rot()関数

ステッピングモータを1ステップ駆動する関数です。
引数として現状のステップを入力して、それに基づいて次のステップの励磁状態を導き出してI/O出力します。

//------ステッピングモータ1ステップ回転処理-------
//引数
//step:    モータの現状ステップ位置番号(1~8)
//cw_flag:  true:CW方向回転、 false:CCW方向回転
//gp_1: 青線用i/o番号
//gp_2: 桃線用i/o番号
//gp_3: 黄線用i/o番号
//gp_4: 橙線用i/o番号
//リターン値: 移動後のモータステップ位置番号(1~8)
int step_1_rot(int step, bool cw_flag, int gp_1, int gp_2, int gp_3, int gp_4)
{
  int ret_step;   //リターンステップ番号

  //モータのステップ位置判断
  switch(step)
  {
    case 1:   //ステップ1
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, LOW);    //GP0 OFF
        digitalWrite(gp_2, LOW);    //GP1 OFF
        digitalWrite(gp_3, HIGH);   //GP2 ON
        digitalWrite(gp_4, HIGH);   //GP3 ON
        ret_step = 2;               //ステップ2へ
      }
      else
      {
        digitalWrite(gp_1, HIGH);   //GP0 ON
        digitalWrite(gp_2, LOW);    //GP1 OFF
        digitalWrite(gp_3, LOW);    //GP2 OFF
        digitalWrite(gp_4, HIGH);   //GP3 ON
        ret_step = 8;               //ステップ8へ
      }
      break;
    case 2:
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, HIGH);   //GP2 or GP6 ON
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 3;               //ステップ3へ
      }
      else
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, HIGH);   //GP3 or GP7 ON
        ret_step = 1;               //ステップ1へ
      }
      break;
    case 3:
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, HIGH);   //GP1 or GP5 ON
        digitalWrite(gp_3, HIGH);   //GP2 or GP6 ON
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 4;               //ステップ4へ
      }
      else
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, HIGH);   //GP2 or GP6 ON
        digitalWrite(gp_4, HIGH);   //GP3 or GP7 ON
        ret_step = 2;               //ステップ2へ
      }
      break;
    case 4:
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, HIGH);   //GP1 or GP5 ON
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 5;               //ステップ5へ
      }
      else
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, HIGH);   //GP2 or GP6 ON
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 3;               //ステップ3へ
      }
      break;
    case 5:
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, HIGH);   //GP0 or GP4 ON
        digitalWrite(gp_2, HIGH);   //GP1 or GP5 ON
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 6;               //ステップ6へ
      }
      else
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, HIGH);   //GP1 or GP5 ON
        digitalWrite(gp_3, HIGH);   //GP2 or GP6 ON
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 4;               //ステップ4へ
      }
      break;
    case 6:
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, HIGH);   //GP0 or GP4 ON
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 7;               //ステップ7へ
      }
      else
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, HIGH);   //GP1 or GP5 ON
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 5;               //ステップ5へ
      }
      break;
    case 7:
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, HIGH);   //GP0 or GP4 ON
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, HIGH);   //GP3 or GP7 ON
        ret_step = 8;               //ステップ7へ
      }
      else
      {
        digitalWrite(gp_1, HIGH);   //GP0 or GP4 ON
        digitalWrite(gp_2, HIGH);   //GP1 or GP5 ON
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 6;               //ステップ6へ
      }
      break;
    case 8:
      if (cw_flag == false)         //CCW移動
      {
        digitalWrite(gp_1, LOW);    //GP0 or GP4 OFF
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, HIGH);   //GP3 or GP7 ON
        ret_step = 1;               //ステップ1へ
      }
      else
      {
        digitalWrite(gp_1, HIGH);   //GP0 or GP4 ON
        digitalWrite(gp_2, LOW);    //GP1 or GP5 OFF
        digitalWrite(gp_3, LOW);    //GP2 or GP6 OFF
        digitalWrite(gp_4, LOW);    //GP3 or GP7 OFF
        ret_step = 7;               //ステップ7へ
      }
      break;
  }

  return ret_step;                  //移動後のステップ値をリターン
}

プログラムNo.2 (Stepperライブラリ使用)

Arduino言語のStepperライブラリを使用してステッピングモータを駆動するプログラムを作成します。
表3にStepperライブラリの概略を示します。

関数説明
Stepper(steps, pin1, pin2, pin3, pin4)steps: 1回転あたりのステップ数(int)
pin1,pin2: モータに接続されているピンの番号
pin3,pin4: 4ピンのモータの場合のピン番号
戻り値: 作成されたインスタンス
Stepper:setSpeed(rpms)rpms:モータの回転速度。1分間あたり何回転するかを正の数で設定。(long)
step()をコールしたときのスピードをセットする。
Stepper:step(steps)steps:モータが回転する量(ステップ数)。逆回転させる場合は負の値を設定。(int)
回転が終了するまで関数を抜け出ない。
表3.Sttepperライブラリーの関数概略

Stepperライブラリ使用時の励磁

今回使用したステッピングモータ28BYJ-48は1-2相励磁の場合、1回転4096ステップとなります。ただStepperライブラリのstep()関数は、ソースコードを見る限りでは2相励磁方式です。2相励磁の場合1回転2048ステップとなります。
Stepper(step2, pin1, pin2, pin3, pin4)
関数でI/Oピンを割り振った場合の励磁順序はソースコードを見る限りでは表4の様になっています。

引数ステップ1ステップ2ステップ3ステップ4
pin1HIGHLOWLOWHIGH
pin2LOWHIGHHIGHLOW
pin3HIGHHIGHLOWLOW
pin4LOWLOWHIGHHIGH
表4.step()関数の励磁順序

また、ステッピングモータ28BYJ-48の仕様書では1-2相励磁方式の励磁順序の表が載っていますが、それから推測した2相励磁の場合の励磁順序は表5の通りです。(時計方向回り時)

ドライバボード入力モータリード線ステップ1ステップ2ステップ3ステップ4
+ (5V)リード線色 赤5Vに接続5Vに接続5Vに接続5Vに接続
IN1リード線色 青HIGHLOWLOWHIGH
IN2リード線色 桃HIGHHIGHLOWLOW
IN3リード線色 黄LOWHIGHHIGHLOW
IN4リード線色 橙LOWLOWHIGHHIGH
表5.ステッピングモータ28BYJ-48の2相励磁順序

以上から次の様にステッピングモータを配線する必要があります。
pin1: リード線色 青 (ドライバボード入力 IN1)
pin2: リード線色 桃 (ドライバボード入力 IN3)
pin3: リード線色 黄 (ドライバボード入力 IN2)
pin4: リード線色 橙 (ドライバボード入力 IN4)
ドライバーボード入力のIN2とIN3がひっくり返っていることに注意が必要です。
最初は、このことに気が付かずにpin2で指定したI/OにIN2をpin3で指定したI/OにIN3を接続した所、step()関数を実行してもステッピングモータは振動するのみで全く回転しませんでした。

プログラム説明

以下にStepperライブラリを使用してステッピングモータを1024ステップ駆動するプログラムを示します。Stepperライブラリを使用しないプログラムと比べると非常に簡単になっています。
stepper()関数でモータの1回転のステップ数とI/Oの接続を設定して、setSpeed()関数で回転スピード(rpm)を設定すれば、step()関数でモータを設定したステップ数駆動することが出来ます。
注意点はstep()関数は2相励磁で動作するので、同じステップ数駆動しても1-2相励磁の場合と比べると2倍の回転角度の移動となります。
また、step()関数は設定したステップ数終了するまで抜け出てこないので、並行して別の処理を行いたい場合には1回のstep()関数でのステップ数を少なくして、繰り返しstep()関数を実施してstep()関数とstep()関数の間に別処理を挿入する等の工夫が必要となります。

//-----Raspberry Pi Pico W ステッピングモータ駆動テストNo.2-----
#include <Stepper.h>

Stepper stepper(2048, 0, 2, 1, 3); //モータNo.1設定(1回転2048ステップ)

void setup() {
  pinMode(15, INPUT_PULLUP);    //GP15を入力端子に設定(スイッチ接続)
  stepper.setSpeed(14);         //モータNo1回転スピードを14RPMへ
}

void loop() {
  while (true){
    //スイッチ読込(GP15 LOW判断)
    if (digitalRead(15) == LOW){
      break;
    }
  }
  
  //1024ステップ回転
  stepper.step(1024);            //モータ回転 (1/2回転)
}

動作動画

以下動画は実際の動作状況です。

最後に

Raspberry Pi Pico Wに1個のステッピングモータを接続して動作させることが出来ました。次は1個のRaspberry Pi Pico Wで2個のステッピングモータを同時に動作させる実験を行いたいと思います。


PAGE TOP