PICマイコン(PIC12F675)のAD変換の使い方

組み込みエンジニア

こんにちは、ENGかぴです。

マイコンのソフトを開発しているとAD変換をよく使います。アナログ入力を受けて信号が何ボルト相当のデータであるかを判断して処理を分岐したりするなど応用範囲は様々です。AD変換の使い方についてPIC12F675を使って説明しています。

AD変換はタイマで変換のタイミングを作ることが多くなります。正弦波の波形データを取りたい時などは同間隔でサンプリングする必要があります。AD変換のタイミングをタイマ管理で行う方法について下記記事にまとめています。

PICマイコン(PIC12F675)のAD変換とタイマーの組み合わせ

PIC12F675を使ってマイコンの動きを勉強するためにPIC12F675の機能でできることについてまとめています。

PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ

マイコンのAD変換について

AD変換のイメージ図
AD変換のイメージ図

ADコンバータ(ADC)はアナログ入力電圧をデジタルデータに変換します。マイコンには10ビットの分解能をもつものや12ビットの分解能をもつものがあります。

ADCが10ビットとしてリファレンス電圧(基準電圧)がDC5Vとすると0Vを0x000、5Vを0x3FFというようにアナログ入力電圧を10ビットのデジタルデータに変換します。

アナログ入力電圧範囲

マイコンによって入力電圧範囲は異なりますが、電気的特性を確認して入力電圧が範囲以内に収める必要があります。基本的に電源電圧以上の入力にならないように外部回路を組む必要があります。

AD変換の電圧範囲

リファレンス電圧が5Vであるなら正弦波の波形の波高値が5Vよりも低いところに設定しておいた方が良いでしょう。

ぎりぎりに設定するとリファレンス電圧によってはデジタルデータに変換した時に変換値が頭打ちになり正確に表現できなくなってしまいます。

リファレンス電圧

引用:PIC12F675のAD変換の電気的特性
引用:PIC12F675のAD変換の電気的特性

リファレンス電圧はADCにおける基準電圧になります。外部で電源を設置して精度良い基準電圧を作ってリファレンス電圧とすることもできます。電源電圧をリファレンス電圧として使用することもできます。

VREFについての特性を見ると2.5V以上で10ビットの精度を保証するとなっています。2.5V以下になると精度が落ちてしまうこともあるので注意が必要です。

リファレンス電圧をGP1/VREF端子の電圧にする場合は、GP1/VERF端子をアナログ入力モードの入力設定にして、外部回路からリファレンス電圧を入力します。

外部回路は1000uA以上の電流を流すようにし、リファレンス電圧をVDD+0.3V以下にする必要があります。

ホールドコンデンサ

マイコンのADCにはサンプリングスイッチとホールドコンデンサがついています。ADCの電源がオンになっていてADCが実行中でないときは、サンプリングスイッチが閉じてホールドコンデンサに電流が流れてチャージします。

マイコンの種類によっては、ADC設定にホールドコンデンサをディスチャージするかしないかを選択するものもあります。

ADCを実行する前にホールドコンデンサがアナログ入力電圧と同じになるまで待つ必要があります。マイコンにもよりますが、数十μs以上待つ必要があります。

ADCの入力モデルとインピーダンス

引用:PIC12F675のデータシート(ANALOG INPUT MODEL)
引用:PIC12F675のデータシート(ANALOG INPUT MODEL)

マイコンのADCの入力モデルを確認するとインピーダンスを持っていることが分かります。Rsはアナログ入力のインピーダンスデータシートには最大で10kΩ(ブロック図の上のNOTEに記載されています)までと書いてあります。

マイコンのデータシートのADC部分に等価回路と制限事項が書いてあるので確認してから外部回路の設計を行ったほうが良いでしょう。

マイコンのADCには内部でインピーダンスを持っており入力インピーダンスとの関係からアナログ入力電圧が分圧されるので誤差にならないように注意する必要があります。

PICマイコン(PIC12F675)の初期化

PICマイコンを使用するためには、マイコンをどのような機能で使用するのかを選択する初期化を行う必要があります。ADCを使用するまでの初期化について説明していきます。

共通部分の初期化

Configuration BitやWDTの設定については下記記事を参考にしてください。

PICマイコン(PIC12F675)を使ってWDTの意味を考える

I/Oポートの設定

アナログ入力端子として使用するI/O端子をDI端子に設定しておきます。今回はGP0をアナログ入力端子として使用します。

引用:PIC12F675のデータシート(TRISIOレジスタ)
引用:PIC12F675のデータシート(TRISIOレジスタ)

bit0がGP0のI/Oの設定になります。入力端子として使用したいので1をセットします。

ADCON0レジスタの設定

ADCONレジスタはADCに電源を入れるのかなどの設定が行えます。また、アナログ入力端子として使用するGPIO端子を選択します。

引用:PIC12F675のデータシート(ADCON0レジスタ)
引用:PIC12F675のデータシート(ADCON0レジスタ)

