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

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
パルス発生器

18F14K50のPWM (Pulse Width Moduration) と割込みを使用し、パルス発生器を作成しました。発生させるパルスの精度を高めるために、クロックは高精度の12MHzクリスタルを使用しています。 発生するパルスは、VRで以下の範囲を可変することができます。

繰返し周期:  1/8 Hz~1.2MHz 区切りのよい値、49段階
デューティサイクル:  5%~95% 5%毎

本来、外部にパルスを出力するには、PICからの出力パルスをFET等でバッファするのですが、私は、ロジック回路の実験が主なので、直接PICの端子から出力しています。ただし、このパルス発生回路は5vで動作させているため、最近増えている 3.3vシステム用に抵抗で3vに分圧した端子も用意しました。

また、乾電池1本から、電源の 5vを発生させるDC-DCコンバータ、HT7750、も組み込み、小型の透明ケースにすべてを納めることができ、気軽に他の実験回路に接続できるようにしています。

プログラム

繰返し周期により 1/8 Hz~2KHz をLOW Band、 3KHz~1.2MHz をHI Bandとして、2 Bandに分けて処理しています。 LOW Bandは、割込みを利用してパルスを発生させ、HI BandはPWMを利用して発生させています。

文字を画面に表示するLCDライブラリ、AD変換のadcライブラリを利用しています。プロジェクトファイルには、同梱してありますが、このページに記載するのは省略します。

プロジェクトファイル 100_PulseGen_5v.zip

<プログラム>

//****************************************************************
//    PIC18F14K50 信号発生器   <<5v >>    100_PulseGen_5v
//        Hi-Band   1.2MHz -  3KHz                    2011/06/24
//        Low-Band    2KHz - 1/8Hz
//        デューティサイクルは、5%ことに設定できる
//     1)Define I/O PORT
//         PORTB(4)pin13    :  SDA
//         PORTB(6)pin11    :  SCL
//         PORTB(7)pin10    :  Band SW デジタル入力  (WPU ON)
//         PORTC(0)pin16    :  LED
//         AN 9(RC7)pin 9   :  VR アナログ入力 デューティサイクル
//         AN11(RB5)pin12   :  VR アナログ入力 周波数
//         CCP1(RC5)pin 5   :  信号出力(正論理)
//         P1B (RC4)pin 6   :  信号出力(負論理)
//     2)OSC
//         外部クロック  12MHzOSC  PLLで48MHz
//******************************************************/
//     非安定状態の時LEDを点灯
//******************************************************/
#include <p18f14k50.h>
#include <delays.h>
#include "lib_adc.h"
#include <timers.h>
#include <i2c.h>           //I2C関数
#include "lib_i2cLCD.h"    //I2C接続LCD関数
//-------------- 関数プロトタイプ ------------------------
void DevicesIntlz (void);
void CCP_PWM(void);
void frq_PWM(void);
void itostr(unsigned long, char*, char);
void cutNoize(unsigned char);
void dutyPWM(void);
void CCP_Cmp(void);
void frq_Cmp(void);
void dutyCmp(void);
//-------------- コンフィグレーション ---------------------
#pragma config MCLRE  = OFF
#pragma config PWRTEN = OFF, BOREN  = OFF, BORV   = 30
#pragma config WDTEN  = OFF, WDTPS  = 32768
#pragma config STVREN = ON
#pragma config FOSC   = ECH //  外部クロック
#pragma config PLLEN  = ON, CPUDIV = NOCLKDIV, USBDIV = OFF
#pragma config FCMEN  = OFF, IESO   = OFF, HFOFST = OFF
#pragma config LVP    = OFF, XINST  = OFF, BBSIZ  = OFF
#pragma config CP0    = OFF, CP1  = OFF, CPB  = OFF, CPD  = OFF
#pragma config WRT0   = OFF, WRT1 = OFF, WRTB = OFF, WRTC = OFF, WRTD = OFF
#pragma config EBTR0  = OFF, EBTR1 = OFF, EBTRB = OFF
//
#define BandSW     PORTBbits.RB7
#define signalOut  LATCbits.LATC5
#define LED        LATCbits.LATC0
// ----- 割り込みベクトル -----
void isr_high(void);
#pragma interrupt isr_high
#pragma code h_int_vect = 0x0008
void _h_isr (void){
     _asm goto isr_high _endasm
}
#pragma code
// 周波数定数 (Hi Band) ----------------------------------------------------------
    rom const unsigned int frqH[22]={3,5,6,10,12,15,20,30,40,50,
                    60,80,100,150,200,300,400,500,600,800,1000,1200};
    rom const unsigned char prT2[22]={249,149,124,74,249,199,149,99,74,59,
                    199,149,119,79,59,39,29,23,19,14,11,9};
    rom const unsigned char psT2[22]={16,16,16,16,4,4,4,4,4,4,
                    1,1,1,1,1,1,1,1,1,1,1,1};
