UMEHOSHI ITA TOP PAGE    COMPUTER SHIEN LAB

UMEHOSHI IT (my_adc.c と my_adc.h)

マイクロコントローラ (PIC32MX270F256B-50I/SP)のADCは10 ビット で16個アナログ入力があります(参考:61104F_JP.pdf)
このの使い方はいくつかありますが、 次のようにAN0とAN1の2つのアナログ入力を、それぞれMUX A およびMUX B の入力に設定し、 マルチプレクサでADCを交互に使うアナログ入力を変換する処理を組み込んでいます。

サンプリングしたワードは、左赤マルで示したADC1BUF0〜ADC1BUFFのレジスタに 記憶されます。(16 ビット符号なし整数の0〜1024を記憶させています。)
この16個のレジスタを 2つの8ワードバッファ(ADC1BUF) に分割構成して(AD1CON2bits.BUFM = 1)にしています。
一方のADC1BUFにサンプリング中に、もう一方のADC1BUFから取り出して後述の「struct ADC_BUFF」内に記憶します。

AN0とAN1の2つの入力を交互にサンプリングする指定(AD1CON2bits.ALTS = 1)で行っています。
組み込まれるプログラムでは、AN0かAN1の一方を指定する場合も、 AN0とAN1の2つの入力を交互にサンプリングしており、USBの出力だけが一方だけ出力をしています。
この2回のアナログからデジタル変換のサンプリングが完了するたびにADC割り込みが 発生するような初期設定をset_adc_exe関数で行っています。 (これはAD1CON2のSMPI<3:0>に1の設定で、サンプル数が2回ごとに割り込みさせています。)
ADC割り込み(下記adc_umeFunc1_1関数)ではサンプリング結果は、ADC1BUFの分割した一方に記憶し、 もう一方から取り出して「struct ADC_BUFF」に蓄えます。
ハード的にADC1BUF0とADC1BUF1レジスタ(それぞれAN0とAN1)に記憶さるタイミングで、 割り込みソフトによりADC1BUF8とADC1BUF9レジスタの内容を「struct ADC_BUFF」に蓄えます。
ハード的にADC1BUF8とADC1BUF9レジスタ(それぞれAN0とAN1)に記憶さるタイミングで、 割り込みソフトによりADC1BUF0とADC1BUF1レジスタの内容を「struct ADC_BUFF」に蓄えます。