bit0はADCを使用するためにADCの電源をONします。1をセットするとADCの電源がONになり0をセットするとOFFになります。

bit1は1をセットするとAD変換を開始します。AD変換が完了するとハード側(マイコン)でクリアされます。

bit3-2はアナログ入力として使用するGPIO端子を端子を選択します。ユーザが使用したいアナログチャンネルを選択してビットをセットします。AN0(GP0)を使用する場合は0b00となりAN1(GP1)の場合は0b01のようにセットします。

bit5-4は使用しません。

bit6はリファレンス電圧について選択します。1をセットすると外部から電圧を供給する設定になります。0をセットすると電源電圧を使用することになります。

bit7はAD変換のデータを右詰左詰めを選択します。1をセットすると右詰になり0をセットすると左詰めになります。

AD変換結果の右寄せ左寄せ
AD変換結果の右寄せ左寄せ

AD変換の結果を10ビットすべて使う場合は右詰の方が使いやすいと思います。

ANSELレジスタの設定

引用:PIC12F675のデータシート(ANSELレジスタ)
引用:PIC12F675のデータシート(ANSELレジスタ)

ANSELレジスタはADCのクロックやGPIOとアナログ入力モードの切り替えを行います。

bit3-0はGP0/AN0端子からGP4/AN3までの端子をアナログ入力モードで使用するかI/Oモードで使用するかを選択します。1をセットするとアナログ入力モードになり0をセットするとI/Oモードになります。

bit6-4はADCのクロックを設定します。

ADCには専用のRC発振器がついておりこれを使用するときは0x?11(?は0でも1でもよく無効となる)を設定することができます。FRCの周期は約4usになっています。FRC使うとスリープモードでもAD変換ができます。

ADCクロックの注意事項(PIC12F675)

クロックの設定については注意する事項があります。データシートには注意事項が記載されています。AD変換の実行にはAD変換クロックを使用します。

AD変換クロックの周期をアクジション時間(TAD)といい下の表に従って設定することが推奨されています。

引用:PIC12F675のデータシート(クロック周波数について)
引用:PIC12F675のデータシート(クロック周波数について)

内部クロックを選択しているので動作クロックが4MHzとなるためDevice Frequencyの4MHzの欄をみて1.6us以上になる8Tosc以上を選択することが推奨されます。

逆に長すぎるとホールドコンデンサが自己放電する可能性もあるので可能な限り1.6usに近い値にすることが推奨されています。

初期化部分を実装する

#include <xc.h>
//--------------定数定義----------------------------
#define _XTAL_FREQ	4000000
#define VTH3 0x02FF		//電源電圧の3/4
#define VTH2 0x01FF		//電源電圧の2/4
#define VTH1 0x0FF		//電源電圧の1/4

//--------------変数定義----------------------------
unsigned short    vdd; //10ビットのデータを計算するため2バイトデータとする

