忍者ブログ

Fグループ電子工作講座

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

PWM(MTU TIOC)の設定 初期化編

SH7125マイコンのPWM機能を設定します。
関連:PWMとは
PWM設定は爪車様のSH7125でPWMサーボの制御を参考にしております。

PWM出力の流れ

PWM出力プログラムは以下の流れで動かします。

1) 端子の特殊機能設定
2) PWMの機能設定
3) デューティー比設定
あとは、手順3)を繰り返すだけです。

が、メインの処理はこれだけで良いのですが、裏で割り込み処理が機能している必要があります。
割り込み処理の考え方は「割り込み処理とは」を参照ください

PWM出力として必要となる機能は内部割込みのカウンター処理です。
同じカウンター処理を使うタイマー割り込みの方が設定内容が分かりのですが、PWMの方が使っていて面白いので先にPWMの解説を行います。

今回はまずPMW用の割り込み処理を動作させるための設定を行います。


PWM処理の基本

1.繰り返し動作

PWMはパルス生成を一定周期で繰り返し行います。
そこでまず一定周期で繰り返す機能が必要となります。
CPUのクロックをカウントしていき、上限値になったらカウント値を初期値にリセットします。
リセット直後から再びカウントを行い、再び上限値になったらカウント値をリセットします。
 
とりあえずこれで繰り返し動作を実行できます。
繰り返しの周期はクロックの周期×上限値となります。
クロックが一定であるため、カウント値の増加時間の経過に比例タイマーにより自動的に増加していることと同じと考えることができます。

図では10カウント目でリセットをかけていますが、実際には数百カウントでリセットをかけます。また純粋なCPUのクロック周期では早すぎるためCPUのクロックがn回動作したら1カウントアップさせるといった方法をとります。また、正確に言うとカウントが繰り返される期間は初期値~上限値の間となるため、実際の周期はクロック周期×n×(上限値ー下限値)となります。

2.出力の制御

続いて出力のON-OFF動作について考えます。
これについては話が比較的簡単です。
上限値と初期値の間のある値を設定しておきます。
この設定値カウント値を比較して、カウント値設定値より小さい間はOFFし設定値を超えたらON、あるいは反対にカウント値設定値より小さい間はONし設定値を超えたらOFFします。


図の例ではカウント値設定値より小さい間をONとしています。
設定値を変化させると出力も変化します。
 
出力される波形はまんまPWM波形になります。
設定値を大きくすると出力が大きくなり、設定値を小さくすると出力が小さくなります。

タイマー
動作で増えるカウント値と設定値を比較(コンペア)し条件に一致(マッチ)していれば出力を制御します。SH7125ではこの機能をコンペアマッチタイマーと呼んでいます。

※のこぎり波の生成および設定値との比較によるPWM波形生成は電子回路におけるPWM処理によく似ています。

SH7125の設定

設定する内容を大まかに分けると4つ

端子の初期化関数
1.マイコンの端子の特殊機能設定

PWMの初期化関数
2.CPUのクロックがn回動作したらカウンターを1アップする機能設定
3.カウンターが上限値に到達したら初期値にリセットする機能設定
4.カウンター値と設定値との比較により出力をON-OFF制御する機能設定

これを1つずつ設定していきます。

1.マイコンの端子の特殊機能設定

PWMとして使用可能なのはTIOCと表記のある端子です。
PE0~PE15がこれに該当します。
今のところPE2を除き全てPWM出力に設定することも可能ですが、他の特殊機能(パルスカウンタ)との干渉を防ぐためにTIOC1とTIOC2に該当するPE4~PE7はPWMに設定せず放置します。
※パルスカウンタはロータリーエンコーダを接続する際に使用します。

TIOC:タイマIOコントローラとか多分そんな感じ
PWM出力として使用します。
パルス入力の間隔測定にも使えるらしいですか、TIOCでパルス間隔を測定したこと無いので知りません。


設定するのは秋月SH7125サンプルプログラム解説の回で0に設定して放置していたPECRL1~PECRL4です。(ただし、PECRL2はPE4~PE7なので設定する必要はありません。)

