Wednesday, September 5, 2012

The writeup

Hi, everybody!

I like playing with microcontrollers.  When I heard that TI was going to release a hobbyist centered Stellaris evaluation kit, the ek-lm4f120XL, I got excited.  When I convinced a coworker to let me have a rev0 board to work with, I got even more excited.  I decided that I would combine my love of microcontroller projects with my love of all things audio and make an LED frequency analyzer.

Many thanks to my wife, Heather Wills, for taking a decent photo of the board for me!


The total cost of my setup is about $25 (the majority of which is from the LED display).  The Stellaris ADC requires that its input signal be a low impedance signal that is between ground and 3.3V.  This is not consistent with line level audio, so I implemented a very simple circuit on a breadboard to condition the input signal using an NTE987 opamp that I grabbed from Fry's, two 47k resistors, and a 1 uF capacitor.  The output of this circuit goes to one of the ADC input pins on the launchpad.  I found an MSP430 booster pack that is essentially an SPI controlled 8x8 LED array that I decided to use for the display.

Bill of Materials

ek-l4f120XL: $5
olimex MOD-LED8x8: $15
Opamp: $2
Resistors/Capacitors: Negligible (had them lying around in my wiring kit)

Total Cost: $22


The signal condition circuit has to perform two functions.  First, it must bias the signal to center at ~1.65V and be limited to a 1.65V swing.  Line level audio is centered at ground and has at most around a 1.5V swing, so a simple resistor network can be used to bias this up to 1.65V.  Second, the output must be a signal with low enough impedance to power the switched capacitor array that the ADC input uses for its signal capture.  A low grade opamp can be wired up at unity gain to accomplish this.  The final result looks something like this:

For the input, I also realized that I'd need a headphone jack with exposed leads, which I picked up at Radio Shack for something like $1.50.  Were I a more patient engineer, I'm sure I could find something cheaper on digikey or mouser.  I slapped all of that together on my breadboard, which gave me the following:

The last piece of hardware work was connecting the Olimex booster pack to the Launchpad.  Unfortunately, I had to blue wire this, as the SPI RX pin on the booster pack was not aligned with the SPI TX pin on the launchpad.  Not a big deal, though, as the interface to the board is only five pins (SPI_CLK, SPI_TX, SPI_LATCH, VCC, GND).


The software running on the Stellaris was responsible for the following functions:
  • ADC Sampling at a specific frequency
  • Digital Signal Processing on the captured audio data
  • SPI communication with the LED array
I'm a software developer by day, so I did all that I could to make those steps run as efficiently as possible.  The source code I'm using is up on github at

Audio Capture

For the ADC interaction, I ended up using three separate peripherals to give me an incredibly software efficient audio capture process.  First, I set up a timer to count down at 44.6 kHz.  I set up the timer to trigger an ADC capture at the hardware level.  The ADC capture complete signal was set up to initiate a uDMA transfer, which was configured to move the data from the ADC capture FIFO to a global array of samples.  The result is very, very software efficient; One line of code starts the sampling timer, and SAMPLE_SIZE/44600 seconds later a software interrupt occurs, letting you know that the data has been captured and is ready in the sample array.  I was very proud of the efficiency of this process :-)

Digital Signal Processing

I am fortunate in that I've been playing around with audio hardware for years in the form of running soundboards and messing around with studio mixing and recording.  My last DSP class was a long, long time ago, but my interest in audio gave me a decent foundation in audio based DSP.  I am also fortunate in that ARM has a DSP library specifically designed to take advantage of the M4F's floating point instructions.  TI even has an application note detailing how to build that DSP library in Code Composer Studio.  From what I've heard, the amazingly handsome, charismatic, genius of an engineer who wrote the note did an incredible job on it, and it is wonderfully thorough and well written.  It might also be a little out of date (as ARM released a new version of CMSIS since the app note was published), but it's still good.

