2016-01-26_L6470ステッピングモータドライバを動かす。 (移植記事)

## 2016-01-26_L6470ステッピングモータドライバを動かす。
---
2017-11-16コメント
この記事は過去のブログ[火狐商會](http://foxcorp.exblog.jp/22399258/)の移植記事になります。
むかーしに少しだけ書いていたArduinoブログが地味に引用されているなどしたので、きちんと書き直す＋メジャーなQiitaに今後移動する意味も含めて、多少の修正を加えつつ移植してみる。
今後はArduinoとかvvvvとかで技術系記事を書いていきたいと思っとりますのでどうかよろしく。

---

ストロベリーリナックスで買ったステッピングモータドライバと、バイポーラのステッピングモータで動作の確認をする。

##参考
北の国から電子工作(仮) : L6470ステッピングモータドライバキットでマイクロステップ
http://spinelify.blog.fc2.com/blog-entry-1.html
L6470をArduinoで簡単に動かすスケッチ
http://spinelify.blog.fc2.com/blog-entry-41.html
週刊 パタパタをArduinoで動かしてみた:試作第二弾！
http://blog.livedoor.jp/pata2make/archives/2013-03.html

##使用したモノ
L6470 ステッピングモータ・ドライバキット(StrawberryLinux)
2100円
https://strawberry-linux.com/catalog/items?code=12023
ステッピングモーターＳＴ－４２ＢＹＨ１００４
1900円
http://akizukidenshi.com/catalog/g/gP-07600/
Arduino Uno

##実装
ドライバを組み立てる(ハンダづけする)。
[写真]
こんな感じ。裏のパターンはしっかりハンダ付けをしないと十分な電流が流れない可能性があるので注意。

Arduinoとの接続はリボンケーブルを使うと綺麗になるが、ちょうど持っていなかったのでメスメスのジャンパワイヤで代用。

<details><summary>商品詳細</summary>
２ｘ５（１０Ｐ）両端コネクタ付ＩＤＣリボンケーブル（フラットケーブル）
http://akizukidenshi.com/catalog/g/gC-03796/
ブレッドボード・ジャンパー延長ワイヤ（メス－メス）　１５ｃｍ（青
http://akizukidenshi.com/catalog/g/gP-03476/
</details>

__Arduino - ドライバCN1 ピン配置対応表__

|Arduino |L6470 |名称 |説明 |
|--- |--- |--- |--- |
|13 |5 |SCK |SPIクロック入力 |
|12 |3 |SDO |SPIデータ出力 |
|11 |6 |SDI |SPIデータ入力 |
|10 |7 |CS |SPIチップセレクト |
|9 |1 |PU |BUSY出力 |
|5V |4 |EXT |ロジック電源 |
|GND |8 |GND|グランド |

※ストロベリーリナックスの製品の場合。秋月版はピン配置が違うらしい(が買ったことないので割愛)
モーターとドライバは相A,Bをそれぞれつなぐ。ST-42BYH1004の場合は上から黒緑赤青。製品によって色と順番が異なるので、注意してデータシートを参照されたし。
[写真]
今回はArduinoUNOから5V電源を供給するので、ジャンパピンは(a)で短絡する。
[写真]
配線し終わったものがこちら。

今回は9V2.0Aの電源を使用した。秋月でDCジャック-生ケーブルがあるので下のようにつなぐ。
<details><summary>商品詳細</summary>
ＤＣジャック付ケーブル１．８ｍ （２．１ｍｍ） ＭＣ－２３７６
250円
http://akizukidenshi.com/catalog/g/gC-06566/
スイッチングＡＣアダプター９Ｖ２Ａ １００Ｖ～２４０Ｖ ＧＦ１８－ＵＳ０９２０－Ｔ
950円
http://akizukidenshi.com/catalog/g/gM-02193/
</details>
DCジャック付きケーブルは、白いラインが入っている方が＋で真っ黒なほうがGND。(ACアダプターの規格によっても変わるらしいがよくわからん)

##コーディング
ハード側はできたので、コーディングをする。
ソースコードは[北の国から電子工作(仮):L6470ステッピングモータドライバキットでマイクロステップ](http://spinelify.blog.fc2.com/blog-entry-1.html)様を全面的に参考に~~写経~~させていただいた。感謝。

今回は電源周りでかなり手こずって力尽きたので、北の国から電子工作(仮)さんのテスト用スケッチを動かしてみる。

```c++:L6470_test01.ino
#include <SPI.h>
#include <MsTimer2.h>

// ピン定義。
#define PIN_SPI_MOSI 11
#define PIN_SPI_MISO 12
#define PIN_SPI_SCK 13
#define PIN_SPI_SS 10
#define PIN_BUSY 9

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(0x20); //[R, WR]停止時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
L6470_setparam_kvalrun(0x20); //[R, WR]定速回転時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
L6470_setparam_kvalacc(0x20); //[R, WR]加速時励磁電圧default 0x29 (8bit) (Vs[V]*val/256)
L6470_setparam_kvaldec(0x20); //[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);
}


//-----------------------RegisterCommand-------------------------

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);}

//-----------------------CommandSet----------------------------------

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;
}
```
ソースを見ると、後半はライブラリ？的なコマンドセットで、前半で主な駆動を行っている。
setupは、
SPI通信を開始→L6470の初期設定→モニタ用のタイマーの設定→モーターのテスト駆動
の流れで実行する。すべて実行するとモーターの静止トルクを落として停止する。

テスト駆動は、
1600step正方向移動→5000ms待機→負方向速さ10000で回転→6000ms継続→静止トルク有りで停止→5000ms待機→指定座標まで移動→5000ms待機→負方向速さ0x4567で回転→6000ms待機→静止トルク無しで急停止(？)を行う。

L6470_setupでステップの設定を1/8にしているので、1600stepは200ステップ角分回ることになる。

##結果
動作確認となんとなくの機能の把握ができたので今回はここまで。次回でPCからの入力によって回転を操作することを試してみる。
あと実使用はL6470の複数接続(デイジーチェーン)をしたいので、そっちも調べる。