UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
[UMEHOSHI ITA]の制御で使っているIC「PIC32MX270F256B-I/SO」のフラッシュメモリには、テスト用プログラムが書き込まれいています。
以降では、このプログラムを「テスト・ウメ・フラッシュ」と呼ぶことにして解説します。
また、「テスト・ウメ・フラッシュ」を利用したユーザー用のプログラムを
「ウメ・エディットプログラム」と呼ぶことにします。
「ウメ・エディットプログラム」の開発では「umehoshiEditツール」が必要で、
その取得や最初の操作情報は、こちらを参照してください。
(「PICKit3などの書き込みツール」をお持ちの方で、「テスト・ウメ・フラッシュ」を利用しないで、
「MPLAB X IDE」の開発環境ですべてをプログラミングする場合の情報ではありません。)
「テスト・ウメ・フラッシュ」をどのように利用して、「ウメ・エディットプログラム」を
作るかの解説で、「umehoshiEditツール」の使用例を示しています。
各サンプルは、このWebページ上でドラック&コピーして、貼り付けしてご利用ください。
| 各種確認プログラム | 左記に必要な部品の追加例 | 
|---|---|
| とくに必要ありません。(D1のLEDはあるとよい) | |
| PWM対応の部品追加例 | |
| ADC 対応の部品追加例 | |
| BEEP SWITCH 対応の部品追加例 | |
| Reset SW, Type-A, CN2 部品追加例 | |
| U20,D4,D5,NPN, D3 部品追加例 | |
| CN11,CN-12 ,Bluetoothなどの基板 の部品追加例 | |
| U19 部品追加例 | |
| U17にRN4020の部品を追加する例 | 
部品の取り付けやハード情報→こちらのページで紹介

