秋月電子 L6470ステッピングモータードライバをPICで使う方法 /外部制御の方法 PIC16F1827編 

最初にお断りしておくが、まだL6470の機能に熟知しているわけではなく間違いも多いと思う。ご指摘願いたい。
とは言え実際に動作させることは出来ている。
<img width="50%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/c44e0bfd-1ed9-12e9-6720-4835658ca166.png">


2022.02.18時点では情報が少ない秋月電子から発売されているバイポーラステッピングモータードライバをPIC16F1827にて駆動する方法を記載する。駆動方法だけで無くUARTを使った外部制御とLCD1602の使い方も合わせて記載する。

当記事を記載するにあたっては以下のサイトからの情報を元にしている。感謝
https://rikeden.net/?p=501
https://tyokota.hateblo.jp/entry/2017/06/16/191852

<h4>【主な部品リスト】</h4>
・直流安定化電源

秋月電子 2022.02.18時点
・L6470使用　ステッピングモータードライバ 通販コード K-07024
・バイポーラ　ステッピングモーター　ST-57BYG076-0604　通販コード P-7601 ※バイポーラ仕様ならば目的に応じて適宜選択可。今回産業機器で使用する為、静止トルク重視で選択した。末尾が0604である点に注意
<img width="70%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/eec94107-1e80-4bb3-e508-b2f10c95934d.png">

・PICマイコン PIC16F1827-I/P 通販コード I-04430

12V →　5V変換用
   ・三端子レギュレータ 5V1.5A NJM7805FA 通販コード I-08678
   ・TO-220用 小型放熱器(ヒートシンク) 30x30x30mm 通販コード P-00429
   ・熱伝導グリス　※今回はKryonaut TG-K-001-RS 熱伝導率 12.5W/mkを使用したが適当でOK
   ・0.3μFコンデンサ 
      積層セラミックコンデンサー　０．３３μＦ５０Ｖ　Ｘ７Ｒ　５ｍｍ
   ・0.1μFコンデンサ  <b>2個</b>
      積層セラミックコンデンサー　０．１μＦ２５０Ｖ　Ｘ７Ｒ　５ｍｍピッチ

Amazon
LCD1602 ※今回PICのピンアサインの制限でパラレル接続で使用する。I2C/SPI/EUSARTを同時使用するとかぶってしまいます。

<img width="70%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/6491bb86-ebcc-5ecf-e7b0-0d4a1e3f78ee.png">
<img width="70%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/5957e155-503d-8dfc-5d1b-fa90480c724a.png">

FT232RLモジュール　※搭載されているチップが本物では無いと謎の説明が本文にあります...
使った感じだとWindows10からだとちゃんと認識されます。ドライバーも本家のものが入りますが、産業機器と接続すると認識しませんでした。
おそらく送受信バッファが少ない為と思われます。産業機器との接続は別途考える。本物使えば可？？？
<img width="50%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/ea808c7c-d5c1-811e-bae3-7f73e331dcb5.png">
<img width="50%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/56b29b01-4bf9-802e-8c9b-32f76879de5e.png">

<h4>【回路図】</h4>
クリックすると拡大表示されます。右クリックでダウンロード可
<b>転載禁止</b>
![16F1827_L6470.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/0ebbebc8-7830-b004-1fd8-b6aac40557fd.png)
<解説>
①L6470モジュールのJP1は外部電圧使用に設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/ea9b681d-90a7-4a5f-8085-a06692cf01e0.png)

②LM7805に流れる電流は28mAと直流安定化電源で表示されている為、熱損失は(12V) - (5V) x 0.028A = 0.196W
多分ヒートシンクもいらない....
熱抵抗の計算は以下のサイトを参照したが理解に至っていない。
http://www.picfun.com/heatsink.html

③FT232RLの接続で問題になるのはVCCをどうするか？製品によって異なるようで外部電源を使用する場合は外部電源に接続とか、そもそもUSBからの電源供給しか想定していなく説明が無いとか（今回のものはこれに該当）

④SW1/SW2 への接続はプルダウン抵抗を接続して入力。+5VでロジックHigh
 テスト回路なので作りこんでおらず、特にリミットスイッチにセンサーなどを使う場合は回路ごと変更しなければならない。
⑤FLAGとBUSYのＰＩＣへの入力はＰＩＣ内部でプルアップを有効にするので外部抵抗無し。
　ここで、最初RA5をSPI通信のCSに接続してしまいはまった。MRCLピンをは出力に使えず入力専用だと知るまで２日間悩んだ。

