PIC 16F18857のクロック設定と切り替え時間の計測

投稿日 2016/09/04

Microchip社のPICマイコン 16F18857は、データシートの発行が2016年1月となってるので比較的新しいCPUデバイスです。

 

どんなデバイスか調べたかったのでMicrochip社のサンプル・サービスを利用して2個取り寄せました。Microchip社のホームページからログインして、簡単にサンプル・オーダーができます。処理は迅速で、すぐにオーダーを受けた旨のメールが返ります。そして3日後にはタイから物が届きました。素晴らしいですね。

 

今回オーダーしたのはPIC 16F18857-I/STです。これは28ピンのDIPパッケージです。

 

主な仕様は以下のようになっています。

 

Program Memory(Flush) 56Kb

Data SRAM 4096b

EEPROM 256b

I/O Pins 25

10bit AD Convertor 24ch

5 bit DAC 1

Comparator 2

8 bit/16bit Timers 3/4

SMT 2

Windowed Watchdog Timer Yes

CRC and Memory Scan Yes

CCP/10 bit PWM 5/2

Zero-Cross Detect Yes

CWG 3

NCO 1

CLC 4

DSM 1

EUSART/I2C・SPI 1/2

Periphral Pin Select Yes

Periphral module Disable Yes

 

プログラムメモリが56kbと大きく、多彩な周辺機能が搭載されています。PIC24Fシリーズで搭載されていたPeriphral Pin Select(周辺機能の入出力ピン配置を自由に設定できる)やPeriphral module Disable(使わない周辺機能を切り離して省電力化できる)などはうれしいですね。

 

今回はPIC 16F18857のクロック設定についてスタディーしてみました。データシートを読めばわかる範囲ですが、頭の中を整理するために書き残します。加えて、クロック切り替え(Clock Switching)の速度を計測してみました。

 

まずは、最も簡単なLチカプログラム(RC0につないだLEDを1秒毎に点滅)です。

 

#include <xc.h>


#pragma config RSTOSC = HFINT32

#pragma config WDTE = OFF

 

#define _XTAL_FREQ 32000000

 

void main(void) {

    TRISC = 0; //All Output

    ANSELC = 0; //All Digital
 

    while (1) {
        LATCbits.LATC0 = 1;

        __delay_ms(500);
        LATCbits.LATC0 = 0;

        __delay_ms(500);
    }
}

 

このプログラムのクロックに関する設定はコンフィグレーション・ワード1(CONFIG1)のRSTOSCビットにHFINT32をセットしているだけです。

 

これによりPIC16F18857は、電源投入またはリセット直後に内部発振器(HFINTOSC)の発振周波数を32MHz(16F18857の最高クロック周波数)に設定して起動されます。

 

_XTAL_FREQ 32000000は、__delay_ms(500)が正確に500mSカウントできるように、クロック周波数が32MHzであることをコンパイラに教えているだけで、発振周波数の設定には直接関係はありません。

 

MPLAB X IDEとXC8コンパイラでコンパイルしてPICに書き込めば、RC0ポートにつないだLEDが1秒毎に点滅します。

 

しかしコンフィグレーション・ワードは、プログラムの書き込み時の設定ですから、プログラムの実行中に発振周波数を変更することはできません。

 

ところでPICには様々なCPUデバイスがありますが、グループによってクロック・システムの構成と規模は様々です。一般的にはPIC 12Fより16F/18F、さらに24F/33Fと、上位のCPUになるほど複雑で規模も大きくなります。USB内蔵のCPUでは48MHzも作れるようになっています。

16F18857_消費電流2.jpg

PIC 16F18857のクロック・システム

左側の原発振(Clock Source)を中央のマルチプレクサで選び、

右のポストスケーラ(分周器)で分周できるので、かなり自由度が高い。

内部発振器(HFINTOSC)は直接1から32MHzを2のn乗段階で指定できる

データシートより引用

 

PIC 16F18857のクロック・システムは16Fシリーズですからさほど複雑なものではありませんが、新しいCPUであることから、いくつかの特徴があるようです。16F1XXXに比べるとシンプルで使いやすくなったと感じます。

 

上の図を見ると、クロックシステムはシンプルな構成ながら、プログラムからの変更が自由にできるようになっています。

 

