PICのCCPによる PWM



PICマイコンのPWMの設定は結構面倒です。
PWMの設定方法を根本から学ぶにはマニュアルを
読むのが一番ですが、あいにく英語のマニュアルしか
有りません。

ここで仕方なしに英語マニュアルを翻訳して、自分なりの
解説を加えました。

これは PIC12F683のマニュアル のPWMの部分の日本語訳です。

11.3 PWMモード

PWMモードはCCP1ピンにPWM信号を発生させる。
デューティ・サイクル(Duty cycle)、周期(period)と分解能
(resolution)は次のレジスターで決められる。

・PR2
・T2CON
・CCPR1L
・CCP1CON

PWMモードにおいては、CCPモジュールはCCP1ピンに
10ビットの分解能のPWMを出力する。
CCP1ピンはポート・データーラッチと多重化されている為に
このピンのTRISはCCP1ピンが出力となるようにクリアー
されなければならない。

ノート: CCP1CONレジスターをクリアーするとCCP1ピンの
CCP1コントロールは解除される。

[解説]
 TRISはTRISIOレジスタのことで、このレジスターはGPIOを
 デジタル入力か出力に設定するためのものである。
 PIC12F683の場合CCPのポートはGP2であるため、
 TRISIO2 = 0 ; 
 と設定してポートをデジタル出力にする。

 CCP1CONレジスターの下位4ビットはCCP1のモード
 を決めます。
 CCP1CON = 0x0c;
 とするとCCP1ピンはPWMモードにセットされ
 CCP1CON = 0;
 とするとPWMモードが解除されます。


   図 11-1に単純化されたPWMのブロック図を示します。
図 11-4に典型的なPWMの波形を示します。

PWMの為のCCPモジュールの一歩一歩のセットアップ
方法は11.3.7のセクション「PWMオペレーションの
セットアップ」を見られたし。

図 11-3 簡略化された PWMのブロック図
PICのCCPによる PWM

Note
1:8ビットのタイマーであるTMR2レジスターは、
  10ビットのタイムベースを作る為に、2ビットの内部クロック(Fosc)
  又は2ビットのプリスケーラーに結合される。
2:PWMモードに於いてはCCPR1Hは読み込み専用レジスタである。

PWMのアウトプット(図 11-4)はタイムベース(周期)と
出力がハイ(Dutyサイクル)になる時間を有する。

PICのCCPによる PWM


[解説]
TMR2は8ビットのインクリメントカウンターで、通常は
システムクロック(Fosc/4)をプリスケーラ値で割った
クロックをカウントしますが、PWMモードの時は、
下位に2ビット足して、Foscを4で割り更にプリスケーラ
値で割ったクロックを10ビットのカウンタでカウントします。

例えば、PR2に255をセットすると、Timer2は
通常の場合255をカウントするとコンパレータ―がオンしますが
PWMモードの時は、
1024でコンパレータ―がONします。
なぜならコンパレーターはPR2のセット値とT10ビットに
拡張されたTMR2の上位8ビットを比較する為です。

この辺は深く考える必要は有りませんが、PR2にセット出来る最大の
値は8ビットなので255ですが、この時PWMの分解能は10ビットの
で1024になります。


Dutyセット サンプルコード
void set_duty(unsigned int duty){
    //上位8ビットを CCPR1Lにセット
    CCPR1L =  duty >> 2;  
    //下位2ビットをCCP1CONの4、5ビットセットする
    CCP1CON |= (duty & 0x0003) << 4; 
}

11.3.1 PWMの周期

PWMの周期はTimer2のPR2レジスターに設定される。
PWMの周期は次の式で計算される。
PICのCCPによる PWM
TMR2がPR2と同じになった時、次のアップカウント・サイクルで
3つのイベントが発生する。
・TMR2がクリアーされる
・CCP1ピンがセットされる(例外:PWMのDutyが0の
 場合はこのピンはセットされない)
・PWMのDutyサイクルがCCPR1LからCCP1Hに
 ラッチされる。

Note:
Timer2のポスト・スケーラー(セクション7.0の「Timer2モジュール」
を参照のこと)はPWMの周波数の設定には使われない。



[解説]
ここで注意すべきはToscはPICのクロックの周期だと言うこと。
たとえば8MHzのクロックの場合
Tosc = 0.125 x 10^(-6) となる。




11.3.2 PWM DUTY サイクル

