TOTAL:1962, TODAY:75

dsPIC33Fの12bit-ADコンバータ

オシロスコープを作成する前に、dsPIC33FのADコンバータを試してみました。使用したのは、マイクロチップ社から購入したデモボード(16-Bit 28-Pin Starter Board)です。このボードにはdsPIC33FJ12GP202が付属しており、可変抵抗を通してサンプリング用のアナログピンに接続されているので、簡単に実験ができます。プログラムは統合開発環境MPLAB IDE v8.0MPLAB C30 コンパイラ v3.02を使用しました。

実験ボードの回路図

デモボード(16-Bit 28-Pin Starter Board)の回路はもっと色々なものが付いていますが、今回の実験に関係するところだけ回路図として書いてみました。

デモボードにはあらかじめAN5ピンに10KΩの可変抵抗が接続されているので、この電圧をサンプリングします。250msecごとにサンプリングし、1秒ごとに4回の平均値をシリアル端末に出力します。LEDが4個ついているので、サンプリングするごとにピカピカ光るようにします。dsPIC33Fではサンプリングの量子化ビットとして10bitと12bitの二つが選択できます。10bitは4チャンネル同時にサンプリング可能で、1チャンネルあたり1.1Msps(※spsはサンプリング/秒)と高速なサンプリングが可能です。12bitは同時には1チャンネルのサンプリングで、500kspsの速度です。今回は12bitモードでサンプリングすることにしました。

12bit-ADコンバータのプログラム

dsPIC33Fのレジスタ設定が書かれている日本語サイトは少ないようなので、コンフィギュレーションレジスタは、データシートとヘッダー(C:\Program Files\Microchip\MPLAB C30\support\h\ p33FJ12GP202.h)を見ながら適当に設定しました。後閑先生のdsPIC30Fの本も参考にしたのですが、dsPIC30Fに比べて設定項目が多いです。ブートコードセグメントや汎用コードセグメントの保護機能が追加されたみたいですが、個人の電子工作ではあんまり関係ないので、すべてオフです。FRCで起動後、PLLありで水晶発振子で動かす設定です。

/*
 * PIC33FJ12GP202 デモボードのテストプログラム
 * C30ライブラリを使用
 */
#include <stdio.h>

#include "p33FJ12GP202.h"
#include "timer.h"
#include "uart.h"
#include "adc.h"

// ブートコードセグメントの保護領域なし(書き込み可)
// ブートコードセグメントの保護機能オフ
_FBS(BSS_NO_FLASH & BWRP_WRPROTECT_OFF);

// 汎用コードセグメントの保護機能なし(書き込み可)
// 汎用コードセグメントの保護機能オフ
_FGS(GSS_OFF & GCP_OFF & GWRP_OFF);

// PLLありの主発振器(XT/HS/EC)、FRCで起動した後、主発振器にスイッチ
_FOSCSEL(FNOSC_PRIPLL & IESO_ON);

// クロック切り替え無効、フェールセーフクロックモニタ無効
// I/Oピンの再コンフィギュレーション不可
// OSC2ピンは汎用デジタルI/O
// クリスタル(XT)発振モード
_FOSC(FCKSM_CSDCMD & IOL1WAY_ON & OSCIOFNC_ON & POSCMD_XT);

// ウォッチドッグタイマーをユーザソフト(RCONレジスタ)で設定
// ウォッチドッグタイマーウィンドウオフ
// ウォッチドッグタイマープリスケーラ 128
// ウォッチドッグタイマーポストスケーラ 32768
_FWDT(FWDTEN_OFF & WINDIS_OFF & WDTPRE_PR128 & WDTPOST_PS32768);

// パワーオンリセット時間 128msec
// I2Cピン SDA1/SCL1マップ
_FPOR(FPWRT_PWR128 & ALTI2C_ON);

// バックグラウンドデバッグオフ
// デバッガ/エミュレータオフ
// JTAGオフ
// PGC1/EMUC1、PGD1/EMUD1での通信
_FICD(BKBUG_OFF & COE_OFF &JTAGEN_OFF & ICS_PGD1);

