UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
マイクロコントローラ (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の初期化デフォルト関数です。 |
void init_timer_3() | Timer3の初期化デフォルト関数です。TypeBの各種設定をしています。
T3CONのTGATE=0、
TCKPS=0b000 // [1:1]プリスケール値、
T32=0 // 16bitカウンタ指定、
TCS=0 // 周辺モジュールバス クロック(PBCLK) などの設定 |
int set_adc_mode (int channel_bit, int text_mode_flag) |
引数channel_bitで、AN0のサンプリングを得るUSBの出力指定が1、AN1を得るUSBの出力指定が2、
AN0とAN1の2つをサンプリングを得るUSBの出力指定が3です。 |
int set_adc_exe (int block_size, int loop_flag) |
ADCのの使い方を設定する関数 |
void adc_umeFunc1_1(struct ADC_BUFF *p_buff) | ADCの割り込み処理 |
void adc_usb_out(struct ADC_BUFF *p_buff) | Timer3の割り込み処理 |
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割り込みで起動できるように
なっています。
#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 */
/* * 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 }