PIC 18F14K50を48MHzで動かす

投稿日 2013/10/19

PICの定番18F14K50を48MHzで動かしてみました。

 

18F14K50のスペックでは最高動作周波数は32MHzですが、その1.5倍の48MHzに上げても動作するか試してみました。

 

実験の結果、動作しましたが、当然このような使い方での動作保証はありません。

 

秋月のPIC18F14K50使用 USB対応超小型マイコンボードには12MHzのXtalが搭載されているので、これを内部のPLLで4逓倍してやるとFoscが48MHzになります。

 

実際にFoscが48MHzになっていることを確認するために、タイマー2とCCPのPWMモードを使用してPWM出力を得、それをフィルターに通して1KHzの正弦波が得られるようにしてみました。

 

オシロで波形を観測して1KHzが観測できればOKとします。(波形観測の範囲での動作確認です。)

18F14K50_1.jpg

PIC18F14K50使用 USB対応超小型マイコンボード


 

オシレータは12MHz Xtalのプライマリオシレータをクロック源とします。PLLはONにして4逓倍を行います。

 

FOSC = HS;
PLLEN = ON;

 

この設定でFoscが48MHzとなります。

 

タイマー2の設定で、PWM周波数を50KHzに設定します

 

1/48000000 x 4 x 240 = 20uS = 50KHz となるので、タイマー2のPR2レジスタには239(240-1)を設定します。

 

PR2 = 239;

 

Dutyのほうは、あらかじめエクセルで計算しておいた360度を50分割したSin値に重みを加えた値を定数でプログラムにはめ込んでおきます。

 

PWM周波数を分割数で割ったものが、出力周波数となります。

 

50KHz / 50 = 1KHz

 

Duty値の計算は、

 

360度の50分割なので、7.2度間隔となります。

 

0度から352.8度までの7.2度間隔のサイン値を計算し、8ビット幅に合わせるため、127を中心として0から254の間の値をとるよう重みを加えDuty値とし、プログラムに定数としてDutyテーブルにはめ込んでおきます。

 

0.0度では、=SIN(RADIANS(0.0)) * 127 + 127 = 127 Duty[0] = 127
3.6度では、=SIN(RADIANS(7.2)) * 127 + 127 = 143 Duty[1} = 143
  ・
  ・
  ・

 

Duty値はCCPR1Lにセットしながら50分割をぐるぐる回します。

 

CCPR1L = Duty[count];

 

プログラムはタイマー2の割り込みを使用しました。

 

タイマー2の動作は、PR2がPeriodつまりPWM周期です。今回は50KHzなので20usです。

 

CCPR1LはDutyです。

 

タイマー2のカウンタ TMR2はFoscの1/4の周波数でカウントアップされます。

 

カウントアップを開始するとPWM出力がHighレベルとなります。

 

TMR2は、PR2の値と比較されていて、一致すると、TMR2が0クリアされるとともに、割り込みが発生します。

 

割り込み処理ルーチンでは、割り込み毎にDutyテーブルからDuty値を取り出してCCPR1Lにセットします。

 

TMR2はCCPR1Lとも比較されており、一致するとPWM出力をLowレベルに落とします。

 

以上によりPWM出力はPR2の周期で、CCPR1Lで決まるDutyの信号となります。

18F14K50_2.jpg

PWM出力 1KΩ 0.1uFのフィルター通過後の波形 1KHz 正弦波

 

今回試した範囲ではFosc 48MHzでも問題なく動作するようです。(あくまでもアマチュア的使い方ですが)

 

使用開発環境:MIcrochip MPLAB X IDE XC8コンパイラ

 

#include <xc.h>
#include <p18F14K50.h>

 

#pragma config FOSC = HS          //POSC 12MHz
#pragma config WDTEN = OFF
#pragma config MCLRE = OFF
#pragma config PLLEN = ON         // Fosc = 12MHz x 4 = 48MHz
#pragma config CPUDIV = NOCLKDIV
#pragma config LVP = OFF

 

unsigned char cnt_pos = 0;

 

//7.2度間隔 50カウント
//Sin(rad) * 127 + 127
unsigned char count = 50;
const unsigned char Duty[] =
{127, 143, 159, 174, 188, 202, 214, 225, 234, 242,

248, 252, 254, 254, 252, 248, 242, 234, 225, 214,
202, 188, 174, 159, 143, 127, 111, 95, 80, 66,
52, 40, 29, 20, 12, 6, 2, 0, 0, 2,
6, 12, 20, 29, 40, 52, 66, 80, 95, 111};

 

void interrupt low_priority LPIsr(void){
if(PIR1bits.TMR2IF){          //Timer2 割り込み?

PIR1bits.TMR2IF = 0;

CCPR1L = Duty[cnt_pos++];      // Get Duty value

if(cnt_pos >= count) cnt_pos = 0;

}
}

 

void main(void){

OSCCONbits.SCS = 0b00;         // システムクロック = 内部オシレータ
OSCCONbits.IRCF = 0b110;        // 内部オシレータ Fosc 4MHz
LATC = 0b00000000;
TRISC = 0b00000000;          // PORTC = OUTPUT

CCP1CONbits.CCP1M = 0b1100;       //PWM mode Active High
CCPR1L = 127;              // Initial Duty Val
PSTRCON = 0b00001000;          // PWM Output P1D(RC2)
T2CONbits.TMR2ON = 1;          // Timer2 ON
T2CONbits.T2CKPS = 0b00;        // Timer2 Prescaler 1:1
PR2 = 239;               // PWM Freq = (Fosc/4) / 240 = 50KHz (Period 20us)
IPR1bits.TMR2IP = 0;          // Timer2 からの割込みを低優先に設定
PIR1bits.TMR2IF = 0;          // Timer2 からの割込みフラッグをクリア
PIE1bits.TMR2IE = 1;          // Timer2からの割り込みを許可
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;

while(1);

}



 

(JF1VRR)