左端が原発振(Clock Source)とPLL、中央が原発振を選択するマルチプレクサ、右がポストスケーラ(分周器)です。

 

原発振はEXTOSC、SOSC、LFINTOSC、HFINTOSCの4種類。EXTOSCはPLLで4倍。HFINTOSCは2倍できるので、計6種類です。いずれの場合でも、最高周波数は32MHz(Fosc)です。(このときのCPUクロックはFosc/4でパフォーマンスは8Mipsです。)

 

EXTOSC(外部発振子、または発振器)またはEXTOSCの4倍(x4 PLL)

SOSC(セカンダリ外部発振子、または発振器)

LFINTOSC(31KHz内部発振器)

HFINTOSC(1から32MHzの内部発振器)またはHFINTOSCの2倍(x2 PLL)

 

このように原発振が複数用意されているのは、

 

外部発振でなるべく正確な周波数のクロックを得たい。(内部発振の精度は低い)

複数用意しておいて発振器が停止したような場合、切り替えたい。

外部の発振器が安定するまでの代替が必要。

用途や実行ステージによってクロックスピードを抑え、省電力化したい。

 

などの要求に応えるためと思われます。

 

この点、16F18857のクロックシステムは融通が効くように作られています。

 

クロック関係でプログラムの書き込み時に決めておかなければならないのは、先に出てきたCONFIG1のRSTOSCビットと、もうひとつはFEXTOSCビットです。

 

CONFIG1 RSTOSCビット 起動時のクロック源発振を決める

  以下から選べます。

    EXTOSC 外部発振子または外部発振器

    HFINT1 内部発振器HFINTOSC 1MHz

            LFINT     内部発振器LFINTOSC 31KHz

            SOSC      セカンダリ外部発振子、または発振器

            EXT4X    EXTOSC x 4倍 PLL

            HFINTPLL HFINTOSC x 2倍PLL(HFINTOSCは16MHzまで)

            HFINT32 HFINTOSC 32MHz

 

CONFIG1 FEXTOSCビット EXTOSCにつなぐ外部発振子、または外部発振器のモードを決める

  以下から選べます。

            ECH 8MHzを超える発振器(High Power)

            ECM 500KHzから8MHzの発振器(Medium Power)

            ECL 500KHz以下の発振器(Low Power)

            OFF オフ

            HS 4MHz以上の発振子(High Power)

            XT 500KHzから4MHzの発振子(Medium Power)

            LP 32.768KHzの発振子(Low Power)

 

RSTOSCビットで EXTOSCを選んだ場合、FEXTOSCビットにはつながっている外部発振子、または発振器に対応した設定が必要です。

 

たとえば、EXTOSCに8MHzの水晶発振子をつなぎ、4倍PLLを通して32MHzのクロックにしたい場合は、以下のように設定します。クロック(Fosc クロック周波数)は32MHzになります。

 

RSTOSC = EXT4X

FEXTOSC = HS

 

ただしRSTOSCビットはあくまでも起動時のクロック原発振を指定しているだけで、原発振はプログラム実行中に変更することができます。

 

また、HFINTOSC(内部発振器)は、OSCFRQレジスタのHFFRQビットで、1, 2, 4, 8, 12, 16, 32MHzのいずれかの周波数に自由に設定できます。

 

原発振の切り替えはOSCCON1レジスタのNOSCビットで行います。現在の原発振はOSCCON2レジスタのCOSCビットで確認できます。

 

ポストスケーラ(分周器)は、1:1から1:512の分周比をOSCCON1レジスタのNDIVビットで選択できます。例えば、HFINTOSCが32MHzの場合、1:32を選べばFoscは1MHzとなります。現在の分周比はSOSCON2レジスタのCDIVビットで確認できます。

 

OSCCON1レジスタのNOSCビット、およびNDIVビットの変更により、原発振の変更と、分周比の変更を有効にするために、コンフィグレーション・ワードCONFIG1のCSWEN(Clock Switch enable bit)をONにしておく必要があります。

16F18857のクロック設定と切り替え時間の計測1.jpg

CSWHOLDビット = 0の場合のクロック切り替えタイミング・チャート

右上はPIC 16F18857-I/SP

 

 