注意点は以上だが、PICのピンが足りていない為最終的に産業機器と連携させる為には少なくとも２８ピンのPICを使う必要がある。別途記事にする予定

<h4>MPLAB X IDE　MCCの設定</h4>
PeripheralsにEUSARTとMSSP1を追加してから設定開始

①Internal Clockを8Mhzに設定 PLL有効 これ32MHz動作
<img width="80%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/f2f59f6b-da70-1582-fc1e-983a8ab490b9.png">

②割り込みの設定とプルアップ抵抗の設定に注意
<img width="100%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/1715ec90-0abb-7fe3-00fa-a07581c67792.png">
<img width="80%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/c0e9f645-a654-f7e2-9a33-4fa748ee3b6a.png">

③Pin Moduleの割り込みを最優先に設定　特にリミットスイッチのONが遅れると事故になる為、
<img width="100%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/a4817103-e875-40a1-e58b-9f641c897c06.png">

④WUSARTの設定
<img width="100%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/54ef1361-656b-7ce3-12d5-9ab5d2967068.png">

⑤MSSP1の設定
<img width="100%" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/02fab926-6744-f650-edcc-184fd6c78896.png">

<h4>ソースコード</h4>
①L6470制御部
http://zattouka.net/GarageHouse/index.htm
http://zattouka.net/GarageHouse/micon/MPLAB/16F1786/Stepper/Stepper.htm
基本的に上記サイトのコードを使用した。多謝
変更点はSPI通信のメソッドと各種レジスタの設定となる。
変更点を含めて転載して良いか迷ったが、特に転載禁止などの文言が無かった為載せておく。
プロジェクトフォルダ構造

```
Root
　--build
　--debug
　--dist
　--ext
　　  --L6470
　　  --skL6470.h
  　  --skL6470.c
　--mcc_generated_files
　--nbproject
  --main.c
　--lcd.c
　--lcd.h
```
skL6470.h

