制御工学博士の日常+備忘録

ようやく制御工学で博士を手に入れたので真っ当な人間になるべく研究以外の事とか色々と備忘録的にやっていく。そんな感じ

M5Stackを使ってステッピングモータを回したい(L6740ドライバ使用)

目的

ステッピングモータをモータドライバから回したい。 これができれば、ステッピングモータで位置制御、速度制御をしてみたい その内、ターンテーブルを作るまでいけると嬉しい

参考にしたリンク

http://spinelify.blog.fc2.com/blog-entry-80.html ↑これが一番参考にした

http://sheep-me.me/2019/11/22/geidai_19/ https://qiita.com/BotanicFields/items/fff644f408c291e5a5f0 https://energy-note.hatenablog.com/entry/2020/09/20/090719

使用物品

  • L6470:ステッピングモータドライバ
  • ST-42BYH1004: バイポーラステッピングモータ
  • M5Stack Gray(Basic+IMU9軸センサの一体型)
  • KX-100L: 直流安定化電源(直流リンク供給用)

配線等は後で確認する。

実行手順

実行手順は以下のとおりである。 1. http://spinelify.blog.fc2.com/blog-entry-80.html こちらのリンクを参考にM5Stack gray用のSPI通信を実装 2. L6740とM5Stackの各種PINを配線 3. プログラムを実行

参考リンクにあったプログラムではSPIポートの設定が異なっているようだった。 M5Stack grayはM5stack Basicに9軸IMUセンサが内蔵されたタイプでどうやらチャンネルが微妙に異なることが分かった。 https://energy-note.hatenablog.com/entry/2020/09/20/090719 のリンクを見るとSPI通信用のPIN対応は

#define CK 5
#define MI 17
#define MO 16
#define SS 22

となっている。 ここで、理解する必要のある信号名として、

CK:SCK

MI:MISO

MO:MOSI

SS:GPIOからディジタルIOを設定

https://qiita.com/BotanicFields/items/fff644f408c291e5a5f0 こちらのリンクを参考にしてみると

SCLK(Serial CLocK)

MISO(Mater In Slave Out)

MOSI(Master Out Slave In)

SS(Slave Select)

LCD(Liquid Crystal Display)

TF(TransFlash)

という意味があるらしい。(M5Stack Basicの対応)

アルゴリズムについて

以降は理解していないのでアルゴリズムについての説明は省略する。 こちらは理解できるようになったら少しずつ加筆する。 理解できるようになった時にSPI通信の感覚が掴めるようになるのではないかと思われる。 ※あくまでArduino IDEで使えるマイコンに関してだけ

プログラムは一番下に貼っておく。

配線状況と実験風景

配線の対応や設定について https://strawberry-linux.com/catalog/items?code=12023 こちらのリンクにl6470r2-manual.pdfがある。

このPDFによるとM5Stackから5Vを制御IC用に供給できるので左のジャンパー設定をする。 このインターフェースとマイコンとの通信にはSPI通信を行うわけだが、SPI通信の各社インターフェースはこのマイコンに依存することなく利用できるらしい。 詳しいことはPDFに書いてある。

L6740_PIN設定.PNG (347.7 kB) L6740_PIN設定1.PNG (182.5 kB)

実際の実験装置と配線状況

DSC_1155.JPG (4.0 MB)

DSC_1160.JPG (4.0 MB)

DSC_1157.JPG (4.2 MB)

写真より、各ピン設定についてまとめておく。

PIN 2:Slave Select

これは複数台になった時に色々使えるらしいがよくわかっていない。

PIN 5:BUSY

