Microcontroller FFT - Page 1
Forum section: electronics / beginners

author: selkathguy
author_rank: Unknown
timestamp: September 03, 2013, 01:21:45 pm
content: I'm working on a project that involves performing an FFT on a line audio signal directly in a microcontroller. I have noticed that a lot of music players and such with 3.5mm stereo out have no DC offset from ground (assuming to prevent pop on connect/disconnect), and each channel generally has a 1-2 volt(p-p) swing under no load (earbuds/speakers not connected).The ADC on the microcontroller I'm assuming will only measure to the rails. Considering I'm doing single-supply operation from a +3.3v rail, I'm pretty confident that it would be impossible to sample the waveform directly as it dips below circuit ground and would cause massive clipping issues.The question is: would simply adding a DC offset to half-rail voltage (say +1.6v) alleviate this problem if the volume (input amplitude) stays within +-1.5v? I'm thinking I can map values the ADC reads out back to fixed-point 0-centered values for the FFT calculation (where 0 represents +1.6v on the ADC sense line).

author: Zero999
author_rank: Super Contributor
timestamp: September 03, 2013, 01:29:00 pm
content: Yes it should work, bias the input to half of VDD and couple with the appropriate capacitor to give a cut-off of below 20Hz, i.e 22uF with a potential divider made with 1k resistors would give a cut-off of 14Hz.

author: selkathguy
author_rank: Unknown
timestamp: September 03, 2013, 03:20:26 pm
content: Quote from: Hero999 on September 03, 2013, 01:29:00 pmSo I've made some changes as you suggested. Might something like this work? (it's got a passthrough so the user can still plug in their own headphones and listen, but I'm wondering if that cap is going to basically blank whichever ear's channel is selected)**EDIT** I think any noise from the rail supply will not be an issue considering the reference voltage is the rail itself. However, it may generate noise on the audio channel that the user might hear, but most speakers don't have a frequency response that high.... Methinks....

author: fcb
author_rank: Super Contributor
timestamp: September 03, 2013, 08:53:01 pm
content: A couple of suggestions.1. If your <AUDIOSENSE> is the input to the ADC, it will cope with a much higher impedance.2. The 2x1K will present a 500R load to your audio source - that's really low for consumer line outs (but fine if it's always a headphone output). I would increase the resistors by a factor of 10 at least and use a smaller capacitor to set your high-pass cut-off.3. Your at risk of coupling noise into your design from the 2x1K potential divider, might not be a problem if your supply is quiet and your ADC resolution is fairly course (10bit?). As you've labeled it AVCC i'm assuming you've factored this in already.4. I'm guessing that K1 is a relay or similar. I wouldn't bother and just feed use two inputs into your microcontroller. If you only have one ADC input, then I would use a 4066B or similar and switch the signal (you could use a spare switch to create an inverter) - much cheaper.

author: Zero999
author_rank: Super Contributor
timestamp: September 03, 2013, 11:04:53 pm
content: Quote from: fcb on September 03, 2013, 08:53:01 pmIf the source is capable of driving earphones it shouldn't be a problem. In fact you can go down even lower than that. A typical earphone is 32R so two 100R resistors shouldn't cause any problems.I suggested 1k because some MCU ADCs have fairly low impedance inputs and a high impedance source could cause unpredictable results.

author: Psi
author_rank: Super Contributor
timestamp: September 03, 2013, 11:21:29 pm
content: You will hear a click/pop as the relay changes state due to the loading of the signal disappearing for 200ms and the bypass capacitor needing to be charged/discharged to match the other channel.fcb's idea of using two ADC inputs, instead of a relay, would solve this issue.