// 周波数定数 (LowBand) ----------------------------------------------------------
    rom const unsigned int frqL[]={-8,-5,-4,-2,1,2,3,4,5,6,8,10,20,30,40,50,60,
                    80,100,200,300,400,500,600,800,1000,2000};
    rom const unsigned int CCPR[]={59999,37499,29999,14999,7499,37499,24999,
                    18749,14999,12499,9374,59999,29999,19999,14999,11999,9999,
                    7499,5999,2999,1999,1499,1199,999,749,599,299};
    rom const unsigned char psT1[]={10,10,10,10,10,8,8,8,8,8,8,1,1,1,1,1,1,
                    1,1,1,1,1,1,1,1,1,1};
// ***** 共通変数の定義 *****
    char msgHz[]   ="      Hz ";
    char msgKHz[]  ="      KHz";
    char msgSlow[] ="  1/  Hz ";
    char msgFreq[] =" Freq";
    char msgPerC[] ="   %";
    char msgDuty[] =" Duty";
    unsigned char fqCmd;        // 現、周波数
    unsigned char dcCmd;        // 現、DutyCycle
    unsigned char VRpos;        // VR position
    unsigned char bdCmd;        // 周波数帯  Hi:1、Low:0
    unsigned char inCmd;        // 入力指示
    unsigned char fqVRe;        // VR 期待位置  周波数
    unsigned char dcVRe;        // VR 期待位置  Duty
    unsigned char refsh = 1;    // 表示の再描画
    unsigned char dcCnt = 0;    // インタラプトの発生回数
    unsigned char dcLow = 10;    // Lowになるタイミング
    unsigned char dcMax = 20;    // 一周期に必要なインタラプト数
