第4回 PWM制御(3) 〜PWM機能を使う〜

今回はPICマイコンのPWM機能を使ってLEDの明るさを調整します。

PWM機能?

前回はLEDをPWM制御して明るさを調整しました。PWM制御ってプログラムで簡単に実現できることがわかりましたよね。

ところで、PIC12F1822には「PWM制御」機能が搭載されています。。。

って言われても、前回、プログラムを作ってPWM制御した身からすると、何をいってるんだろう、って感じですよね。

実は、PICマイコンの「PWM制御」は、ハードウエアで実現するPWM制御のことなんです。

プログラムで簡単にPWM制御できるのに、わざわざマイコン側でハードウエア機能として用意するのって、意味がよくわからないですよね。

そこで、PWM制御についてもう少し考えてみます。

前回作成したPWM制御プログラムの動作は、LEDを点灯して、ちょっと待って、LEDを消灯して、ちょっと待って、、、という処理を繰り返すことによりPWM制御を実現しました。このような方法でソフトウエアによりPWM制御する場合、状況によっては面倒なことが発生します。

例えば、前回の記事のプログラムで、PWM制御中に何か他の処理をしたい時はどうすればいいでしょうか。PWM制御中に何か他の処理をする、ということは、以下のプログラムのwhileループ内のどこかに処理を追加することになります。

// LED PWM制御
while(1){
    // LEDを0.5ms点灯
    LATA5 = 1;
    __delay_us(500);
    // LEDを4.5ms消灯
    LATA5 = 0;
    __delay_us(4500);            
}

このwhileループ内に、例えば処理に数msかかるようなプログラムを追加したい場合、どこに入れても周期やデューティーサイクルが変わってしまいますよね。変えないようにするには、追加する処理の時間分、__delay_us()関数の待ち時間を変えれば良さそうですが、追加したプログラムが状況に応じて処理時間が変わる場合は解決が難しそうです。

この問題をソフトウエアで解決するのは無理がありますので、PWM制御をハードウエアで実現してしまう方がよさそうです。ハードウエアでPWM制御を行うと、ソフトウエアではPWMのパラメータ(周期やデューティーサイクルなど)を設定するだけで、あとはハードウエアが勝手にPWM信号を生成してくれます。このハードウエアで実現するPWM制御が、PIC12F1822によるPWM制御になります。

今回の記事では、PIC12F1822のPWM制御機能を使って、LEDの明るさを変えてみます。

なお、このようにPWM制御をハードウエアで実現する意味は大きいため、PICマイコンに限らず、他のマイコンやRaspberry PiなどのワンボードPCなどでもハードウエアPWM制御機能が実装されています。

 

PICマイコンのPWM機能の概要

ところで、PICマイコンのPWM制御機能を使うのは、かなり大変です。理解してしまえば大したことはないのですが、初めて使うときは、嫌がらせではないか、と思われるほどいろいろな用語や設定が出てきます。

そこで、詳しい説明をする前に、PICマイコンのPWM制御手順の概要を説明し、その手順と対応づける形で完成したプログラムを見てみます。制御手順の概要と完成したプログラムを確認した後、各項目を細かく説明し、補足が必要な場合は寄り道して補足する、という方法で説明を進めたいと思います。多分長い説明になると思いますので、自分がどこにいるのか見失わないように読み進めてください。

まずこのセクションでは、PICマイコンのPWM制御手順の概要を説明します。

ハードウエアによるPWM制御は、PICマイコンのどのピンでも使える、というわけではありません。PWMをハードウエアで実現するためにはPWM制御モジュールを用意する必要がありますが、全てのピンにPWM制御モジュールを実装するのはコストがかかります。

そのため、ハードウエアによるPWM制御は、PICマイコンごとにPWM制御モジュール数と割り当てピンに制限があります。

