【演習】A/D変換処理

A/D変換の処理を利用するための設定をしてみたいと思います。
A/D変換は割り込みを使用する/しない。両方設定ができますが、今回は割り込みを使用するパターンで解説をします。

PIC16F1614では10bitのA/Dポートが8ch用意されています。
今回は全てのポートをA/Dポートとして使用しなくても良いので、そのうちの1chを設定したいと思います。

A/Dポートとして設定する

8ch用意されたA/D変換のポートがどれなのか?を確認してみましょう。
仕様書には機能割り当て一覧表が用意されています。
A/Dは赤枠で囲まれた範囲が充当されています。

最左列がI/Oピンのピン名となっており、ピン名にA/Dのチャンネル記載があると、そのチャンネルとしてA/Dが設定できます。

例えば、RA4であればAN3というA/Dチャンネル名で利用ができる。ということです。

設定するレジスタ

何も設定をしなければ、デフォルトでは通常のI/Oピンとしての利用になります。
今回はRA4をA/Dの入力として設定したいと思います(他のピンに設定したい場合は読み替えしてください)

RA4をA/Dポートとして使用したい場合に設定が必要なレジスタは、以下になります。
TRISA、ANSELA、ADCON0、ADCON1 です。

TRISAから見てみましょう。
このレジスタはI/Oの設定を入力 or 出力のどちらで使用するかを設定するレジスタです。
ここでは、A/Dとして使用したいポートは、入力に設定しておく必要があります。
今回、RA4を設定したいので、TRSA4のbitに1の設定をする必要があります。

次にANSELAレジスタを見てみましょう。
このレジスタはA/Dポートへ変更したいポートをアナログ入力に設定するレジスタです。
このレジスタが設定されていないと、A/Dポートにはなりません。
今回、RA4を設定したいので、ANSA4のbitに1の設定をする必要があります。

次にADCON0レジスタをみてみましょう。
このレジスタはA/Dの変換処理の開始/停止を設定するレジスタです。
変換開始したいタイミングで開始。止めたいタイミングで停止。の設定ができます。
また、変換/停止したいA/Dのポートも指定をする必要があります。

今回であればRA4なので、CHS<4:0>bitに00011(AN3)を設定し、ADON bitに1を設定します。
ADON bitは、A/D変換を有効にする設定bitです。

Go/DONE bitを1にすると変換開始になります。
その為、Go/DONE bitは変換開始したい処理タイミングで設定することになります。

次にADCON1を見てみましょう。
このレジスタはA/D変換の条件を設定するレジスタです。
ADFM bitは、変換後の結果値をレジスタの右詰で格納するか、左詰で格納するかを設定します。
10bitのA/Dなので、2進数で 00 0000 0000 の範囲を右詰するか、左詰するかの設定です。
今回は右詰で設定としますので、ADFMは1を設定します。

ADCS bitは変換に使用するクロックを設定します。
変換速度に関わるもので、早ければ良いわけではありません。
マイコンの内部回路としてコンデンサへの充電時間に関わるので、適切な設定にしなければ、
変換結果が理論上の数値と合わないことが起きます。
今回は、マイコンの内部発振をそのまま利用しますので、ADCSは111を設定します。

ADPREF<1:0> bitは、基準となる電位を何にするかを設定します。
A/Dポートの入力電位を何と比較したいか?です。
今回はマイコンの電源電圧と比較したいので、ADPREFは0を設定します。

割り込みで利用したいので、タイマーの時と同様にA/Dの割り込みレジスタへ設定を行います。
PIR1レジスタのADIFを0にして、割り込み設定前に割り込み状態のフラグになっていない様にします。
その上でPIE1レジスタのADIEを1に設定してA/D変換の割り込みを有効にします。

ここまでで事前設定は完了です。

実際のA/D変換処理

実際にA/D変換を行うときは上記の設定を初期化部でプログラム記載を行い、
mainで以下の様な記述をします。

#include <xc.h>
#include <stdint.h>

#define _XTAL_FREQ 16000000UL

volatile uint16_t ad_value = 0;
volatile uint8_t ad_complete = 0;
valatile float vin;

void ADC_Init(void)
{
  OSCCON = 0x7A;   /* 内部発振周波数 16MHz 設定 0b01111010 */

    // RA4 = AN3
    TRISAbits.TRISA4 = 1;
    ANSELAbits.ANSA4 = 1;

    // ADC設定
    ADCON1bits.ADFM   = 1;      // 右詰め
    ADCON1bits.ADCS   = 0b111;  // FRC
    ADCON1bits.ADPREF = 0b00;   // VREF+ = VDD

    // AN3選択
    ADCON0bits.CHS = 0b00011;

    // ADC割り込み
    PIR1bits.ADIF = 0;          // ADC割り込みフラグクリア
    PIE1bits.ADIE = 1;          // ADC割り込み許可

    INTCONbits.PEIE = 1;        // 周辺割り込み許可
    INTCONbits.GIE  = 1;        // 全体割り込み許可

    ADCON0bits.ADON = 1;        // ADC ON
}

void main(void)
{
    ADC_Init();

    __delay_us(10);     // 最初のAcquisition Time
    ADCON0bits.GO_nDONE = 1;

    while (1)
    {
        if (ad_complete)
        {
            ad_complete = 0;

            // ad_valueを使った処理
      vin = (float)(ad_value * 5.0f) / 1023.0f;

            //2回目以降もA/D変換したい場合は開始設定
            ADCON0bits.GO_nDONE = 1;
        }
    }
}

void __interrupt() ISR(void)
{
    if (PIE1bits.ADIE && PIR1bits.ADIF)
    {
        PIR1bits.ADIF = 0;

        ad_value = ((uint16_t)ADRESH << 8) | ADRESL;
        ad_complete = 1;
    }
}

割り込み部はタイマーの時と同じ様な記述になります。

A/D変換は、変換開始 → ADC変換中 → 変換完了 → 割り込みの順で推移します。
その為、自動的にA/D変換が開始されるわけではないので、プログラム上で開始するタイミングを作り、開始設定をしなければ変換は始まりません。