```
/*******************************************************************************
*  skL6470.h - L6470ステッピングモータドライバ関数ライブラリ用ヘッダファイル       *
*                                                                              *
* ============================================================================ *
*  VERSION DATE        BY                    CHANGE/COMMENT                    *
* ---------------------------------------------------------------------------- *
*  1.00    2016-07-07  きむ茶工房(きむしげ)  Create                             *
*  1.10    2021-07-22  きむ茶工房(きむしげ)  ステップ設定とアラームON機能を追加   *
 * 1.10α  2022-02-18  Kamoshika            レジスタをST-57BYG076-0604仕様に変更 *
 * 　　　　　　　　　　　　　　　　　　　　　　SPI関連メソッドをMCC仕様に変更       *
* ============================================================================ *
*  PIC 16F1827                                                                 *
*  MPLAB X (v5.50)                                                             *
*  MPLAB(R) XC8 C Compiler Version 1.3.90/2.32                                 *
*******************************************************************************/
#ifndef _SKL6470_H_
#define _SKL6470_H_

/*
#ifndef _XTAL_FREQ
 // Unless already defined assume 64MHz system frequency
 // This definition is required to calibrate __delay_us() and __delay_ms()
 #define _XTAL_FREQ 32000000       // 使用するPIC等により動作周波数値を設定する
#endif
 */

#define L6470_CS	RB6	        // L6470のSPI-CSのピン番号
#define L6470_BUSY	RA5         // L6470のBUSYのピン番号

// レジスタのアドレス
#define L6470_ACC	0x05	// 加速時の係数(12bit)
#define L6470_DEC	    0x06	// 減速時の係数(12bit)
#define L6470_MAX_SPEED	0x07	// 最大回転速度(10bit)
#define L6470_MIN_SPEED	0x08	// 最少回転速度(13bit)
#define L6470_KVAL_HOLD	0x09	// 停止時の電圧(8bit)
#define L6470_KVAL_RUN	0x0A	// 定速回転時の電圧(8bit)
#define L6470_KVAL_ACC	0x0B	// 加速時の電圧(8bit)
#define L6470_KVAL_DEC	0x0C	// 減速時の電圧(8bit)
#define L6470_OCD_TH	0x13	// 過電流の閾値(4bit)
#define L6470_STALL_TH	0x14	// ストール電流の閾値(7bit)
#define L6470_STEP_MODE	0x16	// ステップモードの設定(8bit)
#define L6470_ALARM_EN	0x17	// 各種アラームの有無設定(8bit)
#define L6470_CONFIG	0x18	// コンフィグの設定(16bit)
#define L6470_STATUS	0x19	// 現在のステータス(16bit)
#define L6470_FS_SPD    0x15   //フルステップ速度のしきい値(10bit))

// 定数の定義
#define Forward         1       // モータは正転で回転
#define Reverse         0       // モータは逆転で回転
#define SOFTSTOP        0       // 減速停止、保持トルク有り
#define HARDSTOP        1       // 即時停止、保持トルク有り
#define SOFTHIZ         2       // 減速停止、保持トルク無し
#define HARDHIZ         3       // 即時停止、保持トルク無し
#define FULL_STEP	0	// フルステップ(モータと同じステップ数)
#define HALF_STEP	1	// ハーフステップ(モータと同じステップ数x2)
#define MICROSTEP8	3	// 1/8 マイクロステップ(モータと同じステップ数x8)
#define MICROSTEP64	6	// 1/64  マイクロステップ(モータと同じステップ数x64)
#define MICROSTEP128	7	// 1/128 マイクロステップ(モータと同じステップ数x128)
#define OSC_INT16MHZ    0       // クロックは内蔵16MHz


// レジスタの構造体定義
// STEP_MODE Register
union STEP_MODE_t {           // ステッピングモードレジスタ(デフォルト=0x07)
     unsigned char DT ;
     struct {
          unsigned STEP_SEL   :3 ;      // ステッピングモードの設定
          unsigned SP         :1 ;      // 予備
          unsigned SYNC_SEL   :3 ;      // 同期周波数の選択
          unsigned SYNC_EN    :1 ;      // BUSY/SYNCの選択
     } ;
} ;
// CONFIG Register
union CONFIG_t {              // コンフィグレジスタ(デフォルト=0x2E88)
     unsigned int DT ;
     struct {
          unsigned OSC_SEL    :3 ;      // クロックの選択
          unsigned EXT_CLK    :1 ;      // クロック出力の有無
          unsigned SW_MODE    :1 ;      // SW機能の選択
          unsigned EN_VSCPMP  :1 ;      // モータの電源電圧補償の有無
          unsigned SP         :1 ;      // 予備
          unsigned OC_SD      :1 ;      // 過電流発生時のブリッジ回路停止の有無
          unsigned POW_SR     :2 ;      // モータ出力のスルーレート値の設定
          unsigned F_PWM_DEC  :3 ;      // PWM周波数生成の整数分周比を設定
          unsigned F_PWM_INT  :3 ;      // PWM周波数生成倍率を設定
     } ;
} ;


// 関数のプロトタイプ宣言
unsigned long L6470_Init() ;
unsigned long L6470_GetRegister(char regadrs,int num) ;
void L6470_SetStep(unsigned char mode) ;                // (v1.10)
int  L6470_ALARM() ;
void L6470_Run(char dir,unsigned long speed) ;
void L6470_Move(char dir,unsigned long step) ;
void L6470_Stop(int mode) ;

#endif

```
skL6470.c