上図のタイミング・チャートにように、NOSCビットに新しい(OSCCON2レジスタのCOSCと異なる)周波数を設定すると、クロック切り替え動作が始まり、OSCCON3レジスタのORDYビットがクリアされます。(ORDYビットは新しいクロックが設定され準備ができると自動的にセットされます。)前のクロックから新しいクロックに切り替わる瞬間、クロックが無い期間が存在し、その間OSCCON3レジスタのNOSCRビットがセットされますが、クロックが無いためプログラムは動いていないので、実質NOSCRビットは見えません。代わりに、NOSCRビットがセットされると同時に割り込み要因のCSWIFビットがセットされ、割り込み可能(CSWIEビットがセットされている)なら、割り込みがかかります。割り込みルーチンでORDYビットがセットされるのを待てば、新しいクロックでプログラムを継続できます。以上はOSCCON3レジスタのCSWHOLDビットがクリアされている場合の話ですが、CSWHOLDビットをセットしておくと、割り込みルーチンでCSWHOLDビットをクリアするまで、前のクロックが使用されます。CSWHOLDビットを使用すれば、クロックが切り替わる前の処理が可能です。なお、割り込みを使わなくてもステータス・ビット等のポーリングで対応することもできます。

 

NOSCビットを変更しない(原発振は変えない)で、NDIVビットでポストスケーラ(分周器)の分周比のみを変更して、クロックを変更することもできます。

 

今回は実験として、クロックの切り替わるときにかかるオーバヘッドタイムを計測してみます。

PIC 16F18857に16MHZの発振器を接続

クロック周波数16MHzでLED(RC0)を点滅(1秒間隔)させ、10カウントしたら、

クロックを8MHzの内部発振器に切り替えてLEDを2秒間隔で点灯させる

LEDの点滅スピードでクロックの切り替わりが目で判別できる

周波数(クロックの1/4)はOSC2ピンで波形観測できる

RC1のLEDにプローブを当てればクロック切り替え時間が観測できる

 

 

実験: EXTOSC 16MHz からHFINTOSC 8MHzに切り替えたとき

 

下記プログラムで計測しました。

 

EXTOSCは、16MHzの外部発振器を使用しました。この場合接続にOSC1ピンしか使用しないので、クロックアウト機能によりOSC2ピンで発振周波数を見ることができます。ただしクロックFoscの1/4(Fosc/4)です。EXTOSC 16MHzで実行中は4MHz、クロックが切り替わってHFINTOSC 8MHzになると2MHzが観測されます。

 

オーバーヘッド・タイム(クロックが切り替わるために要した時間)は、切り替え直前でRC1につないだLEDをONし、割り込み処理ルーチンでORDYビットを確認した後OFFにし、それをオシロのワンショットトリガ・モードで観測しました。

 

結果、クロック切り替え時のオーバーヘッド・タイムは、約33usでした。

 

さまざまなクロック切り替えのオーバーヘッド・タイムを観測した結果を以下に示します。

 

以下はさまざまなクロック切り替えのオーバーヘッド・タイム実測値です。

 

左は起動時の設定。右はプログラム内で切り替えたクロック

 

①EXTOSC ECH 16MHz(水晶発振器) -> HFINTOSC 8MHz 33us

②HFINTOSC 32MHz -> EXTOSC ECH 16MHz(水晶発振器) 5.0uS

③HFINTOSC 32MHz -> HFINTOSC 16MHz (分周器で切り替え) 5.0us

④HFINTOSC 32MHz -> LFINTOSC 31KHz 2.2ms

⑤EXTOSC HS 8MHz(水晶発振子) -> EXTOSC HS 8MHz + x4 PLL 140us

⑥EXTOSC HS 8MHz(水晶発振子) + x4 PLL -> HFINTOSC 32MHz 25.6us

 

注:クロックが変わればインストラクション・サイクルも変わるため厳密な相対的比較はできません。このため細部にこだわっても意味がありませんので、大まかに遅いか、早いか程度の結果でしかありません。

 

ここでいうオーバーヘッド・タイムとはクロックが切り替わるまでの時間ではなく、切り替えるために必要な時間(割り込みを使用した場合)です。

 

以下はオーバーヘッド・タイムの計測方法

①PORTCbits.RC1 = 1; // Probe(On Triggered)

