macsbug

Just another WordPress.com site

SplitRadixRealP FFT Analyzer of Arduino DUE

with 2 comments

Arduino DUE で SplitRadixRealP FFT Analyzer を製作。                 2015.01.29

FFT Library は、SplitRadixRealP FFT LIbrary を使用しました。

coolarduino.wordpress.com に感謝致します。
Append(追記):上記のサイトはリンクが切れ「pierinz/randusynth」に保存されています。2016.06.29
The above sites are stored in the link expired “pierinz / randusynth“.
Arduino DUE のスケッチは下に記載しました。

右下は、coolarduino氏のVscroll機能のあるWaterFall Spectrogramを見て作りました。

fft_1 line_1
fcs d

 

3Dプリンターでケースを作る。色は Kinoma Create と同色。以前作ったケースのデーターを使用し、
厚みを変える程度で楽に出来ました。こういう点、CAD や3Dプリンターは便利です。

速度について:
Arduino DUE での FFT Library は、動く物を探すのが難しいですが、私の知りうる所、
SplitRadixRealP FFT Library が、最も速く動作すると思われます。
入力装置について:
入力マイクアンプは、秋月電子通商の高感度マイクアンプキットで、コンデンサーマイクを使用し、
増幅率は、100倍。周波数帯域は、50Hz 〜16KHz です。

表示:
DUE + SplitRadixRealP の最大帯域は、計測していません。
今回は、マイクの帯域に合わせて、20KHz までの表示としました。
ま〜、周囲には高い周波数はなかなかありませんので、広いくらいかも知れません。
画面をタッチすると、バーグラフとライングラフと交互に切り替わります。

表示と速度について:
表示には、UTFT Library を使用しています。
データー数は、1024個で、これを、表示するとなると Arduino DUEの速度が響きます。まして、その下の Arduino MEGA だと更に遅く、Arduino UNOだとかなり厳しいですね。速度を上げるには、256 bit とか、データー数を減らしますが、そうすると帯域が狭くなります。
1024個のラインで表示する場合(写真右)の掃引時間は、約0.5secで少し遅いです。
バーグラフ(左)は、10個分で1つのバーを表示しています。これなら、音に合わせて充分見れる速度です。
データーと表示の関係では、1024個取っていますが表示には、450個(横軸)と半分以下しか使用していません。
プログラムを工夫すればもっと良い方法があるかも知れません。
表示方法の高速化もやってみたいテーマです。

構成:
1. Arduino DUE     :32bit ARM Cortex-M3 84MHz( Atmel SAM3X8E )
2. 3.5int 480×320 TFT  :aitendo 3.5インチ液晶 (1980円)(海外では$9位のものあり)
_ この製品は、Color(R,G,B) 設定が違いUTFT Library に合わない為、値をずらします。
3. Mic AMP       :秋月電子通商 高感度マイクアンプキット (500円)
4. SplitRadixReal FFT Library :上の ↓ を選択する。
_ pierinz/randusynth:SplitRadixRealP.zip :追加:2016.06.29
_ LIbraryは、ユーザー > USER > 書類 > Document> Arduino > libraries へ入れておく。

速度:幾つかの製作結果。
1. MicroView(ATMEGA328)(UNO)(8bit 16MHz) で 10KHz。
2. Arduino MEGA 2560(16bit 16MHz) で 13KHz。
3. Arduino DUE(32bit 84MHz) + Radix4 FFT Library は、30KHz。
4. Arduino DUE(32bit 84MHz) + SplitRadixRealP FFT LIbrary は、90KHz 位?。

メモ:
1. coolarduino は、高度な記事で、とても参考になります。
2. Arduino DUE + TFT Display ( ILI9486 ) での記事
3. 今回の帯域:
_ 1024bit 取得し、表示には、450 bit で 20KHz。
_ SplitRadixRealP は、2048 bit まで取得できるので、単純計算では、91KHz。 えっ、どうなんだろう。
4. USB5V電源の動作電流は、340mA。

他のFFT :coolarduino
1. waterfall-spectrogram:20 – 20000Hz, スペクトラムの垂直スクロール機能。
_ 、、やり方によっては、ここまでできるんですね。
2. Rdax4 FFT:スケッチは、ほぼ同じで動作しました。

 

スケッチ:

</pre>
<pre class="brush: plain; title: ; notranslate" title="">// SplitRadixRealP FFT Audio Analyzer for Arduino DUE
// ORG 2015.01.28            s.ono
// https://coolarduino.wordpress.com/2015/01/16/waterfall-spectrogram/
// https://coolarduino.wordpress.com/2014/09/25/splitradixreal-fft-library/
// FFT --------------------------------------------------------------------------------
#include <SplitRadixRealP.h>
#define   SMP_RATE     48000UL 
#define   CLK_MAIN  84000000UL
#define   TMR_CNTR    CLK_MAIN / (2 *SMP_RATE)
#define   FFT_SIZE        1024 //2048
#define   MIRROR      FFT_SIZE / 2
#define   INP_BUFF    FFT_SIZE         
volatile   uint16_t   sptr              =   0 ;
volatile    int16_t   flag              =   0 ;
           uint16_t  inp[2][INP_BUFF]   = { 0}; // DMA likes ping-pongs buffer
            int         f_r[FFT_SIZE]   = { 0};
            int         out1[MIRROR]    = { 0}; // Magnitudes
            int         out2[MIRROR]    = { 0}; // Magnitudes
const       int         dc_offset       = 1023; //2047;
unsigned long time_start;
unsigned int  time_hamng, time_revbn, time_radix, time_gainr, time_sqrtl, time_sqrtl2;
              SplitRadixRealP     radix;
// TFT --------------------------------------------------------------------------------
#include <UTFT.h>                              // UTFT Routine
#include <UTouch.h>                            // UTouch Routine
extern uint8_t SmallFont[];                    // Small Font
extern uint8_t BigFont[];                      // Big   Font
extern uint8_t SevenSegNumFont[];              // SevenSegNumFont
UTFT   myGLCD(CTE35IPS,38,39,40,41);           // Model=CTE35IPS 480x320 (aitendo)
// Because Display of aidendo is out of Color, the change of the value is necessary.
UTouch myTouch(6,5,4,3,2);                     // Touch setup
int mode = 2; int mx;                          // display mode

void setup(){
 //Serial.begin (115200) ;
 adc_setup ();         
 tmr_setup ();         
 pinMode( A0, INPUT);                          // Analog A0 input
 // TFT setup -------------------------------------------------------------------------
 myGLCD.InitLCD(LANDSCAPE);                    // LCD Initialize
 myGLCD.lcdOn();                               // -
 myGLCD.setContrast(64);                       // -
 demo();                                       //
 myGLCD.setFont(SmallFont);                    // Font : smallfont
 myGLCD.clrScr();                              // LCS Clear
 myTouch.InitTouch();                          // Touch Initialize
 myTouch.setPrecision(PREC_EXTREME);           // Touch Sence MAX
 myGLCD.setColor(255,230,230);                 // panel background color 222,222,222
 myGLCD.fillRect(0, 0, 480, 320);              // panel all fill
 myGLCD.setColor(255,100,100);                 // waku color
 myGLCD.drawRect(19, 29, 475, 301);            // waku
 myGLCD.setColor(0, 0, 0);                     // moji color=white
 myGLCD.setBackColor(255,230,230);             // moji background color
 myGLCD.print(" SplitRadixRealP FFT ANALYZER FOR ARDUINO DUE ", LEFT, 1); // TITLE
 myGLCD.setColor(0,0,0);
 myGLCD.setBackColor(255,230,230);             // moji background color
 myGLCD.print(" 1  2       5            10           15           20KHz", 30, 305);
}
//-------------------------------------------------------------------------------------
inline int mult_shft12( int a, int b){ return ((a * b) >> 12);}
//-------------------------------------------------------------------------------------
void loop(){
  while (myTouch.dataAvailable() == true) {    // interupt Touch
    if ( mode == 1 ){ mx = 2;}
    if ( mode == 2 ){ mx = 1;}
    mode = mx;
  }
  if ( flag ){
  // FFT ------------------------------------------------------------------------------
   uint16_t indx_a = flag -1;
   uint16_t indx_b = 0;
   time_start = micros();
   for ( uint16_t i = 0, k = (NWAVE / FFT_SIZE); i < FFT_SIZE; i++ ){
      uint16_t windw = Hamming[i * k];
      f_r[i] = mult_shft12((inp[indx_a][indx_b++] - dc_offset), windw);
   }
   time_hamng  = micros() - time_start;
   time_start = micros();
   radix.rev_bin( f_r, FFT_SIZE);
   time_revbn  = micros() - time_start;
   time_start = micros();
   radix.fft_split_radix_real( f_r, LOG2_FFT);
   time_radix  = micros() - time_start;
   time_start = micros();
   radix.gain_Reset( f_r, LOG2_FFT -1); 
   time_gainr  = micros() - time_start;
   time_start = micros();
   radix.get_Magnit1( f_r, out1);
   time_sqrtl  = micros() - time_start;
   time_start = micros();
   radix.get_Magnit2( f_r, out2);
   time_sqrtl2  = micros() - time_start;
   // ---------------------------------------------------------------------------------
   switch(mode){
     case 1:
         prnt_out5( f_r, MIRROR);                // line type
         break;
     case 2:
         prnt_out6( f_r, MIRROR);                // bar  type
         break;
   }  
   flag = 0;
  }   
}
//-----------------------------------------------------------------------------

