忍者ブログ

Fグループ電子工作講座

秋月電子SH7125ボードで始めるマイコン開発

PWM(MTU TIOC)の設定 割り込み関数編

前回に続き、PWMの設定を行います。
今回はPWMで使用する割り込み関数の設定と各種設定プログラムの関数化を行います。
PWM設定は爪車様のSH7125でPWMサーボの制御を参考にしております。

割り込み動作の有効化

初期状態では割り込みを受け付けないように設定されているので、割り込みを有効化しておきます。
resetprg.c
の36行目付近
#define SR_Init    0x000000F0

#define SR_Init    0x00000000
へ変更します。


割り込み関数の設定

前回の設定で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 TCIV0
void 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 || portA>15 ) return;        //間違ったポートを指定した場合は関数中断
    if( portB<0 || portB>15 ) return;        //間違ったポートを指定した場合は関数中断
    if( portA>=2 && portA<=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の特殊機能一覧
PR

コメント

プロフィール

HN:
ぼんどF博士
性別:
男性
自己紹介:

最新コメント