PWMって
Pulse Width Modulationの略でパルス幅で変調するんだけど、パルス幅はいいとして変調ってなんだを調べると長くなりそうなんで割愛してと。
簡単には「決まった周期でパルスの幅を変える」と。
PWMのパルスの幅でLEDを点灯させ、それ以外は消灯と明滅を繰り返させれば人間の目の残像効果でLEDは常に光っているように見える、このパルス幅を動的に変化させて幅が短ければ暗く、長いと明るく感じてしまうんです。
今回はPICのPWMモジュールを使わないでソフトでPWMの実装をしてみよう。
最初に決める事は、「決まった周期」と「パルス幅の分解能」。
分解能と使っているけど消灯とフル発光を何段階にするかで、PWM制御で「デューティー比」という言葉を使ってパルス幅をあらわしててオフが0%で最大が100%。この0%から100%をどの位で分解するかです。
「決まった周期」おおむね50から60Hz以上なら常に点灯しているように見えるらしい、「分解能」はデューティー比のパーセントをそのまま使って100にする。
周期を60Hz、分解能を100と決めタイマー割り込みの間隔を決めれることにする。PICの動作クロックを4MHzに決めていたから、これからタイマーの定数をまず計算。
割り込み間隔は
1/60Hz / 100 = 16.66mSec / 100 = 166.666μSec
Timer0から166.666μSec間隔で割り込みを発生させればよい、今回は内部クロックを4Mhzで使用しているからTimer0のクロック源は内部クロックの1/4となり1Mhzが供給される。プリスケーラを1:16に設定すると。
1MHz / 16 = 16μSec
Timer0は16μSecでカウントアップするから
166.666μSec / 16μSec = 10.375
端数がでちゃったから10としよう。タイマー0はアップカウントなので、設定した値からカウントし始めて255(0xFF)から0になったところで割り込みが発生する、10カウントして欲しいからカウンターには246を設定すればよいことになる。
256 - 10 = 246
最初はプリスケーラを1:2にしカウント値を計算し実装したのだが計算値と巧く合わず周期が違っていた、原因はわからないがプリスケーラ値は出来るだけ大きめの方がよいのかも知れない。
これで
- 内部クロック4MHz
- プリスケーラ1:16
- カウント値246
割り込みルーチンの実装は
if (T0IF && T0IE) { T0IF = 0; TMR0 = 246; if (kPeriod == 0) kPeriod = 100; LEDPin = (kPeriod <= Duty) ? 1 : 0; kPeriod--; }
kPeriodは分解能が100なんで1Cycle(16.66ms)をカウントする変数。