”FPGAで遊んでみる”では、セミナー用に作成したサンプル回路などを整理して少しづつ紹介していきます。
セミナ開催します
手ぶらでOK!実習・1日でわかる!CANプログラミング入門
-- M5StickC Plus2でCAN通信
2025年4月18日(金)CQ出版社セミナルーム CQ出版社セミナ・ルーム
詳しくはこちらへ
ZYBOの本
FPGAパソコンZYBOで作るLinux I/Oミニコンピュータ CQ出版 発売中
Papilioの本
FPGA版Arduino!!Papilioで作るディジタル・ガジェット CQ出版 発売中
第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) 初版