With those tools at my disposal, the DSP portion of my code wasn't too difficult to figure out once I wrapped my head around CMSIS.  Step one is to multiply the samples by a windowing function.  I chose a hamming window, because I vaguely remember hearing that that was good for audio.  Next, I take the FFT of the input data.  I'm pretty proud of this part as well; the M4F ended up being powerful enough to take a 2048 point fft on the input data, which gives you 1024 frequency bins, each of which represents the energy found in a 20.3 Hz wide frequency band.  So once I have the fft output, I take the complex magnitude of each sample, giving me the total power found in each frequency bin.

LED Display

To figure out how to translate 1024 frequency bins into 8 LEDs, I had to do some trial and error to figure out what looked the best.  I ended up splitting the 8 LEDs into logarithmicly spaced frequency ranges.  Each time I execute my signal processing loop, I calculate the average power found in the bins for a given frequency range (again using CMSIS for efficiency).  I divide this value by the maximum value I've seen in that frequency range.  Then, I just say if the result is greater than 1/8, turn on one LED; Greater than 2/8, turn on 2, etc.  If the result is bigger than the max, then turn on all 8 LEDs, and make that the new maximum. I also multiplied the current maximum by 0.999 to have a decay factor, which made it so my output wouldn't be decimated by a single spike in the audio.

To actually turn on the LEDs, I used a second timer for an easily configured refresh rate.  Each time the timer hits 0, I use the hardware SPI module to update the next column of the 8x8 LED display based on the value found by the above method.  The results are, if I say so myself, pretty impressive.

Future Work

I have a few ideas of where to go from here.  For one, the Olimex LED tile has an expansion header that can be used to have multiple tiles connected to the same SPI bus.  My code is theoretically written such that I can change a single macro to use a different number of frequency display elements (so I can go from 8 to 16 frequency LED columns with a simple recompile).  It'd be pretty fun to try expanding my LED tile to 16 columns wide.

A coworker of mine built what he calls his LED cube a few months ago.  Basically, it's an 8x8x8 cube of LEDs that functions very similarly to the olimex booster pack.  I have some cool ideas for how to do 3 dimensional frequency displays :)

I also found a booster pack that contains a 160x128 LCD display.  I'm pretty sure I can talk to the LCD just using Stellaris' graphics library without much work at all, and I think it'd be really visually impressive to expand my display to be contain 160 frequency bins.

Finally, it's been a long time since I've done a board layout from scratch, so I've been toying with the idea of making a custom PCB to get rid of the protoboard and blue wires that I currently have to use to make everything work.

Showing Off

For those interested in seeing how this works, I have a rather short video showing off the display!  For those who don't care much about the setup, you can fast forward to about the 2:05 mark to see the demo with music :)