②OSCCON1bits.NOSC = 6; //HFINTOSCに切り替え -> 割り込み処理へ

 

割り込み処理

static void interrupt isr(void) {
③    if (PIR1bits.CSWIF == 1) {
④        PIR1bits.CSWIF = 0;
⑤        while (OSCCON3bits.ORDY == 0) continue; // Wait New Clock
⑥        PORTCbits.RC1 = 0; // Probe(Off)
    }

}

 

①から⑥までの経過時間を計測(純粋な切り替え時間ではなくこれらの命令実行時間も含む)

 

結果から、

外部発振器から内部発振器への切り替えは33usですが、逆に内部発振器から外部発振器への切り替えは5.0usで、早いと言えます。

 

内部発振器を分周器で変えた場合も早いようです。

 

PLL使用に切り替えると、PLLがロックするまで時間がかかります。

 

LFINTOSCの起動はかなり遅いようです。

16F18857のクロック設定と切り替え時間の計測2.jpg
16F18857のクロック設定と切り替え時間の計測3.jpg

RC1に接続したLEDにオシロのプローブを当てて観測

外部発振器EXTOSC ECH 16MHzから内部発振器HFINTOSC 8MHzに

切り替えたときのオーバーヘッド・タイム

33usと観測

 

以下のプログラムは最低限の設定になっています。ウォッチドッグタイマーはディセーブル(WDTE = OFF)にしておかないと、挙動がおかしくなります。

 

このプログラムでは分周器の設定は行っていませんが、1:1になっています。

 

__XTAL_FREQ が16000000に設定されているので、クロックが16MHzから8MHzに切り替わると、__delay_ms(500)は遅くなり、LEDの点滅が倍の2秒間隔となるので、目で確認できます。

 

上記①EXTOSC ECH 16MHz(水晶発振器) -> HFINTOSC 8MHz を計測するプログラム

#include <xc.h>

 

// CONFIG1
#pragma config FEXTOSC = ECH
#pragma config RSTOSC = EXT1X
#pragma config CLKOUTEN = ON
#pragma config CSWEN = ON

 

// CONFIG3
#pragma config WDTE = OFF 

 

#define _XTAL_FREQ 16000000

 

int i;

 

static void interrupt isr(void) {
    if (PIR1bits.CSWIF == 1) {
        PIR1bits.CSWIF = 0;
        while (OSCCON3bits.ORDY == 0) continue; // Wait New Clock
        PORTCbits.RC1 = 0; // Probe(Off)
    }
}

 

void main(void) {
    TRISA = 0; // PORTA すべて出力
    TRISC = 0; // PORTC すべて出力
    ANSELA = 0; //すべてデジタルI/O
    ANSELC = 0; //すべてデジタルI/O

 

    while (OSCSTATbits.EXTOR == 0) continue; // EXTOSCがReadyか確認

 

    OSCFRQbits.HFFRQ = 3; //HFINTOSCを8MHzに設定
    OSCCON3bits.CSWHOLD = 0; //クロックホールドを行わない
    PIE1bits.CSWIE = 1; // クロック切り替え割り込み許可
    PIR1bits.CSWIF = 0; // クロック切り替え割り込み要因フラグ クリア

    LATCbits.LATC0 = 0;
    LATCbits.LATC1 = 0;

    INTCONbits.PEIE = 1; //周辺機能割り込み許可
    INTCONbits.GIE = 1; //グローバル割り込み許可


    for (i = 0; i < 10; i++) {
        LATCbits.LATC0 = 1; //ハートビートLED 1秒毎に点滅(10回)
        __delay_ms(500);
        LATCbits.LATC0 = 0;
        __delay_ms(500);
    }

 

    PORTCbits.RC1 = 1; // Probe(On Triggered)
    OSCCON1bits.NOSC = 6; //HFINTOSCに切り替え

    while (OSCSTATbits.HFOR == 0) continue; // HFINTSCがReadyか確認

    //新しいクロックで開始
    while (1) {
        LATCbits.LATC0 = 1; // ハートビートLED 2秒毎に点滅
        __delay_ms(500);
        LATCbits.LATC0 = 0;
        __delay_ms(500);
    }
}
 

 

 

 

 

(JF1VRR)