Jump to content

Recommended Posts

I was inspired a while back by the simplicity of the FFT application written by Shane Ormond and featured on the 43oh blog.  It was easy to duplicate and I've made a few changes, additions, and such that seemed worth documenting.

 

I didn't have a signal generator other than the 1kHz square wave on my oscilloscope and some clunky code that I wrote for a microcontroller so I ordered an inexpensive AD9850 and hooked it up to a FR6989 LP so I could use the LCD to display frequency.  I've been pleased with the AD9850 and it is hard to beat it for the price.  The sine wave is more than sufficient for my needs up to 40 MHz - I don't see any deviation from the scope.  The code is here.  This is a picture of the setup being tested on the oscilloscope and nailing it:

post-45284-0-23016200-1453338687_thumb.jpg

I need to make a little boosterpack for this so it is a little handier to use.

 

I made several modifications to Shane's code:

  • Number of samples can be specified
  • Bin readings are matched with corresponding frequency interval
  • Frequency resolution of bins can be set
  • Frequencies of up to 5 kHz or more can be measured

I used a MSP-EXP432 for the most part but the code was also tested and works on the TM4C123.  You really need an ARM to get this granularity.  The code is here.

 

To increase the sample size and allow measurement up to higher frequencies I used Energia's delayMicroseconds instead of millis.  The right way to do this would be with timers and I hope to come back and address this at some point.  To calibrate the bins to their actual frequencies I used a simple one step approach with a single pass that measures the deviation in the sampling time from expected to actual.  Deviations occur due to the lag associated with Energia code and the actual time it takes to sample.

 

Precision depends on the bin size and number of samples as well as inaccuracies in using delayMicroseconds.   I posted the serial output into a spreadsheet to get some plots...

post-45284-0-42866700-1453339643_thumb.jpg

1000 Hz Square Wave

post-45284-0-44224900-1453339705_thumb.jpg

1000 Hz Sine Wave

post-45284-0-43450700-1453339735_thumb.jpg

5000 Hz Sine Wave

 

My original goal was to create something that could process sound in the range of human hearing and this pretty much gets there.  I need to clear my desk for another project but hopefully I get back to it some day or perhaps someone else will find it interesting and report back ;)

 

This is my list of potential improvements:

  • use timer for sampling times
  • add a microphone
  • improve the graphical display / GUI

It would be neat to get this working on an Educational BoosterPack.

Share this post


Link to post
Share on other sites

@@Fmilburn.....  A very interesting writeup, thanks.   What a coincidence;   I am working with the AD9850 DDS module this week !

 

Although your writeup is on FFTs done on ARM MSPs in the KHz range, here are some data and thoughts on the AD9850 DDS I captured which you, or others, may find useful.   Note that the DDS frequency range here is 7.0 MHz.   For instance, DDS spurs are not big at KHz, but being aware of them (as I'm sure you are!) is handy --  see 1 C    below.

 

 

This info may be useful to folks embedding DDS into radio / signal processing devices and is part of an investigation I'm doing around :

 

 -- DDS - AD9850 module and breadboarded cousins: AD5930 (sweep); AD5933 (impedance); AD9834c (DDS); AD9835 (DDS).

 -- 'clock generator' Si5351  -  a rather interesting, tiny device with 3 high frequency outputs and an I2C interface.

 -- analog PLL - own designs.

 

 

1- Spectral performance of AD9850 sine wave.

 

Attached is a screenshot of the close-in spectrum output of the AD9850 as measured on a high-end-hobbyist SDR (software defined radio).   Although the sinusoid AD9850 on a scope can look nice, a spectrum analyser can show us a bit more.       Some notes....

 

a)  The SDR is tuned to 7,000,000 Hertz with resolution bandwidth at 0.5 Hz, so two spectral lines are 1.0 Hertz. (yes, RBW = 0.5 Hz !)

     The DDS output has been trimmed (see 2a, below) and is seen to be about 1 Hz lower.

     The SDR frequency readout had been previously trimmed against a time-and-frequency standard long wave transmitter.

 

B        The fundamental DDS output at 7.0 MHz is at -60 dBm.  Vertical divisoins are 10dBm.

 

c)  Spurs (spurious) frequencies are seen at +- 100 Hz consistently around 7.0 MHz.  These are -90 dBm, only 30dB down from the fundamental - rather strong, but can be typical for such a DDS setup.  The other two spurs come and go at other DDS fundamentals.  

 

 (DDS spurs are complex, BUT - Analog Devices has a nice spur calculator with graphics of the step-waveform and the resulting spectrum)

 

 