<設定内容>
void PWM_IO_init(void){
    //ポートEの入出力を設定する
    PFC.PEIORL.WORD = 0xFFF3; // PE3-PE2 is input other output
    //PE2を出力に設定しないこと。

    PFC.PECRL4.BIT.PE15MD = 0x01; // PE15 is TIOC4D
    PFC.PECRL4.BIT.PE14MD = 0x01; // PE14 is TIOC4C
    PFC.PECRL4.BIT.PE13MD = 0x01; // PE13 is TIOC4B
    PFC.PECRL4.BIT.PE12MD = 0x01; // PE12 is TIOC4A

    PFC.PECRL3.BIT.PE11MD = 0x01; // PE11 is TIOC3D
    PFC.PECRL3.BIT.PE10MD = 0x01; // PE10 is TIOC3C
    PFC.PECRL3.BIT.PE9MD = 0x01; // PE9 is TIOC3B
    PFC.PECRL3.BIT.PE8MD = 0x01; // PE8 is TIOC3A
/*TIOC2は今回使用しない*//*
    PFC.PECRL2.BIT.PE7MD = 0x01; // PE7 is TIOC2B
    PFC.PECRL2.BIT.PE6MD = 0x01; // PE6 is TIOC2A
    PFC.PECRL2.BIT.PE5MD = 0x01; // PE5 is TIOC1B
    PFC.PECRL2.BIT.PE4MD = 0x01; // PE4 is TIOC1A
/**/
//    PFC.PECRL1.BIT.PE3MD = 0x01; // PE3 is TIOC0D
//    PFC.PECRL1.BIT.PE2MD = 0x01; // PE2 is TIOC0C  //PE2は出力に設定しない
    PFC.PECRL1.BIT.PE3MD = 0x00; // PE3 is I/O
    PFC.PECRL1.BIT.PE2MD = 0x00; // PE2 is I/O
    PFC.PECRL1.BIT.PE1MD = 0x01; // PE1 is TIOC0B
    PFC.PECRL1.BIT.PE0MD = 0x01; // PE0 is TIOC0A
}
<解説>
入出力の設定は秋月SH7125サンプルプログラム解説を参照。
PECRL1PECRL3PECRL4を1端子ずつ設定しています。
PECRLの設定内容はハードウェアマニュアルポートE コントロールレジスタに記載されています。
端子ごとに微妙に内容が異なりますが、TIOCに関してはPE*MD0 0 1→1に設定すれば良いことが分かります。
従ってPWM出力に設定する端子は全て1に設定しています。

2.CPUのクロックがn回動作したらカウンターを1アップする機能設定

