前回に続き、PWMの設定を行います。
今回はPWMで使用する割り込み関数の設定と各種設定プログラムの関数化を行います。
PWM設定は爪車様の
SH7125でPWMサーボの制御を参考にしております。
割り込み動作の有効化
初期状態では割り込みを受け付けないように設定されているので、割り込みを有効化しておきます。
resetprg.cの36行目付近
#define SR_Init 0x000000
F0
を
#define SR_Init 0x000000
00
へ変更します。
割り込み関数の設定
前回の設定でMTU20のOVF割り込みが機能するようになりました。
割り込み動作自体は機能するのですが、割り込み動作が発生した時に
何を実行するかはまだ何も設定していません。
そこで、割り込み動作が発生した時に何を実行するかを設定していきます。
割り込み関数の実行について解説
SH7125では設定を行った
割り込み条件を満たしたときに、その割り込み条件に対して
特定の場所に記載された関数が実行されるように設定されています。
ハードウェアマニュアルの
割り込み例外処理ベクタテーブルの項によると
MTU20のOVF要因による割り込みTCIV_0は
ベクタ番号92にて実行されます。
これに相当する設定がSH7125のプロジェクトを作成した際に自動生成されている
vecttbl.cに記載されています。
MTU20のOVF割り込みが発生すると92番目の
INT_MTU2_0_TCIV0が実行されます。
この関数はvect.hで宣言され、実体は
intprg.cに記載されています。
で、intprg.cで
INT_MTU2_0_TCIV0を検索すると関数の実体が見つかりますが、
// 92 MTU2_0 TCIV0void
INT_MTU2_0_TCIV0(void){
/* sleep(); */}
となっており、関数の中身は
コメントしかないので結局
何も実行されません。
そこで、関数
INT_MTU2_0_TCIV0の中に
実行したい内容を記述しておけば目的の動作となります。
しかし、intprg.cに各割り込みにて
実行したい内容を片っ端から書いていくと、
行数が増えに増えて非常に読みづらくなります。
そこで、
実行したい内容をまとめて記述した関数を作成し、INT_MTU2_0_TCIV0の中にはその
1つの関数を実行するという1行だけ記述します。
これでintprg.cの中身は最小限に抑えられます。また、実行したい内容を割り込みの初期化処理等を記載した場所に並べて書くことができます。
割り込み関数の中身
_MTU2_0_TCIV0内で実行される関数として
MTU20_INT_OVFを作成します。
関数名は何でも構いません。<設定>void MTU20_INT_OVF(void){
MTU2_syn_stop();
// タイマ0~4停止 MTU20.TSR.BIT.TCFV = 0;
// OVFフラグクリア MTU20.TCNT = PWM_DEFAULT;
// タイマーセット if(_pwm_duty[0]<=0) MTU20.TIOR.BIT.IOA = 0x01;
// TGRA_0 PE0 OFF→OFF else MTU20.TIOR.BIT.IOA = 0x05;
// TGRA_0 PE0 ON→OFF if(_pwm_duty[1]<=0) MTU20.TIOR.BIT.IOB = 0x01;
// TGRB_0 PE1 OFF→OFF else MTU20.TIOR.BIT.IOB = 0x05;
// TGRB_0 PE1 ON→OFF// if(_pwm_duty[2]<=0) MTU20.TIOR.BIT.IOC = 0x01; // TGRC_0 PE2 OFF→OFF// else MTU20.TIOR.BIT.IOC = 0x05; // TGRC_0 PE2 ON→OFF// if(_pwm_duty[3]<=0) MTU20.TIOR.BIT.IOD = 0x01; // TGRD_0 PE3 OFF→OFF// else MTU20.TIOR.BIT.IOD = 0x05; // TGRD_0 PE3 ON→OFF/**//* if(_pwm_duty[4]<=0) MTU21.TIOR.BIT.IOA = 0x01; // TGRA_1 PE4 OFF→OFF else MTU21.TIOR.BIT.IOA = 0x05; // TGRA_1 PE4 ON→OFF if(_pwm_duty[5]<=0) MTU21.TIOR.BIT.IOB = 0x01; // TGRB_1 PE5 OFF→OFF else MTU21.TIOR.BIT.IOB = 0x05; // TGRB_1 PE5 ON→OFF/**//* if(_pwm_duty[6]<=0) MTU22.TIOR.BIT.IOA = 0x01; // TGRA_2 PE6 OFF→OFF else MTU22.TIOR.BIT.IOA = 0x05; // TGRA_2 PE6 ON→OFF if(_pwm_duty[7]<=0) MTU22.TIOR.BIT.IOB = 0x01; // TGRB_2 PE7 OFF→OFF else MTU22.TIOR.BIT.IOB = 0x05; // TGRB_2 PE7 ON→OFF/**/ if(_pwm_duty[8]<=0) MTU23.TIOR.BIT.IOA = 0x01;
// TGRA_3 PE8 OFF→OFF else MTU23.TIOR.BIT.IOA = 0x05;
// TGRA_3 PE8 ON→OFF if(_pwm_duty[9]<=0) MTU23.TIOR.BIT.IOB = 0x01;
// TGRB_3 PE9 OFF→OFF else MTU23.TIOR.BIT.IOB = 0x05;
// TGRB_3 PE9 ON→OFF if(_pwm_duty[10]<=0) MTU23.TIOR.BIT.IOC = 0x01;
// TGRC_3 PE10 OFF→OFF else MTU23.TIOR.BIT.IOC = 0x05;
// TGRC_3 PE10 ON→OFF if(_pwm_duty[11]<=0) MTU23.TIOR.BIT.IOD = 0x01;
// TGRD_3 PE11 OFF→OFF else MTU23.TIOR.BIT.IOD = 0x05;
// TGRD_3 PE11 ON→OFF/**/
if(_pwm_duty[12]<=0) MTU24.TIOR.BIT.IOA = 0x01;
// TGRA_3 PE12 OFF→OFF else MTU24.TIOR.BIT.IOA = 0x05;
// TGRA_3 PE12 ON→OFF if(_pwm_duty[13]<=0) MTU24.TIOR.BIT.IOB = 0x01;
// TGRB_4 PE13 OFF→OFF else MTU24.TIOR.BIT.IOB = 0x05;
// TGRB_4 PE13 ON→OFF if(_pwm_duty[14]<=0) MTU24.TIOR.BIT.IOC = 0x01;
// TGRC_4 PE14 OFF→OFF else MTU24.TIOR.BIT.IOC = 0x05;
// TGRC_4 PE14 ON→OFF if(_pwm_duty[15]<=0) MTU24.TIOR.BIT.IOD = 0x01;
// TGRD_4 PE15 OFF→OFF else MTU24.TIOR.BIT.IOD = 0x05;
// TGRD_4 PE15 ON→OFF/**/
MTU2_syn_start();
//タイマ同期開始 //PWMの設定値を変更 MTU20.TGRA = PWM_DEFAULT + _pwm_duty[0];
MTU20.TGRB = PWM_DEFAULT + _pwm_duty[1];
// MTU20.TGRC = PWM_DEFAULT + _pwm_duty[2];// MTU20.TGRD = PWM_DEFAULT + _pwm_duty[3];// MTU21.TGRA = PWM_DEFAULT + _pwm_duty[4];// MTU21.TGRB = PWM_DEFAULT + _pwm_duty[5];// MTU22.TGRA = PWM_DEFAULT + _pwm_duty[6];// MTU22.TGRB = PWM_DEFAULT + _pwm_duty[7]; MTU23.TGRA = PWM_DEFAULT + _pwm_duty[8];
MTU23.TGRB = PWM_DEFAULT + _pwm_duty[9];
MTU23.TGRC = PWM_DEFAULT + _pwm_duty[10];
MTU23.TGRD = PWM_DEFAULT + _pwm_duty[11];
MTU24.TGRA = PWM_DEFAULT + _pwm_duty[12];
MTU24.TGRB = PWM_DEFAULT + _pwm_duty[13];
MTU24.TGRC = PWM_DEFAULT + _pwm_duty[14];
MTU24.TGRD = PWM_DEFAULT + _pwm_duty[15];
}
<解説>タイマーを一時停止、次の割り込みに備えて割り込みフラグクリア、タイマーを初期化。
_pwm_duty[0~15]の値に応じて各端子の設定を変えます。
0または0以下(ありえない)の時:出力停止
それ以外:前回の解説に従い、カウンタが設定値を超えるまでON→超えたらOFF
タイマの同期開始
PWMの設定値を変更
前回の解説に従い、設定値は
初期値+出力値この関数はintprg.cの_MTU2_0_TCIV0内で実行されるように設定する必要があります。
PWMの設定値を変更する関数
最後にPWMの設定値を変更する関数を作成します。
グローバル変数に直接値を書き込む事もできるのですが、
間違った変数に書き込んだ場合や
間違った値を書き込んだ場合に予期せぬ動作を起こす場合があります。
今回の例では_pwm_dutyはshort型の0~15の配列ですが、_pwm_duty[20]=1000000;等と書いてもビルドが通り、実行が可能です。で、本来別の変数に割り当てられている場所に数字を書き込んでしまうことになります。
これを防ぐために
関数を経由させます。
間違って
大きすぎる値を入力した際に
自動的に上限値に設定する役割も持ちます。
上限値は
PWM_INTERVAL(1024) - 2 としました。
//1端子ずつ設定する関数void set_pwm(short port ,short value){
if( port<0 || port>15 ) return;
//間違ったポートを指定した場合は関数中断 if( port>=2 && port<=7 ) return;
//間違ったポートを指定した場合は関数中断 value=(value>PWM_INTERVAL-2)?PWM_INTERVAL-2:(value<0)?0:value;
//0-1022になるように保護 _pwm_duty[port]=value;
}
この関数を使っている限り、_pwm_duty[20]を指定したり1000000を書き込んだりすることがなくなります。
ついでに2つの端子を排他的に設定する関数も用意しました。
//2端子を一度に設定する関数void set_pwm2(short portA ,short portB ,short value){
if( portA<0 || port
A>15 ) return;
//間違ったポートを指定した場合は関数中断 if( portB<0 || portB>15 ) return;
//間違ったポートを指定した場合は関数中断 if( portA>=2 && port
A<=7 ) return;
//間違ったポートを指定した場合は関数中断 if( portB>=2 && portB<=7 ) return;
//間違ったポートを指定した場合は関数中断 set_pwm(portA,value);
//プラスの時Aから出力 set_pwm(portB,-value);
//マイナスの時Bから出力}
※2017/07/10ポートAの保護設定が間違っていたので修正
モータを制御する場合に使います。
set_pwmに負の値を入力していますが、set_pwm内で負の値の時は0に変換しているので誤動作はしません。
ソースファイル、ヘッダファイルの作成
PWMに関する設定が出揃ったところで、PWMの使いまわしができるようにこれらの設定を独立したファイルとして作成します。
ソースファイルの中身:関数の実体、グローバル変数の宣言
ヘッダファイルの中身:PWM_DEFAULT等のdefine、関数のプロトタイプ宣言
詳細は以下のファイルを確認してください。
ダウンロード:ソースファイル
ダウンロード:ヘッダファイル
割り込み関数の登録
intprg.cの195行目付近
void INT_MTU2_0_TCIV0(
void){
/* sleep(); */}
を
void INT_MTU2_0_TCIV0(
void){
MTU20_INT_OVF();}
に修正します。
これでPWMを使用する準備が整いました。
次回はプログラムを実行してみます。
関連:
当ブログで扱うSH7125の特殊機能一覧