void prnt_out5( int *yd, int ct){                // fft data,count
  int xr=20; int yr=300;
  int x1; int x2; int y1; int y2;
  yd[0]=0; yd[1]=0;                              // first clear
  for ( uint32_t x = 2; x < 450; x++){           // 1 - 450
      yd[x] = abs( yd[x] );                       //
      //if ( yd[x] > 270 ){ yd[x] = 270;}          // display limiter
      x2 = x + xr;                               // x2 : 480/(1024/3)
      x1 = x2 - 1;                               // x1
      y1 = yr - 4*yd[x-1];                       // y1
      y2 = yr - 4*yd[x];                         // y2
      if ( y1 < 30 ){ y1 = 30;}                  // Limitter
      if ( y2 < 30 ){ y2 = 30; yd[x] = 270;}     // disp,data Limitter
      myGLCD.setColor(255,255,255);              // clear(black)
      myGLCD.drawLine(x2, yr, x2, 30);           // clear(black)
      myGLCD.setColor(255,0,0);                  // blue draw
      myGLCD.drawLine(x1, y1, x2, y2);           // blue draw
   }
}
//-----------------------------------------------------------------------------

void prnt_out6( int *yd, int ct){                // fft data,count
  int x0=20; int w = 10;                         // w : bar width
  int x1; int x2; int y1=300; int y2;            // y1
  int c1=250; int c2=190; int c3=130; int c4=70; //
  yd[0]=0; yd[1]=0;                              // first clear
  for ( int x = 2; x < 450; x = x + w ){         // 1 - 450
      y2 = 0;                                    // y2 reset
      for ( int k = 0; k < w; k++ ){             // 0 to 9
        y2 = y2 + abs(yd[ x + k]);               // x + 0  to  x + 9
      }                                          //
      y2 = y1 - 4*(y2 / w );                     // y2 *4
      x1 = x + x0;                               // x1
      x2 = x1 + w - 2;                           // x2
      if ( y2 < 30 ){ y2 = 30;}                  // Limitter

      myGLCD.setColor(255,255,255);              // clear(black)
      myGLCD.fillRect(x1, y2, x2+2, 30);         // clear(black)   

      if ( y2 < c4 ){                            // < 70
        myGLCD.setColor(0,255,255);
        myGLCD.fillRect(x1, c4, x2, y2);
        myGLCD.setColor(0,255,0);
        myGLCD.fillRect(x1, c3, x2, c4);
        myGLCD.setColor(0,0,255);
        myGLCD.fillRect(x1, c2, x2, c3);
        myGLCD.setColor(255,0,255);
        myGLCD.fillRect(x1, c1, x2, c2);
        myGLCD.setColor(255,0,0);
        myGLCD.fillRect(x1, y1, x2, c1);
      }
      if ( y2 > c4 && y2 < c3 ){                 // y2 =  70 - 130
        myGLCD.setColor(0,255,0);
        myGLCD.fillRect(x1, c3, x2, y2);
        myGLCD.setColor(0,0,255);
        myGLCD.fillRect(x1, c2, x2, c3);
        myGLCD.setColor(255,0,255);
        myGLCD.fillRect(x1, c1, x2, c2);
        myGLCD.setColor(255,0,0);
        myGLCD.fillRect(x1, y1, x2, c1);
      }
      if ( y2 > c3 && y2 < c2 ){                 // y2 = 130 - 190
        myGLCD.setColor(0,0,255);
        myGLCD.fillRect(x1, c2, x2, y2);
        myGLCD.setColor(255,0,255);
        myGLCD.fillRect(x1, c1, x2, c2);
        myGLCD.setColor(255,0,0);
        myGLCD.fillRect(x1, y1, x2, c1);
      }
      if ( y2 > c2 && y2 < c1 ){                 // y2 = 190 - 250
        myGLCD.setColor(255,0,255);
        myGLCD.fillRect(x1, c1, x2, y2);
        myGLCD.setColor(255,0,0);
        myGLCD.fillRect(x1, y1, x2, c1);
      }
      if ( y2 > c1 ){                            // > 240
        myGLCD.setColor(255,0,0);
        myGLCD.fillRect(x1, y1, x2, y2);
      }      
  }
}
//-------------------------------------------------------------------------------------

