”FPGAで遊んでみる”では、セミナー用に作成したサンプル回路などを整理して少しづつ紹介していきます。


Papilioの本
FPGA版Arduino!!Papilioで作るディジタル・ガジェット CQ出版 発売中

セミナ開催します。
"実習・MAX10 NiosIIプロセッサ活用超入門”
2017年8月23日(水)、CQ出版社セミナルーム
DE10-Liteを使ってMAX10上でNisoIIを動かします.
詳しくはこちらへ



第12回 FPGAでステッピングモータを回してみる

 今回は、FPGAでステッピングモータを回す回路を紹介します。また、FPGAの出力で直接モータを制御することはできないの、モータドライブ用FETを使用したドライブ回路も紹介します。

使用部品
 SPG20-1332(ステッピングモータ) 入手先:秋月電子
 TND012(モータドライブ用FET) 入手先:秋月電子
 MFPGA_SPAR3E(FPGAボード) 入手先:マルツパーツ
 74HC244(バッファ) 入手先:秋月電子




ステッピングモータ制御回路(stepmotor_ctrl)のソース(zip圧縮してあります)
 stepmotor_ctrl.zip
 stepmotor_ctrl.v ステッピングモータ制御回路Verilog-HDLソース
 test_stepmoptor_ctrl.v テストベンチ
 stepmotor_ctrl.ucf FPGAボードにマルツパーツのMFPGA_SPAR3Eを使用した場合のPIN配置指定
 ※ソースコードが長いのでダウンロードして中身を見てください。

仕様説明(stepmotor_ctrl.v)

 動作概要
  SW0〜3の入力によりステッピングモーターを制御する信号を生成する
  SPI通信を行う。通信完了後に読み出しデータを出力する。

 入力信号
 clk:クロック入力/今回使用してたボードは40MHzです。
 rstb:リセット入力 0でリセット
 sw0:スイッチを押すと0、0で右回転
 sw1:スイッチを押すと0、0で左回転
 sw2:1->0の変化で回転速度アップ
 sw2:1->0の変化で回転速度ダウンダウン

 出力信号
 led:LED制御信号、sw0〜3のどれかを押すと点灯します。
 pwm_1:モーター制御信号、1相正
 pwm_1b:モーター制御信号、1相逆
 pwm_2:モーター制御信号、2相正
 pwm_2b:モーター制御信号、2相逆

 パラメータ
 p_1ms_cnt:入力クロックclkが40MHzの場合の1msのカウント値
 

 機能ブロック図
 setpmotor_ctrl.vは下位モジュールはありません。各機能の関係を機能ブロック図に表しています。

 

 詳細動作
 cnt_ms_baseを0〜39999の範囲でカウントして1ms周期で1clk間だけ1になるms_sigを発生。
 ms_sigをカウントイネーブルとしてcnd_pwm_baseを0〜cnt_pwm_base_maxの範囲でカウントして、
 cnd_pwm_base==cnt_pwm_base_maxの時にpwm_enが1clkだけ1になります。pwm_enの周期はcnt_pwm_base_maxの値によって変わってきます。
 pwm_enをカウントイネーブルとしてcnt_pwmを0〜3の範囲でカウントします。

 cnt_ms_base、cnt_pwm_base、cnt_pwmの波形


cnt_ms_base,cnd_pwm_base,cnt_pwmのVerilog-HDLソース

parameter p_1ms_cnt = 39999 ;//CLK=40MHzのときの1msのカウント値
   