author: ve7xen
author_rank: Super Contributor
timestamp: September 04, 2013, 05:10:12 am
content: I would buffer the signal as well. The input impedance of the ADC, at least on AVR, is not negligible and is frequency dependent (it's basically capacitive). Probably not a problem if your input impedance is 10k, but I just wouldn't mess around, throw a $0.25 opamp in there.

author: tszaboo
author_rank: Super Contributor
timestamp: September 04, 2013, 05:31:43 am
content: Make sure, that the MCU has enough power for the calculations. Back in the day we used a TI TLV320xx codec and a Spartan 3 to make the same thing. You will need to make a really long FFT calculation, and logarithm the data, bunch of multiplying. It is not an easy task for a small micro. A cortex M4F might be able to cut it. Also, i believe that 8-10-12 bit ADC built into microcontrollers is just not detailed enough for audio applications. You will only see a big bar, where the main frequency plays, nothing else. So in general, I dont think that the line input is your main problem here.

author: ve7xen
author_rank: Super Contributor
timestamp: September 04, 2013, 06:13:26 am
content: elm-chan did it with a mega8:http://elm-chan.org/works/akilcd/report_e.html

author: fcb
author_rank: Super Contributor
timestamp: September 04, 2013, 08:46:11 am
content: Quote from: NANDBlog on September 04, 2013, 05:31:43 amI'm not sure we know what the OP has in mind, he might be looking for a lower level buried signal at a specific frequency (in which case the FFT would need an appropriately narrow bandwidth), or might be making a simple graphic EQ-type spectrum (in which case you might only need 64 FFT bins - which you can do easily on a dsPIC or half-decent microcontroller).Personally I'd encourage the OP to make a quick prototype - it should be very easy to test their proposed relay solution and the others we proposed - as NANDblog said the input MUXing won't be the biggest challenge here.Please report progress back selkathguy - add to the sum of human (EE) knowledge!

author: selkathguy
author_rank: Unknown
timestamp: September 04, 2013, 02:21:44 pm
content: Incoming epic reply post.Quote from: fcb on September 03, 2013, 08:53:01 pmIt is indeed. The Datasheet mentions a maximum of 1uA analog input leakage on the microcontroller ADC.Quote from: fcb on September 03, 2013, 08:53:01 pmDuly noted. I was curious about how much a capacitor to ground would load it down hence my fear of "blanking" or quieting a channel, so this helps. I'll definitely step up the resistances. This should also cut down the quiescent current draw for the whole device, as I'll no longer have just 2k from rail to ground. Thank you very much for this!Quote from: fcb on September 03, 2013, 08:53:01 pmFully acknowledged. The AVCC rail is also directly tied to the analog reference pin on the MCU, so it may be presumptuous but I'm going to assume that the noise on AVCC (and thus the bias) from the supply will also be present on the reference pin, mitigating itself. I don't think the MCU has any real significant capaitance or buffering on the reference input, so itshouldn'tbe a problem(?)Quote from: fcb on September 03, 2013, 08:53:01 pmYep it's an optical relay (solid state). I was previously using it because I had another device in the schematic that just complicated everything. It was an active filter. Since I was only going to be sampling one channel at a time, the relay was there to switch the active sample line. I think I can remove this device from the project (phew, saved 5 bucks there). I do actually have another ADC input on a pin adjacent to the one I'm using already, so I can just have a pin per channel.Quote from: Psi on September 03, 2013, 11:21:29 pmI am going to use two ADC inputs now thanks to fcb, and the pop from switching wouldn't be an issue for me really anyway; it's not going to be switching channels frequently, and i can delay sampling in software. That relay switched in 3-5ms according to the datasheet. (Solid state optical)Quote from: ve7xen on September 04, 2013, 05:10:12 amHmmm, I am using an AVR so this concerns me. According to my endless studies of the datasheet it doesn't seem like this would be necessary but I will take your word for it if you are saying that it's significant under 20khz. This would pretty much only be used for headphone-driving outputs; the ability to work with line-out would be a luxury. Actually that could be kind of neat to be able to detect whether it's line out or headphone out, and if it's line out have an op-amp pair drive the signals for the output so a user could connect headphones and listen to line-out, but I digress and that would involve an entire reorganization and additional switching. Would what you are mentioning still be an issue on headphone/speaker outs?Quote from: NANDBlog on September 04, 2013, 05:31:43 amI chose the MCU specifically for it's dedicated DSP instruction set. It can perform these maths in a fraction of the time, so I think there's real hope. Especially since I'll likely be running the core on a PLL at 60Mhz or so. I'll figure out how big of a heatsink I have to slap on it later, but it's well under its maximum core clock. The real issue I foresee is running out of SRAM to store the samples... Here's the DSP-ey stuff if you're interested in giving it a once-over:http://www.atmel.com/Images/doc32120.pdfIt sounds like you've done this kind of thing a few times before so please let me know if that's not going to cut it. Note: it's a dual MCU design, so the MCU in question is ONLY the frontend 'DSP'. After it calculates the FFT in a side buffer while sampling at the same time, it switches over to the other buffer for calculation while throwing the "processed" FFT buffer out across SPI to the main controller (where visual-y stuff happens).Quote from: ve7xen on September 04, 2013, 06:13:26 amI'm a young ham, so I absolutelylovethe radio version of his project. However I'm looking to create a relatively high-resolution rapidly updating display, on about 256*64 OLED display with as low as one bin per pixel. Bin resolution is kind of important to me in this project. And I want to be able to update the entire display a bare minimum of 20 times per second, so I'll likely have to set it up for direct display buffer writing and just one bit per pixel. Yes this is an expensive toybut it's also my first real electronics project (personal, not for school or anything) that I'm using to test my own knowledge in application.Quote from: fcb on September 04, 2013, 08:46:11 amI would need to be able to pick out things like faint high frequency content sitting on loud low-frequency bass tones. This is where I hope the 10-bit resolution and 20-40ksps will help. The sample memory and FFT (real and non-real components) will likely take almost all of the 32Kbytes of SRAM in the sampling/FFT MCU. The FFT actually will generate a bunch of crap bins that I won't really need since each bin covers a static frequency range (at least as I understand from the DSPlib documentation). For example, 20-26hz is definitely a bin I need. But not every bin up high like 8,020-8,026hz is necessary, so they'll get pruned [insert really annoying math]. To answer the first comment, I am trying to do a simple graphic EQ-type spectrum display, but at much higher resolution than I've seen before. The idea is to be able to "zoom in" to a particular frequency range and have that expanded to the whole display. To this end, the main MCU will issue control commands to the DSP MCU (the one we've discussed thus far) specifying some acquisition parameters like sample depth (this will directly affect rate at which the main MCU will get updates to display to the user) and sample rate, etc. The display is going to be monochrome OLED, because displaying spectrum of both channels at the same time would not be feasible anyway with this method.Let me know what you guys think!EDIT: This is actually the closest thing I've seen:http://members.jcom.home.ne.jp/felm/gvspec_b.aviI would like some more functionality, but that's about the update rate I'm looking for.

author: fcb
author_rank: Super Contributor
timestamp: September 04, 2013, 04:58:58 pm
content: Great reply selkathguy.I reckon you'll have had major pain with a SSR, they are a bit shit for audio levels.Detecting line-out vs. headphones? I guess you could switch on a load and monitor the drop in signal and gauge the drive impedance - but I don't think it would be reliable.As far as buffering is concerned, it would be a great way to minimize anything feeding back onto the audio lines - your call.I'd probably try and keep the unit as a single micro-solution - what you gain on the swings you might well loose on the roundabout, and unless you have complex graphics you should be able to find sometime to process the GUI. If you've got an audio interface on your AVR (I only speak PIC) then I would drop in a cheap audio ADC, it won't cost you more to process 16 bits against 12 bits.

author: selkathguy
author_rank: Unknown
timestamp: September 04, 2013, 07:51:01 pm
content: Quote from: fcb on September 04, 2013, 04:58:58 pmI think I will now that you mention it. It isn't too much trouble with the extra board space we've cleared up and it would certainly nip problems in the bud if I want to sample from line-out. It also will allow (with like a quad op amp or something) me to take the line-out levels from the input and drive headphones that someone wants to plug into the output. Kind of added ability to directly listen and analyze line-out level signals at the same time.Quote from: fcb on September 04, 2013, 04:58:58 pmThe AT32UC3B(1256 46TQFN package) datasheet has been the book I've been pouring over for a long time, and I've more or less committed to it, but I'm concerned that I'm getting tunnel-vision. My reason for picking this particular MCU is that it's pretty small, and the fewest pins that I could find for the amount of SRAM it has. It also had the 10-bit ADC built in, and the DMA required to shuffle the stuff around (although that is becoming a standard feature on almost every micro these it seems). I'll seriously look at 12-bit external ADCs again.My #1 concern is memory and aside from that, processing power (the latter being a problem I hope to solve with the DSP instruction set running at 60Mhz). There are lots of things that need to be happening at the same time in order to achieve the update rate and 256x64 resolution that I'm after. There's also a lot of memory-intensive stuff happening, which I would like to keep on the HSB in the MCU. It kinda sucks that this micro doesnt support addressing external memoryonly flash. As for having to go with two micros, I would actually like to keep the option open of doing more visual/post-processing things like maybe have cursors, indicators for zoom, sample depth or whatever other wanky crap I feel like adding at the time.All of this concern for performance stems from some calculations I did about a year ago when I was even more naive than now. Just a short notes list I made for myself, am I going about this correctly at all?Code:Sampling and memory:Sample window must be at least 100ms long in time. (to contain two full cycles @ 20hz)Sample rate must be at least 40ksps (for 20khz bw) or 20ksps (10khz bw)The sample memory must contain 4,000 samples (40ksps, window 100ms long [from above]: 40k*.1=4k)The sample memory must be (SAMPLERATE*WINDOW*REALITIES*BYTESRESOLUTION*NUMBUFFERS)bytes in size:where SAMPLERATE = 40,000 (ksps)where WINDOW = .1 (for 100ms)where REALITIES = 2 (for real and unreal components of the FFT)where BYTESRESOLUTION = 2 (to contain 10-bit ADC results, not compressed)where NUMBUFFERS = 2 (to provide working set and backbuffer memories for result transfer)Gives 32,000 bytes memory requirement.NOTE: only 16,000 bytes required for 20ksps (10khz bw). In theory, this could fit onboardthe AT32UC3B1256, if the USB Driver and controller are not configured in memory. It is anextremely tight fit with just under 16k SRAM available while the USB device driver configurationloaded into memory. The AT32UC3B1256 communications interface could therefore be switchedto SPI, but the maximum clock f the peripheral bus remains to be determined and will dictateplausability of SPI data transfer between the frontend MCU and the main MCU.FFT Requirements:The display must update at a minimum rate of 30 frames per second, displaying full sweep of new fourier transform data.Assuming 30 full transforms/sec, the uC must process SAMPLERATE*WINDOW*FFTRATE samples/sec via DSP instructions.This equates to 120,000 samples processed per second:While only 4,000 samples are in the window, it is rolling, and must be recalculated at least 30 times per second, giving us the 120,000.Questions:How long does it take to perform a single, full FFT at 12Mhz?at 60Mhz?Can 30 FFTs (120,000 samples) be processed via DSP in under 1 second?How long does it take to transfer the entire FFT result from the memory backbuffer on the DSP mcu to the controller/display mcu?Can the FFT result be serialized on an output in a shorter amount of time than the translation can be performed? (critical)The questions at the end there are what pushed me to adopt a twin-micro design. Perhaps I'm underestimating the power of these micros, but like anything, I would rather have it and not need it than need it and not have it. One samples and calculates the FFT and organizes the data, the other does the serial communications with a PC via an external USB controller on USART, does post-processing, and controls the display. Do you think it's justified?Something I just realized: the DSP instructions are going to want a contiguous array of special structs, meaning that in order to have a rolling window, I'll have to do two memory copies into another buffer: one from current sample(+1) to end, and another from beginning to current sample, throwing them into that buffer in that order. That way they'll always be arranged oldest->newest in the destination buffer. That means no room for a backbuffer hmm... I might have to think of a smarter way to store and shuffle this stuff.

author: tszaboo
author_rank: Super Contributor
timestamp: September 04, 2013, 08:47:28 pm
content: The AT32 is a good choice for this. I was hoping you don't try to sniff this into a PIC16F84. Atmel is really pushing the AT32 to the A/V applications. I back fcb with the external ADC. It is not that complicated, Wolfson makes SO8 pin ADCs for 2 bucks. And AT32 has I2S interface AFAIK.Only your application can tell if your calculations are right. I think it is worth playing around in MATLAB Simulink, and see what are the results for changing the window, samplerate or resolution or anything else. It is an excellent program for signal processing, I highly recommend trying it. Personally, I think your window is too big, but that is just me.BTW. we sent the data to a monitor through some simple 6 bit VGA interface. It was still way too much fun.

author: selkathguy
author_rank: Unknown
timestamp: September 04, 2013, 09:02:15 pm
content: Quote from: NANDBlog on September 04, 2013, 08:47:28 pmI'm going to check out MATLAB, but I've never touched MATLAB ever before so that's a bit spooky. Im terrible with maths, and don't even fully understand the FFT process, hence me just using the instruction set and the Atmel documentation. As for VGA, I've been wanting to add that functionality to my project ever since I started. If you are telling me that I can do it on the controller micro somehow, please give me some pointers or something cause that sounds awesome. Just autodetect VGA and switch over to that output or something.

author: fcb
author_rank: Super Contributor
timestamp: September 04, 2013, 10:08:46 pm
content: Watch out for feature-creep (VGA / phones drivers etc...)I2S is straightforward and then you can chuckaway as much resolution as you want - 10 bits seems a bit low. Most ADC's claim 24bit, so you get a usable 20bits, i'd be surprised if you needed more than 16bits.Your right about the SRAM issues, but that's why you should build a prototype and stop fannying about. Start with a coarse FFT and work up till it falls over. You'll also find I suspect that 20Hz update rate is unnecessarily fast.

author: tszaboo
author_rank: Super Contributor
timestamp: September 04, 2013, 11:43:44 pm
content: Well, I did it on an FPGA. It is doable on a Microcontroller, but that is a whole different, very hard story. If I were you, I would start with the calculation, and add the functionality after one part is done. I mean your project is complicated enough without it.

author: selkathguy
author_rank: Unknown
timestamp: September 06, 2013, 01:28:04 am
content: Quote from: fcb on September 04, 2013, 10:08:46 pmI have done some testing on the EVK1101 evaluation board for this chip and it looked really promising. I think the DSP instruction set is making a huge difference here because I believe it does transforms in a single shot. It still takes quite a few cycles, but from what I was able to test in performance on the evaluation kit for the AT32UC3B series, it was a heck of a lot faster than running it in my own code, I think by an order of magnitude or more.Unfortunately they don't make a DIP package with the DSP set built in, so I can't just slap it on a breadboard. I need an adapter or socket that changes TQFP-48 to DIP-48 or something so I can really whack at this particular micro...http://www.ebay.com/itm/5x-LQFP48-TQFP48-Universal-Prototyping-SMT-SMD-DIP-6-Proto-Test-Module-5pcs-/380662545894?pt=LH_DefaultDomain_2&hash=item58a1417de6Something interesting: NAND said something about the window being maybe too large. If I can reduce the size of the window and still get the low tones relatively accurately, that would solve a lot of problems. Memory AND processing required. Hell maybe even get this down to a single micro solution.