
投稿日 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とします。(波形観測の範囲での動作確認です。)

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の信号となります。

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)