```
/*******************************************************************************
*  skL6470.c - L6470ステッピングモータドライバ関数ライブラリ                   *
*                                                                              *
*    L6470_Init    - L6470の初期化を行う処理                                   *
*    L6470_GetRegister - レジスタのデータを読み込む処理                        *
*    L6470_SetStep - レジスタにステップモードを書き込む処理             (v1.10)*
*    L6470_ALARM   - アラームの情報を読み込む処理                              *
*    L6470_Run     - モータを一定速度で回転させる処理                          *
*    L6470_Move    - モータを指定ステップ数まで回転させる処理                  *
*    L6470_Stop    - モータを停止させる処理                                    *
*                                                                              *
* ============================================================================ *
*  VERSION DATE        BY                    CHANGE/COMMENT                    *
* ---------------------------------------------------------------------------- *
*  1.00    2016-07-07  きむ茶工房(きむしげ)  Create                            *
*  1.10    2021-07-22  きむ茶工房(きむしげ)  ステップ設定とアラームON機能を追加  *
 * 1.10α    2022-02-18  Kamoshika            レジスタをST-57BYG076-0604仕様に変更
 * 　　　　　　　　　　　　　　　　　　　　　　SPI関連メソッドをMCC仕様に変更
* ============================================================================ *
*  PIC 16F1827                                                                 *
*  MPLAB X (v5.50)                                                             *
*  MPLAB(R) XC8 C Compiler Version 1.3.90/2.32                                 *
*******************************************************************************/
#include "../../mcc_generated_files/mcc.h"
#include "skL6470.h"

int Busy ;

// L6470にコマンドを送信する処理
void CMD_Send(char cmd,unsigned long val,int num)
{
     unsigned char dt[4] ;
     int i ;

     for (i=0 ; i<num ; i++) {
          dt[i] = val & 0xff ;
          val = val >> 8 ;
     }

     L6470_CS = 0 ;
     SPI1_ExchangeByte(cmd) ;                // コマンドの送信
     L6470_CS = 1 ;
     switch (num) {
        case 3:
               L6470_CS = 0 ;
               SPI1_ExchangeByte(dt[2]) ;    // パラメータ２の送信
               L6470_CS = 1 ;
        case 2:
               L6470_CS = 0 ;
               SPI1_ExchangeByte(dt[1]) ;    // パラメータ１の送信
               L6470_CS = 1 ;
        case 1:
               L6470_CS = 0 ;
               SPI1_ExchangeByte(dt[0]) ;    // パラメータ０の送信
               L6470_CS = 1 ;
               break ;
     }
}
/*******************************************************************************
*  ans = L6470_Init()                                                          *
*    L6470の初期化を行う処理                                                   *
*                                                                              *
*    ans : 現在のSTATUS情報を返す                                               *
*******************************************************************************/
unsigned long L6470_Init()
{
     union STEP_MODE_t step_mode ;
     union CONFIG_t config ;
     long  val ;
     int i ;

     // L6470にリセットを掛ける
     for (i=0 ; i<4 ; i++) {
          L6470_CS = 0 ;
          SPI1_ExchangeByte(0x00) ;     // NOPの送信
          L6470_CS = 1 ;
     }
     L6470_CS = 0 ;
     SPI1_ExchangeByte(0xC0) ;          // ResetDeviceコマンドの送信
     L6470_CS = 1 ;
     // 各種レジスタに設定を行う(SetParam)
     CMD_Send(L6470_ALARM_EN,0xFF,1) ;       // 全てのアラーム信号を ON に選択(v1.10)
     CMD_Send(L6470_ACC,0x008A,2) ;          // 加速時の係数 
     CMD_Send(L6470_DEC,0x0008A,2) ;          // 減速時の係数
     CMD_Send(L6470_MAX_SPEED,0xF,2) ;    // 最大回転速度
     CMD_Send(L6470_MIN_SPEED,0x0000,2) ;    // 最少回転速度(0 Step/s^2 低速の最適化機能無効)
     CMD_Send(L6470_KVAL_HOLD,0xFF,1) ;      // 停止時の電圧(最大 L6470_KVAL_HOLD/0xFF*vs)
     CMD_Send(L6470_KVAL_RUN,0xFF,1) ;       // 定速時の電圧(同上)
     CMD_Send(L6470_KVAL_ACC,0xFF,1) ;       // 加速時の電圧(同上)
     CMD_Send(L6470_KVAL_DEC,0xFF,1) ;       // 減速時の電圧(同上)
     CMD_Send(L6470_OCD_TH,0x08,1) ;         // 過電流の閾値(3A)　375mAステップ 3000mA/375mA=0x08
     CMD_Send(L6470_STALL_TH,0x20,1) ;       // ストール電流の閾値(1A) 31.25mAステップ 1000mA/31.25mA=0x20
     CMD_Send(L6470_FS_SPD,0x3FF,2);        //フルステップ速度のしきい値(常にマイクロステップ動作)
     
      // ステップモードの設定
     step_mode.DT = 0x07 ;                   // デフォルト値
     step_mode.STEP_SEL = MICROSTEP8 ;        // 1/8ステップに設定 1600ステップで１回転
     step_mode.SYNC_EN  = Busy = 0 ;         // BUSY出力モード　値が'0'でBUSY出力  :ピン出力はLOWでBUSY
     val = step_mode.DT ;
     CMD_Send(L6470_STEP_MODE,val,1) ;
     // コンフィグの設定
     config.DT = 0x2E88 ;                    // デフォルト値(PWM周波数:15.6KHz)
     config.OSC_SEL = OSC_INT16MHZ ;         // クロックは内蔵16MHz
     config.EXT_CLK = 0 ;                    // クロック出力(OSCOUT)はなし
     config.SW_MODE = 0 ;                    // ハード停止モード
     config.EN_VSCPMP = 1;                  // モータの電源電圧補償の有 VR1の調整が必要です。
     config.F_PWM_DEC = 0b000;              //PWM 2.8kHz　高周波が耳障りなので変更した
     config.F_PWM_INT = 0b110;              //PWM 2.8kHz　高周波が耳障りなので変更した
     val = config.DT ;
     CMD_Send(L6470_CONFIG,val,2) ;          // コンフィグの設定
     return L6470_GetRegister(L6470_STATUS,2) ;// 現在のステータスを返す。
}
/*******************************************************************************
*  ans = L6470_GetRegister(regadrs,num)                                        *
*    レジスタのデータを読み込む処理                                               *
*                                                                              *
*    regadrs : 読み取りたいレジスタのアドレスを指定(0x01-0x1B)                    *
*    num     : レジスタの読み取るデータのバイト数(1-3byte)                        *
*    ans     : 読み取ったデータをlong値で返す                                    *
*******************************************************************************/
unsigned long L6470_GetRegister(char regadrs,int num)
{
     unsigned long ans , dt ;
     int i ;

     L6470_CS = 0 ;
     if (regadrs == L6470_STATUS) SPI1_ExchangeByte(0xD0) ; // GetStatusコマンドの送信
     else SPI1_ExchangeByte(regadrs|0x20) ;                 // GetParamコマンドの送信
     L6470_CS = 1 ;
     ans = dt = 0 ;
     for (i=0 ; i<num ; i++) {
          ans = ans << 8 ;
          L6470_CS = 0 ;
          dt = SPI1_ExchangeByte(0x00) ;     // レスポンデータを得る為にダミーの送信
          L6470_CS = 1 ;
          ans = ans | dt ;
     }
     return ans ;
}
/*******************************************************************************
*  L6470_SetStep(mode)                                                 (v1.10) *
*    レジスタにステップモードを書き込む処理                                       *
*                                                                              *
*    mode : ステップモードを指定します                                           *
*******************************************************************************/
void L6470_SetStep(unsigned char mode)
{
     union STEP_MODE_t step_mode ;
     long  val ;

      // ステップモードの設定
     step_mode.DT = 0x07 ;              // デフォルト値
     step_mode.STEP_SEL = mode ;        // 指定のステップに設定
     step_mode.SYNC_EN  = Busy = 0 ;    // BUSY出力モード
     val = step_mode.DT ;
     
    while( (Busy==0) && (L6470_BUSY==0) ) ; // コマンド実行中なら待つ
    CMD_Send(L6470_STEP_MODE,val,1) ;
}
/*******************************************************************************
*  ans = L6470_ALARM()                                                         *
*    アラームの情報を読み込む処理                                                *
*    FLAGピンがLOWにてアラーム発生です、割り込みで受けましょう。                   *
*    この関数は、”GetStatus”コマンドを発行しているのでアラームは解除されます        *
*                                                                             *
*    ans：発生したアラーム情報を返す                                            *
*         0=アラームは発生していない                                            *
*         3=スイッチイベントを検出                                              *
*         7=コマンドが実行できない                                              *
*         8=コマンドが全く存在しない                                            *
*         9=定電圧を検出                                                       *
*        10=熱警告を検出                                                       *
*        11=サーマルシャットダウンを検出                                        *
*        12=過電流を検出                                                       *
*        13=Ａ相ストールを検出                                                 *
*        14=Ｂ相ストールを検出                                                 *
*******************************************************************************/
int L6470_ALARM()
{
     unsigned long dt ;
     int i ;

     dt = L6470_GetRegister(L6470_STATUS,2) ;
     for (i=14;i>=9;i--) {
          if ((dt & (1<<i)) == 0) return i ;
     }
     for (i=8;i>=7;i--) {
          if ((dt & (1<<i)) != 0) return i ;
     }
     if ((dt & (1<<3)) != 0) return 3 ;
     return 0 ;
}
/*******************************************************************************
*  L6470_Run(dir,speed)                                                        *
*    モータを一定速度で回転させる処理                                             *
*    選択されているステッピングモードにより回転します                              *
*                                                                              *
*    dir   : 回転させる方向を指示します(0=逆転  1=正転)                           *
*    speed : 回転させる目標速度を指示します                                      *
*            0.015step/sの分解能で0(0.015)-104166(15625)のステップです。         *
*            MAX_SPEEDより低く、MIN_SPEEDより大きくなければなりません。           *
*******************************************************************************/
void L6470_Run(char dir,unsigned long speed)
{
     while( (Busy==0) && (L6470_BUSY==0) ) ; // コマンド実行中なら待つ
     CMD_Send(0x50|dir,speed,3) ;            // 一定速度回転コマンドの送信
}
/*******************************************************************************
*  L6470_Move(dir,step)                                                        *
*    モータを指定ステップ数まで回転させる処理                                     *
*    選択されているステッピングモードによりステップ当りの回転角度は変わります。      *
*    例えば、step=200でフルステップモードなら１回転(360度)です。                  *
*                                                                             *
*    dir   : 回転させる方向を指示します(0=逆転  1=正転)                          *
*    step : 回転させる目標ステップ数を指示します(0-4194303:22bit)                *
*******************************************************************************/
void L6470_Move(char dir,unsigned long step)
{
     while( (Busy==0) && (L6470_BUSY==0) ) ; // コマンド実行中なら待つ
     CMD_Send(0x40|dir,step,3) ;             // 移動コマンドの送信
}
/*******************************************************************************
*  L6470_Stop(mode)                                                            *
*    モータを停止させる処理                                                      *
*    Hizはモータを停止させた後、モータの出力をOFF(ハイインピーダンス)にします。      *
*                                                                              *
*    mode : 停止モードを指定します                                               *
*           0 = SoftStop 減速しながらモータを停止させます、保持トルク有り。        *
*           1 = HardStop 即時にモータを停止させます、保持トルク有り。             *
*           2 = SoftHiz  減速しながらモータを停止させます、保持トルク無し。       *
*           3 = HardHiz  即時にモータを停止させます、保持トルク無し。             *
*******************************************************************************/
void L6470_Stop(int mode)
{
     unsigned char cmd[4] = {0xB0,0xB8,0xA0,0xA8} ;

     //while( (Busy==0) && (L6470_BUSY==0) ) ; // コマンド実行中なら待つ
     L6470_CS = 0 ;
     SPI1_ExchangeByte(cmd[mode]) ;               // ストップコマンドの送信
     L6470_CS = 1 ;
}
```
<h4>モータードライバ制御部解説</h4>
コードの中で使用されているレジスタ設定の意味は下記サイトを参照した
https://tyokota.hateblo.jp/entry/2017/06/16/191852
https://strawberry-linux.com/support/12023/1488227

