PICのPWMを使ってブザーを鳴らす

PWMを利用してブザーを鳴らすプログラムを組んでみた。例によってこれだけでは何も役にはたちません。:)
回路は、こちらと同じ。
使用したデバイスはPIC16F88。まあ、PWMがついていればどれでもいいですが、最近のECCPをつんだ物ではコンフィグが少し異なると思います。ECCPについては、また後日実験するつもりです。

アルゴリズム

普通なら半周期の待ち時間をループを用いて作って、ON/OFFを切り替えることで任意の周波数の交流信号を作るところですが、この部分をPWMにやらせることで、少しプログラムが簡単になります。
初めにPWMのセットアップをしておき、音を鳴らしたいときだけPWMをenableにし、鳴らしたくないときはRB3としてセットアップしhiに固定しています。
20msecブザーを鳴らして、1秒待つ動作の繰り返しです。周波数は1Khz程度になるようにしています。 おまけで、音が鳴っているとき、PortAに接続されたLEDを点灯させています。

PICの設定

RB3にBuzzer, RA0にLEDを接続していますので、この2つは出力に設定します。面倒なので全ポート出力にしてしまっています。
クロックは内部発振で8MHzにしています。
次にPWMの設定ですが、これはTMR2(タイマー2)の値で周期(period)とデューティー(onの期間)が決まります。
periodはPR2レジスタに、dutyはCCPR1Lレジスタにそれぞれ設定します。 計算方法はデータシートに詳細がありますが、この例ではカウンタがオーバーフローするのでプリスケーラーを利用します。
クロック8MHz(周期0.125usec)ですので、周波数を1khzにするためにはプリスケーラーを利用して1/16に分周し、
period=1000usec=(PR2+1)*0.125*4*16
PR2=0x7Dとなります。
dutyは50%で固定するので、CCPR1L=0x3Eとなります。(実際はdutyはCCP1CON<5:4>を用いて分解能10bitで表現可能ですがここでは使っていません。)
これらをふまえたアセンブラコードは次の通りです。
	BCF	INTCON,GIE	;Interrupt Disable
	BSF	STATUS,RP0	;Set page 1
	CLRF	TRISA
	CLRF	TRISB
	CLRF	ANSEL	; Analog input disable
	MOVLW	0x70	; set INTOSC to 8Mhz
	MOVWF	OSCCON
	MOVLW	0x7D
	MOVWF	PR2		; period 1 msec
	BCF	STATUS,RP0	;Set page 0
	MOVLW	0xff
	MOVWF	PORTA
	MOVWF	PORTB
	MOVLW	0x3e
	MOVWF	CCPR1L	; Duty cycle 50%
	MOVLW	0x06
	MOVWF	T2CON	; TMR2 on, pre scaler 16
ブザーを鳴らしたいときは、このようにし、
	MOVLW	0x0c
	MOVWF	CCP1CON	; CCP1 enable
とめたい時はこのようにすればよいです。
	CLRF	CCP1CON	; CCP1 disable
	BSF	PORTB,3
CCP1CONに11xxを設定するとPWMが有効になるのですが、はじめ1111(0xff)に設定したらエミュレーター上で動かなくて悩みました。
Microchip Technology社に問い合わせたところ、どうやらMPLAB IDE v7.0のバグのようで、v8.0にアップグレードしたら直りました。0x0cに設定している分にはバグにはヒットしません。
また、今回は必要なかったですが、dutyの設定値が有効になるのは、TMR2が0x0になったとき(つまり次の周期から)なので、直ちに反映させたい場合は次のようにしてカウンタをリセットしてしまうと良いです。
	MOVLW	0xff
	MOVWF	TMR2
ソースコードはこちら