上記左側にコンデンサマイクを2個(CN8とCN9の端子)取り付けた場合の例です。
その情報は2段のアンプを経て、AN0、AN1のADコンバータ入力端子に繋がれており
これが入力対象です。
ADCは、Timer3の割込み間隔で動作しています。初期段階でTimer3はOFFになっているので、
これをONにするとADCも連動するようになっています。
ADCの分解能は、10bitですが16bitの符号なし整数(0〜1024の値)の情報で、1024個のデータが蓄えた後に
それをUSBで連続送出仕組みが作られています。
内部ソースで、次の宣言があります。(common.h)
(実際のソースコードは、
こちらを参照してください。)
#define ADC_BUFF_SIZE 1024 // チャンクサイズ(Chunk Size)
uint16_t adc_buffer0[2][ADC_BUFF_SIZE]; //AN0(CN8)
uint16_t adc_buffer1[2][ADC_BUFF_SIZE]; //AN1(CN9)
このように、CN8,CN9コネクタに接続される2つののADC入力に対するバッファを用意して使っています。
[2]の配列は一方がADCの割込みで記憶に使っている時、もう一方はTimer3でADC記憶データをUSBへ
送出するようになっており、この添え字切り替えてで、記憶対象と出力対象が切り替わるように作られています。
「テスト・ウメ・フラッシュ」で用意されるADCに関する変数とAPI関数を以下に示します。
| マクロの表現 | 概要 | 
|---|---|
| _set_adc_mode(c,t) | ADCの対象とUSBで出力する際のモードを指定する関数で、ADCスタート前で使う。 c:ADCサンプリング対象のパラメタ 1: CN8 、2: CN9 、3: CN8とCN9 (3がデフォオルト) t: USBのデータ出力モードフラグ 1: TEXT MODE 、 0 : BINARY MODE | 
| _set_adc_exe(n,f) | ADCのスタートと終了を制御する。 n:1回分ADCサンプリングブロック数パラメタ 1から63までのブロック数(1ブロックが1024ワード) f: ループフラグで1で上記ブロックのサンプリングを繰り返す。 0で上記ブロックサンプリング後に終了する。 (既に動作中に0の指定で呼び出すと、nは無視されて現ブロック送信で終了) (既に動作中に1の指定で呼び出すと、nは無視されて現ブロック送信後に、次のブロック数をnで指定可能) | 
#include <xc.h>//ADC.c
#include "common.h"
#define AdrStart	0x80005000
__attribute__((address( AdrStart  ))) void start (void);
void start()
{
	_RB15 = ! _RB15;// 動作確認用のD1 LEDの点灯を反転
	PR3=1249;	//サンプリング周波数を 16KHzに指定するパラメタ
	// (1/16000)/(1/40e6)/2-1=2500/2-1=1249
	_set_adc_mode(3,1);// CN8のAN0とCN9のAN1アナログサンプリングで出力テキストモード 
	_set_adc_exe(2 , 0);// 2block, loopしない1回だけのサンプリング実行スタート
}
上記のサンプリング周期設定のPR3の算出方法を示します。
サンプリングレートを f Hz  とした場合、サンプリング周期は=1/f秒で求められます。
PR3は、「内部の周辺モジュール用クロック(周期:1/40e6)のカウント数」-1をを設定するものです。
また一つの逐次ADCをAN0、AN1を交互にサンプリングするプログラムが埋め込まれているため、
この2回分のサンプリングの周期を指定するためには、PR3を2で割った設定にする必要があります。
以上から、サンプリングレートを f Hzにしたい場合、
一つのチャンネルのサンプリング周期に使うPR3設定÷2=((1/f)/(1/40e6)-1)÷2の計算で求めることになります。
よってサンプリングレートを16KHzにする場合のPR3設定値は、
PR3=((1/16e3)/(1/40e6)-1)/2=(2500-1)/2=1249.5 となります。
上記の場合は、切り捨てた1249が設定値になります。この場合の実際のサンプリング周波数は、次のように求められます
PR3が1249の場合は、サンプリリング周期が(1249+1)*(1/40e6)*2=6.25e-05秒です。
周波数は、1/((1249+1)*(1/40e6)*2)=16000Hzとなります。
以下は、上記コードをビルドして実行した時の画面例です。
(1ブロックの1028ワードはCHUNKサイズと同じです)
2ブロックで、AN0とAN1の2つの入力を行うので、1028 × 2ブロック × 2入力 = 4096ワードのデータが並びます。
これを16進にすると、1000の値になりますが、これが上記Tの直後に表示している値の意味です。 
 
実行の前に、「Tools」メニューの「ADC Plot」を選択しておくとよいでしょう。
グラフのイメージはデータの受信ごとに更新されます。
これにより下記のようなグラフイメージのフォームでグラフ表示ができます。
1ブロックを1028ワードとい管理なので、下記の横幅は2ブロックの2048個のデータがあることになります。
 