// ****** 割り込み処理 ****************************
// dcCnt 20回または200回で1周期
//    0             : 計測準備完了
//    1     - dcLow : HI
//    dcLow - dcMax : LOW
// ************************************************
void isr_high(){
    if (PIR1bits.CCP1IF == 1) {        // CCPからの割り込みか
        PIR1bits.CCP1IF = 0;           // CCP割込フラグをOFF
        if(dcCnt == 0){                // 信号をHIにする
            Nop();Nop();Nop();         // タイミングを合わせる遅延
            Nop();Nop();
            LATC = 0b00100000;
        }else if(dcCnt == dcLow){      // 信号をHIにする
            LATC = 0b00010000;
        }
        if( ++dcCnt >= dcMax) dcCnt = 0; // 一周期終了
    }
}
//*************** メイン関数   ***************
void main(void){
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    TRISA = 0b11111111;           //PortA すべて入力
    TRISB = 0b11111111;           //PortB すべて入力
    TRISC = 0b11001110;           //PortC RC5(CCP1),RC4(P1B)は出力他は入力
    ANSEL  = 0b00000000;          //AN11、AN 9はアナログ  他はデジタル
    ANSELH = 0b00001010;          //    ↑
    WPUA = 0b00000000;            //RB7だけWeak Pull Upを行う
    WPUB = 0b10000000;            //    ↑
    INTCON2bits.NOT_RABPU = 0;    //    ↑
    DevicesIntlz();               // 内部デバイスの初期化
    bdCmd = 1;                    // Hi Bandnに仮設定
    CCP_PWM();                    // CCPをPWMモードにする
    refsh = 1;                    // 再セットを行う
// ---------------- LCD 設定 ---------------------
    LCD_int();                    // LCDを初期化
    LCD_str(msgFreq);             // LCD上段に初期値表示
    LCD_posyx(1,0);               // LCD表示位置指定
    LCD_str(msgDuty);             // LCD下段に初期値表示
    while(1){
        // ++++++++++ 周波数帯 確認 ++++++++++
        inCmd = BandSW;
        if(bdCmd != inCmd){               // BandSWが操作されたら
            LED = 1;                      // 非安定を点灯
            bdCmd = inCmd;                // BandCommandを変更し
            refsh = 1;                    // 再セットを行う
            if(bdCmd==1){                 // Hi Band なら
                CCP_PWM();                    // CCPをPWMモード
                INTCONbits.PEIE = 0;          // 割込を不許可
                INTCONbits.GIE = 0;
            }else{                        // Low Band なら
                 CCP_Cmp();                    // CCPをConpareモード
                INTCONbits.PEIE = 1;           // 割込を許可
                INTCONbits.GIE = 1;
            }
        }
        // ++++++++++ 周波数 設定 ++++++++++
        SetChanADC(ADC_CH11);            // 周波数VR選択
        ConvertADC();                    // AD変換開始
        while(BusyADC());                // AD変換終了待ち
        VRpos = ADRESH;                  // AD変換結果取得
        cutNoize(fqVRe);                 // ノイズ除去
        if(bdCmd==1)frq_PWM();           // Hi Band  PWMモードで設定
        else frq_Cmp();                  // Low Band Conpareモードで設定

        // ++++++++++ デューティ 設定 ++++++++++
        SetChanADC(ADC_CH9);             // デューティサイクルVR選択
        ConvertADC();                    // AD変換開始
        while(BusyADC());                // AD変換終了待ち
        VRpos = ADRESH;                  // AD変換結果取得
        cutNoize(dcVRe);                 // ノイズ除去のため閾値から遠ざける
        inCmd = ((int)VRpos*19/256+1)*5; // DutyCycleの計算
        if ((inCmd != dcCmd)||(refsh == 1)){ // -- 変更された --
            LED = 1;                         // 非安定警告を点灯
            refsh = 0;                       // 再セットFlag OFF
            dcCmd = inCmd;                   // DutyCycleの変更
            dcVRe = (int)(dcCmd/5-1)*256/19+8;  // 指示の中心VR位置計算
            // ---------  デューティ値表示  ----------
            itostr(dcCmd, msgPerC, 2);      // 値を文字列に変換
            LCD_posyx(1,9);                 // LCD表示位置指定
            LCD_str(msgPerC);               // 表示
                if(bdCmd==1)dutyPWM();        // Hi Band  PWMモードで設定
                else dutyCmp();               // Low Band Conpareモードで設定
        }
        // ++++++++++++++++++++++++++++++
            LED = 0;                        // 非安定警告を消灯
    };
}
//********** デューティサイクルの設定 PWM *******
void dutyPWM(){
    unsigned int dutyReg;
    dutyReg = ((int)prT2[fqCmd]+1) << 2;        // PR2から周期を取得
    dutyReg = (long)dutyReg * dcCmd/100;        // CCPの設定値を計算
    CCP1CONbits.DC1B0 = dutyReg & 1;            // 下位2ビットの設定
    CCP1CONbits.DC1B1 = (dutyReg >> 1) & 1;     //     ↑↑↑↑
    CCPR1L = dutyReg >> 2;                      // 上位8ビットの設定
}
//************  デューティサイクル設定 Cmpare *****
void dutyCmp(void){
        if (psT1[fqCmd] > 8){    // psT1が8を超える場合
            dcLow = dcCmd * 2;    // インタラプト200回で1周期
            dcMax = 200;
        }else{                    // それ以外は
            dcLow = dcCmd / 5;    // インタラプト 20回で1周期
            dcMax = 20;
        }
}
//****** 正整数を文字列に変換する関数  (ゼロサプレス)********
//  num     変換する整数
//  strAdd  変換結果の格納される文字列
//  digit   変換される文字数
//************************************************************
void itostr(unsigned long num, char *strAdd, char digit){
    char i;
    char flag=1;                   //ゼロサプレス Flag
    char coma=0;                   //コンマ Flag
    strAdd += digit;               // 文字列の最後
    for(i=digit; i>0; i--) {       // 下位桁から処理
        strAdd--;                  // 1桁上に
        if (flag == 1){
            if (coma++ == 3){      //コンマ の位置か
                *strAdd = ',';           //コンマ を格納
                coma=0;
            }else{                 // 1桁の数値を文字で格納
                *strAdd = (num % 10) + 0x30;
                num = num / 10;         // 次桁の準備
                if (num == 0) flag=0;   // ゼロなら以降は抑制
            }
        }else{                     // 上位の0は抑制する
            *strAdd = 0x20;        // 0の代わりにスペース格納
        }
    }
}
// ************ CCPの 設定 PWM ***********
void CCP_PWM(void){
    PIE1bits.CCP1IE = 0;   // 割り込みを使用しない。
    PIR1bits.CCP1IF = 0;
    PSTRCON = 0b00000011;  // PWM出力がP1A P1Bピン
    CCPR1L  = 0x7E;        // 仮の値
    CCP1CON = 0b00001101;  // PWMモード、Single Output、P1A:正 P1B:負
}
// ************ CCPの 設定 Compare ***********
void CCP_Cmp(void){
    CCP1CON = 0b00001011;  // Compare モードspecial event
    CCPR1L = 0xFF;         // 仮の値
    CCPR1H = 0xFF;         //
    PIE1bits.CCP1IE = 1;   // 割り込みを使用する。
    PIR1bits.CCP1IF = 0;
}
// ************ 内部デバイス初期化 ***********
void DevicesIntlz(void){
// -------------------- I2C 設定 ---------------------
    OpenI2C(MASTER, SLEW_ON);       //I2Cマスターモード指定
    SSPADD = 0x1D;                  //Board Rate 400kHz
// -------------------- ADC 設定 ---------------------
    OpenADC(ADC_FOSC_64 &
            ADC_LEFT_JUST &
            ADC_12_TAD,
            ADC_REF_VDD_VDD &
            ADC_REF_VDD_VSS,
            ADC_CH11 &
            ADC_INT_OFF );
// -------------------- Timer 1 設定 ---------------------
    OpenTimer1(
        TIMER_INT_OFF &
        T1_8BIT_RW &
        T1_SOURCE_INT &
        T1_PS_1_8 &
        T1_OSC1EN_OFF &
        T1_SOURCE_CCP );
    PIR1bits.TMR1IF = 0;
    TMR1H = 0;
    TMR1L = 0;
// -------------------- Timer 2 設定 ---------------------
    OpenTimer2(
        TIMER_INT_OFF &
        T2_PS_1_4 &
        T2_POST_1_1 );
    PIR1bits.TMR2IF = 0;
    TMR2 = 0;
    PR2 = 0xFF;
}
//******************************************
// AD変換のノイズで、コマンドが変動しないよう
// AD変換値をコマンド中心値に近づける
//******************************************
void cutNoize(unsigned char VRexp){
    if(VRpos > VRexp){
        if(VRpos > 2) VRpos -= 2;
    }else{
        if(VRpos < 0xFE) VRpos += 2;
    }
}
//******** PWM 周波数設定 *******************
void frq_PWM(void){
    inCmd = ((int)VRpos * 22)/256;        // 周波数番号の計算
    if ((inCmd != fqCmd)||(refsh == 1)){  // -- 変更された --
        LED = 1;                            // 非安定警告を点灯
        fqCmd = inCmd;                      // 周波数番号の変更
        fqVRe = fqCmd*256/22+5;             // 指示の中心VR位置を計算
        itostr(frqH[fqCmd], msgKHz, 5);     // 表示周波の取得
        LCD_posyx(0,6);                     // LCD表示位置指定
        LCD_str(msgKHz);                    // LCD下段に周波数
        switch (psT2[fqCmd]){               // プリスケラの設定
            case 1: T2CON = 0b00000100;break;    //  x1
            case 4: T2CON = 0b00000101;break;    //  x4
            default:T2CON = 0b00000111;          //  x16
        }
        PR2 = prT2[fqCmd];                // 周期プリセットの設定
        dutyPWM();                        // DutyCycleの更新
    }
}
//******** Compare 周波数設定 *******************
void frq_Cmp(void){
    int fqDsp;
    inCmd = ((int)VRpos * 27)/256;        // 周波数番号の計算
    if ((inCmd != fqCmd)||(refsh == 1)){  // -- 変更された --
        LED = 1;                            // 非安定警告を点灯
        fqCmd=inCmd;                        // 周波数番号の変更
        fqVRe = fqCmd*256/27+5;             // 指示の中心VR位置を計算
        fqDsp = frqL[fqCmd];                // ---- 周波数の表示 ----
        LCD_posyx(0,6);                     // LCD表示位置指定
        if(fqDsp<0){                        // 1Hz未満の周波数
            fqDsp = - fqDsp;                    // 1Hz未満は負で示す
            itostr(fqDsp, msgSlow+4, 1);        // 表示
            LCD_str(msgSlow);
        }else{                              // 1Hz以上の周波数
            itostr(fqDsp, msgHz, 5);            // 表示周波の取得
            LCD_str(msgHz);                     // 表示
        }
                                            // プリスケラの設定
        if (psT1[fqCmd]==1)T1CON = 0b00000001;        //  x1
            else           T1CON = 0b00110001;        //  x8
        CCPR1H = CCPR[fqCmd] >> 8;            // CCPプリセットの設定
        CCPR1L = CCPR[fqCmd] & 0xFF;
        dutyCmp();                            //  Dutyの設定
    }
}
// --- Copyright (C) 2011-2012 Kazuo Iwamoto All Rights Reserved. ---