//1msのカウンタ   
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  cnt_ms_base <= 16'd0;
else
  if (ms_sig == 1'd1)
    cnt_ms_base <= 16'd0;
  else
    cnt_ms_base <= cnt_ms_base + 16'd1;

assign  ms_sig = (cnt_ms_base == 16'd39999)?1'b1:1'b0; //1msに1clkだけ1になる
   
//モーター制御周期カウンタ
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  cnt_pwm_base <= 10'd0;
else
  if (ms_sig == 1'b1)
    //cnt_pwm_base_maxの値で周期を変更
    if (cnt_pwm_base >= cnt_pwm_base_max)
      cnt_pwm_base <= 2'd0;
    else
      cnt_pwm_base <= cnt_pwm_base + 10'd1;
  else  
   cnt_pwm_base <= cnt_pwm_base ;


//モーター制御信号信号更新イネーブル
assign pwm_en =  ((cnt_pwm_base == cnt_pwm_base_max)&&(ms_sig == 1'b1))?1'b1:1'b0;
   
//モーター回転制御カウンタ   
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  cnt_pwm <= 2'd0;
else
  if (pwm_en == 1'b1)
    if (cnt_pwm == 2'd3)
      cnt_pwm <= 2'd0;
    else
      cnt_pwm <= cnt_pwm + 2'd1;
  else  
    cnt_pwm <= cnt_pwm ;


 モータ制御信号はcnt_pwmの値をデコードして生成します。sw0=0で右回転、sw1=0で左回転します。

 sw0=0右回転

cnt_pwd 1 1b 2 2b
0 1 0 0 1
1 1 0 1 0
2 0 1 1 0
3 0 1 0 1

sw1=0左回転

cnt_pwd 1 1b 2 2b
0 1 0 1 0
1 1 0 0 1
2 0 1 0 1
3 0 1 1 0

モター制御信号の波形その1 sw0=0で右回り、sw1=0で左回り

モーター回転の様子

モータ制御信号生成のVerilog-HDLソース

//制御パルス生成1相正極   
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  pwm_1 <= 2'd1;
else
  if((sw_0==1'b0)||(sw_1==1'b0))
    case (cnt_pwm)
      2'd0:pwm_1 <= 2'd1;
      2'd1:pwm_1 <= 2'd1;
      2'd2:pwm_1 <= 2'd0;
      2'd3:pwm_1 <= 2'd0;
    endcase // case(cnt_pwm)
  else 
    pwm_1 <= 2'd1;
  
//制御パルス生成1相逆極   
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  pwm_1b <= 2'd0;
else
  if((sw_0==1'b0)||(sw_1==1'b0))
    case (cnt_pwm)
      2'd0:pwm_1b <= 2'd0;
      2'd1:pwm_1b <= 2'd0;
      2'd2:pwm_1b <= 2'd1;
      2'd3:pwm_1b <= 2'd1;
    endcase // case(cnt_pwm)
  else 
    pwm_1b <= 2'd0;

//制御パルス生成2相正極   
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  pwm_2 <= 2'd0;
else
  if(sw_0==1'b0)
    case (cnt_pwm)
      2'd0:pwm_2 <= 2'd0;
      2'd1:pwm_2 <= 2'd1;
      2'd2:pwm_2 <= 2'd1;
      2'd3:pwm_2 <= 2'd0;
    endcase // case(cnt_pwm)
  else if(sw_1==1'b0)
    case (cnt_pwm)
      2'd0:pwm_2 <= 2'd1;
      2'd1:pwm_2 <= 2'd0;
      2'd2:pwm_2 <= 2'd0;
      2'd3:pwm_2 <= 2'd1;
    endcase // case(cnt_pwm)
  else 
    pwm_2 <= 2'd0;
  
//制御パルス生成2相逆極   
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  pwm_2b <= 2'd1;
else
  if(sw_0==1'b0)
    case (cnt_pwm)
      2'd0:pwm_2b <= 2'd1;
      2'd1:pwm_2b <= 2'd0;
      2'd2:pwm_2b <= 2'd0;
      2'd3:pwm_2b <= 2'd1;
    endcase // case(cnt_pwm)
  else if(sw_1==1'b0)
    case (cnt_pwm)
      2'd0:pwm_2b <= 2'd0;
      2'd1:pwm_2b <= 2'd1;
      2'd2:pwm_2b <= 2'd1;
      2'd3:pwm_2b <= 2'd0;
    endcase // case(cnt_pwm)
  else 
    pwm_2b <= 2'd1;



モータの回転速度は、cnt_pwm_base_maxの値によって変わってきます。cnt_pwm_base_maxの値が小さくなるpwm_enの間隔が短くなりモータ制御信号も短い周期で切り替わるので
モーターが早く回ります。逆にcnt_pwm_base_maxの値が大きくなるとモーターは遅くまわります。
SW2が押されると値が1→0と変化します。この変化を検出するとcnt_pwm_base_maxの値を-1します。SW3の値が1→0と変化するとcnt_pwm_base_maxの値を+1します。

モター制御信号の波形その1 sw2が1→0で速度アッップ、sw3が1→0で速度ダウン


cnt_pwm_base_maxのVerilog-HDLソース

//sw_2の1ms遅延した信号生成
always @ (posedge clk or negedge rstb )
if (rstb==1'b0)
  sw_2_delay <= 1'b1;
else
  if (ms_sig==1'b1)
    sw_2_delay <= sw_2;
  else
    sw_2_delay <= sw_2_delay;

//sw_2の立下り検出
always @ (posedge clk or negedge rstb )
if (rstb==1'b0)
  sw_2_sig <= 1'b0;
else   
  if ((ms_sig==1'b1)&&(sw_2==1'b0)&&(sw_2_delay==1'b1))
    sw_2_sig <= 1'b1;
  else
    sw_2_sig <= 1'b0;

//sw_3の1ms遅延した信号生成
always @ (posedge clk or negedge rstb )
if (rstb==1'b0)
  sw_3_delay <= 1'b1;
else
  if (ms_sig==1'b1)
    sw_3_delay <= sw_3;
  else
    sw_3_delay <= sw_3_delay;   

//sw_3の立下り検出
always @ (posedge clk or negedge rstb )
if (rstb==1'b0)
  sw_3_sig <= 1'b0;
else   
  if ((ms_sig==1'b1)&&(sw_3==1'b0)&&(sw_3_delay==1'b1))
    sw_3_sig <= 1'b1;
  else
    sw_3_sig <= 1'b0;

//モーター制御周期カウンタの最大値設定
always @ (posedge clk or negedge rstb )
if (rstb==1'b0) 
  cnt_pwm_base_max <= 10'd9;
else
  if (sw_2_sig==1'b1) //速度アップ
    cnt_pwm_base_max <= cnt_pwm_base_max - 10'd1;
  else if (sw_3_sig==1'b1) //速度ダウン
    cnt_pwm_base_max <= cnt_pwm_base_max + 10'd1;
  else
    cnt_pwm_base_max <= cnt_pwm_base_max ; 



ステッピングモーターのドライバ回路
 FPGAの出力は弱いので、直接モータを駆動することはできません。ドライバ回路でモーターを駆動します。
 今回は、手元にあったモータドライブ用FET(TND012)でドライバを駆動しています。FPGAからTND012を駆動するのも心配なので
 74HC244(バッファ)を介してTND012しています。

 ドライバー回路図




動作の様子


紹介した回路を試す場合は、自己責任でお願いします。

リンクフリーです。
リンクされた場合にご連絡をいただけると嬉しです。
メール:


変更履歴
(2012/9/20) 初版


TOPへ戻る