Fmilburn 445 Posted January 21, 2016 Share Posted January 21, 2016 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: 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... 1000 Hz Square Wave 1000 Hz Sine Wave 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. bluehash, gsutton, zeke and 2 others 5 Quote Link to post Share on other sites
veryalive 49 Posted January 21, 2016 Share Posted January 21, 2016 @@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, bluehash, gsutton and Fmilburn 3 Quote Link to post Share on other sites
NickTompkins 4 Posted March 18, 2016 Share Posted March 18, 2016 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 ;-) Fmilburn, tripwire, gsutton and 1 other 4 Quote Link to post Share on other sites
Ymir 3 Posted April 17, 2016 Share Posted April 17, 2016 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 Quote Link to post Share on other sites
Fmilburn 445 Posted April 17, 2016 Author Share Posted April 17, 2016 Hi @@Ymir I expect that it does since it worked on a TM4C123 but I don't have a TM4C129. Quote Link to post Share on other sites
Ymir 3 Posted April 17, 2016 Share Posted April 17, 2016 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? Quote Link to post Share on other sites
Fmilburn 445 Posted April 17, 2016 Author Share Posted April 17, 2016 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 Quote Link to post Share on other sites
Ymir 3 Posted April 17, 2016 Share Posted April 17, 2016 Im working on Energia v17 and windows 8, and after actualize that line, it is giving the same errors. I'll work on it Quote Link to post Share on other sites
Fmilburn 445 Posted April 17, 2016 Author Share Posted April 17, 2016 @@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. gsutton 1 Quote Link to post Share on other sites
Ymir 3 Posted April 18, 2016 Share Posted April 18, 2016 Yes, the library and the function are there Quote Link to post Share on other sites
Fmilburn 445 Posted April 18, 2016 Author Share Posted April 18, 2016 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? gsutton 1 Quote Link to post Share on other sites
Ymir 3 Posted April 19, 2016 Share Posted April 19, 2016 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)' Quote Link to post Share on other sites
Ymir 3 Posted April 19, 2016 Share Posted April 19, 2016 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? Quote Link to post Share on other sites
Fmilburn 445 Posted April 19, 2016 Author Share Posted April 19, 2016 Those values should allow measurement up to 635 Hz according to the comments I left in fix_fft.h. Have you tried it yet? Quote Link to post Share on other sites
Ymir 3 Posted April 20, 2016 Share Posted April 20, 2016 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. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.