トップページ > dsPIC入門 > dsPICでAD変換(4)
前回まで、AD変換モジュールの基本的な使い方を見てきました。今後コンフィグレーション等は、 前回までと全く同じものを使用していくことにします。(テスト基板のハードウェアに変更が生じない限り) 今回はより実用的な使い方として、タイマーモジュールと組み合わせて一定時間間隔でAD変換を実行することを 試してみます。実際の信号処理では入力波形のアナログ値を一定間隔でサンプリングしてメモリ上にどんどん 保存し、その一連の値を時間の関数と見なして処理を行う…というのがメジャーだと思います。 その基礎として、簡単なプログラムを組んでみます。
dsPICのAD変換モジュールは、タイマー3と連携しています。タイマー3のカウンタの値が設定値をこえると、 自動的にAD変換が実行されます。AD変換モジュールの方の割り込みを許可しておけば、AD変換終了と共に 割り込み用の関数へ飛びますので、そこで処理を記述…という流れになります。
AD変換モジュールとハードウェア的に連携することができるのはタイマー3だけですが、べつにプログラム上では
「タイマー1で時間間隔設定→タイマー1の割り込み関数内でAD変換の処理を記述」という感じで記述してやれば、
タイマー1の使い方さえ知っていれば新しい事を覚えずに済みます。。。
しかし、タイマー1は機能豊富で、いざという時のために温存(?)しておきたいので、ここでは今後の拡張性なども
考慮してタイマー3の方を使う方法を選びます。
今回のプログラムの仕様は、0.5[秒]間隔でAD0入力ピンの電圧をAD変化し、ポートEのLED(緑色、6個) に出力します。変換のタイミングごとに、ポートDの赤色LEDが点滅するようになっています。
///******************************************************************** //一定時間間隔でAD変換 @dsPIC30F4012 //******************************************************************** //==================================================================== //ヘッダファイル #include "p30f4012.h" #include "timer.h" #include "adc10.h" //==================================================================== //コンフィギュレーション _FOSC(CSW_FSCM_OFF & XT_PLL8); //10MHzセラロックを使っているのでクロックは80MHz。 _FWDT(WDT_OFF); _FBORPOR(PBOR_ON & BORV_20 & PWRT_64 & MCLR_EN); _FGS(CODE_PROT_OFF); //==================================================================== //タイマー用のコンフィグ unsigned int T3config = T3_ON & T3_GATE_OFF & T3_PS_1_256 & T3_SOURCE_INT; //タイマー3のカウント上限値設定(設定周期T[s]のための計算方法) //システムクロックが80[MHz]の場合、1サイクル(4クロック)あたり、Tcy=4/(80*10^6)[秒] //今回の設定では、タイマー側で1/256に分周されます。 //よって、タイマー3のコンペア・レジスタの値は、T[s] = Tcy*256*(SampleTime-1) ですから… //SampleTime = T/(Tcy*256) + 1 …となります。 //ここでは、0.5秒ごとにAD変換実行します。 unsigned int SampleTime = 39063; //16ビットint型の最大値「65535」を超えないように!! //==================================================================== //AD変換用のコンフィグ unsigned int ADconfig1 = ADC_MODULE_ON & ADC_IDLE_STOP & ADC_FORMAT_INTG & ADC_CLK_TMR & ADC_AUTO_SAMPLING_ON & ADC_SAMPLE_SIMULTANEOUS & ADC_SAMP_ON; unsigned int ADconfig2 = ADC_VREF_AVDD_AVSS & ADC_SCAN_OFF & ADC_CONVERT_CH0 & ADC_SAMPLES_PER_INT_1 & ADC_ALT_BUF_OFF & ADC_ALT_INPUT_OFF; unsigned int ADconfig3 = ADC_SAMPLE_TIME_1 & ADC_CONV_CLK_SYSTEM & ADC_CONV_CLK_8Tcy; unsigned int ADconfigPORT = ENABLE_AN0_ANA; unsigned int ADconfigSCAN = 0; unsigned int ADconfigCH0 = ADC_CH0_POS_SAMPLEA_AN0 & ADC_CH0_NEG_SAMPLEA_NVREF; //==================================================================== //メイン関数 int main(void) { //------------------------------------------------------------------- //ポート初期設定 TRISB = 0x01; //ポートBは0ビット目だけAD変換用入力 TRISE = 0x00; //ポートEは出力(緑色LED) TRISD = 0x00; //ポートDも出力(赤色LED) PORTD = 0xff; //------------------------------------------------------------------- //AD変換モジュールの初期設定 OpenADC10(ADconfig1,ADconfig2,ADconfig3,ADconfigPORT,ADconfigSCAN); SetChanADC10(ADconfigCH0); //チャンネル設定 ConfigIntADC10(ADC_INT_ENABLE & ADC_INT_PRI_5); //AD変換機からの割り込みを許可 //------------------------------------------------------------------- //タイマー3の初期設定 OpenTimer3(T3config,SampleTime); //------------------------------------------------------------------- //何もしないで、割り込み待ち while(1); } //==================================================================== //タイマー3からの割り込みでAD変換後、AD変換終了時に呼ばれる関数。 void __attribute__((__interrupt__,__shadow__)) _ADCInterrupt(void) { //割り込みフラグをクリアしておく IFS0bits.ADIF = 0; //PORTEの緑色LEDへ、変換結果を出力 PORTE = ReadADC10(0); //赤色LEDのON/OFFを入れ替える PORTD = !PORTD; }
ほとんどはAD変換の最初のテストプログラムと同じです。メイン関数もただ初期設定だけ
をして、あとはwhile(1)で割り込み待ちです。
(このwhile(1)を書かないと…プログラムが毎回終わってしまって
正常に割り込み待ち動作をしなかったような気がします 汗)
流れとしては非常に単純です。
タイマー3モジュールはタイマー1の時と同じで、各種コンフィグを設定してOpenTimer3()関数を呼ぶだけです。 サンプリング周期の設定はタイマー内のカウンタと、上限値レジスタとの比較によって行う(コンペアマッチ) のですが、この上限値レジスタの値はOpenTimer3()関数の2番目の引数です。上限値レジスタの値はunsigned int型 として扱われているので、「65535」より大きい値を入れないように注意します。(65535をオーバーすると なんかオカシイ時間でサンプリングします…)
AD変換モジュールのコンフィグは、1箇所だけ変更が必要です。例の、タイマー3のコンペアマッチをトリガとして
AD変換を実行するための設定です。
unsigned int ADconfig1 = ADC_MODULE_ON & ADC_IDLE_STOP & ADC_FORMAT_INTG &
ADC_CLK_TMR
& ADC_AUTO_SAMPLING_ON & ADC_SAMPLE_SIMULTANEOUS & ADC_SAMP_ON;
…赤字の部分です。AD変換開始のトリガを「TMR」、タイマーに設定します。これで、タイマー3のカウンタが
設定値に達したときにAD変換が実行されます。
void __attribute__((__interrupt__,__shadow__)) _ADCInterrupt(void) { //処理内容 }