上記において、上の赤がCN8コネクタのAN0端子のサンプリング画像で、上の赤がCN9のAN1のサンプリング画像です。
同じ音源で試した例ですが、このようなバラツキがあります。(各素子のバラツキによると考えています。)
どちら側の音が大きいかを比較したい場合は、各基板に合わせてソフト的に調整するか、ハード的に調整する必要があります。
ハード的に調整する場合は、VR1とVR3に可変抵抗器などを取り付けることで、それぞれの増幅率を調整できます。
上記プログラムの実行で、「ADC_START」の直後に「T1000」の表示がありますが、
この先頭の「T」文字がテキストモードを意味し、続く「1000」の数字は16進で表現されたデータ数を意味します。
つまりこの直後に16ビットワードの情報が0x1000(つまり10進なら4096)個のデータが並んでいることを意味します。
「_set_adc_exe(2 , 0);」で、2ブロックを要求していますが、1ブロックが1048個のデータを意味します。
また「_set_adc_mode(3,1)」で、AN0とAN1の2入力のサンプリッグを指定しているので、交互に2つのデータが並びます
よって、[1ブロックの1024]×[2ブロック]×[2入力]=1024*2*2=4096のデータが並んでいます。
なお、1つのデータは16byteなので、4文字を意味します。下記の先頭は[01E5]を意味します。
なお[01][E5]の並びで、[01]が上位byte,[E5]が下位byteを意味します。
16個のデータ出力ごとに改行(\r\n)が追加されます。
R00800050000061 START:80005000 ADC_START T1000 01E50203022F020501FE0201021402070253021001E901E501DD01D901F701CA 020601ED01F301E50253020101B101E701FB020A022A0204021801FE02240202 ・・・・・省略・・・・・ 0208020A020F01F2021D01DA01F801E7022A0201020E0201021201D901B901D5 01C101EB019F01EE017201F0020F021002470203022201F201E5020A02040209 ADC_END
上記のデータは、AN0とAN1の2つをサンプリングしており、[AN0]のAD変換値、[AN1]のAD変換値、・・と交互に並ぶように送信されます。
16進4桁のデータが所定の個数(上記例では4096個)に達すると「ADC_END」の文字列の出力で終わります。
上記のサンプリング周波数は、「PR3=1249;」の指定で 16KHzになっています。
これを指定しない場合は、デフォルトの8KHzになります。
さて、バイナリモード時はテキストモード時と逆で、リトルエンディアンになって改行は追加されません。
またバイナリー指定時は、「umehoshiEditツール」の「Communication」タグにデータの表示を行いません。
以下に例を示します。
この例では、CN9コネクタ入力(AN1)のAD変換した結果だけUSB出力します。
また 1ブロックのサンプリングで、周波数は 400KHzにした例です。
#include <xc.h>//ADC2.c
#include "common.h"
#define AdrStart	0x80005000
__attribute__((address( AdrStart  ))) void start (void);
void start()
{
	_RB15 = ! _RB15;// 動作確認用のD1 LEDの点灯を反転
	PR3=99; // 400KHz のサンプリング (1/400e3)/(1/40e6)-1 = 99 
	_set_adc_mode(2,0);// 2でCN9のAN1端子だけ出力、0でバイナリモード 
	_set_adc_exe(1 , 0);// 1block(1024ワード)のサンプリングを終えたら終了
}
左下が、これを実行した時の「umehoshiEditツール」の「Communication」タグにに表示される内容です。| ADC_START1 0400 ADC_END ADC_START0がCN8だけの上表示、 ADC_START1がCN9だけの下表示 をします。 |   | 
バイナリモードでは"ADC_START1\r\n"の直後に'T'がありません。単にデータ数の16進文字列と改行のみです。(これでバイナリかテキストモードか区別できます。)
上記は、個数が0400でこれは1ブロックの1024個を意味します。
このようにバイナリ時はデータの16進表示を行いません。
ブロック数も1でサンプリングがAN1の1チャンネルだけなので、1つのデータが並びます。
Timer3のPR3設定値でサンプリング周期が決まってレジスタに変換値が記憶されます。
そしてADCの割り込みでレジスタからメモリに記憶し、Timer3の割り込みでメモリからUSB出力を行っています。
この周期が短くなると、AD変換でメモリに記憶した速度でそれをUSBに出力することが不可能になります。
ですが、入出力をバッファリング(2ブロック分の1024×2ワード)で制御しているので、
USBの送信スピードを超えるAD変換速度にある程度対応でます。
ですから、_set_adc_exeの第2引数が1または2であればUSB送信速度よりはるかに速い場合でも、
サンプリングできるでしょう。
しかし、_set_adc_exeの第2引数が2を超えて指定して、PR3設定値でサンプリング周期を小さくすると、
USBの出力が間に合わなくなります。その例を以下に示します。
CN8のAN0端子だけを32KHzでサンプリングさせる例です。なおブロック数を5(最大設定は63)にしてループにしています。
#include <xc.h>//ADC3.c
#include "common.h"
#define AdrStart	0x80005000
__attribute__((address( AdrStart  ))) void start (void);
void start()
{
	_RB15 = ! _RB15;// 動作確認用のD1 LEDの点灯を反転
	PR3=624;	//サンプリング周波数を 32KHzに指定するパラメタ
	//(1/32000)/(1/40e6)/2-1=624
	_set_adc_mode(1,0);// CN8のAN0で出力バイナリモード 
	_set_adc_exe(5 , 1);// 5blockのUSB出力を, ループする指定でサンプリング実行スタート
}
「Tools」メニューの「ADC Plot」を選択した時のイメージも示します。| 
R00800050000061
START:80005000
ADC_START0
1400
ADC_END
 |   | 
これは、ADC取得データの送信が間に合わなないエラーが起きている状態です。
このような場合、デフォルトで「・・ーー ・・・・ ・・・・ ・・・・ ・・・ー 」の
通知音がでます。
(上記プログラムで、必ず起きる訳ではありません。PC側の受信能力によって起きない場合もあります。)
  
上記グラフで青の水平線が、AN0の場合のゼロの値です。ですから上記では4つの負のデータがあることになります。
ADCのデータは符号なし16進です。(10bitの分解能なので0〜1024の範囲です)
それなのに負のデータがあるのどうしてでしょうか?実はこれがサンプリングできなかった数を負として出力した箇所のマークなのです。
ADCの割込みでサンプリングしてバッファへの記憶、それに平行してTimer3の割込みによりバッファからUSB出力が行われます。
(2つのブロックがあり、一方に記憶中である時、もう一方の内容を出力する関係になっています。)
_set_adc_exeの第1引数が2以下でサンプリングを止める使い方であればこのエラーは起きません。
しかしより大きなブロックを使う場合や、
サンプリングのループモードで使う場合、USBの送信が追い付かないと、サンプリングが待たされることになります。
そのタイミングでは本来の周期のサンプリングが記憶できないので、その数をカウントしています。(その時、エラー情報のビットが記憶されます。)
その後、USBの送信でバッファに空きが出来て再び記憶できるようになった時、サンプリングできなかった数を負の値で記憶しています。
つまり、上記の負の所はその数だけサンプリングを記憶しなかったデータが隠れている所と判断して使う必要があります。
このようなエラーの大きな要因はUSB送信が追い付かないためなので、USB送信量を減らすことでエラーを回避できます。
例えば、AN0やAN1の両方でなくどちらか一方だけの出力指定をする場合は、両方のUSB送信よりデータ量が少なくなります。
また、テキストモードよりバイナリーモードにした方が、USB送信からのデータ量が少なくなります。
(なおPR3の設定値が99以下の場合は、速すぎる割り込み周期なのでUSBの出力速度が上がらない制御をしており、PR3のエラー限界値が示せません。)
さてこのエラーは、どのようなサンプリングで起きる可能性があるか? USBの転送速度から検証します。
このUSBはCDCプロトコで115200bps Parity無し 8bit 1StopBits の非同期通信です。
つまり、1byte転送で実質10bit以上使うことになります。
これは、論理的に115200bps÷10=11520byte/秒を超える速度の送信ができないということです。
ADCは2byteなので、1秒間転送可能数は11520byte÷2=5760個ということになります。
つまり場合、連続サンプリングレートは最大5.76Ksps(samples/second)程度と予想されます。
(Textモードで転送すると、1/2の速度で、2.88Kspsになります。)
 対してステレオ(音楽向け)のサンプリングレートは48kspsです。品質を落としたモノラルでも8Kspsの速度が必要です。
よって 音楽録音の、連続的サンプリングとUSB送信を永続的に行えません。
これを実現する可能性として、サンプリングデータを圧縮してしてからUSB送信するなどの手法があります。
_UME_ENCODE_ADC_BUFFのマクロ変数には何もしない関数が登録されていますが、
ここに、送信データの圧縮処理する関数を登録することで、対応できる可能性があります。
また現在使っているUSBの「CDC (Universal Serial Bus Communications Device Class)」ではなく、
「Audio Class」用に「テスト・ウメ・フラッシュ」全体のプログラムを作り直せば可能となるのでしょう。
また、ADCやTimer3の割り込み処理だけをそっくりと入れ替えて対応する方法もあります。以下でその解決する考え方を説明をします。
 
	AD1CON1bits.SSRC=0b000;//変換トリガ源選択ビット(b7-b5)
	AD1CON1bits.SAMP=1;//この2行で_ADC_VECTOR割込み処理を指定
	// ・・・・
	AD1CON1bits.ASAM=1;
	AD1CON1bits.CLRASAM=1;
	T4CONbits.ON = 1;// timer4割込みオン
//-----------------------------------------------------
void __ISR(_TIMER_3_VECTOR,IPL7SOFT)Timer3Handler(void)// Timer3の割り込み
{
	AD1CON1bits.SAMP=0;// サンプリング終了/ ホールドして逐次変換開始をトリガする  
	IFS0CLR = _IFS0_T3IF_MASK; // Clear the timer interrupt status flag   
	// ・・・・
}
//-----------------------------------------------------
void __ISR(_ADC_VECTOR,IPL76OFT)ADC12Handler(void)// ADCの割り込み
{
	// 逐次AD変換が終わって、レジスタに格納して呼び出される。
	// AD1CON1bits.ASAM=1により自動的に「AD1CON1bits.SAMP=1」が働き、サンプル状態になる。
	// 次のtimer4で、AD1CON1bits.SAMP=0が実行されてサンプリング終了を待つ。
}
void __ISR(_TIMER_3_VECTOR,IPL6SOFT)Timer3Handler(void)
{
   ((void (*)(struct ADC_BUFF*))handlers[_IDX_TIMER_3_FUNC])(p_buff);
	// この要素に記憶されるadc_usb_out関数で、ADC結果のバッファ用メモリ情報をUSBに出力している
}
void __ISR(_ADC_VECTOR,IPL7SOFT)ADC1Handler(void)
{
    ((void (*)(struct ADC_BUFF*))handlers[_IDX_ADC_1_FUNC])( p_buff );
	// この要素に記憶されるadc_umeFunc1_1関数で、ADC結果のレジスタをバッファ用メモリに記憶している
}
つまり、handlers配列に記憶される関数へのポインタが、各割り込み処理が行われています。
// 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 
}
これは「handlers[_IDX_INIT_TIMER_3] = init_timer_3;」と記憶されて、それが初期化ルーチンで呼び出されています。
// 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    
}
これは「handlers[_IDX_INIT_TIMER_3] = init_timer_3;」と記憶されて、それが初期化ルーチンで呼び出されています。 
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;
};
以下ではADCの割り込み用のadc_umeFunc1_1関数と、Timer3の割り込み用のadc_usb_out関数を定義して
既存の「テスト・ウメ・フラッシュ」の、この割り込み処理を
置き換える「ウメ・エディットプログラム」の例を示します。
#include <xc.h> // ADC_8K_AN3_2B.c  (AN0, AN1) 2block sampling
#include "common.h"
#define MY_ADC_ERR_OVERFLOW  0x0001 // ADC取得データの送信が間に合わなないエラー
#define AdrStart	0x80005000
__attribute__((address( AdrStart  ))) void start (void);
#define AdrStop	0x80006000
__attribute__((address( AdrStop))) void stop (void);
void adc_umeFunc1_1(struct ADC_BUFF *p_buff);//ADC割り込みルーチン
void adc_usb_out(struct ADC_BUFF *p_buff);//USBに出力するTimer3の割り込みルーチン
void start()
{
	_RB15 = ! _RB15 ;//LED反転
	_HANDLES[_IDX_ADC_1_FUNC]=  (void *)adc_umeFunc1_1;//ADCの割り込み(バッファに記憶)を自作の処理に置き換える
	_HANDLES[_IDX_TIMER_3_FUNC] = (void *)adc_usb_out;//Timer3割り込み(バッファの出力)を自作の処理に置き換える
	// (1/8000)/(1/40e6)/2-1=2499
	PR3=2499;	//サンプリング周波数を 8KHzに指定するパラメタ
	_set_adc_mode(3 , 0); // チャンネル指定と、bin/textの選択
	_set_adc_exe(2 , 0);  // ブロックのサンプリングを繰り返し数とループフラグ指定
	T3CONbits.ON = 0; // すぐにtimer3を停止し、以下で変更
	AD1CON2bits.ALTS = 1; // MUX AおよびMUX B入力マルチプレクサ設定を交互に使う
	AD1CON2bits.CSCNA = 0; // MUX A 入力マルチプレクサ 入力をスキャンしない。
	AD1CON2bits.SMPI = 1; // 割り込みあたりサンプリング 
	AD1CHSbits.CH0NA = 0; // MUX A チャンネル 0 の負極性入力に VR- を選択する
	AD1CHSbits.CH0NB = 0; // MUX B チャンネル 0 の負極性入力に VR- を選択する
	AD1CHSbits.CH0SA = 0; // MUX A チャンネル 0 の正極性入力に AN0 を選択する
	AD1CHSbits.CH0SB = 1; // MUX B チャンネル 0 の正極性入力に AN1 を選択する
	AD1CON3bits.SAMC = 0b01010; // b12-b8  自動サンプリング時間ビット= 2TAD
	//上記はアナログ入力ピンをサンプル/ホールドアンプ(SHA)に接続するアクイジション時間
	AD1CON3bits.ADCS = 0b00001011; /// b7-b0  ADC 変換クロック選択ビット
	// ADCの変換時間(8bit) =  TPB * 2 * (ADCS<7:0> + 1) = 512 * TPB = TAD
	IEC0CLR=_IEC0_T3IE_MASK;// // timer3の出力割り込み不許可★
	T3CONbits.ON = 1; // timer3 On 
	//デフォルトのままであれば、上記の黄色の部分の記述は不要です。ADC関連のSFR変更例として示しました。 
}
void stop()
{
	_set_adc_exe(1 , 0);// 現ブロック送信で終了
}
// ADCの新しい割り込み処理(AN0とAN1の交互サンプリング情報をp_buffに記憶)
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  
}
int usb_receiver_disable_flag = 0;//受信を無効にするフラグ
int my_adc_set_err = 0;
// Timer3の新しい割り込み処理(p_buffの内容をUSBへ出力する)
void adc_usb_out(struct ADC_BUFF *p_buff) {
    static int flag_start_ADC = 1;
    static int flag_end_ADC = 0;
    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) == 0) {// ★
        IFS0CLR = _IFS0_T3IF_MASK; // Clear the timer interrupt status flag
        return;        //USB出力権限取得失敗で、、次の呼び出しにする。
    }
    // ADC DATA 送出.============>USB
    int text_mode = p_ctrl->adc_out_usb == (void (*)()) _HANDLES[_IDX_API_SEND_HEX_LOW]; 
    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 (text_mode == 1) _send_char('T'); //TextMode
            _send_hex_low(p_ctrl->count_end_ADC); //送信データ数(X2))
        } else if (p_ctrl->out_channel_bits == 0x1) {//AN0の出力のみ
            _send_string("\r\nADC_START0\r\n");
            if (text_mode == 1)_send_char('T'); //TextMode
            _send_hex_low(p_ctrl->count_end_ADC); //送信データ数
        } else if (p_ctrl->out_channel_bits == 0x2) {//AN1の出力のみ
            _send_string("\r\nADC_START1\r\n");
            if (text_mode == 1)_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 = 0; //開始処理を終えた〇
        flag_end_ADC = 0;
    } else if (p_ctrl->counter_ADC >= p_ctrl->count_end_ADC) {
        // ----------送出ブロック終了の処理------
        if (!flag_end_ADC) {
            flag_end_ADC = 0;
            flag_start_ADC = 1;
            _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,AN1 の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 && text_mode == 1) {
                // 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
}
ここで定義したadc_umeFunc1_1関数とadc_usb_out関数で
既存の割り込み処理を置き換えています。
START:80005000 ADC_START 1000 ADC_END