<設定>
void MTU2_init(void){
    STB.CR4.BIT._MTU2 = 0; // モジュールスタンバイの解除
    MTU2.TSTR.BYTE&=~0xC1; // タイマ0~4停止
    MTU2.TRWER.BIT.RWE = 1; // 3,4のレジスタのリードライトを許可する
//    MTU2.TSYR.BYTE = 0xC7; // TCNT0~4同期動作 PEを全てPWMにする場合
    MTU2.TSYR.BYTE = 0xC1; // TCNT0,3,4同期動作
   
    // タイマ0~4同期動作
    MTU20.TCR.BYTE = 0x63; // 同期クリア、MPφ/64
//    MTU21.TCR.BYTE = 0x63; // 同期クリア、MPφ/64
//    MTU22.TCR.BYTE = 0x63; // 同期クリア、MPφ/64
    MTU23.TCR.BYTE = 0x63; // 同期クリア、MPφ/64
    MTU24.TCR.BYTE = 0x63; // 同期クリア、MPφ/64
   
    MTU20.TMDR.BYTE = 0x00; // タイマ通常動作
//    MTU21.TMDR.BYTE = 0x00;
//    MTU22.TMDR.BYTE = 0x00;
    MTU23.TMDR.BYTE = 0x00;
    MTU24.TMDR.BYTE = 0x00;

<用語>
MTU:マルチファンクションタイマパルスユニット
カウンタ関連の機能の名前です。
SH7125にはMTU2のみ存在しているようです。MTU1は知りません。
MTU20MTU24がありそれぞれTIOC0~TIOC4に対応しています。

TSTR:タイマスタートレジスタ。カウントの停止or実行を指定します。0で停止、1で実行。
TRWER.BIT.RWE:タイマリードライトイネーブルレジスタのリードライトイネーブル。1で書き込み許可。
TSYR:タイマシンクロレジスタ。他のカウンタと連動させる場合に使用します。1で連動。
TMDR:タイマモードレジスタ。通常動作の場合は0。

<解説>
モジュールが停止状態なのでまずスタンバイを解除して機能を有効にします。
タイマ(カウンタ)を停止しておきます。
設定の変更を許可する設定をします。(許可しないと設定が変更できません)

今回はTIOC0、TIOC3、TIOC4のPWMの周期を一緒にします。
クロックが何回動作したらカウントアップするか設定します。
今回は周期を数ミリ秒程度に設定したいので64回クロックが動作したらカウント値が1増える設定とします。
25MHzで動作しているのでクロックが64回動作するのに
( 1/(25×10^6) ) *64=2.56×10^-6 → 2.56マイクロ秒
かかります。
1024カウントで2.6ミリ秒くらいになります。

タイマーモードは通常モードにします。

3.カウンターが上限値に到達したら初期値にリセットする機能設定

<設定>
※void MTU2_init(void)の続き
    MTU20.TIER.BIT.TCIEV = 1; // OVFによる割り込み許可
//    MTU21.TIER.BIT.TCIEV = 1; // OVFによる割り込み許可
//    MTU22.TIER.BIT.TCIEV = 1; // OVFによる割り込み許可
//    MTU23.TIER.BIT.TCIEV = 1; // OVFによる割り込み許可
//    MTU24.TIER.BIT.TCIEV = 1; // OVFによる割り込み許可

    INTC.IPRD.BIT._MTU20C = 15; // MTU20 OVF 割り込み優先度 15

<用語&解説>
TIER:タイマインタラプトイネーブルレジスタ。カウンタ関連でどの種類の割り込みを使用するか設定します。
TCIEV:オーバフローインタラプトイネーブル。カウント値が上限に達した時に割り込みが発生します。
今回は全てのカウンターを連動させ、MTU20のOVF割り込みのみで各種制御を行うためMTU20以外のOVF割り込みを設定する必要はありません。

上限値は0xFFFF(65535)

INTC:割り込みコントローラ。
IPRD:インタラプトプライオリティレジスタD。優先順位の設定D。MTU20をレジスタDで設定します。
_MTU20C:MTU20のTCI系(オーバーフロー、アンダーフロー)の優先順位を設定します。

4.カウンター値と設定値との比較により出力をON-OFF制御する機能設定

<設定>
※プログラムの先頭に書いておきます。
    #define PWM_INTERVAL 1024
    #define PWM_DEFAULT (0xFFFF-PWM_INTERVAL)

※void MTU2_init(void)の続き
    MTU2.TOER.BYTE = 0xFF; // 3,4の出力許可
   
    MTU20.TIOR.BIT.IOA = 0x01; // TGRA_0 PE0 出力停止 初期0→コンペアマッチで0
    MTU20.TIOR.BIT.IOB = 0x01; // TGRB_0 PE1 出力停止
//    MTU20.TIOR.BIT.IOC = 0x01; // TGRC_0 PE2 出力停止
//    MTU20.TIOR.BIT.IOD = 0x01; // TGRD_0 PE3 出力停止
/*
    MTU21.TIOR.BIT.IOA = 0x01; // TGRA_1 PE4 出力停止
    MTU21.TIOR.BIT.IOB = 0x01; // TGRB_1 PE5 出力停止
    MTU22.TIOR.BIT.IOA = 0x01; // TGRA_2 PE6 出力停止
    MTU22.TIOR.BIT.IOB = 0x01; // TGRB_2 PE7 出力停止
*/
    MTU23.TIOR.BIT.IOA = 0x01; // TGRA_3 PE8 出力停止
    MTU23.TIOR.BIT.IOB = 0x01; // TGRB_3 PE9 出力停止
    MTU23.TIOR.BIT.IOC = 0x01; // TGRC_3 PE10 出力停止
    MTU23.TIOR.BIT.IOD = 0x01; // TGRD_3 PE11 出力停止
    MTU24.TIOR.BIT.IOA = 0x01; // TGRA_4 PE12 出力停止
    MTU24.TIOR.BIT.IOB = 0x01; // TGRB_4 PE13 出力停止
    MTU24.TIOR.BIT.IOC = 0x01; // TGRC_4 PE14 出力停止
    MTU24.TIOR.BIT.IOD = 0x01; // TGRD_4 PE15 出力停止
   
    MTU20.TGRA = PWM_DEFAULT + 0; // 比較用の設定値を設定 出力0
    MTU20.TGRB = PWM_DEFAULT + 0;
//    MTU20.TGRC = PWM_DEFAULT + 0;
//    MTU20.TGRD = PWM_DEFAULT + 0;
/*
    MTU21.TGRA = PWM_DEFAULT + 0;
    MTU21.TGRB = PWM_DEFAULT + 0;
    MTU22.TGRA = PWM_DEFAULT + 0;
    MTU22.TGRB = PWM_DEFAULT + 0;
*/
    MTU23.TGRA = PWM_DEFAULT + 0; // 比較用の設定値を設定 出力0
    MTU23.TGRB = PWM_DEFAULT + 0;
    MTU23.TGRC = PWM_DEFAULT + 0;
    MTU23.TGRD = PWM_DEFAULT + 0;
    MTU24.TGRA = PWM_DEFAULT + 0;
    MTU24.TGRB = PWM_DEFAULT + 0;
    MTU24.TGRC = PWM_DEFAULT + 0;
    MTU24.TGRD = PWM_DEFAULT + 0;
   
    MTU20.TCNT = PWM_DEFAULT ; //カウント値初期化
   
    MTU20.TSR.BIT.TCFV = 0; // OVFフラグクリア
} //MTU2_init(void)終了

