PWMをマスターする

PWMとは?

 PWMとは、Pulse Width Modulationの略で、パルス幅変調という意味です。わけわかりませんね。何ができるかというと、矩形波のH区間とL区間の比率(=デューティ比)を変化させることができます。

PWMの例

 PWMの出力波形です。上の図は普通の矩形波ですが、H区間とL区間が1:1で、デューティ比50%ということができますね。下は、デューティ比を変えてみました。H区間とL区間が3:7で、デューティ比30%です。

 さて、このPWMを実際どう使うかといいますと、例えばこんなことができます。

とまぁ使い方はアナタ次第!?という感じでしょうか。電子工作的には、LEDの明るさをアナログ的に変化させるとか、そういう使い方が多いのかもしれませんね。擬似カーセキュリティなどすぐに作れます。

使い方を調べる

PWMモジュールの機能をON

 まずは、PWMモジュールを使える状態にしましょう。mikroCにはPWM制御用の関数が用意されているのでそれを使うのが最も早いのですが、せっかくなので動作を理解して自分で制御してみましょう。PIC16F628Aのデータシートを読んでみます。PWMは、「CCPモジュール」の中の一機能で、CCP1CONレジスタのCCP1M<3:0>ビットによってCCPモジュールのどの機能を使うかを選ぶことができます。PWMモードにするには、CCP1M<3:0>="11xx"と設定します。

CCP1CONレジスタ
--CCP1XCCP1YCCP1M3CCP1M2CCP1M1CCP1M0

 おっとその前に、RB3/CCP1ピンを出力モードにする必要があります。これをしておかないと、内部で作られたPWMの波形が外に出てきませんね。TRISBレジスタのTRISB1を0にします。

TRISBレジスタ
TRISB7TRISB6TRISB5TRISB4TRISB3TRISB2TRISB1TRISB0

ベースのクロック

 今使っているPIC16F628Aでは、PWMの制御のためにTimer2という内部で持っているタイマーを使います。Timer2へは、PICのメインクロックFoscの1/4のクロックが供給され、このサイクルに合わせてTMR2レジスタの値をカウントアップしていきます。つまり、Foscが4クロック分でTMR2の値が1アップするわけですね。ちなみに、PIC16F628Aですと内蔵発振器を使うとFosc=4MHzなどが使えますね。

 「4クロック分で1アップ」と言いましたけど、実はそうじゃないときもあります。メインクロックFoscの1/4になったTimer2クロックは、さらにTMR2プリスケーラを通過します。ここで、そのまま、1/4、1/16のいずれかの分周比でクロックダウンしています。この値によって、Foscの4×1または4×4または4×16クロック分でTMR2の値をひとつアップさせるのです。つまり、TMR2の値がアップするまでの時間を1倍、4倍、16倍にすることができるってわけです。

 このTMR2プリスケーラの値は、T2CONレジスタのT2CKPS<1:0>ビットによって変更します。

T2CONレジスタ
-TOUTPS3TOUTPS2TOUTPS1TOUTPS0TMR2ONT2CKPS1T2CKPS0

 T2CKPS<1:0>ビットの値とTMR2プリスケーラの値の関係は次の通りです。データシートの「8.0 Timer2 Module」に詳しく書いてあります。

 おっと、Timer2をONしておかなければなりません。これは同レジスタのTMR2ONレジスタによって行います。

T2CONレジスタ
-TOUTPS3TOUTPS2TOUTPS1TOUTPS0TMR2ONT2CKPS1T2CKPS0

サイクル

 さて、メインクロックがぱたぱたとHレベルLレベルを繰り返すうちに、0から始まったTMR2は徐々にカウントアップしていきます。TMR2の値がPR2レジスタに設定した値まで達すると0にクリアされ、次のサイクルが始まります。つまり、これがPWMピリオドとなるわけです。

 データシートにも書いてありますが、次の式で表すことができます。

 この中で、Toscはメインクロックサイクルで、Tosc = 1 / Foscで表すことができます。「× 4」とありますが、デューティサイクルが10ビットなのに対してPWMピリオドが8ビットなので2ビットシフトしているものだと考えてもいいと思います。データシートの「9.3.2 PWM DUTY CYCLE」のところに2-bitがどうとかこうとか書いてありますが・・・。