秋月電子からダウンロードできるマニュアルを見ると以下の記載があるが、この意味の理解に時間がかかったので記載する。
tick・・・250ns ※動作クロックが250ns(4MHz?)なんか合わないな250nsの根拠が不明
MAX_SPEEDレジスタの単位は step/tickと記載されており、step/秒に換算する式が示されている。
試しに初期値を計算して合っているか見てみる。
991.8(step/s) = {0x41*2^-18}/(250*10^-9)
まあ合っているんだが...248e-6(step/tick)と0x41の換算がどうなっているかわからん。248e-6を固定小数点で表現すると0x41になるのか...固定小数点の小数点の位置が記載無いが、指定が無い場合は一般的な考え方で調べてみる。
仮数部が第9bti目で残り8bitが小数部なのか？？2^-18の意味からするとそうなる仮定する。
0x41 = 0b0001000001
仮数部が'0' 小数部は0b001000001
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/c6a0105d-1bd0-8942-39ea-7ba0d001e219.png)
MATLABまで使って調べてみたが、合わんし...もうわからん　だれか教えて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/586435e6-ac26-4766-359c-3bd2aea3a430.png)
(初期値)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/6e4e5db3-cacf-187f-9f0f-3679a42c695f.png)

細かいところは解らないが...
今回使用するモーターが脱調せずに回転可能な速度が300setp/秒らしいので計算してみる。
300 = MAX_SPEED x 2^(-18) / 250 *10^(-9)
式変形して、
MAX_SPEED = 300 / {2^(-18)/ 250 *10^(-9)}
　　　　　=300 / 15.25878906
　　　　　=19.6608 ≒ 20 =0x14h
実際にやってみたが、300step/秒だと脱調してしまった。徐々に下げていって最終的に0x0fで脱調が収まった。
step/秒に換算すると
(0x0f*2^-18)/(250*10^-9)≒229step/秒 68.7rpm..ステッピングモーターってこんなに遅かった？？？
脱調の原因なんだろ、Duty比が50%から大きく外れているんじゃないだろうかオシロで調べてみた。

