PWMの設定方法



<概要>

 PICのPWMのユニットの存在は、様々な用途に有用である。が・・・設定方法がちと難しい。下の図に示すように、ようは出力周波数(1/周期)と、デューティサイクルの長さを指定すればよい。



 具体的には、下の図の赤箱のレジスター値を適切に設定することになる。周期(1/周波数)はタイマー2のプリスケラーの値とPR2の値から決まる。デューティはプリスケラーの値とデューティサイクルレジスター(実際にはCCPExLの8ビットと、CCPxCONのビット5と4のトータル10ビット)で決まる。例えば、モーターなどの出力を可変させる場合は、周期は固定して、デューティサイクルを可変させて使うことになる。



 試しに、いくつか条件を設定して、周期とデューティサイクルの値を計算してみる。まずは、GMカウンターに使っているクリスタル2.097152MHzを使った場合である。PR2を255に固定した場合、その周波数は、プリスケラーの値で4通りに変えられる。例えば、プリスケラーの値を1:16に設定すると、周波数は128Hzとなる。本ホームページのポケット型GMカウンターでは、この値を使っている。ちなみにプリスケラーの値を1:1に設定すると、周波数は2.048kHzとなる。



 なお、PR2の値で周波数は細かく可変できるものの、PR2の設定値を255以外にとると、なんとデューティサイクルの分解能が落ちてしまう。ここは、設計時に分解能と欲しい周波数との関係を考えておかなければならない点である。ちなみに、DC1(実際にはCCPExLの8ビットと、CCPxCONのビット5と4のトータル10ビット)を0,1,2・・・とかえると、デューティサイクルは0,7.6,15.3μsecとなる。本ホームページのポケット型GMカウンターでは、DC1の値を1にセットして、高圧電圧の発生のためのパルス幅を約7.6μsecにとっている。この値がむやみに長いと、むちゃくちゃ効率が落ちるので、オシロとにらめっこして適性値を決める。


 クリスタルに4MHzを使った場合の表を示す。この場合、最小周波数は244Hzとなる。逆にデューティサイクルの最小値は4μsecと小さくなるわけだ。



 なお、PWMの設置方法は、文献1にわかり易く書かれている。また、資料2も熟読した方がよい。

<実例>

 次に設定実例を示す。これはFED-Cでのプログラム例である。まずは初期化から行ってみる。ここでは、2つあるPWMユニットの1番目を使うことを前提にする。まずは、関連レジスターを見てみよう。下の表の中で、PR2、T2CON、CCP1CON、CCPR1Lを設定する必要がある。



まず、RC2/CCP1(PICの13ピン)が出力に設定されていなければならない。
これは、例えば次のようにする。CCP1の出力は、RC2と共通なので、Cポートを全部出力に設定してしまおう。

  TRISC = 0x00 ;		// Port C OOOO OOOO

これで、Cポートは出力に設定された。次は「PWMを使いますよー」という宣言をする必要がある。これは、CCP1CONレジスターで行う。CCP1CONレジスターの設定は、下の表のように行う。CCP1CONレジスターの中の、ビット5と4は、デューティサイクルを指定するために使う。これは後で説明する。PWMを使うには、ビット2と3を1にセットする必要がある。


実際には、次のようになる。

  CCP1CON = 0x0c ;		// CCP1 is PWM mode

さて、これでPWMのユニット1が使えるようになったのだ。クリスタル2.097152MHzを使って128Hzを発生するには、上の表から、PR2をoxff、タイマー2のプリスケラーを1:16に設定する。タイマー2のレジスターは下の表のように設定する。同時にビット2に1をセットしてタイマー2をONにしている。


  PR2 = 0xff 	;		//
  T2CON = 0x07 ;		// PoS = 1:1, PreS = 1:16, ON


さて、これでPWMモジュールは動作をはじめている。あとは、CCP1CON、CCPR1Lにデューティサイクルを指定するのだ。10ビットのデータを8ビットのCCPR1Lと、CCP1CON中の2ビットに分けて設定する必要がある。いちいちこんなことやっていると面倒なので、設定する専用のサブルーチンを書くと次のようになる。dutyには0から1023の値が入る。すると、CCP1CON、CCPR1Lに値を設定してくれるわけである。ちなみに、PWMのユニット2を使うなら、リスト中のCCPR1LとCCP1CONをCCPR2LとCCP2CONに書き換えればよい。

//--- PWM subroutine --------------------------------------------//
//
void set_duty(unsigned int duty)
{
unsigned int temp1 ;
unsigned char temp2 ;

  temp1 = duty ;
  temp1 >>= 2 ;
  temp2 = temp1 & 0xff;
  CCPR1L = temp2 ;

  temp1 = duty ;
  temp1 <<= 4 ;
  temp2 = temp1 | 0x0c ;
  CCP1CON = temp2 ;
}
//


 だいたい、こんな感じである。

<文献>
1. 後閑哲也、PIC活用ガイドブック
2. PIC16F877のPDF説明資料(マイクロチップス社のホームページのもの)



戻る