<用語&解説>
カウンタが65535+1オーバーフローによるリセット動作が発生します。
1024回カウントしたらリセットさせたい場合は初期値を(65535-1024 → 64511)とすれば64511からカウントを開始し 64511、64512、64513・・・とカウントアップし65536にて1024回カウントアップしたところでオーバーフローが発生します。
この値を初期値PWM_DEFAULTとしてdefineしておきます。
PWM_DEFAULTから65535の間の値を比較用の設定値としてPWM_DEFAULT + xの様に設定とすることでPWM_DEFAULTを初期値としたPWM動作となります。

TOER:タイマアウトプットマスタイネーブルレジスタ。TIOC4A~4D、TIOC3B、TIOC3Dの出力許可を設定します。

TIOR:タイマIOコントロールレジスタ
PWMの動作設定を行います。
設定値とカウント値を比較してどのタイミングで端子から5Vを出力するかを設定します。
各端子ごとに設定値に対する動作が異なりますが、全端子共通で1を指定すると出力停止(初期値0、設定値を超えても0)になります。

TGRA~TGRD:カウンタのと比較する設定値を入力します。
TCNT:カウンタの初期値を入力します。

TSR.BIT.TCFV:タイマステータスレジスタのオーバフローフラグをリセットします。


その他機能作成

その他必要な機能を作成します。

タイマ動作制御用関数作成

タイマ動作を制御する関数を作っておきます。

// タイマ0~4シンクロスタート
void MTU2_syn_start(void){
//    MTU2.TCSYSTR.BYTE = 0xF8;    // タイマ0~4シンクロスタート
    MTU2.TCSYSTR.BYTE = 0x98;    // タイマ0,3,4シンクロスタート
}

// タイマ0~4停止
void MTU2_syn_stop(void){
//    MTU2.TSTR.BYTE = 0x00;        // タイマ0~4停止
    MTU2.TSTR.BYTE&=~0xC1;        // タイマ0,3,4停止
}

デューティー比設定用変数の準備

volatile short _pwm_duty[16]={0}; // PWMデューティー比設定値0-1023
volatile:コンパイラによる最適化を防ぎます。使用目的・・・・ごめんなさい。分かりません。

今回設定するPWMは10ポート分ですが最大値である16ポート分準備しておきます。
いくつかの関数で使いまわす変数なので、グローバル変数として設定します。
他で使用する変数と変数前が被ると予期せぬ動作をするため、特殊な扱いをする変数は変数名をアンダーバー「_」で始めます。これで他と変数前が被ることを防げます。

ついでにデューティー比設定用変数を初期化する関数も用意しておきます。

void pwm_duty_init(void)
{
    int i;
    for (i = 0; i < 16; i++) {
        _pwm_duty[i] = 0; //PWM出力を全て0に設定
    }
}


初期化関数のとりまとめ

ここまでで、以下の関数を作成しました。
・ポートの初期化関数
・PWMの初期化関数
・設定用変数の初期化関数
・タイマーのシンクロ開始関数
これらの関数はプログラム起動時に一連の流れとして実行します。
初期設定(下位層)を作成した私としてはこれらの関数の一つ一つが重要なのですが、これから上位層を作成する私としてはPWMの初期化さえ実行できれば一つ一つの関数がどの様に動いているのかはほとんど関係ありません関数を一つポンと実行すれば初期化が完了してくれるのが理想です。
そこでPWMに関する初期化をポンと実行するための関数を作成します。

//PWM初期化関数
void init_MTU(void){
    PWM_IO_init();
    MTU2_init();   
    pwm_duty_init(); // ServoPuls初期化
    MTU2_syn_start(); // タイマシンクロスタート
}

メインプログラムでこの関数を実行すればPWMの詳しい動作を知らなくても(忘れてしまっても)PWM関連の初期化が完了します。

初期設定はここまでです。
次回は割り込み関数の設定を行います。

関連:当ブログで扱うSH7125の特殊機能一覧
PR

コメント

プロフィール

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

最新コメント