これはL6740ボードから処理が終わったら来る信号?まだわからない。 詳しいことは(http://spinelify.blog.fc2.com/blog-entry-41.html )に説明があるので要勉強

PIN 18:SCK

これはSPI用の基準クロック(Serial Clock)

PIN 19:MISO

これはMaster In Slave Outという意味なのでSlave機側からのデータが来る。ここで言うMaster機はM5Stack grayの事である。

PIN 23:MOSI

これはMaster Out Slave Inという意味。これが今回L6740側に送るシリアルデータ送信端子になる。

さらなる理解にはSPI.hを理解する必要がある。 https://blog.goo.ne.jp/namva/e/2733bdfc3ab15105cf3efabfb36ea29f http://www.musashinodenpa.com/arduino/ref/index.php?f=1&pos=539 https://stupiddog.jp/note/archives/976 これが参考になりそうである。

まとめ

とりあえずサンプルをコピペしてステッピングモータを回すことができた。 しかしSPI.h内やサンプルプログラムも全て理解できていない。

これを理解せずして先へは進めないのでよく調査する必要がある。 これが今後の大きな課題である。 解決後は回転位置の指定(位置制御)や速度制御を行い、ターンテーブルが作れたら最高である。

ビットの補足

0xFFとかあるが、これは16進数なので、

0b11111111 = 0xFFになる。 16進数の1byteが0b1111になるので0b1111=0xFになる。

つまり0xFFは2byteデータである。 ちなみに28=256である。

つまり8bitは10進数で28=256である。

10bit=1024, 12bit=4096

世の中のエンコーダでは17bit, 20bit, 30bit+2bitのような物もある。 これは多摩川精機のエンコーダ(シングルシン)が該当。あとはNICONあたり? 参考リンク https://haselab.net/class/literacy/note/2/0xff.html

プログラム(100%コピペ)

#include <SPI.h>
//#include <MsTimer2.h>

// ピン定義。
#define PIN_SPI_MOSI 23
#define PIN_SPI_MISO 19
#define PIN_SPI_SCK 18
#define PIN_SPI_SS 2
#define PIN_BUSY 5

void setup()
{
  delay(1000);
  pinMode(PIN_SPI_MOSI, OUTPUT);
  pinMode(PIN_SPI_MISO, INPUT);
  pinMode(PIN_SPI_SCK, OUTPUT);
  pinMode(PIN_SPI_SS, OUTPUT);
  pinMode(PIN_BUSY, INPUT);
  SPI.begin();
  SPI.setDataMode(SPI_MODE3);
  SPI.setBitOrder(MSBFIRST);
  Serial.begin(9600);
  digitalWrite(PIN_SPI_SS, HIGH);
 
  L6470_resetdevice(); //L6470リセット
  L6470_setup();  //L6470を設定
  
//  MsTimer2::set(50, fulash);//シリアルモニター用のタイマー割り込み
//  MsTimer2::start();
//  delay(4000);
  
  L6470_move(1,1600);//指定方向に指定数ステップする 
  L6470_busydelay(5000); //busyフラグがHIGHになってから、指定ミリ秒待つ。
  L6470_run(0,10000);//指定方向に連続回転
  delay(6000);
  L6470_softstop();//回転停止、保持トルクあり
  L6470_busydelay(5000);
  L6470_goto(0x6789);//指定座標に最短でいける回転方向で移動
  L6470_busydelay(5000);
  L6470_run(0,0x4567);
  delay(6000);
  L6470_hardhiz();//回転急停止、保持トルクなし
}

void loop(){
}

void L6470_setup(){
L6470_setparam_acc(0x40); //[R, WS] 加速度default 0x08A (12bit) (14.55*val+14.55[step/s^2])
L6470_setparam_dec(0x40); //[R, WS] 減速度default 0x08A (12bit) (14.55*val+14.55[step/s^2])
L6470_setparam_maxspeed(0x40); //[R, WR]最大速度default 0x041 (10bit) (15.25*val+15.25[step/s])
L6470_setparam_minspeed(0x01); //[R, WS]最小速度default 0x000 (1+12bit) (0.238*val[step/s])
L6470_setparam_fsspd(0x3ff); //[R, WR]μステップからフルステップへの切替点速度default 0x027 (10bit) (15.25*val+7.63[step/s])
L6470_setparam_kvalhold(0x50); //[R, WR]停止時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
L6470_setparam_kvalrun(0x50); //[R, WR]定速回転時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
L6470_setparam_kvalacc(0x50); //[R, WR]加速時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
L6470_setparam_kvaldec(0x50); //[R, WR]減速時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)

L6470_setparam_stepmood(0x03); //ステップモードdefault 0x07 (1+3+1+3bit)
}

void fulash(){
  Serial.print("0x");
  Serial.print( L6470_getparam_abspos(),HEX);
  Serial.print("  ");
  Serial.print("0x");
  Serial.println( L6470_getparam_speed(),HEX);
}


/*ver 1.00 2013/4/24*/
/*ver 1.01 2013/12/14 コメント追記*/

/*L6470 コントロール コマンド
 引数-----------------------
 dia   1:正転 0:逆転,
 spd  (20bit)(0.015*spd[step/s])
 pos  (22bit)
 n_step (22bit)
 act   1:絶対座標をマーク  0:絶対座標リセット
 mssec ミリ秒
 val 各レジスタに書き込む値
 ---------------------------
 L6470_run(dia,spd); //指定方向に連続回転
 L6470_stepclock(dia); //指定方向にstepピンのクロックで回転
 L6470_move(dia,n_step); //指定方向に指定数ステップする 
 L6470_goto(pos); //指定座標に最短でいける回転方向で移動
 L6470_gotodia(dia,pos); //回転方向を指定して指定座標に移動
 L6470_gountil(act,dia,spd); //指定した回転方向に指定した速度で回転し、スイッチのONで急停止と座標処理
 L6470_relesesw(act,dia); //スイッチがOFFに戻るまで最低速度で回転し、停止と座標処理
 L6470_gohome(); //座標原点に移動
 L6470_gomark(); //マーク座標に移動
 L6470_resetpos(); //絶対座標リセット
 L6470_resetdevice(); //L6470リセット
 L6470_softstop(); //回転停止、保持トルクあり
 L6470_hardstop(); //回転急停止、保持トルクあり
 L6470_softhiz(); //回転停止、保持トルクなし
 L6470_hardhiz(); //回転急停止、保持トルクなし
 L6470_getstatus(); //statusレジスタの値を返す (L6470_getparam_status();と同じ)
 
 L6470_busydelay(msec); //busyフラグがHIGHになってから、指定ミリ秒待つ。
 
 レジスタ書き込みコマンド
 L6470_setparam_abspos(val); //[R, WS]現在座標default 0x000000 (22bit)
 L6470_setparam_elpos(val); //[R, WS]コイル励磁の電気的位置default 0x000 (2+7bit)
 L6470_setparam_mark(val); //[R, WR]マーク座標default 0x000000 (22bit)
 //ありませんL6470_spped //[R] 現在速度read onry  (20bit)
 L6470_setparam_acc(val); //[R, WS] 加速度default 0x08A (12bit) (14.55*val+14.55[step/s^2])
 L6470_setparam_dec(val); //[R, WS] 減速度default 0x08A (12bit) (14.55*val+14.55[step/s^2])
 L6470_setparam_maxspeed(val); //[R, WR]最大速度default 0x041 (10bit) (15.25*val+15.25[step/s])
 L6470_setparam_minspeed(val); //[R, WS]最小速度default 0x000 (1+12bit) (0.238*val+[step/s])
 L6470_setparam_fsspd(val); //[R, WR]μステップからフルステップへの切替点速度default 0x027 (10bit) (15.25*val+7.63[step/s])
 L6470_setparam_kvalhold(val); //[R, WR]停止時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
 L6470_setparam_kvalrun(val); //[R, WR]定速回転時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
 L6470_setparam_kvalacc(val); //[R, WR]加速時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
 L6470_setparam_kvaldec(val); //[R, WR]減速時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
 L6470_setparam_intspd(val); //[R, WH]逆起電力補償切替点速度default 0x0408 (14bit) (0.238*val[step/s])
 L6470_setparam_stslp(val); //[R, WH]逆起電力補償低速時勾配default 0x19 (8bit) (0.000015*val[% s/step])
 L6470_setparam_fnslpacc(val); //[R, WH]逆起電力補償高速時加速勾配default 0x29 (8bit) (0.000015*val[% s/step])
 L6470_setparam_fnslpdec(val); //[R, WH]逆起電力補償高速時減速勾配default 0x29 (8bit) (0.000015*val[% s/step])
 L6470_setparam_ktherm(val); //[R, WR]不明default 0x0 (4bit) (0.03125*val+1)
 //ありませんL6470_adcout //[R] read onry (5bit) ADCによる逆起電力補償の大きさかな?
 L6470_setparam_ocdth(val); //[R, WR]過電流しきい値default 0x8 (4bit) (375*val+375[mA])
 L6470_setparam_stallth(val); //[R, WR]失速電流しきい値?default 0x40 (7bit) (31.25*val+31.25[mA])
 L6470_setparam_stepmood(val); //[R, WH]ステップモードdefault 0x07 (1+3+1+3bit)
 L6470_setparam_alareen(val); //[R, WS]有効アラームdefault 0xff (1+1+1+1+1+1+1+1bit)
 L6470_setparam_config(val); //[R, WH]各種設定default 0x2e88 (3+3+2+1+1+1+1+1+3bit)
 //L6470_status //[R]状態read onry (16bit)

 [R]:読み取り専用
 [WR]:いつでも書き換え可
 [WH]:書き込みは出力がハイインピーダンスの時のみ可
 [WS]:書き換えはモータが停止している時のみ可
 
 
 レジスタ読み込みコマンド(返り値 long型)
 L6470_getparam_abspos();
 L6470_getparam_elpos();
 L6470_getparam_mark();
 L6470_getparam_speed();
 L6470_getparam_acc();
 L6470_getparam_dec();
 L6470_getparam_maxspeed();
 L6470_getparam_minspeed();
 L6470_getparam_fsspd();
 L6470_getparam_kvalhold();
 L6470_getparam_kvalrun();
 L6470_getparam_kvalacc();
 L6470_getparam_kvaldec();
 L6470_getparam_intspd();
 L6470_getparam_stslp();
 L6470_getparam_fnslpacc();
 L6470_getparam_fnslpdec();
 L6470_getparam_ktherm();
 L6470_getparam_adcout();
 L6470_getparam_ocdth();
 L6470_getparam_stallth();
 L6470_getparam_stepmood();
 L6470_getparam_alareen();
 L6470_getparam_config();
 L6470_getparam_status();
 */



void L6470_setparam_abspos(long val){L6470_transfer(0x01,3,val);}
void L6470_setparam_elpos(long val){L6470_transfer(0x02,2,val);}
void L6470_setparam_mark(long val){L6470_transfer(0x03,3,val);}
void L6470_setparam_acc(long val){L6470_transfer(0x05,2,val);}
void L6470_setparam_dec(long val){L6470_transfer(0x06,2,val);}
void L6470_setparam_maxspeed(long val){L6470_transfer(0x07,2,val);}
void L6470_setparam_minspeed(long val){L6470_transfer(0x08,2,val);}
void L6470_setparam_fsspd(long val){L6470_transfer(0x15,2,val);}
void L6470_setparam_kvalhold(long val){L6470_transfer(0x09,1,val);}
void L6470_setparam_kvalrun(long val){L6470_transfer(0x0a,1,val);}
void L6470_setparam_kvalacc(long val){L6470_transfer(0x0b,1,val);}
void L6470_setparam_kvaldec(long val){L6470_transfer(0x0c,1,val);}
void L6470_setparam_intspd(long val){L6470_transfer(0x0d,2,val);}
void L6470_setparam_stslp(long val){L6470_transfer(0x0e,1,val);}
void L6470_setparam_fnslpacc(long val){L6470_transfer(0x0f,1,val);}
void L6470_setparam_fnslpdec(long val){L6470_transfer(0x10,1,val);}
void L6470_setparam_ktherm(long val){L6470_transfer(0x11,1,val);}
void L6470_setparam_ocdth(long val){L6470_transfer(0x13,1,val);}
void L6470_setparam_stallth(long val){L6470_transfer(0x14,1,val);}
void L6470_setparam_stepmood(long val){L6470_transfer(0x16,1,val);}
void L6470_setparam_alareen(long val){L6470_transfer(0x17,1,val);}
void L6470_setparam_config(long val){L6470_transfer(0x18,2,val);}

long L6470_getparam_abspos(){return L6470_getparam(0x01,3);}
long L6470_getparam_elpos(){return L6470_getparam(0x02,2);}
long L6470_getparam_mark(){return L6470_getparam(0x03,3);}
long L6470_getparam_speed(){return L6470_getparam(0x04,3);}
long L6470_getparam_acc(){return L6470_getparam(0x05,2);}
long L6470_getparam_dec(){return L6470_getparam(0x06,2);}
long L6470_getparam_maxspeed(){return L6470_getparam(0x07,2);}
long L6470_getparam_minspeed(){return L6470_getparam(0x08,2);}
long L6470_getparam_fsspd(){return L6470_getparam(0x15,2);}
long L6470_getparam_kvalhold(){return L6470_getparam(0x09,1);}
long L6470_getparam_kvalrun(){return L6470_getparam(0x0a,1);}
long L6470_getparam_kvalacc(){return L6470_getparam(0x0b,1);}
long L6470_getparam_kvaldec(){return L6470_getparam(0x0c,1);}
long L6470_getparam_intspd(){return L6470_getparam(0x0d,2);}
long L6470_getparam_stslp(){return L6470_getparam(0x0e,1);}
long L6470_getparam_fnslpacc(){return L6470_getparam(0x0f,1);}
long L6470_getparam_fnslpdec(){return L6470_getparam(0x10,1);}
long L6470_getparam_ktherm(){return L6470_getparam(0x11,1);}
long L6470_getparam_adcout(){return L6470_getparam(0x12,1);}
long L6470_getparam_ocdth(){return L6470_getparam(0x13,1);}
long L6470_getparam_stallth(){return L6470_getparam(0x14,1);}
long L6470_getparam_stepmood(){return L6470_getparam(0x16,1);}
long L6470_getparam_alareen(){return L6470_getparam(0x17,1);}
long L6470_getparam_config(){return L6470_getparam(0x18,2);}
long L6470_getparam_status(){return L6470_getparam(0x19,2);}


void L6470_run(int dia,long spd){
  if(dia==1)
    L6470_transfer(0x51,3,spd);
  else
    L6470_transfer(0x50,3,spd);
}
void L6470_stepclock(int dia){
  if(dia==1)
    L6470_transfer(0x59,0,0);    
  else
    L6470_transfer(0x58,0,0);
}
void L6470_move(int dia,long n_step){
  if(dia==1)
    L6470_transfer(0x41,3,n_step);
  else
    L6470_transfer(0x40,3,n_step);
}
void L6470_goto(long pos){
  L6470_transfer(0x60,3,pos);
}
void L6470_gotodia(int dia,int pos){
  if(dia==1)    
    L6470_transfer(0x69,3,pos);
  else    
    L6470_transfer(0x68,3,pos);
}
void L6470_gountil(int act,int dia,long spd){
  if(act==1)
    if(dia==1)
      L6470_transfer(0x8b,3,spd);
    else
      L6470_transfer(0x8a,3,spd);
  else
    if(dia==1)
      L6470_transfer(0x83,3,spd);
    else
      L6470_transfer(0x82,3,spd);
}  
void L6470_relesesw(int act,int dia){
  if(act==1)
    if(dia==1)
      L6470_transfer(0x9b,0,0);
    else
      L6470_transfer(0x9a,0,0);
  else
    if(dia==1)
      L6470_transfer(0x93,0,0);
    else
      L6470_transfer(0x92,0,0);
}
void L6470_gohome(){
  L6470_transfer(0x70,0,0);
}
void L6470_gomark(){
  L6470_transfer(0x78,0,0);
}
void L6470_resetpos(){
  L6470_transfer(0xd8,0,0);
}
void L6470_resetdevice(){
  L6470_send_u(0x00);//nop命令
  L6470_send_u(0x00);
  L6470_send_u(0x00);
  L6470_send_u(0x00);
  L6470_send_u(0xc0);
}
void L6470_softstop(){
  L6470_transfer(0xb0,0,0);
}
void L6470_hardstop(){
  L6470_transfer(0xb8,0,0);
}
void L6470_softhiz(){
  L6470_transfer(0xa0,0,0);
}
void L6470_hardhiz(){
  L6470_transfer(0xa8,0,0);
}
long L6470_getstatus(){
  long val=0;
  L6470_send_u(0xd0);
  for(int i=0;i<=1;i++){
    val = val << 8;
    digitalWrite(PIN_SPI_SS, LOW); // ~SSイネーブル。
    val = val | SPI.transfer(0x00); // アドレスもしくはデータ送信。
    digitalWrite(PIN_SPI_SS, HIGH); // ~SSディスエーブル 
  }
  return val;
}

void L6470_transfer(int add,int bytes,long val){
  int data[3];
  L6470_send(add);
  for(int i=0;i<=bytes-1;i++){
    data[i] = val & 0xff;  
    val = val >> 8;
  }
  if(bytes==3){
    L6470_send(data[2]);
  }
  if(bytes>=2){
    L6470_send(data[1]);
  }
  if(bytes>=1){
    L6470_send(data[0]);
  }  
}
void L6470_send(unsigned char add_or_val){
  while(!digitalRead(PIN_BUSY)){
  } //BESYが解除されるまで待機
  digitalWrite(PIN_SPI_SS, LOW); // ~SSイネーブル。
  SPI.transfer(add_or_val); // アドレスもしくはデータ送信。
  digitalWrite(PIN_SPI_SS, HIGH); // ~SSディスエーブル。
}
void L6470_send_u(unsigned char add_or_val){//busyを確認せず送信するため用
  digitalWrite(PIN_SPI_SS, LOW); // ~SSイネーブル。
  SPI.transfer(add_or_val); // アドレスもしくはデータ送信。
  digitalWrite(PIN_SPI_SS, HIGH); // ~SSディスエーブル。
}
void L6470_busydelay(long time){//BESYが解除されるまで待機
  while(!digitalRead(PIN_BUSY)){
  }
  delay(time);
}
long L6470_getparam(int add,int bytes){
  long val=0;
  int send_add = add | 0x20;
  L6470_send_u(send_add);
  for(int i=0;i<=bytes-1;i++){
    val = val << 8;
    digitalWrite(PIN_SPI_SS, LOW); // ~SSイネーブル。
    val = val | SPI.transfer(0x00); // アドレスもしくはデータ送信。
    digitalWrite(PIN_SPI_SS, HIGH); // ~SSディスエーブル 
  }
  return val;
}