FFT Example's bug

General discussion on mikroPascal for dsPIC30/33 and PIC24.
Post Reply
Author
Message
SamY Fareast
Posts: 46
Joined: 05 Aug 2007 07:15
Location: Shizuoka JAPAN

FFT Example's bug

#1 Post by SamY Fareast » 07 Mar 2009 03:17

Hi All.

I found a bug in FFT example.

Because PIC hardware clears ADCON1.DONE after A/D conversion started, (not after sampling started),
this code

Code: Select all

function ReadAdc: word;
begin
  ADCON1.1 := 1;                  // Start AD conversion
  while ADCON1.0 = 0 do           // Wait for ADC to finish
    nop;
  result := ADCBUF0;              // Get ADC value
end;
should be

Code: Select all

function ReadAdc: word;
begin
  ADCON1.0 := 0;                  // Clear Done-Flag before sample start
  ADCON1.1 := 1;                  // Start AD conversion
  while ADCON1.0 = 0 do           // Wait for ADC to finish
    nop;
  result := ADCBUF0;              // Get ADC value
end;
or

Code: Select all

function ReadAdc: word;
begin
  ADCON1.1 := 1;                  // Start AD conversion
  while ADCON1.1 = 1 do nop;   // Wait for finish Sampling
  while ADCON1.0 = 0 do           // Wait for ADC to finish
    nop;
  result := ADCBUF0;              // Get ADC value
end;
or

Code: Select all

function ReadAdc: word;
begin
  ADCON1.1 := 1;                  // Start AD conversion
  while ADCON1.0 = 1 do nop;   // Wait for ADC to start
  while ADCON1.0 = 0 do           // Wait for ADC to finish
    nop;
  result := ADCBUF0;              // Get ADC value
end;
After this modification, sampling rate become half the before. Adjustment ADCON3 may be required.

SamY Fareast
Posts: 46
Joined: 05 Aug 2007 07:15
Location: Shizuoka JAPAN

#2 Post by SamY Fareast » 09 Mar 2009 15:13

OK.
First I modified 'ReadADC'.

Code: Select all

function ReadAdc: word;
begin
  ADCON1.0 := 0;
  ADCON1.1 := 1;                  // Start AD conversion
  while ADCON1.0 = 0 do           // Wait for ADC to finish
    nop;
  result := ADCBUF0;              // Get ADC value
end;
Then I modified 'SampleInput'.

Code: Select all

procedure SampleInput;
var
  i: integer;
begin
  i :=0;
  while i < 255 do
  begin]
    { Debug code. when remove this, add another 'NOP' for timing.  }
    asm
      BTG LATB, 6;
    end;
    {end debug}
    Samples[i] := ReadAdc;   // Re
    inc(i);
    Samples[i] := 0;
    inc(i);                  // Im
    nop;
    nop;
  end;
  // "Samples" now contains 128 pairs of <Re, Im> samples
end;
And modified 'WriteData' to use yo2lio's code.http://www.mikroe.com/forum/viewtopic.php?t=16666 and for some speedup and visual effects.
Thank you yo2lio.

Code: Select all

procedure WriteData;
var
  Re, Im: word; volatile;
  Re2, Im2: dword; volatile;
  tmpw, j, k, l, max, fq: word;
  tmp2: dword;
begin
  j      := 0;                      // If you wan to skip DC component then make j >= 1
  k      := 0;
  max    := 0;
  fq := 0;                        // Reset current max. frequency for new reading
  while k <= 63 do
    begin
      Re := Samples[j];             // Real part of DFT sample
      inc(j);
      Im := Samples[j];             // Imaginary part of DFT sample
      inc(j);
//      if Re and $8000 = $8000 then
//        Re := -Re;
//      Re2 := Re * Re;
//      if Im and $8000 = $8000 then
//        Im := -Im;
//      Im2 := Im * Im;
      asm
        MOV [W14], W0
        MUL.SS W0, W0, W0
        MOV W0, [W14+4]
        MOV W1, [W14+6]
        MOV [W14+2], W0
        MUL.SS W0, W0, W0
        MOV W0, [W14+8]
        MOV W1, [W14+10]
      end;
      tmp2 := (Re2 + Im2);
      Re := sqrt_int(tmp2);
      Re := Re shr 8;   // Gain setting. Needs for adjust.

      if (k = 0) or (k = 63) then
        Re := Re shr 1;

      if (Re > max) and (k <> 0) then              // Find max amplitude
        begin
          max := Re;
          fq := k;                // This should be the center frequency of the signal
        end;
      if Re > 55 then
        Re := 55;

      tmpw := Written[k];
      if tmpw > Re then
      begin
        for l := 64 - tmpw to 63 - Re do
          Glcd_Dot(k shl 1, l, 0);
        Written[k] := Re;       // Mark that the current sample has been drawn
      end
      else if tmpw < Re then
      begin
        for l := 64 - Re to 63 - tmpw  do
          Glcd_Dot(k shl 1, l, 1);
        Written[k] := Re;       // Mark that the current sample has been drawn
      end;

      inc(k);                      // Move current X coordinate
     end;
   // Write the frequency of max. sample
   fq := fq * 100;
   if fq <> freq then
   begin
     freq := fq;
     WordToStr(freq, txt);
     Glcd_Write_Text(txt, 70, 0, 1);
   end;
end;
Finally added small modification to 'MainInit'.

Code: Select all

  Vector_Set(Written, 64, 0);  // Fill "Written" with 0
  freq := $FFFF;
After these modification my EasydsPIC4(with dsPIC30F4013 20x8MHz) works as a nice FFT machine.

Post Reply

Return to “mikroPascal for dsPIC30/33 and PIC24 General”