2- MSP430/432 code size reduction.    Integer arithmetic.

 

Using integer arithmetic to generate the AD9850 frequency word. I also started with floating point.  For the 2553, the flash saved is important.

    This line of code worked on both the 2553 and 432 in CCS:    (not tested for overflows across all input to this function call).

 

 

void
sendFrequency(unsigned long frequency) {

int b;

P4OUT &= ~DATA; // use DATA line to time the following calculation
P4OUT |= DATA;
unsigned long freq_32val = ((frequency * 4294967296) / 125001340) ; // nominal 125000000 xtal
P4OUT &= ~DATA;

Some things to note:

 

a) - the DDS clock divisor is 125,001,340 MHz instead of the nominal 125MHz.  This was determined empirically and is important for accuracy.

     This offset from nominal will be measured periodically with respect to temperature and stored in a 2553 Flash Info Segment.  SW trim.

 

B   - code size : On a 2553 in CCS6.1, this calculation, setups and a few blinking LEDs was around 2.2 K bytes

 
c) - execution time : the same setup as in   B   , with the 2553 set to the calibrated 1 MHz clock.
                   The DATA line was raised during the routine's execution ...   Time =  about 13 mSec, I recall.

 

 

3- Environment

 

- coded for MSP430G2553 LP (Energia17 and CCS6.1)      and MSP432 LP (CCS6.1)

- SDR receiver and spectrum analysis on 'Perseus SDR'

- signal coupling from the AD9850 to the SDR was through the air - about 50 cm distance.

    The SDR was connected to a short outdoor antenna, so the (quiet) background level of about -110dBm is local airwaves.

 

 

 

None of this is particularly new, but I hope someone will find this writeup of use.

 

This is intended for learning, possibly make a small, battery powered signal generator for my workbench.  As such, next steps are:

- test other devices (esp Si5351)         - LCD freq display       - UART interface       - rotary encoder freq knob        - attenuator           - etc

       ..............    all driven by the MSP430/2

 

Cheers,

post-28129-0-80066200-1453379913_thumb.jpg

Share this post


Link to post
Share on other sites

Here is some C code I have used to make the sine table for the fix_fft function if I wanted to change the size of the FFT

#include <stdio.h>
#include <math.h>
#define POINTS 128
#define BITS   16




int main(void)
{
  int n;
  int a=0;
  int TF_FLAG=POINTS-POINTS/4;
printf("#define N_WAVE      %d    /* full length of Sinewave[] */\n",POINTS);
printf("#define LOG2_N_WAVE %d      /* log2(N_WAVE) */\n",(long)log2(POINTS));
printf("/* 3/4 of N_WAVE = %d  */\n",TF_FLAG);
printf("const int8_t Sinewave[N_WAVE-N_WAVE/4] = {\n");
  for (n=0; n<POINTS; n++){
        if(n==TF_FLAG){
          printf("/*");
        }




  if(a<15){
    printf("%d,", (int)floor(0.5 + ((1 << (BITS-1)) - 0.50001) * sin(2 * 3.1415926535897932 / POINTS * n)));
        a++;}
       else{
        a=0;
        printf("%d,\n", (int)floor(0.5 + ((1 << (BITS-1)) - 0.50001) * sin(2 * 3.1415926535897932 / POINTS * n)));
        }
}
printf("*/");
printf("};\n");




  return 0;
}

You can copy and paste the output into your fix_fft.c file ;-)

Share this post


Link to post
Share on other sites

Hi!

 

Im working in a guitar tuner with a TM4C129 and i would use tour code to get the frequency, do you know if this code works in a tm4c129?

 

Thanks

Share this post


Link to post
Share on other sites

i'm very sorry cos im a noob with microcontroler and energia environment, but i have copied the 3 files (FFt.ino, fix_fft.ccp and fix_fft.h) in the same folder or sketchbook (named FFT) and i have the next compiling errors:

 

 

FFT.ino: In function 'void setup()':
FFT.ino:25:41: error: 'analogReadResolution' was not declared in this scope
 
I tried to solved defining the resolution with this line:
 
#define analogReadResolution 14
 
the 14 value it takes in fix_fft.h in #define ANALOG_RESOLUTION 14
 
but the i get this errors
 
fix_fft\fix_fft.cpp.o: In function `fix_fft(int*, int*, int, int)':
fix_fft.cpp:(.text._Z7fix_fftPiS_ii+0x0): multiple definition of `fix_fft(int*, int*, int, int)'
fix_fft.cpp.o:fix_fft.cpp:(.text._Z7fix_fftPiS_ii+0x0): first defined here
fix_fft\fix_fft.cpp.o: In function `fix_fftr(int*, int, int)':
fix_fft.cpp:(.text._Z8fix_fftrPiii+0x0): multiple definition of `fix_fftr(int*, int, int)'
fix_fft.cpp.o:fix_fft.cpp:(.text._Z8fix_fftrPiii+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
 
 
i am bit lost, how can i solve it?

Share this post


Link to post
Share on other sites

I tried specifying the TM4C129 in Energia v17 and Windows 10 and I did not get the error.  Since I can't reproduce the problem it is hard for me to know what the solution is.  Note that analogReadResolution() is a function in Energia - what version of Energia are you using?

 

Also, note the comment that I placed in fix_fft.h after ANALOG_RESOLUTION

#define ANALOG_RESOLUTION 14     //CPU specific

The analog resolution is CPU specific and the MSP432 has 14 bit analog resolution, the TM4C129 has 12 bit if it is like the TM4C123.  You can find this information in the datasheet.  So change that line to something like this:

#define ANALOG_RESOLUTION 12     //CPU specific - set to 12 for TM4C123/129 and 14 for MSP432

Share this post


Link to post
Share on other sites

@@Ymir

 

Look inside your Energia folder for wiring_analog.c.  It will be located at \hardware\lm4f\cores\wiring_analog.c.  Do you see the following function inside it?

void analogReadResolution(int res) {
    _readResolution = res;
}

I think this is what the TM4C129 should be calling.  If not, maybe someone else can tell us.

Share this post


Link to post
Share on other sites

I am running out of suggestions and this is a bit out of my league but try this...

  • Select File -> Preferences from the pulldown menu in Energia and select "Include debug information in the output ELF file"
  • Look in the output window of Energia for wiring_analog.c - it may help to copy everything in the window and then paste it into a text editor and do a search
  • You should find the location for wiring_analog.c (hopefully the same place you checked above) and the location where it put the object file
  • Then select Sketch -> Show Compilation Folder where wiring_analog.c.d and wiring_analog.c.o should be present

Can you see the files?  Do you see any new errors that might be pertinent?

Share this post


Link to post
Share on other sites

The compiler folder is in a temp folder and those files are there. But now appears this errors:

 

fix_fft\fix_fft.cpp.o: In function `fix_fftr(int*, int, int)':
C:\Users\alvaro\Desktop\FFT\Energia\Afinador\libraries\fix_fft/fix_fft.cpp:169: multiple definition of `fix_fft(int*, int*, int, int)'
fix_fft.cpp.o:C:\Users\alvaro\AppData\Local\Temp\build5236453045155780907.tmp/fix_fft.cpp:166: first defined here
fix_fft\fix_fft.cpp.o: In function `fix_fftr(int*, int, int)':
C:\Users\alvaro\Desktop\FFT\Energia\Afinador\libraries\fix_fft/fix_fft.cpp:285: multiple definition of `fix_fftr(int*, int, int)'
fix_fft.cpp.o:C:\Users\alvaro\AppData\Local\Temp\build5236453045155780907.tmp/fix_fft.cpp:282: first defined here
collect2.exe: error: ld returned 1 exit status
 
This error appears with and without "Include debug information in the output ELF file" option
 
fix_fft\fix_fft.cpp.o: In function `fix_fftr(int*, int, int)'

Share this post


Link to post
Share on other sites

Ok, now works.....this is stupid, the program creates a libraries folder in the schektbook location and copied fix_fft.ccp and fix_fft.h there. I have delete it and now compiles.

 

For detecting frequencies up to 400Hz with this values 

LOG2N = 8
FREQ_RESOLUTION = 5 
 
should work?

Share this post


Link to post
Share on other sites

I tried the FFT code with LOG2N = 8 and FREQ_RESOLUTION = 5 and it works, i also tried with LOG2N = 10 FREQ_RESOLUTION = 1, and works too. I have to remark that it takes more than 1 sec to get the data, but works.

 

Im using a 555 circuit to test and a multimeter to measure the frequency (i dont have a counter and my oscilloscope is veeery old so this is the stuff with most accuracy i have and it is calibrated).

 

i'll try to decrease the sampling time.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...