PWMのDutyサイクルはCCPR1LレジスタとCCP1CONレジスタの
DC1B<1:0>の複合レジスタに10ビットのデーターを書き込むことにより
指定される。
CCPL1Lは8ビットのMSbsとCCP1CONレジスタのDC1B<1:0>の
LSbs2ビットを有する。
CCP1LとCCP1CONレジスタのDC1B<1:0>は好きな時に書き込む
事が可能である。
PWMのDutyはPWMのサイクルが終了した時に初めて、CCPR1Hに
書き込まれる(たとえばPR2とTMR2のレジスターが同じになった時)。
PWMモードの時はCCPR1Hレジスターは読み込み専用となる。

PWMのパルス幅は11-2の式で計算され、
PICのCCPによる PWM
PWMのDutyは11-3の式で計算される。
PICのCCPによる PWM
CCPR1Hレジスターと2ビットの内部ラッチはPWMのDutyの
ダブルバッファとして使われる。
ダブルバッファリングは基本的にはPWM操作時のグリッジを
防ぐためのものである。

8ビットタイマーであるTMR2レジスターは、2ビットのシステムクロック
Fosc又は、2ビットのプリスケーラーと複合され、10ビットのタイマーとなる。

10ビットのタイムベースがCCPR1Hと2ビットラッチと一致した時、
CCP1ピンはクリアーされる(図 11-1を参照).


[解説]
dutyサイクルをCCPR1Lに設定してしかもこれをCCPR1Hに
コピーするのは、PWM周期の途中でDutyの設定が書き換えられても、
Dutyの設定が直ぐに変わらず、PWMの周期の始まりで、CCPR1Lの
値がCCPR1Hにコピーされて初めてDutyが変えられる為に、
PWMが乱れないようにしているのである。

これによりユーザーはPWMの繋ぎ目を気にすることなく、Dutyを
変えられるのである。

11.3.3 PWM 分解能

分解能は与えられた周期に幾つのDutyサイクルが可能かで
決定される。
たとえば、10ビットの分解能は1024の別々のDutyサイクルを
得ることが出来るが、8ビットの分解能は256のDuytsサイクル
を得る。

分解能の最大はPR2が255のとき、10ビットである。
分解能はPR2レジスター値の関数で11-4式であらわされる。

PICのCCPによる PWM
PICのCCPによる PWM
PICのCCPによる PWM

Note:
 仮にパルス幅が周期より広い場合は、PWMのピンは
 変化しない。


[解説]
PWMの分解能はPWMの周期の中をいくつに分解できるかと
言うことであり、それはPR2の設定値で決まると言うことです。
PR2は下位に2ビット追加されていますので、4倍になります。
すなわちPR2を10に設定すると分解能は40になります。


11.3.4 スリープモード中の操作

スリープモード中はTMR2レジスタはインクリメントしません。
そしてモジュールの状態も変化しません。
もしCCP1のピンに出力がある場合、そのまま出力し続けます。
そのデバイスが起き上がった(ウエイク・アップ)した時は、
TMR2は前の状態を保持します。

11.3.5 システムクロックが変化した時


PWMの周波数はシステムクロックにより供給されます。
システムクロックの辺かはPWMの周波数に変化を与えます。
セクション3.0
"Oscillator Module (With Fail-SafeClock Monitor)"
の追加情報を参照のこと。

11.2.6 リセットの効果

リセットは全てのポートを強制的に入力モードにし、
CCPレジスタをリセットの状態にします。



11.3.7 PWMオペレーションのセットアップ

PWMのオぺーレーションには次の手順を実行します。
1、関連したTRISビットをセットすることで、PWM pin(CCP1)
の出力ドライバーを無効にします。

2、PR2レジスターをセットしてPWMの周期をセットします。

3、CCP1CONレジスタに適当な値を入れて、CCPモジュールを
PWMモードにします。

4、CCPR1LレジスターとCCP1CONレジスタのDC1Bビットを
設定してDutyサイクルを設定します。

5、Timer2のスタート
  ・PIR1レジスタのTMR2IF割り込みフラグをクリアーします。
  ・T2CONレジスタのT2CKPSビットにTimer2のプリスケール
  値を設定します。
  ・T2CONレジスタのTM2ONビットをセットしてTimer2を起動します。

6、PWMの新しいサイクルが始まったら、PWMの出力を許可します。
  ・Timer2がオーバーフローするのを待ちます。
   (PIR1レジスターのTMR2IFビットをセットします)
  ・関連したTRISビットをクリヤーして、CCP1ピンの出力を
   有効にします。

[解説]
この手順によると、最初はTRISレジスタで、PWMの出力端子を
入力にしておいて、全ての設定が終わった状態で、しかもTimer2が
オーバーフローした段階で初めてPWMの端子を出力にしている。