void main(void){
   //各種初期化  
    ADCON0 = 0x81;  //ADコンバータの電源をON。結果は右詰。
    ANSEL = 0x11;   //GP0をアナログ入力に使用。変換クロックは8TOSC
    CMCON = 0x07;   //コンパレータ使用しない
    TRISIO = 0x09;  //GP0,GP3入力、その他出力
    GPIO = 0x00;    //ポートの設定 1:High 0:Low
    OPTION_REG = 0x4F;  //プリスケーラ128 WDTで使用
    //割り込み設定
    INTCON = 0xC0;  //PIE1割り込みを許可
    PIE1 = 0x40;    //AD変換終了
    //初期化
    vdd = 0;
    __delay_ms(10); //AD変換完了ウェイト(ADCが動作開始するまでのウェイト)
    //AD変換起動
    ADIF = 0;	//AD変換終了フラグクリア
    GO_DONE = 1;

ADCが動作開始するまでの安定時間としてウェイトを10msおいています。実際は数十usほどでよいのですが余裕をもって10msにしています。

DC5Vを4分割してLEDの点灯/消灯をさせるた目の基準値を定義します。ADCは0~0x3FFでDC5Vを表現することになるのでDC1.25Vは0xFF、DC2.5Vは0x1FF、DC3.75Vは0x2FFとなります。ADCは10ビットなので計算するための変数は2バイトデータとしています。

回路図を考えてADCを実装する

AD変換の動作確認の回路図
AD変換の動作確認の回路図

可変抵抗で電源電圧を分圧してアナログ入力端子に入力してLEDの点灯/消灯させます。AN0に可変抵抗で分圧した抵抗値を入力するようにしています。入力電圧に対する動作のパターンは以下の通りとします。

  1. 1.25V未満:LED1とLED2はともに消灯
  2. 1.25V以上、2.5V未満:LED1点灯、LED2消灯
  3. 2.5V以上、3.75V未満:LED1消灯、LED2点灯
  4. 3.75V以上:LED1とLED2はともに点灯

ADCを実装し、2つのLEDの点灯/消灯する

    while(1){
        CLRWDT();
        di(); //比較時に割り込みとならないように一旦停止する
        if( vdd >= VTH1 && vdd < VTH2){
            GPIO2 = 1;
            GPIO5 = 0;
        }
        else if( vdd >= VTH2 && vdd < VTH3 ){
            GPIO2 = 0;
            GPIO5 = 1;
        }
        else if( vdd >= VTH3){
            GPIO2 = 1;
            GPIO5 = 1;         
        }else{
            GPIO2 = 0;
            GPIO5 = 0;             
        }
        ei(); //比較後、割り込みを許可する
    }	
//割り込み関数
void __interrupt() intr(void){
    
    if( ADIE == 1 && ADIF == 1){    //AD変換完了割り込みか
        ADIF = 0; //割り込みフラグをクリア
        vdd = ADRESH;
        vdd = ( vdd << 8) + ADRESL; //シフトを使ってデータを格納する
        GO_DONE = 1;    //AD変換開始
    }
}

AD変換を無限ループに入る前にスタートしてメインループではADCに入力された電圧を計算して各種条件と比較してLED1とLED2の点灯/消灯を制御します。

LEDの点灯条件の処理を行っている途中で割り込みが発生し値を書き換えてしまうとメイン関数に戻ってきたときに判定がずれてしまうことがあるので割り込み内で更新し別の個所で参照する場合は一旦割り込みを禁止しておく必要があります。

AD変換が完了するとAD変換完了の割り込みが発生しますので割り込み関数内でAD変換の値をVddに計算しています。Vddを計算した後もう一度GO_DONEによってAD変換をスタートしています。

アナログ入力端子に直流などのほぼ一定の値を入力する際はAD変換のタイミングはそれほど問題になりませんが、正弦波を入力して実効値を算出したい場合などは一定間隔でAD変換する必要があります。一定間隔はタイマーを使って管理する必要があります。

下記記事ではADCとタイマーを組み合わせての考え方について記事にしています。興味があればご覧ください。

PICマイコン(PIC12F675)のAD変換とタイマーの組み合わせ

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。チェックはしておりますがコメントなど細かな部分で間違っている可能性があります。参考としてお使いいただければと思います。

// CONFIG
#pragma config FOSC = INTRCIO
#pragma config WDTE = ON
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config BOREN = OFF
#pragma config CP = OFF
#pragma config CPD = OFF

#include <xc.h>
//--------------定数定義----------------------------
#define _XTAL_FREQ	4000000
#define VTH3 0x02FF		//電源電圧の3/4
#define VTH2 0x01FF		//電源電圧の2/4
#define VTH1 0x0FF		//電源電圧の1/4

//--------------変数定義----------------------------
unsigned short    vdd; //10ビットのデータを計算するため2バイトデータとする

void main(void){
   //各種初期化  
    ADCON0 = 0x81;  //ADコンバータの電源をON。結果は右詰。
    ANSEL = 0x11;   //GP0をアナログ入力に使用。変換クロックは8TOSC
    CMCON = 0x07;   //コンパレータ使用しない
    TRISIO = 0x09;  //GP0,GP3入力、その他出力
    GPIO = 0x00;    //ポートの設定 1:High 0:Low
    OPTION_REG = 0x4F;  //プリスケーラ128 WDTで使用
    //割り込み設定
    INTCON = 0xC0;  //PIE1割り込みを許可
    PIE1 = 0x40;    //AD変換終了
    //初期化
    vdd = 0;
    __delay_ms(10); //AD変換完了ウェイト(ADCが動作開始するまでのウェイト)
    //AD変換起動
    ADIF = 0;	//AD変換終了フラグクリア
    GO_DONE = 1;
    
    while(1){
        CLRWDT();
        di(); //比較時に割り込みとならないように一旦停止する
        if( vdd >= VTH1 && vdd < VTH2){
            GPIO2 = 1;
            GPIO5 = 0;
        }
        else if( vdd >= VTH2 && vdd < VTH3 ){
            GPIO2 = 0;
            GPIO5 = 1;
        }
        else if( vdd >= VTH3){
            GPIO2 = 1;
            GPIO5 = 1;         
        }else{
            GPIO2 = 0;
            GPIO5 = 0;             
        }
        ei(); //比較後、割り込みを許可する
    }
}		
//割り込み関数
void __interrupt() intr(void){
    
    if( ADIE == 1 && ADIF == 1){    //AD変換完了割り込みか
        ADIF = 0; //割り込みフラグをクリア
        vdd = ADRESH;
        vdd = ( vdd << 8) + ADRESL; //シフトを使ってデータを格納する
        GO_DONE = 1;    //AD変換開始
    }
}
//---------------------end file----------------------------

関連リンク

PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。

PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

TECH::CAMPプログラミング教養【無料体験会】

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました