PIC18F14K50の基本動作から応用プログラムまでを学びます。

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
PWM (Pulse Width Modulation)

18F26J50には、ECCP (Enhanced Capture/Compare/PWM)が2セット組み込まれています。なお、2つのECCPは同じ構造をしているため、両方に該当する場合はECCPxと表記しています。関連レジスターも同様に「x」には、1または2の番号が入ります。 ECCP (Enhanced Capture/Compare/PWM)のPWM(Pulse Width Modulation) モードは、分解能10ビットのPWM 信号をタイマー2、または、タイマー4と連携して発生させることができます。PWM 出力モードには、4 種類がありますが、ここでは、 シングルPWMの説明です。

シングルPWMモードの動作

PWMの動作をCCP1とタイマー2の連携で説明しますが、他のCCPやタイマーでも動作自体は同じです。関連するレジスタの名称を読み変えてください。

PWMの繰り返し周期は、タイマーのPR2の設定値で決定し、デューティーサイクルはCCPR1LにDC1B <1:0>の2ビットをあわせた10ビットの値で決まります。タイマー(TMR2)とPR2の値が一致するとTMR2をリセットします。同時に、PWM出力を”1”にセットし、CCPR1LとDC1B <1:0>の値を、CCPR1Lと内部2ビットレジスタにコピーします。この値が、TMR2の値と一致するとPWM出力を”0”にリセットします。つまり、PR2の設定値の半分の値をCCPR1LとDC1B <1:0>にセットすればデューティー50%の波形が出力されます。

PWM出力は、PSTR1CONの該当ビットを「1」にすることで、1~4ピンまで増やすことが可能で、さらにそれらの極性を、CCP1M <3:0>で指定することも出来ます。TCLKCONでタイマー2を使用するか、タイマー4を使用するかを指定します。

*注意*
PWM 信号を出力するピンは、Peripheral Pin Select (PPS)機能で任意のRPnピンに指定することができます。CCPx出力に使用する場合には、該当のTRISCのビットを"0"出力に設定し、ANCONxビットも”1”デジタルにする必要があります。

設定例: CCP1/P1AをRP0(RA0,AN0)に設定する
RPOR0 = 14;              // CCP1/P1A を RP0(RA0,AN0)に設定する
TRISA.bitsTRISA0 = 0;    // RA0 を出力に設定する
ANCON0bits.PCFG0 = 1;    // RA0,AN0 をデジタルに設定する

上図ではTimer2を使用していますがTimer4を選択することもできます。

CCP出力PPS
表記番号
CCP1/P1A14
P1B15
P1C16
P1D17
CCP2/P2A18
P2B19
P2C20
P2D21
PWM繰り返し周波数

代表的なクロック周波数と、PWM繰り返し周波数の関係を表に示します。

4 Mhz 48 Mhz
プリスケラ値 1:16 1:4 1:4 1:1 1:16 1:4 1:4 1:1
PR2 0xFF 0xFF 0xF9 0xFF 0xFF 0xFF 0xF9 0xFF
PWM周波数 244Hz 976Hz 1KHz 3.9KHz 2.9KHz 11.7KHz 12KHz 46.9KHz

高低2レベルの優先度を使用するCCP に関連するレジスターを一覧で説明します。

レジスタ bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
RCON IPEN CM RI TO PD POR BOR
INTCON GIEH GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF
PIR1 ADIF RC1IF TX1IF SSP1IF CCP1IF TMR2IF TMR1IF
PIE1 ADIE RC1IE TX1IE SSP1IE CCP1IE TMR2IE TMR1IE
IPR1 ADIP RC1IP TX1IP SSPIP CCP1IP TMR2IP TMR1IP
PIR2 OSCFIF CM2IF CM1IF USBIF BCL1IF HLVDIF TMR3IF CCP2IF
PIE2 OSCFIE CM2IE CM1IE USBIE BCL1IE HLVDIE TMR3IE CCP2IE
IPR2 OSCFIP CM2IP CM1IP USBIP BCL1IP HLVDIP TMR3IP CCP2IP
CCPxCON PxM < 1 - 0 > DCxB < 1 - 0 > CCPxM < 3 - 0 >
CCPRxL Duty Cycle Register の上位8ビット
CCPRxH CCPRxLのスレーブ Register
PSTRxCON CMPL1 CMPL0 STRSYNC STRD STRC STRB STRA
TCLKCON     T1RUN       T3CCP2 T3CCP1
ODCON1 ECCP2OD ECCP1OD
CCPRxL CCP Register x, Low Byte
CCPRxH CCP Register x, High Byte
レジスタ BIT 説明 1 0
RCON 7 IPEN 2レベルのインタラプト優先度 使用
INTCON 7 GIEH 高優先度インタラプトを使用許可 許可
6 GIEL 低優先度インタラプトを使用許可 許可
PIR1 1 CCP1IF CCP1 発生フラグ 発生  
PIE1 1 CCP1IE CCP1 インタラプトを使用許可 許可
IPR1 1 CCP1IP CCP1 インタラプト優先度
PIR2 0 CCP2IF CCP2 発生フラグ 発生  
PIE2 0 CCP2IE CCP2 インタラプトを使用許可 許可
IPR2 0 CCP2IP CCP2 インタラプト優先度
CCPxCON 3 CCPxM3 0000 =モジュール全体をOFF (初期値)
0010 =CCPxをトグル, 1000 =CCPxLOW, 1001 =CCPxをLOW
1010 =CCPxは変化せず、CCPxIFセット
1011 =Special Event を発生
2 CCPxM2
1 CCPxM1
0 CCPxM0
CCPxCON 7 PxM1 00 = シングル出力(規定値)、01 = フルブリッジ順方向出
10 = ハーフブリッジ出力、  11 = フルブリッジ逆方向出
6 PxM0
5 DCxB1 PWM デューティ サイクル値の下位 ビット 1 0
4 DCxB0 PWM デューティ サイクル値の最下位 ビット 1 0
3 CCPxM3 0000 =CCPxモジュール全体をOFF(規定値)
1100 = PxA-PxCを正論理出力  1111 = PxA-PxCを負論理出力
1101 = PxA,PxCを正論理、PxB,PxDを負論理
1110 = PxA,PxCを負論理、PxB,PxDを正論理
2 CCPxM2
1 CCPxM1
0 CCPxM0
PSTRxCON 4 STRSYNC 出力をPWM周期に同期 する しない
3 STRD PxD PinにPWM波形を出力 する しない
2 STRC PxC PinにPWM波形を出力 する しない
1 STRB PxB PinにPWM波形を出力 する しない
0 STRA PxA PinにPWM波形を出力 する しない
TCLKCON 1 T3CCP2 10 =CCP1,CCP2 両方ともタイマー3を使用する
01 =CCP1はタイマー1、CCP2はタイマー3を使用する
00 =CCP1はタイマー3、CCP2はタイマー1を使用する(初期値)
0 T3CCP1
ODCON1 1 ECCP2OD CCP1出力のオープンドレイン機能 有効 無効
0 ECCP1OD CCP2出力のオープンドレイン機能 有効 無効

PWMのプログラム例

CCP1とCCP2を使いPWM波形を発生させるプログラムを作成しました。繰り返し周波数は、3.9KHzで固定ですが、デューティーサイクルをVRで可変できます。

<プログラム>

//  File name: CCP PWM
//  Description:
//  VRの値で出力するデューティサイクルが変化する
//  CCP1とTimer2で、AN0のAD変換値から波形を発生させ
//    RP3(RB0)のLED0を駆動する。
//  CCP2とTimer4で、AN1のAD変換値から波形を発生させ
//    RP4(RB1)のLED1を駆動する。
//  Notes: 4MHz内部クロック
//        LED0 RC0 RPOR3 = 14;  ECCP1 Timer2 CCP1/P1A
//        LED1 RC1 RPOR4 = 18;  ECCP2 Timer4 CCP2/P2A
//        Pot0 AN0(RA0)
//        Pot1 AN1(RA1)
//    Language: MPLAB xc8
//    Target: PIC18F26J50

#include <xc.h>
#define _XTAL_FREQ 4000000      // delay_ms(x) のための定義

     #pragma config WDTEN = OFF, PLLDIV = 2, STVREN = ON, XINST = OFF
     #pragma config CPUDIV = OSC1, CP0 = OFF
     #pragma config OSC = INTOSC, T1DIG = OFF
     #pragma config LPT1OSC = OFF, FCMEN = OFF, IESO = OFF
     #pragma config WDTPS = 32768
     #pragma config DSWDTOSC = INTOSCREF, RTCOSC = T1OSCREF
     #pragma config DSBOREN = OFF, DSWDTEN = OFF, DSWDTPS = 8192
     #pragma config IOL1WAY = OFF, MSSP7B_EN = MSK7
     #pragma config WPFP = PAGE_1, WPEND = PAGE_0, WPCFG = OFF
     #pragma config WPDIS = OFF

void main(void){
    OSCCON = 0b01100010;           // 内部クロック4Mhz
    LATB = 0;                      // PortCのすべてのビットを「0」
    TRISB = 0b11110000;            // LEDのポートを出力に設定
// ----- CCPの使用するタイマーを設定 -------------------------------
    TCLKCON = 0x01;                // CCP1はT2と、CCP2はT4と連携
// ----- CCP1の 設定 --------------------------------------------
        RPOR3 = 14;                    // CCP1/P1AをRP3(RB0)に指定
        CCP1CON  = 0b00001100;         // シングルPWM 正論理出力
        PSTR1CON = 0b00000001;         // PWM出力がP1A ピン
        T2CON    = 0b00000100;         // PreS 1/1 PostS 1/1
        PR2      = 0xFF;               // 繰返し周波数3.9KHz
// ----- CCP2の 設定 --------------------------------------------
        RPOR4 = 18;                    // CCP2/P2AをRP4(RB1)に指定
        CCP2CON  = 0b00001100;         //   ↑
        PSTR2CON = 0b00000001;         //   ↑
        T4CON    = 0b00000100;         //   ↑
        PR4      = 0xFF;               // CCP1/T2と同一の設定内容
// ----- ADC初期化 ------------------------------------------------
    ADCON1 = 0b00101100;        // 左詰 クロックFosc/4 12_TAD
    ADCON0 = 0b00000001;        // Vdd-Vss AN0(RA0) ADC_ON

    ANCON0 = 0b00011100;        // AN0,AN1をアナログ入力にする
    ANCON1 = 0b00011111;

    while(1){
        ADCON0 = 0b00000001;    // AN0に接続
        ADCON0bits.GO = 1;      // AD変換開始
        while(ADCON0bits.GO);   // AD変換終了待ち
        CCPR1L = ADRESH;        // AD変換結果をCCP1デューティに

        ADCON0 = 0b00000101;    // AN1に接続
        ADCON0bits.GO = 1;      // AD変換開始
        while(ADCON0bits.GO);   // AD変換終了待ち
        CCPR2L = ADRESH;        // AD変換結果をCC2Pデューティに
        __delay_ms(100);        // 更新間隔
    }
}