例えばPIC12F1822では、PWM制御モジュールの数は1個、また、割り当て可能ピンはRA2ピンかRA5ピンのどちらかになります。例えばRA0ピンとRA1ピンの両方を独立したPWM制御ピンとして使う、ということはできません。

PWM機能の割り当てピンは、特定のレジスタを設定することにより行います。似たような例では、基礎編でクロック周波数を1MHzに設定しましたが、その設定はOSCCONレジスタを使って「OSCCON = 0b01011010;」と書きました。このような感じで、特定のレジスタに設定値を代入することによりPWM機能をRA2ピンに割り当てるか、RA5ピンに割り当てるかの設定を行います。

PWM機能のピン割り当てを行なったら、次に、PWM信号の周期とデューティサイクルを指定します。この指定は、クロック周波数を元にして、「周期はクロック何個分」「デューティサイクルはクロック何個分」という形で行います。

Pic app 4 specifying cycle duty

これで、PWM機能のピン割り当てとPWM信号のパラメータを設定しましたので、あとはPWM制御をスタートさせると、PWM機能を割り当てたピンにPWM信号が出力されます。なお、PWM制御スタート後でも、周期とデューティサイクルは変更可能です。

いったんまとめておきましょう。

PICマイコンでPWM機能を使用するには、

  1. 最初にPWM機能を割り当てるピンを指定して
  2. 周期とデューティサイクルの長さを設定して
  3. PWM制御をスタートさせる

という手順で進めます。なんだこれだけの設定でPWM信号が生成できるのか、と思ってしまいそうですが、おそらく解説を読み進めるとげんなりしてきます。げんなりしたら休憩を取るなど対策をしてください。

それでは次に、この手順に対応づける形で、完成したプログラムを確認します。

 

PWM制御プログラム

PWM制御プログラムがどのようになるか確認しておきましょう。

以下は、PWM機能をRA5ピンに割り当て、クロック周波数が1MHz、周期が1ms、デューティーサイクルが0.5msの場合のPWM制御部分のプログラムです。先ほどの(1)〜(3)の手順に対応する形でコメントを入れてあります。

// (1) PWM機能のピン割り当て設定
APFCONbits.CCP1SEL = 1;     // PWM機能をRA5ピンに設定
CCP1CONbits.CCP1M = 0b1100; // PWM機能を有効にして、PWM信号をactive-highに設定
CCP1CONbits.P1M = 0b00;     // RA2ピンはGPIOに設定

// (2) 周期とデューティーサイクルの設定
T2CONbits.T2CKPS = 0b00;    // プリスケーラを1:1に設定
PR2 = 249;                  // 周期を1msに設定 (249 + 1) x 4 x 1us = 1000us = 1ms
CCPR1L = 500/4;             // デューティーサイクルを0.5msに設定
CCP1CONbits.DC1B = 500;

// (3) PWM制御スタート
T2CONbits.TMR2ON = 1;

このプログラムを実行すると、ハードウエアのPWM制御モジュールがPWM信号の発生を始めます。このあとは、プログラムで何をしても、その処理とは関係なくPWM信号を発生し続けます。

手順としては理解できるけど、実際のプログラムはさっぱり、、、って感じですよね。今回の記事は、このプログラムを理解して、他のPICマイコンでも応用できるようにするのがゴールです。

それではこれから、(1)〜(3)まで、各項目の設定方法を詳しく説明します。途中で補足知識も説明しますので、どの部分の説明を読んでいるか常に確認しながら読み進めてください。本当に複雑ですので! げんなりしたら休憩を取ることをお忘れなく!!

 

(1) PWM機能のピン割り当て指定

ハードウエアのPWM制御モジュールは数が限られていて、割り当てることのできるピンも限られていることは先ほど説明しました。

まず最初にデータシートから、PWM制御モジュール数と割り当て可能なピンを読み取って見ましょう。

ここではPIC12F1822のデータシートを確認しますが、他のPICマイコンのデータシートの読み方も同じです。今後のためにも実際にデータシートを確認しながら進めてみてください。