そしてTimer3割り込みでは、サンプリングより蓄えられた「struct ADC_BUFF」からUSBへ送出する処理を行っています。
(対象のチャンネルの「struct ADC_BUFF」から2ワードをUSBへ出力する処理です。)
このUSB出力はset_adc_modeでCN8のAN0や CN9のAN1の入力源の選択と USBへの出力モード(テキストかバイナリ)の設定に従て行われます。
このUSBから出力する一つブロック数のサイズはset_adc_exe関数による設定に従って行われます。
以下が、AN0とAN1の2つの入力をサンプリングイメージです。(赤のマークがADC割り込みタイミングです)
(61104D.pdfのPage 17-57 と17-56を参考にした図です。59ページコードなどを参考にしてます
これらのADCのタイミングはTimer3を指定しています。
(AD1CON1bits.SSRC = 0b010; でTimer3 の周期一致時にサンプリング終了/ 変換開始をトリガに使い、
AD1CON3bits.ADRC = 0で ADC変換クロック源を周辺モジュール用クロック(PBCLK:40MHz)から生成)にしています。
このTimer3 のPR3の設定で、サンプリング周期が決まります。
一つのサンプリング周期は、(PR3の設定数+1)*(1/40e6)と値となりますが、 AN0とAN1の2つサンプリング全体のサンプリング周期は、この値の2分の1となります。
例えば、8KHzのサンプリングレートが必要となる場合、16KHz(周期が2分の1)の設定をします。
このPR3の設定値は、(1/16e3)/(1/40e6)-1=2499になります。

PR3の最低値は100程度です。(これは400KHz程度のサンプリングレートを意味します。)
下記で示す組み込みのADCとTimer3の割り込み関数を別途に作って作り変えれば、 もっと小さな(大きなサンプリングレート)のPR3も可能でしょう。

Timer3のイメージを下記に示します (参考:60001105G_JP.pdf)


ここで示すソース内で、主要関数の概要を以下に示します。

関数名内容
void init_adc()

ADCの初期化デフォルト関数です。
ここでMUX AN0に入力 、 MUX B にAN1に入力して、MUX A および MUX B 入力選択を交互に使う設定にしている。
Timer3 の周期一致時にサンプリング終了/ 変換開始をトリガする設定にして、 ADCモジュールを有効にしています。
つまり、Timer3 が割り込みを始めると、ADCの交互サンプリングが始まり、 AN0のAN1の2つ結果をADC1BUF〜に記憶して次のサンプリングが始まるタイミングで ADC割り込み処理(デフォルトはadc_umeFunc1_1関数)が呼び出されます。
(AD1CON1 = 0x8046;//ON=1, SSRC=010, ASAM=1, SAMP=1)
またTimer3 のT3CONbits.ON制御で、ADコンバータ処理のON/OFFを制御しています。

void init_timer_3()

Timer3の初期化デフォルト関数です。TypeBの各種設定をしています。 T3CONのTGATE=0、 TCKPS=0b000 // [1:1]プリスケール値、 T32=0 // 16bitカウンタ指定、 TCS=0 // 周辺モジュールバス クロック(PBCLK) などの設定
PR3 = 5000/2-1で8KHzのサンプリング周期にしている。 別途に、 PR3 = 2500/2-1; で16KHz、PR3 = 1250/2-1; で32KHz、 PR3 = 1666/2-1; で24KHzに変更でます。
PR3の最低値は100程度です。サンプリング周期は、((1/サンプリング周波数)/(1/40e6)-1)/2で算出できます。

int set_adc_mode
(int channel_bit,
int text_mode_flag)

引数channel_bitで、AN0のサンプリングを得るUSBの出力指定が1、AN1を得るUSBの出力指定が2、 AN0とAN1の2つをサンプリングを得るUSBの出力指定が3です。
(channel_bitに1または2の一方だけのUSBの出力指定でも、交互サンプリングにより両方の入力がADC変換対象になっています)
またtext_mode_flagが1でADCデータの出力をテキストモードに指定します。(0であればバイナリ指定を意味します)

int set_adc_exe
(int block_size,
int loop_flag)

ADCのの使い方を設定する関数
block_sizeで、1回のUSBで送出するデータサイズを1から36の範囲で指定します。 この指定値は、ADC_BUFF_SIZEの1024ワードを1と数えた値を単位です。
(2であれば、ADC_STARTとADC_ENDに挟まれた送出データ数が2048ワードとなる)
そして loop_flagが1であれば、このブロックを連続的に生成する指定を意味し、0であればblock_sizeでサンプリングを終えます。
block_sizeに3以上を指定することやloop_flagに1を指定する場合は、PR3に4999程度以上の設定が必要でしょう。
(PR3の設定値の最低値は、接続USBの性能、USBに出力するチャンネル数などに依存します)
なお、PR3の設定値が500未満の時は、block_sizeに3以上やloop_flagに1しても無効となり、block_sizeが2のサンプリングが終了するまで USBの出力が行われません。(この時はサンプリングが終了した時点で、timer3の割り込みが始まりUSBの出力が始まります)

void adc_umeFunc1_1(struct ADC_BUFF *p_buff)

ADCの割り込み処理
サンプリング結果が記憶される「ADC1BUF0とADC1BUF1」または、「ADC1BUF8とADC1BUF9」を、 p_buff->buffer0とp_buff->buffer1の配列要素に記憶しています。

void adc_usb_out(struct ADC_BUFF *p_buff)

Timer3の割り込み処理
p_buff->buffer0とp_buff->buffer1の配列をUSBに送出する。


これを制御する構造体が、common.hで次のように定義しています。
struct ADC_CTRL {
    uint32_t counter_ADC;//USB出力数のカウント
    uint32_t count_end_ADC;//上記カウント目標値(block_size_ADCより設定)
    int loop_flag_ADC;// ループサンプリングで1
    int block_size_ADC;//サンプリングブロック数(1ブロックがADC_BUFF_SIZE)
    int set_sequence_flag;
    int out_channel_bits;    // 1:AN0 or 2:AN1 or 3:(AN0,AN1)
    void (*adc_out_usb)(uint32_t); // ADC USB output function
};
#define ADC_BUFF_SIZE 1024
#define ADC_OUT_NONE 2 // index_adc_out_blockの出力情報なしの定数
struct ADC_BUFF {
    uint16_t adc_buffer0[2][ADC_BUFF_SIZE]; //AN0(CN8)
    uint16_t adc_buffer1[2][ADC_BUFF_SIZE]; //AN1(CN9)
    int index_adc_block_sample; //録音中の添え字 0 or 1
    int index_adc_sample; // 録音位置
    int index_adc_out_block;// USB出力中の添え字 0 or 1 で、出力情報無し時は2がset
    int index_adc_out_pos; // USBの出力対象の添え字
    int adc_buff_data_numb;// 実際のusbで送信するする際のワードサイズ目標値
    struct ADC_CTRL *p_ctrl;
};
そして、my_adc.cで次の記憶域を用意して管理しています。
struct ADC_BUFF adc_buff = {
    {0},{0},
    0,
    0,
    2,
    0,
    ADC_BUFF_SIZE,     // adc_buff_data_numb
    & adc_ctrl
};
struct ADC_BUFF *p_buff = & adc_buff;

使われている構造は、逐次比較型(SAR) アナログ/ デジタルコンバータ (ADC)と呼ばれるもので、 アナログサンプリングは「アクイジション」と「変換」の 2 つのステップ により構成されます。
この変換が終わったタイミングで、ADCの割り込みが行われています。
ADCの割り込み処理は、「_HANDLES[_IDX_ADC_1_FUNC]」で、変更可能になっており、 初期値はmy_sys.cで、「adc_umeFunc1_1」関数が登録されています。
この割り込みの「adc_umeFunc1_1」関数では、上記ADC_BUFF構想体の メンバadc_buffer0,adc_buffer1の配列に格納する処理が行われています。
各配列は、2byte要素が1024並ぶ領域が2つある2次配列で、 割り込みで一方の1024個並ぶ要素へ1要素ずつ格納する処理が行われ、 同時にもう一方の1024個から取り出して、USBに送信する処理が adc_usb_out(struct ADC_BUFF *p_buff)関数です。 この、デフォルトでTimer3割り込みで起動できるように なっています。

my_adc.hのソース

#ifndef _MY_ADC_H    /* Guard against multiple inclusion */
#define _MY_ADC_H

#include "my_beep.h"

struct ADC_BUFF * get_ptr_adc_buff();
extern struct ADC_BUFF *p_buff;
void encode_adc_buff( struct ADC_BUFF *p_buff );
int set_adc_exe(int block_count, int loop_flag);
int set_adc_mode(int channel_bit, int text_mode_flag);
void adc_usb_out(struct ADC_BUFF *p_buff);
void adc_umeFunc1_1();
void init_timer_3();
void init_adc();

#define MY_ADC_ERR_OVERFLOW  0x0001 // ADC取得データの送信が間に合わなないエラー

#endif /* _MY_ADC_H */

my_adc.cのソース

/*
 *  USB のADCに必要な処理
 *  (関連TIMER3の処理を含む)
 */
#include <xc.h>
#include <stdio.h>
#include <sys/attribs.h>//割込み関係の定義
#include <proc/p32mx270f256b.h>
#include "my_app.h" 
#include "my_usb_cmd.h" 
#include "my_adc.h" 
#include "my_sys.h"
#include "common.h"

/*****************************************************
 *  ADC関連のグローバル変数と関数
 ****************************************************/

uint16_t my_adc_err = 0; // my_adc.c でのエラー情報
// 上記エラー情報への設定関数
int my_adc_set_err(uint16_t d) {
    my_adc_err |= d;
}


/* ADC_CTRL構造体
 * int block_size_ADC = 1;// これが、0ならストップシーケンス、0以外なら起動シーケンス
 *   ADC_BUFF_SIZE * block_count_ADC のサイズが1回のUSB出力のデータ数となる。
 *   この数を "ADC_START\r\n" の直後に16進で送出して、"\r\nを出してデータ出力を始める
 * int set_sequence_flag = 1; //この構造体の設定する登録時に1、無いなら0
 * int out_channel_bits = 0x03; //使用チャンネル(AN0,AN1)の指定
 * int adc_buff_data_numb; // usbで送信する実際のワードサイズ
 */
struct ADC_CTRL adc_ctrl = {// defualt
    0, // counter_ADC
    ADC_BUFF_SIZE * 1, // count_end_ADC
    1, // loop
    1, // block_size_ADC
    1, // set_sequence_flag
    0x03, // out_channel_bits 使用チャンネル(AN0,AN1)の指定
    send_16byte // ADCのUSB出力関数へのポインタ(send_16byte or send_hex_low)
};
struct ADC_CTRL *p_ctrl = &adc_ctrl;


/* 下記構造体(struct ADC_BUFF)のメンバのデータの概要
 * ADCのサンプリング割込みで、index_block_sampleが指すバッファ内に記憶するが、
 * 一杯になったら次の添え字に移動する。その時その添え字がindex_out_blockと同じあれば
 * まだ送信されていないのでサンプリング不可として、割込みを停止する。
 * (サンプリングが可能であれば、index_out_blockを更新する。)
 * index_adc_block_sampleの添え字移動は、0→1、1→0の通り
 * int index_adc_out_block;は 2 である時、出力対象が存在しない。
 * int index_adc_out_pos; 0〜ADC_BUFF_SIZEで、 ADC_BUFF_SIZEは出力済み状態を意味する。
 */
struct ADC_BUFF adc_buff = {
    {0},//  uint16_t adc_buffer0[2][ADC_BUFF_SIZE]; //AN0(CN8)
    {0},//  uint16_t adc_buffer1[2][ADC_BUFF_SIZE]; //AN1(CN9)
    0,  //  int index_adc_block_sample; //録音中の添え字 0 or 1
    0,  //  int index_adc_sample; // 録音位置
    ADC_OUT_NONE,// int index_adc_out_block;// USB出力中の添え字 0 or 1 or ADC_OUT_NONE
    0,  // int index_adc_out_pos; // USBの出力対象の添え字
    ADC_BUFF_SIZE, //  int adc_buff_data_numb;// 1ブロックで出力する実際のデータ数
    & adc_ctrl
};

struct ADC_BUFF *p_buff = &adc_buff;

struct ADC_BUFF *get_ptr_adc_buff() {
    return & adc_buff;
}

// 引数がadc_block_countが0の場合は、終了指示 loop_flagが1でループ
// Timer3が動作中の場合、loop_flag を0にすると、block_countは無視して終了シーケンス
// Timer3が動作中の場合、loop_flag を1であればblock_countの変更可能
// Timer3が動作していない場合中の場合、loop_flag を0にしてもblock_countで起動可能
// 既に、動作していれば、set_sequence_flag を1にする。
// 戻り値:set_sequence_flagの内容返す。(set_sequence_flagの設定前の値)
int set_adc_exe(int block_size, int loop_flag) {
    int rtnval = p_ctrl->set_sequence_flag;
    p_ctrl->block_size_ADC = block_size;
    p_ctrl->loop_flag_ADC = loop_flag;

    if (T3CONbits.ON == 0 && p_buff->index_adc_out_block == ADC_OUT_NONE) {
        //現在割込みが止まっている。
        p_buff->index_adc_block_sample = 0; //録音中の添え字 0 or 1
        p_buff->index_adc_sample = 0; // 録音位置.
        p_buff->index_adc_out_block = ADC_OUT_NONE; //USB送信中は、0か1になる。
        p_buff->index_adc_out_pos = 0; // USBの出力対象の添え字.
        p_ctrl->counter_ADC = 0;
        if (block_size != 0) {
            p_ctrl->count_end_ADC = ADC_BUFF_SIZE * p_ctrl->block_size_ADC; // 目標サンプル数
            if (p_ctrl->out_channel_bits == 0x3) {
                p_ctrl->count_end_ADC <<= 1;  //AN0,AN1 の2つ分
            }
            AD1CON1 = 0x8046;
            T3CONbits.ON = 1; // Timer3スタート
        }
    } else {// 既に動いている場合の指定
        p_ctrl->set_sequence_flag = 1; //上記を設定
    }
    return rtnval;
}

// channel_bitの0x1がAN0のサンプリング指定、0x2がAN1のサンプリング指定、両方は0x3
// text_mode_flagが1で、ADCデータの出力をテキストモードに指定(バイナリなら0を指定)
int set_adc_mode(int channel_bit, int text_mode_flag) {
    p_ctrl->out_channel_bits = channel_bit;

    AD1CON2bits.SMPI = 0b0001; // 2つのADCサンプリング後にADC割り込みを発生させる指定。
    // 全体のサンプリング周期は、「PR3で指定した1つのADCサンプリング時間×2」となる。

    if (text_mode_flag != 0) {
        p_ctrl->adc_out_usb = send_hex_low; // テキスト出力関数へのポインタ
    } else {
        p_ctrl->adc_out_usb = send_16byte; // バイナリー出力関数へのポインタ
    }
    IEC0CLR=_IEC0_T3IE_MASK;// // timer3の出力割り込み不許可★
}

// USBへ送信する直前で呼び出される送信データの加工処理用の置き換え元関数.
void encode_adc_buff(struct ADC_BUFF *p_buff) {
    // パワーオン時に、handlers[_IDX_ENCODE_ADC_BUFF]に記憶される関数 
    // 後述のadc_usb_out関数より呼び出される何もしないダミー関数
}

/*
 *  ADC 割り込み処理 -----------------------------------------------------------
 */
void adc_umeFunc1_1(struct ADC_BUFF *p_buff) {
    static int no_send_count = 0; // 送信できない場合のデクリメント
    static uint16_t valAN0, valAN1;
    int flag_stop = 0;//★ADCの割り込みを終了するタイミングで1

    // 割り込み中で、下記バッファを必ず操作しなければならない。
    // (そうしないと、割込みが永続(persistent)のため、連続発生の不具合が生じる。)
    // バッファへの格納先を確認(ビットチェック格納先を判定)
    int idx = (AD1CON2 & _AD1CON2_BUFS_MASK) != 0;
    if (idx) {//1:ADC はバッファ0x8〜0xF に書き込み中(ユーザは0x0〜0x7 にアクセス可能)
        valAN0 = ADC1BUF0; //AN0
        valAN1 = ADC1BUF1; //AN1
    } else { //0: ADC はバッファ0x0〜0x7 に書き込み中(ユーザは0x8〜0xF にアクセス可能)   
        valAN0 = ADC1BUF8; //AN0
        valAN1 = ADC1BUF9; //AN1
    }

    if (p_buff->index_adc_sample >= ADC_BUFF_SIZE) {
        if (p_buff->index_adc_out_block != ADC_OUT_NONE) {

           IEC0SET=_IEC0_T3IE_MASK; // timer3の出力割り込み許可★ 
           //(adc_buffer0 or 1のバッファを記憶し終わったタイミング)
           if( PR3 < 500 ) {// 約80KHzを超えるサンプリングでは次の割り込みを不可
                flag_stop = 1;                
            }//★

            // エラーのサンプリング停止(メモリオーバー USB出力が間に合わない).
            no_send_count--;
            goto end_proc; // 下記のサンプリングをしない
        }
        // オルタネイト・バッファブが一杯なので、切り替える 
        p_buff->index_adc_sample = 0;
        int next_index_block_sample = p_buff->index_adc_block_sample;
        next_index_block_sample++;
        if (next_index_block_sample == 2) {
            next_index_block_sample = 0;
        }
        p_buff->index_adc_out_block = p_buff->index_adc_block_sample;
        p_buff->index_adc_block_sample = next_index_block_sample;

        if( flag_stop ){ // サンプルレートが高い場合、バッファを使い切ったら止める★
            IFS0CLR = _IFS0_AD1IF_MASK; // Clear ADC interrupt flag
            IEC0CLR = _IEC0_AD1IE_MASK; // ADC 割込み不許可
            return;
        }//★
    }

    if (no_send_count != 0) {// USB出力が間に合わないくて、出力されなかったデータ数?
        valAN0 = valAN1 = no_send_count; //出力できなかった数を(負の数)セット
        no_send_count = 0;
    }
    //valAN0 = valAN1 = cv;//デバック用データ設定.
    p_buff->adc_buffer0[p_buff->index_adc_block_sample][p_buff->index_adc_sample] = valAN0;
    p_buff->adc_buffer1[p_buff->index_adc_block_sample][p_buff->index_adc_sample] = valAN1;
    p_buff->index_adc_sample++;

end_proc:
    IFS0CLR = _IFS0_AD1IF_MASK; // Clear ADC interrupt flag  
}

// =============================================================================
// ADCのUSB出力 (Timer3の割込み処理)
// p_buffが管理する選択チャンネルのバッファから1ワードをUSBへ出力する。
// 選択チャンネル(p_buff->p_ctrl->out_channel_bits)が1であればAN0、2でAN1、
//      3の場合はAN0とAN1の両方の1ワード(2ワード分)を出力する。
// 出力対象の1ワードは、p_buff->p_ctrl->out_channel_bitsが1のAN0であれば次の要素となる
//  p_buff->adc_buffer0[p_buff->index_adc_out_block][p_buff-index_adc_out_pos]
//    ここで、ADCの設定が終わったindex_adc_out_blockは0また1であるが、
//          バッファ内容を全て送信し終わったタイミングで、ADC_OUT_NONEに設定される。
//   つまり、p_buff->index_adc_out_blockが2である時、送信データがない状態と判断している。
void adc_usb_out(struct ADC_BUFF *p_buff) {
    static bool flag_start_ADC = true;
    static bool flag_end_ADC = false;
    static int i, idx_block, i_pos;
    static int16_t data;

    static int tim3_skip_count=0;// timer3が早すぎる場合の送出調整用★
    static int skip_timing =0;//★
    skip_timing = PR3;
    if( skip_timing < 100) { // 1つのサンプル周期が約400KHz以上?★
        skip_timing = 100 - skip_timing;
        tim3_skip_count++;
        if(tim3_skip_count < skip_timing ) {
            IFS0CLR = _IFS0_T3IF_MASK; // 次の割り込みを可能にする
            return;
        }
        tim3_skip_count =0;
     }//★

    struct ADC_CTRL *p_ctrl = p_buff->p_ctrl;

    if (p_buff->index_adc_out_block == ADC_OUT_NONE) {// USB送信するデータがない? .
        IFS0CLR = _IFS0_T3IF_MASK; // Clear the timer interrupt status flag
        return;
    }
    int size = get_capacity();
    if (size < 50) {
        IFS0CLR = _IFS0_T3IF_MASK; // Clear the timer interrupt status flag
        return; //USB出力バッファに余裕がないので、次の呼び出しにする。  
    }
    if (request_acc_outbuff(_ID_ACCESS_T3_TASK) == false) {
        IFS0CLR = _IFS0_T3IF_MASK; // Clear the timer interrupt status flag
        return;        //USB出力権限取得失敗で、、次の呼び出しにする。
    }

    // ADC DATA 送出.============>USB
    if (flag_start_ADC) {// ----------送出ブロック開始----------------
        p_buff->adc_buff_data_numb = ADC_BUFF_SIZE;
        ((void (*)(struct ADC_BUFF*))_UME_ENCODE_ADC_BUFF)(p_buff);
        if (p_ctrl->out_channel_bits == 0x3) { // AN0, AN1 両方の出力
            send_string("\r\nADC_START\r\n");
            if (p_ctrl->adc_out_usb == send_hex_low)send_char('T'); //TextMode
            send_hex_low(p_ctrl->count_end_ADC); //送信データ数(X2))
        } else if (p_ctrl->out_channel_bits == 0x1) {
            send_string("\r\nADC_START0\r\n");
            if (p_ctrl->adc_out_usb == send_hex_low)send_char('T'); //TextMode
            send_hex_low(p_ctrl->count_end_ADC); //送信データ数
        } else if (p_ctrl->out_channel_bits == 0x2) {
            send_string("\r\nADC_START1\r\n");
            if (p_ctrl->adc_out_usb == send_hex_low)send_char('T'); //TextMode
            send_hex_low(p_ctrl->count_end_ADC); //送信データ数
        }
        send_string("\r\n");
        usb_receiver_disable_flag = 1; //USB受信不可
        flag_start_ADC = false; //開始処理を終えた〇
        flag_end_ADC = false;

    } else if (p_ctrl->counter_ADC >= p_ctrl->count_end_ADC) {
        // ----------送出ブロック終了の処理------
        if (!flag_end_ADC) {
            flag_end_ADC = true;
            flag_start_ADC = true;
            send_string("ADC_END\r\n");
            usb_receiver_disable_flag = 0; //USB受信不可解除
            if (p_ctrl->set_sequence_flag) {// set_ADCの登録がある時
                p_ctrl->set_sequence_flag = 0;
                if (p_ctrl->block_size_ADC != 0) {// 継続データ指示がある ?
                    // 目標サンプル数 設定があればセット
                    p_ctrl->count_end_ADC = p_buff->adc_buff_data_numb * p_ctrl->block_size_ADC;
                    if (p_ctrl->out_channel_bits == 0x3) {
                        p_ctrl->count_end_ADC <<= 1; //AN0,AN2 の2倍
                    }
                } else {
                    T3CONbits.ON = 0; // timer3停止
                    p_buff->index_adc_out_block = ADC_OUT_NONE;
                }
            }
            if (!p_ctrl->loop_flag_ADC) {//ループしない?
                T3CONbits.ON = 0; // timer3割込みオフ.
                p_ctrl->block_size_ADC = 0;
                p_buff->index_adc_out_block = ADC_OUT_NONE;
            }
            p_ctrl->counter_ADC = 0;
        }

    } else {// ------------- ADC データブロック送出中 -------------------------
        for (i = 0; i < 2; i++) {// ADCの値の1ワードを2つ分送出する。
            idx_block = p_buff->index_adc_out_block;
            i_pos = p_buff->index_adc_out_pos;
            if ((p_ctrl->out_channel_bits & 1) != 0) {
                data = p_buff->adc_buffer0[idx_block][i_pos]; //出力対象
                if (data < 0) my_adc_set_err(MY_ADC_ERR_OVERFLOW);
                p_ctrl->adc_out_usb(data);
                p_ctrl->counter_ADC++;
            }
            if ((p_ctrl->out_channel_bits & 2) != 0) {
                data = p_buff->adc_buffer1[idx_block][i_pos]; //出力対象
                if (data < 0) my_adc_set_err(MY_ADC_ERR_OVERFLOW);
                p_ctrl->adc_out_usb(data);
                p_ctrl->counter_ADC++;
            }
            ++p_buff->index_adc_out_pos;
            if (p_ctrl->counter_ADC % 16 == 0 && p_ctrl->adc_out_usb == send_hex_low) {
                // TEXT MODE 出力の場合だけ出力(バイナリ時は出力しない)
                send_string("\r\n");
            }
            if (p_buff->index_adc_out_pos >= p_buff->adc_buff_data_numb) {
                p_buff->index_adc_out_pos = 0;
                p_buff->index_adc_out_block = ADC_OUT_NONE; // CHUNKブロックのUSB出力終了.
            }
        }
    }
    release_acc_outbuff(_ID_ACCESS_T3_TASK);
    IFS0CLR = _IFS0_T3IF_MASK; // Clear the timer interrupt status flag
}

// Timer3 の初期化(ADCのサンプリング周期を作る)------------------------------------
void init_timer_3() {
    T3CON = 0x00000000; //typeB,16bit, [1:1]プリスケール
    T3CONbits.ON = 0; // typeBでON(b15)
    T3CONbits.SIDL = 0; // デバイスがアイドルモードに移行しても動作を継続する(b13)
    T3CONbits.TGATE = 0; // ゲート時間積算を無効にする(b7)
    T3CONbits.TCKPS = 0; // タイマ入力クロック 0〜3プリスケール値(b6-4)
    T3CONbits.TCS = 0; //内部の周辺モジュール用クロック(b1)
    // 以上より、タイマーのカウントアップ周期は、次のように算出される。
    // 「TCS = 0」で、周辺モジュールバス クロック(PBCLK) は40MHzになっている。
    //  よってカウント周期は 1/40e6=2.5e-08=250u秒
    //__asm__("nop");  
    TMR3 = 0x00000000; //16bitタイマの設定値(レジスタがカウントアップ)
    PR3 = 0x1;          //16bit周期レジスタビット設定(タイマーが働く最小値)  
    PR3 = 0x0000FFFF;   //16bit周期レジスタビット設定(16タイマーの最大値)
    PR3 = 40000-1;        //16bit周期レジスタビット設定( 割り込み周期が1m秒)
    PR3 = 907-1; //44,100Hzが音楽業界の標準で、この周期に近い割り込み周期(約0.23ミリ秒)
               // 1/(907 * (1/40e6))=44101.43329658214Hzの周波数になり、少しずれる。
               // 2つチャンネルなので、上記設定の実質的サンプリング周波数は÷2=約22050.7Hz
    PR3 = 1250/2-1;//32KHzのサンプリング周期(割り込み周期は2チェンネルで16Kの周期)
    PR3 = 1666/2-1;//24KHzのサンプリング周期(割り込み周期は2チェンネルで12Kの周期)
    PR3 = 2500/2-1;//16KHzのサンプリング周期(割り込み周期は2チェンネルで8Kの周期)

    IPC3SET = 6 << _IPC3_T3IP_POSITION; // Set priority level = 6
    IPC3SET = 2 << _IPC3_T3IS_POSITION; // Set sub-priority level = 2
    IFS0CLR = _IFS0_T3IF_MASK; // Clear the timer interrupt status flag
    IEC0SET = _IEC0_T3IE_MASK; // Timer3 Enable(割込み許可)
    //T3CONSET = _T3CON_TON_MASK;   // timer3 Start 
    //T3CON = 0b1000000000000000;   // timer3 機能ON 
}

// ADCの初期化デフォルト関数------------------------------------------------------
void init_adc() {
    // ----- ADC 関連の初期設定 ここから[61104F_JP.pdf P17-4]
    TRISASET = 0x00000003; // RA0、RA1のポートを入力用に設定
    AD1CON1 = 0; // スタート、ストップ制御関連制御レジスタ
    AD1CON2 = 0; // 入力スキャン指定、出力バッファ指定などシーケンス指定制御レジスタ
    AD1CON3 = 0; // クロック関連指定制御レジスタ
    AD1CHS = 0; // アナログ入力のマルチプレクサの指定
    //AD1PCFG = 0; 当チップではこのポートコンフィグレーションレジスタは存在しない。
    AD1CSSL = 0; // 入力スキャン選択レジスタ

    // AD1CHS設定-----------------------------------
    AD1CHSbits.CH0NB = 0; // b31 CH0NB:0 MUX B 負極性入力選択ビット=VR- を選択する
    // b30-b28 未実装 :「0」として読み出し
    // b27-b24 CH0SB<3:0>: 0b0001 MUX B 正極性入力選択ビット=AN1 を選択する
    // b23 CH0NA:0  MUX A 負極性入力選択ビット=VR- を選択する
    // b22-b20 未実装 :「0」として読み出し
    // b19-b16 CH0SA<3:0>: 0b0000 MUX A 負極性入力選択ビット=AN0 を選択する
    // b15-b0 未実装 :「0」として読み出し
    // CH0NB XXX CH0SB CH0NA XXX CH0SA         XXXXXXXX XXXXXXXX
    // 0     000 0001  0     000 0000          00000000 00000000
    AD1CHS = 0x01000000; // MUX AにはAN0,MUX BにAN1 の入力を接続

    //AD1CON3設定--ADC 制御レジスタ 3 クロック関連指定制御レジスタ-----------------
    AD1CON3 = 0x0; // b31-b16 未実装 :「0」として読み出し
    AD1CON3bits.ADRC = 0; // b15 ADC変換クロック源の選択ビット 
    // 1: FRCクロック使用、0:周辺モジュール用クロック(PBCLK:40MHz)から生成
    // b14-b13 未実装 :「0」として読み出し
    AD1CON3bits.SAMC = 0b00010; // b12-b8  自動サンプリング時間ビット= 2TAD
    //上記はアナログ入力ピンをサンプル/ホールドアンプ(SHA)に接続するアクイジション時間
    AD1CON3bits.ADCS = 0b00000011; /// b7-b0  ADC 変換クロック選択ビット
    // ADCの変換時間(8bit) =  TPB * 2 * (ADCS<7:0> + 1) = 512 * TPB = TAD
    // XXXXXXXX XXXXXXXX    ADRC XX SAMC  ADCS
    // 00000000 00000000    0    00 00010 00111111
    AD1CON3 = 0x0000023F;

    // AD1CON2 (ADC 制御レジスタ 2)設定 --------------------
    // [61104F_JP.pdf P17-4]「17.4.11.2 2 つの入力マルチプレクサを交互に使う」
    AD1CON2 = 0x0;
    AD1CON2bits.VCFG = 0; //参照電圧にAVDDとAVSSを使う(b15-b13)
    AD1CON2bits.OFFCAL = 0; //オフセット校正モードを無効(b12)
    AD1CON2bits.CSCNA = 0; //入力をスキャンしない(b10)
    AD1CON2bits.BUFS = 0; //バッファ書き込みステータスビット(b7)
    // 1 = ADC はバッファ0x8〜0xF に書き込み中( ユーザは0x0〜0x7 にアクセス可能)
    // 0 = ADC はバッファ0x0〜0x7 に書き込み中( ユーザは0x8〜0xF にアクセス可能)
    AD1CON2bits.SMPI = 0b0001; //割り込みシーケンス選択ビット(b5-b2)
    // 上記は、2 回のサンプリング/ 変換が完了するたびに割り込む
    AD1CON2bits.BUFM = 1; //ADCデータ格納バッファモード選択ビット(b1))
    //上記 1: 2つの8ワードバッファ(ADC1BUF) を分割構成
    AD1CON2bits.ALTS = 1; //MUXAおよびMUXBを交互に使う指定(b0))
    //    VCFG OFFCA X CSCNA XX     BUFS X SMPI BUFM  ALTS
    //    000  0     0 0     00     0    0 0001 1     1
    AD1CON2 = 0x0007;

    // AD1CON1設定 ------------------------------------------
    AD1CON1 = 0; // b31-b16 未実装
    AD1CON1bits.ON = 1; //ADC モジュールを有効にする(b15)
    AD1CON1bits.SIDL = 0; //アイドル中もモジュールの動作を継続する(b13)
    AD1CON1bits.FORM = 0b000; //16 ビット符号なし整数の指定(b10-b8)
    AD1CON1bits.SSRC = 0b010; //変換トリガ源選択ビット(b7-b5)
    // 上記3ビット指定:Timer3 の周期一致時にサンプリング終了/ 変換開始をトリガする
    AD1CON1bits.CLRASAM = 0; //割り込み時変換停止ビット(bit4)
    // (0 = 次の変換シーケンスがバッファの内容を上書き)
    // (1 = 最初のADC 割り込みが発生した時点で変換を停止する)
    // SSRCがTimer3の時、CLRASAMの1を行ってもTimer3によるADC割り込みは停止できない。
    AD1CON1bits.ASAM = 1; //ADC サンプリング自動開始ビット(b2)
    //これをセットしないと、割込みが発生しなかった。
    // 変換完了後即座にサンプリングを開始する(SAMP ビットを自動的にセットする)
    AD1CON1bits.SAMP = 1; //ADC サンプリングイネーブルビット(b1)
    AD1CON1bits.DONE; //変換が完了すると1になる。(b0)  
    //    ON X SIDL XX FORM     SSRC CLRASAM X ASAM SAMP DONE
    //    1  0 0    00 000      010  0       0 1    1    0   
    AD1CON1 = 0x0046;

    IPC5SET = 7 << _IPC5_AD1IP_POSITION; // Set priority level = 7
    IPC5SET = 1 << _IPC5_AD1IS_POSITION; // Set sub-priority level = 1

    IFS0CLR = _IFS0_AD1IF_MASK; // Clear the ADC interrupt status flag
    IEC0SET = _IEC0_AD1IE_MASK; // ADC Enable(割込み許可)

    AD1CON1bits.ADON = 1; // Begin Sampling    
}