デューティサイクル

 PWMピリオドが決まったら、次はデューティサイクル、つまり信号がH区間の期間を設定します。PWMの出力信号は(デューティ比が0%のときを除いて)Hレベルでスタートします。そしてTMR2がカウントアップしていき、デューティサイクルとして設定した値に到達するとPWM出力信号をLレベルにします。そしてTMR2PR2に達するとTMR2は0にクリアされ、PWM出力信号は再びHレベルになるというわけです。

 こちらは10ビット分、つまり0〜1023の値を設定することができます。そのため、2つのレジスタに分けて設定します。その10ビットのうち、上位8ビットをCCPR1Lレジスタに、下位2ビットをCCP1CONレジスタのCCP1XCCP1Yレジスタに設定します。書くのはCCPR1Lですが、実際に使われるのはそこからコピーされたCCPR1Hレジスタです。タイミングを計ってCCPR1Hに書き込むことによって、PWMの1周期の途中でデューティが変わってしまわないようになっています。

 そして注意すべきは、デューティサイクルがPWMピリオドより大きかった場合デューティ比100%となり常にHレベルとなります。また、デューティ比が0%のときはTMR2が0にクリアしても常にLレベルになります。

 以上の式から、デューティ比を求める式を作っておきましょう。

PR2をPWM周波数をもとに設定するには

 各レジスタを設定したあとではじめてPWM周波数がわかるようではちょっと使い勝手が悪いですね。狙った周波数で設定できるようにしたいものです。

 PWMピリオドを求めた(1)の式を変換してみましょう。

 PR2の値を求める式はできましたが、TMR2プリスケール値の決め方がよくわかりませんね。まず先にこちらを求める必要があるようです。というわけで、(4)の式をとりだしてきます。

 TMR2プリスケール値は3種類しか選べませんので、代入してしまいます。ついでに、PR2には最大値255を代入します。

 同様に、

 右辺の分母であるPR2に最大値を入れたので、FPWMはそれぞれこれ以下の値をとることができません。ということで、それぞれの値が設定したい周波数の下限値となります。例えばFosc=4MHzだとすると、

 つまり、244Hz未満は設定不可、244Hz以上であればTMR2プリスケール値は16、976Hz以上であれば4、3906Hz以上であれば1を選択します。ちょっとここでPR2に仮に0の値を設定して計算してみますと、

 これが各TMR2プリスケール値でのPWM周波数の最大値ということになります。しかし、PR2を小さくすることは、デューティサイクルの選択肢(=分解能)を狭めることになりますので、PR2の値をできるだけ大きく取れるようにTMR2プリスケール値を選択しましょう。

 例えばFPWMを1000Hzに設定したいとします。TMR2プリスケール値は4でも16でも近い値を取ることができますね。しかし、4にすると(5)式よりPR2は249に、16にすると同じく62となります。このとき、デューティサイクルのMAX値はデューティ比が100%になる値ですから、(3)式よりそれぞれ1000、250です。つまりそれぞれ1000段階、250段階の分解能を持つPWM、ということになります。というわけで、(用途にもよりますが、)(6)〜(8)で得られた値をPWM周波数の下限値としてTMR2プリスケール値を選択するのがいいでしょう。

 このことは、ここでは割愛しますが、データシートでも「PWM Resolution」の式で表されています。

デューティサイクルをデューティ比で設定するには

 デューティ比はデューティサイクルで決めるのですが、PWMピリオドが変わるとデューティサイクルは再度計算しなおさなくてはなりません。PWMの場合(これまた用途によりますが)大事なのはデューティ比であって、サイクルではありません。というわけで、デューティ比をもとに、デューティサイクルを計算できるようにしましょう。

 これを、デューティサイクルを求める式に変換します。

 余談ですが、こういう計算をさせる関数を作って、USARTを使って変更しながら動作を確認しようとしていましたところ、どうもうまくいきませんでした。結局、計算を細切れにして、順番を入れ替えたりして桁あふれがないようにするとうまくいきました。PICのようなマイコンでは大きい値の計算は大変なので、パラメータはできるだけ決め打ちするべきなんでしょうね。

PIC PICの一年生