■PWM (Pulse Width Modulation) とは
PICはプログラムの実行とは独立して、パルス波形を出力することが出来ます。つまり、プログラムでどんなに時間がかかった処理をしていても、PWMを使えば常時波形を出力し続けることが出来るのです。
緑色の矢印で示した部分と周期Tで示した部分の比をデューティ比といいます。緑が赤のちょうど半分の時にデューティ比が50%(または0.5)と呼ばれます。
PICは周期とデューティ比を変化させることが出来ますが、ほとんどがデューティ比の変更用にPWMが使われます。緑の部分は電気を流している時間を表しているため、この部分が大きければ(デューティ比が1に近づくほど)、LEDは明るく光り、DCモーターは早く回転するでしょう。
■デューティ比を変えてみる
0x1a
0x41
0x5e
0x80
これは実際にPICから出力したPWMの波形です。右へいくほどデューティ比が大きくなっていきます。下に書かれた数値はデューティ比設定レジスタに書き込んだ値です。(あとから説明します)
■PWMを使うための必要最小限のプログラム
//
// PWM test
// Version 1.0
#include <16F88.h>
#fuses INTRC_IO, NOLVP, NOWDT, PUT, NOPROTECT, NOBROWNOUT, NOMCLR,CCPB3
#use FAST_IO(B)
#use FAST_IO(A)
#use delay (clock=4000000) //内部クロックを使用した4MHzモード
main(){
set_tris_b(0);
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_16,0x90,1);
set_pwm1_duty(50);
while(1){
}
}必要な関数は setup_ccp1 , setup_timer2 , set_pwm1_duty です。
setup_ccp1(CCP_PWM)
PICはCompare/Capture/PWM の3つの機能をCCP1に割り当てています。setup_ccp1 で CCP1 をPWMとして使うことを指定します。
setup_timer_2(プリスケール値,period,1)
PWM波形の周期を設定します。周期はPICの動作クロック、プリスケール値、periodの3つの値によって変化し、次の式で求めることが出来ます。最後の1はポストスケール値ですが、PWMの場合は使われない値ですので何を書いても問題ありません(無視されます)。しかし0を書くとエラーになりますので1を書いておいてください。
周期=(period+1)×4÷(PICクロック)×プリスケール値
上の波形は 4MHz , period=0x90 , プリスケール16 なので、(0x90+1)×4÷(4×106)×16=0.0023[s] , 1/0.0023=434[Hz] とになります。テスタは423.5[Hz]と表示していますので、だいたい合っていると思います。
![]()
PICを8MHz駆動,プリスケール値16に設定した時の period と周波数の関係プリスケール値には下記の4つの定数を設定できます。
T2_DISABLED : タイマー2 を使用しない
T2_DIV_BY_1 : プリスケーラ設定 1/1
T2_DIV_BY_4 : 〃 1/4
T2_DIV_BY_16 : 〃 1/16
periodは8ビットの数値/変数を設定します。実質、この値で周期を設定すると思えばいいでしょう。
set_pwm1_duty
デューティを設定します。0x3FF(10ビット分)までの値を取ることが出来ます。一般的なデューティ比50%の波形を出力する場合には0x200をセットします。#fuses
16F628/648/819などはCCP1端子がRB3に割り当てられているのですが、PIC16F88はCCP1をRB0またはRB3のどちらかへ割り当てることが出来ます。この指定を#fusesで行うのですが、何も設定しないときはRB0がCCP1になりますので注意してください。RB3をCCP1にする時は #fusesに CCPB3 を書き加えます。
■PWMで音階を!
それではPWMを使ってドレミファソラシドと演奏させてみます。プログラムは次のとおりです。
//
// PWM test
// Version 1.1
#include <16F88.h>
#fuses INTRC_IO, NOLVP, NOWDT, PUT, NOPROTECT, NOBROWNOUT, NOMCLR,CCPB3
#use FAST_IO(B)
#use FAST_IO(A)
#use delay (clock=8000000)
main(){
long duty=0x150;
set_tris_b(0);
setup_ccp1(CCP_PWM);
set_pwm1_duty(duty);
while(1){
setup_timer_2(T2_DIV_BY_16,238,1); //C
delay_ms(3000);
setup_timer_2(T2_DIV_BY_16,212,1); //D
delay_ms(3000);
setup_timer_2(T2_DIV_BY_16,189,1); //E
delay_ms(3000);
setup_timer_2(T2_DIV_BY_16,178,1); //F
delay_ms(3000);
setup_timer_2(T2_DIV_BY_16,159,1); //G
delay_ms(3000);
setup_timer_2(T2_DIV_BY_16,142,1); //A
delay_ms(3000);
setup_timer_2(T2_DIV_BY_16,130,1); //B
delay_ms(3000);
setup_timer_2(T2_DIV_BY_16,119,1); //C
delay_ms(3000);
}
}このようにsetup_timer_2を使って周期を変更するだけでOKです。
YouTubeのビデオで周期を変化させると音階が変化するのが確認できます。ただし、8MHzの内蔵発振器を使用しているためPWMの周期が早くなっています。そのため音階が高く、デジカメのマイクで拾い切れていないようです・・・。ボリュームを少し大きめにして再生してください。


