テストスケッチ <MCP4922_DDS_WaveTableSize_Test.ino>
/* Arduino LFO DDSの波形テーブルの検証 2018.01.26 */ #include <SPI.h> #include "avr/pgmspace.h" #include "wavetable_12bit_8k.h" #define PIN_CHECK (0) #define BIT_LENGTH_8 (0) #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // Pin Assign const int MCP4922Ldac = 9; const int MCP4922Cs = 10; #if (PIN_CHECK) const int CheckPin1 = 18; // A4 const int CheckPin2 = 19; // A5 #endif // MCP4922 SPISettings MCP4922_SPISetting(8000000, MSBFIRST, SPI_MODE0); // Parameter double drate = 50.0; // initial output rate (Hz) const double refclk = 15625.0; // = 16MHz / 8 / 128 // DDS volatile uint32_t phaccu; volatile uint32_t tword_m; //------------------------------------------------------------------------------------------------- // Interrupt Service Routine // // param // channel: 0, 1 // val: 0 .. 4095 void MCP4922Write(bool channel, uint16_t val) { uint16_t cmd = channel << 15 | 0x3000; cmd |= (val & 0x0fff); digitalWrite(MCP4922Ldac, HIGH); digitalWrite(MCP4922Cs, LOW); SPI.transfer(highByte(cmd)); SPI.transfer(lowByte(cmd)); digitalWrite(MCP4922Cs, HIGH); digitalWrite(MCP4922Ldac, LOW); } ISR(TIMER2_OVF_vect) { #if (PIN_CHECK) digitalWrite(CheckPin1, HIGH); #endif // synthesize phaccu = phaccu + tword_m; // テーブルサイズに合わせてシフトするビットを変更 int idx = phaccu >> 19; // use upper n bits (table size) #if (BIT_LENGTH_8) MCP4922Write(0, pgm_read_word_near(sin_table + idx) << 4); #else MCP4922Write(0, pgm_read_word_near(sin_table + idx)); #endif #if (PIN_CHECK) digitalWrite(CheckPin1, LOW); #endif } //------------------------------------------------------------------------------------------------- // Setup // // TIMER2 setup void Setup_timer2() { // non-PWM / Normal port operation, OC0A disconnected. cbi (TCCR2A, COM2A0); cbi (TCCR2A, COM2A1); // Mode 7 / Fast PWM sbi (TCCR2A, WGM20); sbi (TCCR2A, WGM21); sbi (TCCR2B, WGM22); // 16000000 / 8 / 128 = 15625 Hz clock OCR2A = 127; // Timer2 Clock Prescaler to : 8 cbi (TCCR2B, CS20); sbi (TCCR2B, CS21); cbi (TCCR2B, CS22); } void setup() { tword_m = pow(2, 32) * drate / refclk; // calculate DDS tuning word; #if PIN_CHECK pinMode(CheckPin1, OUTPUT); #endif pinMode(MCP4922Cs, OUTPUT); digitalWrite(MCP4922Cs, HIGH); // set CS as inactive pinMode(MCP4922Ldac, OUTPUT); SPI.begin(); SPI.beginTransaction(MCP4922_SPISetting); Setup_timer2(); // disable interrupts to avoid timing distortion cbi(TIMSK0, TOIE0); // disable Timer0 !!! delay() is now not available sbi(TIMSK2, TOIE2); // enable Timer2 Interrupt sei(); } //------------------------------------------------------------------------------------------------- // Main Loop // void loop() { }
Github:
https://github.com/ryood/ArduinoLFO/tree/e2cc9572cc76aa3309c5c6d5e4b51de9a17d279d/Arduino/MCP4922_DDS_WaveTableSize_Test
製作中のArduino LFOのスケッチから不要な部分を削除しました。
テーブルは、ぴゅんぴゅん2号で使っていた8bit✕256と、MCP4922のビット長12bitにして要素数が1024、2048、4096、8192にしたものを比較しました。
DDSはフェーズアキュムレータの上位bitをテーブルのインデックスとして使うので、テーブルの要素数は2^nである必要があります。
Arduino Unoのフラッシュメモリのサイズが32kBで(その一部はプログラムで使用)、int16_t型(2Byte)のテーブルなので、8192以上はメモリーオーバーします。
測定時にはUSB経由のノイズを回避するためにArduinoは電池電源(単3✕6)を使用しました。
Audio I/F: TASCAM US-144 MKII MIC/Line入力
窓関数: FlatTop
Avg: 100
8bit長 256要素
12bit長 1024要素
12bit長 2048要素
12bit長 4096要素
12bit長 8192要素
テーブルが8bit長のときと、12bit長のときでははっきりと歪が改善されます。また、テーブルの要素数が増えると高次の歪が減ります。
聴感で敏感な1kHz~10kHzあたりを比較すると、やはりテーブルの要素数は大きければ多いほどよさそうです。8192になると、サンプリング周波数の15kHz付近のエイリアスが支配的になってきます。
サイン波の傾きが大きい原点あたりのテーブルの値を比較すると、
12bit長 1024要素
// table of 2048 values / one period / stored in flash memory/*** MAX_VALUE = 4096 SAMPLE_NUM = 1024 delta = 3.999023***/ /*** sine wave ***/ const PROGMEM uint16_t sin_table[] = { 2047 , 2060 , 2072 , 2085 , 2097 , 2110 , 2122 , 2135 , 2147 , 2160 ,
12bit長 8192要素
// table of 8192 values / one period / stored in flash memory const PROGMEM uint16_t sin_table[] = { 2047, 2049, 2050, 2052, 2053, 2055, 2056, 2058, 2060, 2061, 2063,
1024要素の場合は値がトビトビで、サンプリング・ポイントでの誤差が歪となって現れる結果だと思います。
ちなみに、12bit長8192要素のテーブルでも1kHzのサイン波を出力すると以下のような波形になります。
随分ガタガタしてますが、12bit@15.625kHzなのでしかたありません(^q^; Arduino Uno+MCP4922で出せる波形もこのあたりが上限だと思います。
処理時間
前回のUIを付けたスケッチで処理時間を測定しました。テーブルは12bit長、2048要素です。UART_TRACEは無効にしています。
Arduinoのスケッチ <MCP4922_LFO.ino>
Github:
https://github.com/ryood/ArduinoLFO/tree/e2cc9572cc76aa3309c5c6d5e4b51de9a17d279d/Arduino/MCP4922_LFO
割り込みとSPI処理
ch1:LDAC ch2:A4
SPI通信にかかっている時間は20usで同じですが、割り込み処理時間は38.4us→44usと増えています。増やした処理は、配列をint16_tにしたことと、switch文で出力する波形を切り替えているぐらいですが、もはやカツカツな感じです。
割り込みとメインループ
ch1:A5 ch2:A4
analogRead()でPOT2個の値を読み取るようにしただけですが、loop()内の処理時間(ch1:A5がHの時間)が増えています。
SPI通信
ch1:MOSI ch2:SCK
メモ:
ノコギリ波はテーブル参照しなくても、位相値(phaccu)の値を見ればできそう?上昇下降は足し算引き算で?
三角波もできそう?(計算量によりそうですが)
矩形波も位相値でH/Lを切り替え?
処理時間次第ですがノイズ(S&H)もできるかも?
0 件のコメント:
コメントを投稿