I will likely add one more in the future that just shows off the finished working product with the audio overlayed directly on top of the video (as opposed to captured as it comes out of my headphones).


  1. Is it possible to program the board with Linux/gcc?

  2. Great demo of the new TI board. Cant wait to get mine in October (international shipping damn it!). Any chance you would be willing to post your source so I could have a look through it and get a better feel for the board?

    Have you made any progress on the booster pack that contains a 160x128 LCD display yet? That sounds a really great solution!



  3. The source code will be up soon! I'm working on getting it into github... I originally wrote it using Code Composer Studio, but my goal is to have the code look the same as proper StellarisWare example code. Basically, that means that there will be a separate directory in the project root folder for CCS, IAR, Keil, CodeSourcery, and a makefile for GCC, along with the necessary startup file for each one of those.

    I'm hoping to get that up and running by this weekend. I'll make sure to put a comment here and update the writeup once it's all in place.

  4. Sweet! Nice to get an early look at this new board, and esp. code for all the different tool chains...



  5. EuphonistiHack, awesome project!
    It would be great to have you at our Stellaris forums to share and discuss your project. We all are eagerly waiting for it.

  6. Nice writeup. Nextup, hooking the TLV320AIC3104 that I am currently playing around at work to do digital pass-through for Mic input and drive 8ohm speakers.

    Some other Handsome, Genius wrote this appnote to accomplish that with 2 SPI ports, and a couple of GPIOS and a I2C port. Maybe it was You:-)

  7. I wish I could claim credit for it, but that app note was written by a coworker of mine, Michael Risley. I haven't had a chance to give it a serious read, but that is a pretty cool future project idea... Take in audio on the launchpad, use CMSIS to implement a 5 band equalizer (with gain and frequency control for those bands dynamically set through either pushbuttons or a UART based command line interface), then audio playback of the manipulated signal via I2S.

  8. Really interested to see your DSP implementation. Are you able to put that source code up? Cheers!

  9. Source code's up! See my latest post for details, but the sauce is served at


  10. Great project EH. I'd like to do embedded beat detection for a dancing robot. I am thinking that your ADC sampling and FFT routines could feed into a beat detection algorithm perhaps along the lines of this other TI project Do you have any suggestions based on your audio/software experience? The algorithms I have looked at seem to use fairly simple indicators of deviation from the average frequency profile across the bins. I am guessing that beat detection is actually somewhat subjective but that bass frequencies are more important than treble.

  11. Hey Tom,
    Unfortunately, I don't have any experience implementing beat detection algorithms. I poked around a bit at the project you referenced (which is pretty dern cool :) and it looks like they released the source code for the algorithm they have running topside that is implementing the beat detection:

    I haven't looked too deeply into what they are doing, but it looks like it'd be feasible to implement that on the lm4f120. The only tricky part would be keeping an eye on your memory usage. It looks like the fft structure they're using is about 159 bytes (assuming a 32 bit compiler, sizeof(double) = 8 bytes) and 186 elements long, which totals to 29k worth of allocated storage for that one array. Given that the lm4f120 has about 32k of SRAM, you'd need to do some memory optimization to get things working.

    If I get some free time, I'll try playing around with beat detection... it seems like one of those problems that sounds simple to solve in the generic case, but becomes increasingly complex as you dig into finding a perfect solution.

  12. This is very cool. I've been thinking about whether it is possible to do something like this with a MSP430 — and figured out that it just *might* be possible with the units that have a hardware multiplier. Probably not with the G2 low-cost series.

    Then I heard about the neat Stellaris Launchpad and decided to revive my project, which was to build a spectrum analyzer using IN-13 graph nixie tubes. And lo and behold, I stumble upon your post, where you did most of the hard work (FFT + binning) :-)

    I'll be looking forward to the code release, as that's something I'll be able to use pretty much directly.

  13. Hey Jan, the code is already up on github. You can find the link on my latest post, and I edited the above writeup to include a link there.

    Best of luck, and have fun!

    1. Cool, thanks for writing this up and posting your code! I'm ordering my Stellaris Launchpad board now. I still have to design the DAC/Nixie driver part, but this is a great starting point for the analyzer part of the project.

  14. This comment has been removed by the author.

  15. So when I try and build, I've ironed out most of the problems, but I still get this:

    gmake: *** No rule to make target `C:/Users/shawn/Desktop/launchpad-freq-analyzer-master/StellarisWare/utils/uartstdio.c', needed by `utils/uartstdio.obj'.
    gmake: Target `all' not remade because of errors.

    No other errors that I can see in the console. What am I doing wrong here?

    1. Once I reinstalled and was sure to use the default paths for the installs of StellarisWare and CCS5, everything compiled correctly.

  16. the math_arm.h file seems to not be found when I try to compile, could you help?

  17. Hello EuphonistiHack.
    Thank you for your source code and your amazing work :D.
    I've made another version of this project(much simpler cause i'm new to this mcu) using the nokia lcd. The demo clip of this project is here:
    And the source code is here:

  18. Hi EuphonistiHack:

    thank you for all the project, thats graet.

    Im ussing a piccolo for c2000 and I have the booster pack, does the stellaris source code works with the piccolo???

  19. Hi, I can't get the link you put to the application note to work, could you possibly provide another link.

  20. Hi, I'm a newbie working on a audio dac project.
    Thank you for your work, those code help me a lot.
    But I have a problem with pinout. Do i need additional connection beside gnd pin and analog input pin for the audio signal input?
    The manual suggest that Vref should be included so that ADC can work. But i can't find Vref pin in my launchpad.
    Would you mind share a little bit about pinout setting?