なお、データシートを確認する前に注意点があります。これからPWM機能を設定しますが、データシートには「PWM」という言葉はあまり登場しません。この点がPWM機能設定の理解を難しくしています。ただ、他のPICマイコンでPWM制御する場合でも避けられませんので、一通り説明します。

まず最初にPWM制御モジュール数の確認です。

Pic app 4 function list
(Microchip Technology社「PIC12F1822データシート」より引用)

この表は、PIC12F1822データシートのp.2にある機能リストです。この表には、GPIOピンは何ピンあるのか、メモリはどのぐらいあるのか、などの情報がまとめられています。PWM機能はピンク色の枠で囲んでいる「CCP」または「ECCP」の欄になります。

CCPは「Capture機能/Compare機能/PWM機能」、ECCPは「Enhanced CCP」を意味しています。このように、PWM機能は、Capture機能、Compare機能とセットになっています。つまりPWM機能は、「CCP」または「ECCP」の一つの機能として扱われています。Capture機能、Compare機能についてはこの応用編では説明しませんが、これら3つは同じハードウエアで実現する機能のためにセットになっている、という程度で理解いただければ問題ありません。

上の図のピンク色枠の記述を見ると、「ECCP(Full-bridge)」「ECCP(Half-bridge)」「CCP」がそれぞれいくつあるか、という表現になっています。例えば、このシリーズて使用しているPIC12F1822は一番上の行で、「0/1/0」と書かれています。これは、「ECCP(Full-bridge)」が0、「ECCP(Half-bridge)」が1個、「CCP」が0という意味です。

ということで、PWMのことを知りたいのに、抱き合わせ商法のような感じで、Capture機能、Compare機能が出てきて、さらにCCP、ECCPが出てきた上に、さらにさらにECCPは2種類、ECCP(Half-bridge)とECCP(Full-bridge)、というように色々な種類が出てきてしまいました。

さっそく道に迷いそうですが、PWM機能を使う場合、PWMを意味する「P」は「ECCP(Full-bridge)」「ECCP(Half-bridge)」「CCP」いずれにもありますので、どれかが1以上であればPWMは使えることになります。PIC12F1822は「ECCP(Half-bridge)」が1個、ということは、PWM制御モジュールが1個ある、と理解してOKです。

これで、PIC12F1822のPWM制御モジュール数が1個であることがわかりました。

次に、PWM機能がどのピンに割り当て可能か、です。この仕様はデータシートの次のペーシに書かれています。

以下の表はPIC12F1822データシートのp.3に書かれているものです。

Pic app 4 function pin assign
(Microchip Technology社「PIC12F1822データシート」より引用)

一番左の列がピン名称の列です。ピンク色の枠で囲んだ部分がECCP機能の割り当て列になります。次にこの表を読み解く必要があります。

結論としては「CCP1」と書かれているピンがPWM機能割り当て可能ピンになります。「CCP1」と書かれているピンは、RA2ピンとRA5ピンであることがわかりますので、PWM機能割り当て可能ピンは、RA2ピンとRA5ピン、ということがわかります。

それでは、この表の読み方を説明します。

ECCP欄には、「CCP1」「P1A」「P1B」という文字列があります(「FLT0」はCCPの付随機能のため説明は省略します)。

まず「CCP1」ですが、これは「Capture機能/Compare機能/PWM機能」の意味で、数字は、PWM制御モジュールが複数ある場合には「CCP2」、「CCP3」と数字が増えていきます。PIC12F1822はPWM制御モジュールが1個ですので、CCP1しかありません。この表では「CCP1」はRA2とRA5のところにあるので、PWM機能はRA2かRA5に割り当て可能、ということがわかります。

次に「P1A」と「P1B」ですが、これは特定用途に使用されるPWM機能名です。例えばモーターを制御する場合、正転/逆転制御したいケースがあります。この場合、以下のような回路で、モーターを制御します。