OWON SDS1022にて計測
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/bf1ffd05-e4f1-5cf5-af96-9b9d6fa572df.png)
画像には表示されていないがDuty比ｈ20%と計測されている。
やっぱり外れていた...どうしよう
http://spinelify.blog.fc2.com/blog-entry-65.html
北の国からサイトさんに情報がありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/c2753b39-1a7d-cacb-c8e1-57b0417d6811.png)
このレジスタを２倍にしてみる。初期値が61.5step/secなので...
INT_SPEED=((61.5*2)*(250*10^-9))/2^-26=2064=0x080F
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/aa4e328f-4744-c297-fc0e-069cc4f99334.png)
duty比74% が脱調する。
その後、ST_SLPとFN_SLP_ACCをそれぞれ２倍にするとフルステップでMAX_SPEED =0x20までは脱調しなくなったが、結局よくわからん
運用は1/8マイクロステップで動作させるので前記３つのレジスタはデフォルトで運用、MAX_SPEED=0x0Fとした。

ちなみに、ソースコードは1/8マイクロステップ動作だが、フルステップにしても１回転の速度が同じだったことからレジスタの値はフルステップを基準に設定することがわかった。


次にわからなかったのはWSとWHの違い
ストップ時のみというのはそのままの意味でハイインピーダンスの意味は、モーターのコイルに電圧がかかっていないときの意味であった。
停止していても静止トルクを出す為に電圧をかけると変更できない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672650/da056825-115a-6f54-c1d6-2cfc352a284f.png)
main.cの中で示すがソースコード中でL6470_Stop(3)としてからで無いとステップモードの変更が出来ないなどの制限がかかる。
<b>ステップモードを変更した瞬間にモーターが微動するので、対策としては精度が重要な場面ではステップモードは変更しない事にした。</b>