void pio_TIOA0 (){
  PIOB->PIO_PDR = PIO_PB25B_TIOA0 ;    // disable PIO control
  PIOB->PIO_IDR = PIO_PB25B_TIOA0 ;    // disable PIO interrupts
  PIOB->PIO_ABSR |= PIO_PB25B_TIOA0 ;  // switch to B peripheral
}
//------------------------------------------------------------------------------------- 
void tmr_setup (){
  pmc_enable_periph_clk(TC_INTERFACE_ID + 0 *3 + 0); // clock the TC0 channel 0
  TcChannel * t = &(TC0->TC_CHANNEL)[0] ;            // pointer to TC0 registers for its channel 0
  t->TC_CCR = TC_CCR_CLKDIS ;                        // disable internal clocking while setup regs
  t->TC_IDR = 0xFFFFFFFF ;                           // disable interrupts
  t->TC_SR ;                                         // read int status reg to clear pending
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |           // use TCLK1 (prescale by 2, = 42MHz)
              TC_CMR_WAVE |                          // waveform mode
              TC_CMR_WAVSEL_UP_RC |                  // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;  
  t->TC_RC = TMR_CNTR;              // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA = TMR_CNTR /2;           // roughly square wave
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.
}
//-------------------------------------------------------------------------------------
void adc_setup (){
  pmc_enable_periph_clk(ID_ADC);
  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  NVIC_EnableIRQ (ADC_IRQn);                 // enable ADC interrupt vector
  adc_disable_all_channel(ADC);
  adc_enable_interrupt(ADC, ADC_IER_RXBUFF);
  ADC->ADC_RPR  =  (uint32_t)  inp[0];       // DMA buffer
  ADC->ADC_RCR  =  INP_BUFF;
  ADC->ADC_RNPR =  (uint32_t)  inp[1];       // next DMA buffer
  ADC->ADC_RNCR =  INP_BUFF;
  ADC->ADC_PTCR =  1;
  adc_set_bias_current(ADC, 0x01); 
  //  adc_enable_tag(ADC);
  adc_enable_channel(ADC, ADC_CHANNEL_7);    // AN0
  adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);
  adc_start(ADC); 
}
//-------------------------------------------------------------------------------------
void ADC_Handler (void){
  if((adc_get_status(ADC) & ADC_ISR_RXBUFF) ==	ADC_ISR_RXBUFF){
    flag = ++sptr; 
    sptr &=  0x01;
    ADC->ADC_RNPR  =  (uint32_t)  inp[sptr];
    ADC->ADC_RNCR  =  INP_BUFF;
  }
}
//-------------------------------------------------------------------------------------
void demo(){
  myGLCD.setColor(255,0,0); myGLCD.fillRect(0,0,480,320);delay(200);
  for ( int i=0; i<240; i=i+2){
    int r=random(0,255);int g=random(0,255);int b=random(0,255);
    myGLCD.setColor(b,g,r); myGLCD.drawRect(i,i*0.7,480-i,320-i*0.7);
    delay(i/30);
  }
  for ( int i=240; i>0; i=i-3){
    int r=random(0,255);int g=random(0,255);int b=random(0,255);
    myGLCD.setColor(b,g,r); myGLCD.drawRect(i,i*0.7,480-i,320-i*0.7);
    delay(i/30);
  }
  delay(200);
}
//-------------------------------------------------------------------------------------

 

Written by macsbug

1月 29, 2015 @ 4:21 am

カテゴリー: Arduino

コメント / トラックバック2件

Subscribe to comments with RSS.

  1. Hello, I’m working on a similar project. However, the link of coolarduino is broken. Could you kindly give me a copy of SplitRadixRealP FFT LIbrary? Many many thanks in advance.

    Fu Jun

    6月 28, 2016 at 11:46 pm

    • Thank you for a visit to this site.
      Link certainly “coolarduino” has expired.
      “SplitRadixRealP” is located at the following site.
      pierinz/randusynth:SplitRadixRealP.zip
      https://github.com/pierinz/randusynth

      Thank you pointed out.
      I was able to be a modification of the linked Thanks.

      macsbug

      6月 29, 2016 at 7:48 am


コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。