Pic app 4 half bridge schematic
(Microchip Technology社「PIC12F1822データシート」より引用)

この回路に深入りするとちょっと大変ですので、この回路のポイントのみを説明します。モーターを正転/反転制御する場合は、2つのPWM制御ピンが必要です(4つ必要な場合もあります)。この回路では、PICマイコン側のP1A、P1Bという2つのPWM機能ピンでモーターを制御します。

この回路において、P1AでPWM制御する場合とP1BでPWM制御する場合とでは、モーターの回転は逆になります。また、P1AとP1Bは同時に制御してはいけません(同時にONにすると回路がショートします)。

つまり、PWM制御モジュールは1個しか使わないけど、モーター制御のように、状況に応じて2つのピンのどちらかをPWM機能として使用したいケースがあります。

このようなケースのために、PWM制御をP1A、P1Bの2ピンで使用できるモードも用意されています。あくまでもハードウエアのPWM制御モジュールは1個で、それを2ピンのどちらかで排他的に使用する、ということです。P1A、P1Bの2ピンあるから、2つのPWM制御モジュールがある、というわけではありません。

なお、上の回路は「ハーフブリッジ(Half-bridge)回路」と呼ばれていて、PWM制御ピンは2ピン必要になります。他に「フルブリッジ(Full-bridge)回路」というものもあり、こちらはPWM制御ピンは4ピン必要になります(PIC12F1822のデータシートp.203に回路図がありますので、ご興味があれば確認してみてください。

なおこの後のPWM設定のレジスタで、これらの用語が出てきますが、「CCP1」と「P1A」は同じ意味で解釈して問題ありません。通常のPWM制御として使用する場合は「CCP1」ですが、上のようにHalf-bridge回路で使用する場合は2ピン必要で、そのときの名称は、「CCP1」を「P1A」と呼び、もう1つのピンを「P1B」と呼んでいます。このように「CCP1」と「P1A」は同じ意味になります。

それでは、これからPWM機能ピンのプログラムでの指定方法を説明します。

PWM機能ピンを指定するには、使用するピンを出力ピンにしておく必要があります。すでに作成している回路では、RA5ピンにLEDを接続して、このピンを出力に設定してありますので(TRISAレジスタ設定)、特にプログラムの変更は必要ありません。

さらに、PWM機能のピンとして使用するには、以下の設定が必要になります。

それでは最初に「APFCON」レジスタです。以下はPIC12F1822データシートのAPFCONレジスタのページの抜粋です。

Pic app 4 apfcon register
(Microchip Technology社「PIC12F1822データシート」p.114より引用・加工)

このページの説明にあるように、APFCONレジスタのCCP1SELビットを0にするとRA2ピンが、1にするとRA5ピンがCCP1/P1Aピンになる、と書かれています。

つまり先ほど説明したように、CCP1 = P1A = PWM制御ですので、このビットでRA2かRA5どちらをPWM制御ピンとして使用するのか指定することになります。

今回はRA5ピンをPWM制御ピンにしますので、このCCP1SELビットを1に設定します。APFCONレジスタは8ビットで、その一番下のCCP1SELビットのみを1に変更したい場合、

APFCON = 0bなんとか;

とか書いてしまうと、CCP1SELビット以外のビットを書き換えてしまう可能性があります。そこで、レジスタの特定ビットを設定する方法がありますので、その方法を説明します。

この方法はAPFCONレジスタだけではなく、他のすべてのレジスタにも有効な方法です。今後も頻出しますので、是非覚えておきましょう。

レジスタの特定ビットを指定したい場合、レジスタ名に「bits.」をつけます(bitsの最後のピリオドを忘れないようにしてください)。例えば「APFCON」レジスタであれば、「APFCONbits.」です。

実際にMPLABXのエディタ上で基礎編で作成したmain.cを開いて、適当な場所で「APFCONbits.」と入力してみてください。最後のピリオドを入力すると、以下のようにそのあとの指定可能なビットが表示されたと思います。

Pic app 4 specifying bits

今回指定するのは、APFCONのCCP1SELビットですので、一番上の「CCP1SEL」を選択した状態でリターンキーを押すと「APFCONbits.CCP1SEL」となります。これは、APFCONレジスタのCCP1SELビット、という意味になります(APFCONレジスタはAPFCONbitsという構造体が定義されていて、この構造体の中に各ビットが定義されています)。

基礎編のmain.cは変更しませんので、保存せずに閉じてください。

ということで、RA5ピンをPWM制御ピンとして使用するために、まず、以下のように設定します。

APFCONbits.CCP1SEL = 1;

次に、PWM機能のタイプを「CCP1CON」レジスタ(CCP1 Control)で設定します。

以下は、データシートのCCP1CONレジスタのページ(p.213)です。

Pic app 4 ccp1con register
(Microchip Technology社「PIC12F1822データシート」p.213より引用・加工)

CCP1CONレジスタは、第7ビット〜第6ビットの「P1M」(図中青色枠)、第5ビット〜第4ビットの「DC1B」、第3ビット〜第0ビットの「CCP1M」(図中ピンク色枠)から構成されています。PWM設定に関わるのは「P1M」と「CCP1M」です。「DC1B」はデューティーサイクル設定で使用しますので、この後のセクションで説明します。

PWMタイプは、ピンク色枠の第3ビット〜第0ビット「CCP1M」で設定します。このビットのデフォルトは0b0000で、この値にすると、Capture/Compare/PWM機能はすべてOFFになります。

PWMとして使用するには、0b1100〜0b1111のいずれかを設定します。設定値の意味を見ると、例えば0b1100であれば、「PWM mode: P1A, P1C active-high; P1B, P1D active-high」と書かれています。これは、「PWMモード、P1A、P1B、P1C、P1DピンはすべてアクティブハイのPWM制御」という意味になります。PIC12F1822はP1AとP1Bしかありませんので、P1C、P1Dは無視してOKです。

設定内容を一通り確認すると、「active-high」「active-low」の設定があることがわかります。これは、PWM制御でONの期間、「active-high」であれば5Vに、「active-low」であれば0Vにする、という意味です。

Pic app 4 active high low

なんだかややこしいですが、電子回路によってはactive-low制御が必要な場合もあります。今回のLEDの制御は、ONの時に5VにしてLEDを点灯させますので、「active-high」設定になります。

P1Aをactive-highにするには、0b1100か0b1101のどちらでもOKです。0b1100を設定することにします。

CCP1CONのCCP1Mビットを0b1100に設定しますので、先ほどと同じように、「CCP1CONbits.」と入力します。最後のピリオドを入力すると、設定可能なビットが表示されますので、「CCP1M」を選択してリターンを押します。「CCP1CONbits.CCP1M」と表示されますので、その後に以下のように設定値を書きます。

CCP1CONbits.CCP1M = 0b1100; // PWM機能を有効にして、PWM信号をactive-highに設定

これで、CCP1CONレジスタのCCP1M設定ができました。

次に、CCP1CONレジスタのP1Mビットを設定します。P1Mビットは以下のような設定内容になっています。

ということで、CCP1CONレジスタのP1Mビットは、赤色文字の設定にすればいいことがわかります。赤色文字は、P1AがPWM信号出力、P1BがGPIO、という設定ですが、「APFCONbits.CCP1SEL」の設定でRA5ピンをPWM機能のピンに設定しましたので、RA5がPWM = CCP1 = P1A、RA2がP1Bになりますので、結局この設定の意味は、「RA5ピンがPWM機能、RA2ピンがGPIO機能」ということになります。

ということで、RA5ピンをPWM(half-bridge)、RA2ピンをGPIOにするために、

CCP1CONbits.P1M = 0b00; // RA2ピンはGPIOに設定

というプログラムになります。

これで、PWM制御モジュールの設定が終わりました。おそらく休憩が必要だと思いますので、コーヒーでも飲んで一息入れましょう。次は周期とデューティーサイクルの設定です。

 

(2) 周期とデューティーサイクルの設定

次に周期とデューティーサイクルの設定です。これらの設定はPWM信号発生中でも変更することができますので、LEDの明るさやモーターの回転速度を可変制御できます。

この記事の最初の方で、周期とデューティーサイクルの設定は「クロック何個分」という設定をする、と説明しましたが、実は素直に何個分、と設定できるわけではありません。今回のプログラムは1MHzで動作させますので、1クロックは1μsです。周期は1msにしたいので、クロックは1000個分になりますが、素直に「1000」という指定ではありません。

周期とデューティサイクルの設定も、コーヒーでも飲みながらゆっくり理解を進めることをおすすめします。

周期の設定は、「PR2」レジスタで行います。「PR2」レジスタは8ビット、つまり0〜255までの設定になります。このPR2の値を元に、周期は以下の式で決定されます。

周期 = ( PR2 + 1 ) x 4 x 1クロック分の時間 x プリスケーラ値

今回のプログラムは基礎編のmain.cを流用します。基礎編ではクロック周波数は1MHzに設定しました。つまり、1秒間に1,000,000個のクロックを発生しています。1秒間に1,000,000個、ということは1クロック分の時間は

1s ÷ 1000000 = 0.000001s = 1μs

です。つまり、クロック周波数を1MHzにした場合、上の計算式は

周期 = ( PR2 + 1 ) x 4 x 1μs x プリスケーラ値

となります。あとは「プリスケーラ値」が何か?ということがわかれば周期が決まります。

最初に、上の計算式で「プリスケーラ値」がない場合を考えてみます。

周期 = ( PR2 + 1 ) x 4 x 1μs

この計算式で、PR2は0〜255までの範囲になりますので、周期として設定できる値は、

PR2=0のとき:
周期 = ( 0 + 1 ) x 4 x 1μs = 4μs

PR2=255のとき:
周期 = ( 255 + 1 ) x 4 x 1μs = 1024μs

ということで、プリスケーラ値がない場合は、周期として設定できる値は、4μs〜1024μs(=約1ms)です。前回、ソフトウエアでPWM制御した場合は、周期を5msにしました。ということは、上の式では周期を5msにすることができません。

このように、1クロック分の時間に比べてある程度長い周期を設定したい場合のために「プリスケーラ値」があります。このプリスケーラ値はデフォルトでは1ですが、他に、4, 16, 64が選択できます。

例えば、周期を5msにしたい場合、プリスケーラ値を16、PR2を77にすれば、

周期 = ( 77 + 1 ) x 4 x 1μs x 16 = 4992μs = 約5ms

ということで、5msに近い値にすることができます。この場合は、PR2を77、プリスケーラ値は16に設定します。

ということで、周期を設定する場合、PR2の他にプリスケーラ値の設定も必要になります。

PR2は単に8ビットのレジスタですので、0〜255の値を代入すればOKです。

プリスケーラ値は「T2CON」レジスタT2CKPSビットで設定します。以下はPIC12F1822データシートのT2CONレジスタのページです。

Pic app 4 t2con register
(Microchip Technology社「PIC12F1822データシート」p.178より引用・加工)

T2CONレジスタのT2CKPSビットの設定値は以下の意味になります。

T2CON/T2CKPS プリスケーラ値
00 1
01 4
10 16
11 64

今回は、周期は1msにします。最初に計算したように、プリスケーラが1の場合、設定できる範囲は4μs〜1024μsで1msの設定ができますので、プリスケーラ値は1に設定します。

T2CONbits.T2CKPS = 0b00; // プリスケーラを1:1に設定

ということになります。

続いて、PR2ですが、

周期 = 1ms = 1000μs = ( PR2 + 1 ) x 4 x 1μs

となるようなPR2を求めると(1次方程式を解けばOKですね)、PR2は249になります。

ということで、周期の設定は、

PR2 = 249; // 周期を1msに設定 (249 + 1) x 4 x 1us = 1000us = 1ms

ということになります。これで周期の設定が終わりました。

次はデューティーサイクルです。デューティーサイクルは10ビットで設定します。デューティーサイクルは以下の式になります。

デューティーサイクル = (10ビットの値) x 1クロック分の時間 x プリスケーラ値

この式で、1クロック分の時間は1μs、プリスケーラ値は1にしましたので、

デューティーサイクル = (10ビットの値) x 1μs

となります。あとは10ビットの値をどこに設定すればよいか、ですが、通常のレジスタは8ビットです。そこで、この10ビットの値を上位8ビットと下位2ビットに分けて、それぞれ別のレジスタに設定します。

代入するレジスタは以下になります。

設定レジスタ
上位8ビット CCPR1L
下位2ビット CCP1CONレジスタのDC1Bビット

デューティー比は50%にしますので、デューティーサイクルは0.5ms、つまり500μsです。

上の式でデューティーサイクルを500μsにするには、

デューティーサイクル = (10ビットの値) x 1μs = 500 x 1μs

ということで、10ビットの値は500になります。ということで、500の上位8ビットをCCPR1Lに、下位2ビットをCCP1CONレジスタのDC1Bビット設定することになります。500は2進数ビットでは0b0111110100ですので、以下のようにレジスタに設定することになります。

Pic app 4 duty cycle setting

さて、500をわざわざ2進数に変換して、その上位8ビットや下位2ビットを求めるのは面倒ですので、楽な方法でプログラムを書くことにしましょう。

まず、上位8ビットの求め方です。2進数表現では、2で割ると1桁右に移動します。一番右の桁は無くなります。

Pic app 4 binary calculation

さらに2で割ればもう1桁右に移動します。ということは、500を4で割れば上位8ビットを取り出すことができます。

ということで、CCPR1Lに上位8ビットを設定するには、

CCPR1L = 500/4;

と書けばよいことになります。

次に、CCP1CONレジスタのDC1Bビットに下位2ビットを設定します。つまり、CCP1CONbits.DC1Bに500の下位2ビットを代入すればOKです。

これはビルド時に警告が出ますが、単純に

CCP1CONbits.DC1B = 500;

と書いてしまいましょう。左辺は2ビットですので、右辺が3ビット以上の数字でも、下位2ビットだけが使用されますので、手抜きでこのように書きました。なお、ビルド時の警告が気持ち悪いようでしたら、以下のように下位2ビット以外をマスクすればOKです。

CCP1CONbits.DC1B = 500 & 0b11;

これで周期とデューティーサイクルの設定は終わりました。

 

(3) PWM制御スタート

これで、PWMの設定ができましたので、あとはPWMをスタートさせるだけです。今まで、周期を設定するだけでもかなり大変だったので、PWMをスタートさせるのも大変では、と余計な心配をしてしまいますが、今回は簡単です。

PWMをスタートさせるには、T2CONレジスタのTMR2ONを1にするだけです。つまり、

T2CONbits.TMR2ON = 1;

とすればOKです。これでPWM信号が生成され始めます。

 

実際のプログラム

それでは、今までの内容をまとめて、周期1ms、デューティー比50%でLEDをPWM制御してみましょう。

前回は、「PWMControl」というプロジェクト名で新規プロジェクトを作成しましたので、今回は「PWMFunction」というプロジェクトで新規プロジェクトを作成しましょう。

作成したら、以下のプログラムをコピペしてビルド、書き込み、実行しましょう。

/*
 * File:   main.c
 * Author: Tool Labs
 */
#include <xc.h>

// PIC12F1822 Configuration Bit Settings
// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)


// クロック周波数指定
// __delay_ms(), __delay_us()関数が使用する
#define _XTAL_FREQ 1000000

void main(void) {

    // PICマイコン設定
    OSCCON = 0b01011010;  // 内部クロック周波数を1MHzに設定
    ANSELA = 0b00000000;  // すべてのピンをデジタルモードに設定
    TRISA  = 0b00001000;  // すべてのピンを出力モードに設定(ただしRA3ピンは常に入力モード)
    
    // (1) PWM機能のピン割り当て設定
    APFCONbits.CCP1SEL = 1;     // PWM機能をRA5ピンに設定
    CCP1CONbits.CCP1M = 0b1100; // PWM機能を有効、active-highに設定
    CCP1CONbits.P1M = 0b00;     // RA2ピンはGPIOに設定

    // (2) 周期とデューティーサイクルの設定
    T2CONbits.T2CKPS = 0b00;    // プリスケーラを1:1に設定
    PR2 = 249;                  // 周期を1msに設定 (249 + 1) x 4 x 1us = 1000us = 1ms
    CCPR1L = 500/4;             // デューティーサイクルを0.5msに設定
    CCP1CONbits.DC1B = 500;

    // (3) PWM制御スタート
    T2CONbits.TMR2ON = 1;

    // 何もしない
    while(1);

    // 以下の命令は実行されない
    return;
}

 

PWM制御のまとめ

PWM制御の設定はかなり複雑ですので、何を設定すればよいかまとめておきます。なお、クロック周波数の設定など、基本的な設定は省略しています。

設定内容 設定レジスタ
使用するピンの出力設定 TRISA
PWM機能のピン割り当て APFCONbits.CCP1SEL(ピンの割り当て)
CCP1CONbits.CCP1M(PWM有効化とactive-high/lowの指定)
CCP1CONbits.P1M(残りのピンの設定)
周期とデューティーサイクル設定 T2CONbits.T2CKPS(プリスケーラ)
PR2(周期)
CCPR1LとCCP1CONbits.DC1B(デューティーサイクル)
PWM制御開始/停止 T2CONbits.TMR2ON

 

PWM信号の確認

前回は、PWM制御のプログラムを自分で作りましたが、今回はPWM制御モジュールのパラメータを設定して、あとはPWM信号生成をPICマイコンにお任せした、という状態ですので、本当に周期が1ms、デューティーサイクルが0.5msかよくわかりませんよね。LEDの明るさを見ても果たしてデューティー比が50%なのか、自信がないでよすね。

そこで、基礎編で作成したブレッドボードと上のプログラムを使用して、RA5ピンの波形を確認しました。以下の図はRA5ピンの電圧をオシロスコープ(電圧の波形の計測器)で観測した結果です。

Pic app 4 osc 1

横軸が時間で1目盛1ms、縦軸が電圧で1目盛2Vです。

1ms周期の約5Vの信号が生成されているのがわかると思います。デューティー比は50%っぽいです。このオシロスコープは自動的に時間幅を測ってくれる機能があるので測ってみました。

Pic app 4 osc 2

周期(Period)は「1ms / 1.000kHz」とありますので指定通りです。またデューティサイクル(Width)も「500us」とありますのでこちらも指定通りです。

PWM制御は、1) PWM機能のピンを割り当てて、2) 周期とデューティーサイクルを設定して、3) PWM制御スタート、という手順でOKですが、かなり大変ですよね。ただ、他のPICマイコンも同様の指定方法ですので、PIC12F1822がマスターできれば他のPICマイコンでも問題なく使えるようになると思います。

また、他のマイコンも同じような感じの設定になりますので、是非PWM制御に慣れてみてください。ロボットアームに使用されるサーボモーターはPWMで制御しますので、色々応用がきくと思いますよ。

 

更新履歴

日付 内容
2017.1.4 新規投稿
2018.11.30 ビット演算子の誤記訂正(&&→&)
プログラムテンプレートをMPLABX IDE v5.10に更新