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サンプルプログラム解説を参照。
PECRL1、
PECRL3、
PECRL4を1端子ずつ設定しています。
PECRLの設定内容は
ハードウェアマニュアルの
ポートE コントロールレジスタに記載されています。
端子ごとに微妙に内容が異なりますが、TIOCに関しては
PE*MDを
0 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は知りません。
MTU20~MTU24がありそれぞれTIOC
0~TIOC
4に対応しています。
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のT
CI系(オーバーフロー、アンダーフロー)の優先順位を設定します。
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-1023volatile:コンパイラによる最適化を防ぎます。使用目的・・・・
ごめんなさい。分かりません。今回設定する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の特殊機能一覧