```
     // ステップモード変更 1/128
    L6470_Stop(3);
    L6470_SetStep(MICROSTEP64);
```

<h4>EUSARTとLCD1602の解説は後回しにして先にメインルーチンを載せておく</h4>
main.c

```
#include "mcc_generated_files/mcc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ext/L6470/skL6470.h"
#include "lcd.h"

void limitpos_int(void);//RB0(POS_LIMIT)の割り込み
void flag_int(void);//モータードライバからの割り込み
void org_btn(void);//原点復帰ボタン
void Wait(int num);// num x 10msの間delayさせる

void putch(char c){
    lcd_DATA(c);
}

/*
     Main application
 */
void main(void)
{
    char rxData;
    char strBuf[20];
    int srtBufPointer=0;
    int16_t pos=0;
    int16_t moveStep = 0;
    unsigned long ans ;

    SYSTEM_Initialize();

    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();

    // Enable the Peripheral Interrupts
    INTERRUPT_PeripheralInterruptEnable();
 
   //割り込みハンドラの設定
    IOCBF0_SetInterruptHandler(limitpos_int);//POS_LIMIT
    IOCBF3_SetInterruptHandler(org_btn);//ORG_BTN
    IOCBF7_SetInterruptHandler(flag_int);//FLAG
    
    //液晶モジュールLCD1602Aの初期化
    lcd_Init();
    //EUSART受信バッファ初期化
    memset(strBuf, '\0', sizeof(strBuf));
    //SPI通信の初期化
     SPI1_Open(SPI1_DEFAULT);
    //モータードライバの初期化
    ans = L6470_Init() ;

    //メインループ
    while (1)
    {
        if(EUSART_is_rx_ready()){ //受信したデータがあるか
            rxData = EUSART_Read(); //受信データを取得
            
            if( rxData == '\n' ){
                lcd_INST(0x01);     //ディスプレイクリア
                Wait(2);

                int i = 0;
                int p = 0;
                while(strBuf[i]!='\0'){
                    if( (strBuf[i]>='0' && strBuf[i]<='9') || strBuf[i]=='-'){
                        strBuf[p++]=strBuf[i];
                    }
                    i++;
                }
                strBuf[p++]='\0';

                pos = atoi(strBuf);
                lcd_SetCursor(0,0);//カーソル移動
                printf("POS=%d",pos);
                
                //-------------------------------------------------------------------
                //ステッピングモーターの制御処理を記述
                //1600ステップで１回転
                //posとmoveStepを分けているのは機械座標の換算の為、ここでは換算していない
                //-------------------------------------------------------------------
                moveStep = pos;
                
                if( RB0 == 0){
                    L6470_Move(p2offset>0?Forward:Reverse,(unsigned long)abs(moveStep)) ;
                }
                while( L6470_BUSY==0 ) ; // コマンド実行中なら待つ
                Wait(100);
                //--------------------------------------
                
                lcd_SetCursor(1,0);//カーソルを2行目に移動
                printf("moveStep=%d",moveStep);
                
                srtBufPointer = 0;
                memset(strBuf, '\0', sizeof(strBuf));
               
            }else{
                lcd_SetCursor(0,14);
                printf("%c",rxData);
                strBuf[srtBufPointer] = rxData;//受信データーをバッファに追加
                srtBufPointer++;
                if( srtBufPointer > 19){//19文字を超えても「CR」未受信ならばエラー表示
                    lcd_SetCursor(1,0);
                    printf("Er1");
                    srtBufPointer = 0;
                    memset(strBuf, '\0', sizeof(strBuf));
                }
            }
        }
    }
}

//RB0(POS_LIMIT)の割り込み
void limitpos_int(void){
    //即時停止
    L6470_Stop(3);
    lcd_INST(0x01);     //ディスプレイクリア
    Wait(2);
    lcd_SetCursor(0,0);//カーソルを1行目に移動
    printf("LIMIT!!");
    Wait(100);
    
}

//モータードライバからの割り込み
void flag_int(void){
    int alm = L6470_ALARM();
    lcd_INST(0x01);     //ディスプレイクリア
    Wait(2);
    lcd_SetCursor(1,0);//カーソルを2行目に移動
    printf("FLAG!! %d",alm);
    Wait(100);
}

//原点復帰ボタン
void org_btn(void){
    unsigned int value0 = 0;//原点復帰時の移動量
    unsigned int value1 = 0;//１回転で+1
    
    lcd_INST(0x01);     //ディスプレイクリア
    Wait(2);
    
    //LIMITセンサー割り込み無効
    IOCBNbits.IOCBN0 = 0;
    IOCBPbits.IOCBP0 = 0;
 
     // ステップモード変更 1/64
    L6470_Stop(3);
    L6470_SetStep(MICROSTEP64);

    while( RB0 == 0){
        //回転
        L6470_Move(Reverse,1) ;//-5μm移動 (1/64ステップ 12800pluse/1回転  M6ねじピッチ1mm)
        while( L6470_BUSY==0 ) ; // コマンド実行中なら待つ

        lcd_SetCursor(0,0);//カーソルを1行目に移動
        printf("MOVE=%d step  ",++value0);
        
        if(value0>12800){
            value0=0;
            value1++;
            if( value1 > 10){
                lcd_SetCursor(1,0);//カーソルを1行目に移動
                printf("ORG ERROR");
                break;
            }
        }
    }
     
    // ステップモードの戻し
     L6470_Stop(3);
     L6470_SetStep(MICROSTEP8);
    
    if(value1<=10){
        lcd_INST(0x01);     //ディスプレイクリア
        Wait(2);
        lcd_SetCursor(0,0);//カーソルを1行目に移動
        printf("ORG DONE");

        int alarm = L6470_ALARM();
        if( alarm != 0){
            lcd_SetCursor(1,0);
            printf("ALARM=%d",L6470_ALARM());
        }
    }
    
     Wait(1000);
    //LIMITセンサー割り込み有効
    IOCBNbits.IOCBN0 = 0;
    IOCBPbits.IOCBP0 = 1;

    position=0;//絶対値リセット
}

/*
//シリアルへ文字列出力
void EUSART_StringWrite(char var[] ){
    for( int i=0;i<sizeof(var);++i){
        EUSART_Write(var[i]);
    }
}
*/

// num x 10msの間delayさせる
void Wait(int num)
{
     // numで指定した回数だけ繰り返す
     do {
          __delay_ms(1);   // 10msプログラムの一時停止
     } while(--num > 0);
}
/**
 End of File
*/
```
-------------2022/02/19----------続きは後日