dsPIC33Fは、PIC24FやdsPIC30Fに比べて、PLL倍率がいろいろと設定です。水晶発振子の周波数をFinとすると、PLLありでの周波数Foscは次のようになります。
       Fosc = Fin * (M / (N1 * N2)
ここで、MはPLL倍率、N1はPLLプリスケーラー、N2はPLLポストスケーラです。デモボードの水晶発振子Finは7.3728MHzであり、Foscは80MHzが最大です。ほぼ80MHzにするため、M=43, N1=2, N2=2としました。この設定により、Fosc=79.2576MHz、Fcy=39.6288MHzになります。一応、FcyとMIPS値は1対1の関係らしく、約40MIPSということになります。

int main(void)
{
    // 発振器の設定:dsPIC33Fは、命令サイクル周波数Fcyは最大40MHz
    // Fosc= Fin * M / (N1 * N2), Fcy = Fosc / 2
    // Fosc= 7.3728 * 43 / (2 * 2) = 79.2576MHz
    PLLFBD = 41;                // M=43
    CLKDIVbits.PLLPOST = 0;     // N1=2
    CLKDIVbits.PLLPRE = 0;      // N2=2

    // ウォッチドッグタイマーオフ
    RCONbits.SWDTEN = 0;

    // PLLロックを待つ
    while(OSCCONbits.LOCK !=1 );

コンフィギュレーションレジスタで、ウォッチドッグタイマーはユーザ側で設定できるようにしたので、RCONレジスタによりオフにします。その後、FRCからPLLあり水晶発振に切り替わるまでOSCCONレジスタのLOCKビットを使って待ちます。

dsPIC33Fの12bitモードADコンバータの設定は、例えば次のようになります。

/*
 * ADC_MODULE_ON        : ADC有効
 * ADC_IDLE_CONTINUE    : IDLE時動作継続
 * ADC_AD12B_12BIT      : 12bitサンプリング
 * ADC_FORMAT_INTG      : 符号なし整数に変換(0-1023)
 * ADC_CLK_TMR          : タイマー3割り込みで変換開始
 * ADC_SIMULTANEOUS     : 複数チャンネル同時サンプリング
 * ADC_AUTO_SAMPLING_ON : AD変換後、自動でサンプリング開始
 * ADC_SAMP_OFF         : サンプル有効化               
 */
unsigned int g_ADCON1 = 
        ADC_MODULE_ON & ADC_IDLE_CONTINUE & ADC_AD12B_12BIT & 
        ADC_FORMAT_INTG & ADC_CLK_TMR & ADC_SIMULTANEOUS &
        ADC_AUTO_SAMPLING_ON & ADC_SAMP_OFF;

/*
 * ADC_VREF_AVDD_AVSS   : VrefH = AVDD(28pin), VrefL = AVSS(27pin)
 * ADC_SCAN_OFF         : 自動スキャンオフ
 * ADC_SELECT_CHAN0     : 有効チャンネル
 * ADC_DMA_ADD_INC_4    : 4回のサンプリングで割り込み
 * ADC_ALT_BUF_OFF      : シングルバッファ(16ワード)
 * ADC_ALT_INPUT_OFF    : MUXAだけを使用
 */
unsigned int g_ADCON2 = 
        ADC_VREF_AVDD_AVSS & ADC_SCAN_OFF & 
        ADC_SELECT_CHAN_0 & ADC_DMA_ADD_INC_4 &
        ADC_ALT_BUF_OFF & ADC_ALT_INPUT_OFF;

/*
 * ADC_CONV_CLK_SYSTEM  : システムクロックを使用
 * ADC_SAMPLE_TIME_1    : サンプリング時間 1*Tad
 * ADC_CONV_CLK         : ADCS = Tad / Tcy - 1
 *   条件:Tcy = 1 / (79.2576MHz / 2) = 25.2ns, Tad > 117.6ns
 *   ※PIC33Fの12bitモードは500Kspsであり、1 / (117.6*17) nsec = 500Ksps
 *   Tad = Tcy * (ADCS + 1) : ADCS = Tad / Tcy - 1 
 *   ADCS = 117.6 / 25.2 - 1 = 3.667 => 4
 *   Tad = 25.2 * (4 + 1) = 126ns
 *   ADCS = b'000100 => 0xFFC4
 * サンプリング時間: 3 Tad,     変換時間 : 14 Tad
 * 合計 : 17 Tad = 17 * 126 ns = 2.142 usec
 */
unsigned int g_ADCON3 = 
        ADC_CONV_CLK_SYSTEM & ADC_SAMPLE_TIME_3 & (0xFFC4);

/*
 * ADC_CH123_NEG_SAMPLEB_VREFN : サンプルBのCH1,CH2,CH3のリファレンスはVref
 * ADC_CH123_POS_SAMPLEB_0_1_2 : サンプルBのCH1,CH2,CH3入力はAN0,AN1,AN2
 * ADC_CH123_NEG_SAMPLEA_VREFN : サンプルAのCH1,CH2,CH3のリファレンスはVref
 * ADC_CH123_POS_SAMPLEA_0_1_2 : サンプルAのCH1,CH2,CH3入力はAN0,AN1,AN2
 */
unsigned int g_AD1CHS123 =
        ADC_CH123_NEG_SAMPLEB_VREFN & ADC_CH123_POS_SAMPLEB_0_1_2 &
        ADC_CH123_NEG_SAMPLEA_VREFN & ADC_CH123_POS_SAMPLEA_0_1_2;

/*
 * ADC_CH0_NEG_SAMPLEB_VREFN   : サンプルBのCH0のリファレンスはVref
 * ADC_CH0_POS_SAMPLEB_AN5     : サンプルBのCH0入力はAN5
 * ADC_CH0_NEG_SAMPLEA_VREFN   : サンプルAのCH0のリファレンスはVref
 * ADC_CH0_POS_SAMPLEA_0_1_2   : サンプルAのCH1入力はAN5
 */
unsigned int g_AD1CHS0 =
        ADC_CH0_NEG_SAMPLEB_VREFN & ADC_CH0_POS_SAMPLEB_AN5 &
        ADC_CH0_NEG_SAMPLEA_VREFN & ADC_CH0_POS_SAMPLEA_AN5;

ACコンバータのクロック周期Tadと命令サイクルTcyには次の関係があります。
    Tad = Tcy * (ADCS + 1)
ここで、ADCSはADxCON3レジスタにある変換クロック設定ビットです。dsPIC33FJ12GP202のデータシートによると、12bitモードの場合、Tadは113nsec以上必要です。Tcy=(1/39.6288MHz) =25.2nsecであり、ADCSを4にしました。これにより、Tadは126nsecとなります。今回は1チャンネルだけを使用するので、AN5をCH0に接続し、4回サンプリングするごとに割り込みが掛かるようにします。ADコンバータはタイマー3をトリガーにAD変換するようにしています。12bitモードの場合、14Tadで変換が完了します。その後、自動でサンプリングが行われ、またタイマー3割り込みによりAD変換が行われます。即ち、
    タイマー3の周期 = サンプル時間 + 14*Tad > 17*Tad
となります。12bitモードの場合、このサンプリング時間が3Tadよりも大きい必要があります。結局、タイマー3の周期は17Tad以上必要ということになります。今回はタイマー3の周期を250msecに設定しているため、十分余裕があります。メインプログラムは次のようになります。

int main(void)
{
    :    : 途中略 :    :
    /* 
     * PIC33FのI/Oピン割付
     */
    /* UART */
    RPINR18 = 9;             // RP9をU1RXに設定
    RPOR4bits.RP8R = 3;      // RP8をU1TXに設定

    /* ADC用アナログピン設定 */
    AD1PCFGL = 0x03C0;       // AN00-AN05をアナログ、AN6-AN9をデジタルピン
    TRISA = 0xFFFF;          // RA0-RA4を入力ピンに設定

    /* LED用のピン設定 */
    LATB = 0x0000;           // LED7 ON : RB0-15をLOWレベルに設定
    TRISB = 0x0FFF;          // RB12-15をLED出力ピンに設定

    /* UART1の設定 */
    OpenUART1(g_U1MODE, g_U1STA, BRGVAL);

    /* 10bit ADコンバータ */
    SetChanADC1(g_AD1CHS123, g_AD1CHS0);
    OpenADC1(g_ADCON1, g_ADCON2, g_ADCON3, 0, 
        0x03C0, 0, 0, 0x0000);
    ConfigIntADC1(ADC_INT_PRI_5 & ADC_INT_ENABLE);

    /* タイマー3の設定 */
    OpenTimer3(g_TIMER3, g_TIMER3_INT);

    /* 
     * 割り込みなしでもTIMER3の周期でAD変換トリガーとして設定可能だが、
     * 本アプリでは、TIMER3の割り込みハンドらでLEDを点灯させたいため、
     * 割り込みをONに設定
     */
    ConfigIntTimer3(T3_INT_PRIOR_4 & T3_INT_ON);

    while(1)
    {
        if (adcFlag != 0)
        {
            unsigned int voltage;

            /* 標準入出力は128~512バイト程度ヒープ領域が必要 */
            /* UART1のみ使用可能 */
            voltage = 3300 * (unsigned long)adcData / 1023;
            printf("A/D = %d[mV], %d\r\n", voltage, adcData);
            adcFlag = 0;
        }
    }

    return 0;
}

void __attribute__((interrupt, no_auto_psv)) _T3Interrupt(void)
{
    IFS0bits.T3IF = 0;          /* 割り込みフラグをクリア */

    if (ledCount == 0)
        LATB = 0xE000;
    else
        LATB &= ~(LED_BIT << ledCount);

    ledCount++;
}

void __attribute__((interrupt, no_auto_psv)) _ADC1Interrupt(void)
{
    IFS0bits.AD1IF = 0;         /* 割り込みフラグをクリア */
    
    adcData = (ReadADC1(0) + ReadADC1(1) + ReadADC1(2) + ReadADC1(3)) / 4;
    adcFlag = 1;

    ledCount = 0;
}

ADコンバータは、SetChannelADC1でチャンネル選択後、OpenADC1関数で起動します。4回のサンプリングで割り込みがかかるように設定したため、割り込みハンドで平均値を取るようにしています。
タイマー3やUARTの設定は上記ソースには載っていませんが、興味があればダウンロードしてください。タイマー3は250msec周期、UARTシリアル通信はボーレート57600、8ビットパリティなし、ストップビット長1にしてあります。

サンプリング電圧の表示

上記のプログラムをMPLAB ICD2でロム焼きし、動かしました。シリアル端末にTeraTermProを使って測定表示するようにしました。下の絵は可変抵抗を手動で変化させた時のサンプリング電圧です。当たり前ですが、3.3V(量子化値:4095)~0V(量子化値:0)が正しくサンプリングされているようです。

今回は12bitモードを使用しましたが、次回は1Mspsの10bitサンプリングを試してみたいと思います。

最新の7件

OpenGL

電子工作

玄箱HG

ホームページ

日記

Copyright (C) 2007 Arakin , All rights reserved.