2018年1月26日金曜日

Arduino LFO ブレッドボードで実験

UIをつけて、スケッチを書いてみました。

ブレッドボード配線図


Arduinoのスケッチ <MCP4922_LFO.ino>

https://github.com/ryood/ArduinoLFO/tree/de0d2a7960f098d09f66701f78884aede1355075/Arduino/MCP4922_LFO

外部割り込みの使用


波形選択用のタクトスイッチの読み取りは外部割り込み(INT0)を利用しました。

attachInterrupt(digitalPinToInterrupt(ButtonWaveShape), waveshape_pushed, FALLING);

loop()内のポーリングでdigitalRead()を使って読み取ると、スイッチの押し下げ→開放状態をそれぞれ読み取って比較する必要がありますが、外部割り込みを使うと立ち上がり、または立ち下がりを補足できるので便利です。

デメリットはArduino Unoの場合、外部割り込みに使えるピンがD2とD3の2本に限られていることです。MCP4922とのSPI通信でD9~D13を使っていて、D0とD1はPCとのシリアル通信のために開けておいた方がいいので、その他に使えるピンがなかなか制限されてきます。

また、波形生成に使っているTimer2よりも割り込みの優先順位が高いので気をつけておく必要があります。

ATMega328PのDATASHEETに割り込みベクタテーブルがのっていますが、割り込みベクター番号が若いほうが優先順位が高くなります。


INT0の割り込み処理を長々とやっているとTimer2の割り込みのタイミングが遅れてしまって、波形に揺れが生じる可能性があります。

チャタリング対策


外部割り込みをそのまま読み取るとチャタリングが発生したので対策しました。外部割り込み時に、読み取り状態を確定させるまでの時間(waveshape_pushed_wait)をセットして、Timer2の割り込み時に設定時間経過後、再度押し下げ状態を読み取って変化がなければ確定するようにしています。

単純に一定時間INT0を無視するようにしても、ある程度効果はありそうですが(^q^?

A/Dコンバーターの読み取り


LFOのRateとパルス幅をPOTで設定してADCで読み取る様にしましたが(analogRead())、無事処理できているようです。

ADC読み取りはloop()内でポーリングしていて、上記割り込みベクタテーブルを見ると、ADCは22番で優先順位が低く、Timer2の割り込みのタイミングには影響しないと思います。

※Arduinoのソースを見ると、ADCの完了待ちはADCSRAレジスタを監視していて、割り込みは使用していないようです。

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\wiring_analog.c
// start the conversion
sbi(ADCSRA, ADSC);
// ADSC is cleared when the conversion finishes
while (bit_is_set(ADCSRA, ADSC));

LEDの点灯はPORTで処理


複数のLEDの点灯はdigitalOut()で処理するとやたら煩雑です。ArduinoのD0~D7の実体はATMega328PのPORTDなので直接レジスタ操作するようにしました。

  // Write to LEDs (D3~D7)
  byte portd_bits = (1 << (waveshape_sel + 3)) | (PORTD & 0x07);
  PORTD = portd_bits;

しかし毎度のことながらビット演算はややこしくて混乱してしまいます。Serial.print()で状態を確認してプログラミングしました(^q^;;;

次回は動作状態や、DDSの波形テーブルのサイズが与える影響を調べてみたいと思います。

メモ:


矩形波のパルス幅は未実装