これはLEDの照度調整などの場合は厳格なプロシジャーは必要ないが、
モーターの制御などでは、ほんの少しのタイミングのずれが大きな
事故になるからであろう。

いずれにしても、基本的にはこのプロシジャーに従うべきである。





簡単な実験回路とソースコード

さて上のマニュアルに従って最も簡単な回路、
ボリュームの値を読み取ってその値でLEDの輝度を
PWMで変える回路です。

AN0(GP0)に0~5Vのボリュームの電圧がかかります。
それをA/DのAN0で読み込み、その読み込み値を
DutyにしてPWMを行い、LEDを駆動します。

クロックは8M、TMR2のプリスケーラーは1を設定、
PR2 = 255 として PWMの分解能は10ビット1024となります。
PWMの周期は、
(255 + 1) x 0.125μSec x 4 = 128 μSec =7.81Khz
となります。
もちろんLED駆動でこんな高い分解能と周波数は必要
有りませんが、あくまでも実験です。
(周波数と分解能が高くていけないと言うことは無く、
これでもきちんとLEDの調光は出来ました。)



回路図です。


バラックで組み立てた実験サーキットです。


PWMのオシロスコープ波形です。




以下この実験に使用したコードを載せます。


#define _LEGACY_HEADERS
#include 
#define _XTAL_FREQ 8000000      //8MHz

__CONFIG(WDTDIS & PWRTEN & BOREN & INTIO & 
         MCLRDIS & IESODIS & FCMDIS & UNPROTECT);

void ini_set();
unsigned int adc_read(unsigned char ch);
void pwm_start();
void set_duty(unsigned int duty);

/***********************************
      初期設定
***********************************/
void ini_set(){
    GIE = 1;    //全体割り込みの許可
    PEIE = 1;   //周辺割り込みの許可

    OSCCON = 0b01110000; //内蔵発振器 8MHz使用に設定
    
    //以下2行はA/Dの読み込みでPWMの設定とは無関係です
    ANSEL  = 0b01011000; //AN3をアナログに FOSC /16
    ADCON0 = 0b10000000; //AD分周比 1/16
    
    //PWMの出力はGP2ですがこの段階では1の入力にしてあります   
    TRISIO = 0b00010101; //GP0,4,2 を 入力 = 1
}

void main() {
    unsigned int value;  //A/D 読値 10ビット
    ini_set();
    pwm_start();
    while(1){
        value = adc_read(3); // A/Dの読み込み
        set_duty(value);     //PWMセット
    }
}

/**************************************
    内臓PWMのDutyの設定
      DutyはA/Dの読み値そのままで
   10ビットで設定されます
***************************************/
void set_duty(unsigned int duty){
    //上位8ビットを CCPR1Lにセット
    CCPR1L =  duty >> 2;  
    //下位2ビットをCCP1CONの4、5ビットセットする
    CCP1CON = (duty << 4) | 0x0C;
}

/***************************************
    PWMのスタート
***************************************/
void pwm_start(){
    //Timer2を使った、組み込みのPWMのスタート
    CCP1CON |= 0b00001100; //PWMモードにする 
    PR2 = 255 ;
    T2CON =0b00000100;  //プリスケールを1に設定
    
    //Timer2がカウントアップするのを待っています
    while(!TMR2IF)
           ;
    //ここでようやくPWMの端子を出力に設定します
    TRISIO2 = 0;
    //TRISIO = 0b00010001;
}

/*****************************************
   A/Dコンバーターの読み込み 10ビットで返す
*****************************************/
unsigned int adc_read(unsigned char ch){
    //右詰め、電源基準、A/D有効化 + チャンネルセレクト
    ADCON0 = 0b10000001 + (ch << 2);  
    __delay_us(20);
    GODONE = 1;
    while(GODONE);
    return (ADRESH * 256 + ADRESL) ;
}




同じカテゴリー(PIC マイコン)の記事画像
買うぞ、nexus 7
PICマイコンPWM Duty設定の方法
メタルなやつら
PIC PWMによるLEDの駆動実験
PICマイコンによる簡単なPWM
同じカテゴリー(PIC マイコン)の記事
 買うぞ、nexus 7 (2012-12-12 08:02)
 PICマイコンPWM Duty設定の方法 (2012-11-17 00:00)
 メタルなやつら (2012-11-16 07:43)
 PIC PWMによるLEDの駆動実験 (2012-11-16 00:00)
 PICマイコンによる簡単なPWM (2012-11-12 00:00)
 ヘッダーの仕様が変わった2 (2012-07-10 23:57)

2012年11月15日 Posted byigoten at 00:00 │Comments(0)PIC マイコン

 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。