tag:blogger.com,1999:blog-5712998695777024394Thu, 04 Dec 2014 04:33:06 +0000EuphonistiHackhttp://euphonistihack.blogspot.com/noreply@blogger.com (EuphonistiHack)Blogger13125tag:blogger.com,1999:blog-5712998695777024394.post-7410974022551989510Thu, 04 Dec 2014 04:32:00 +00002014-12-03T20:33:06.962-08:00Moved!I got tired of using a handle that was 14 characters, and I wanted an excuse to get a personal domain :)<br /><br />All content will stay up here until blogspot gets tired of me and stops hosting it, but I've copied most of it and am posting all new projects on my personal domain, <a href="http://www.njneer.com/">www.njneer.com</a><br /><br />Thanks for stopping by!http://euphonistihack.blogspot.com/2014/12/moved.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-1810248533307663807Wed, 21 Aug 2013 16:49:00 +00002013-08-21T09:49:01.188-07:00Marble Maze: The makeningA quick, hopefully simple guide on how to make a servo controlled marble maze! &nbsp;Pictures for each step will hopefully be coming soon, once I get a chance to take some photos.<br /><br /><h4>Materials Needed:</h4><div><br /></div>1x&nbsp;<a href="http://www.amazon.com/gp/product/B000BNQFQ4/ref=oh_details_o01_s00_i00?ie=UTF8&amp;psc=1">Wooden Marble Labyrinth</a><br />2x&nbsp;<a href="http://www.amazon.com/gp/product/B0006O3XEA/ref=oh_details_o01_s00_i01?ie=UTF8&amp;psc=1">HiTec 322HD servos</a><br />1x&nbsp;<a href="http://www.digikey.com/product-detail/en/712A/SC1048-ND/109407">2.5x5.5mm barrel jack</a><br />1x&nbsp;<a href="http://www.digikey.com/product-detail/en/M20-9981046/952-2130-ND/3728094">2x10 0.100" through hole male pin header</a><br />4x #8-32 x 1-1/2 inch machine screws<br />6x ~2.5 inch lengths of 0.100" diameter wire<br />6x pieces of electrically non-conducive heatshrink<br />Solder<br /><br /><h4>Tools Needed:</h4><div><br /></div><div>Needle-nose pliers</div>Drill press<br />Power drill<br />Drill bits of various sizes<br />Phillips head screwdriver<br />Soldering iron<br />Heat gun (in a pinch, a hair dryer and a funnel)<br /><br /><h4>Method:</h4><div><br /></div><div>The method for modifying the marble maze can be broken down into a series of relatively simple steps. &nbsp;The control knobs will be removed from each axis, rotated around so that the flat side of the knob is facing away from the maze, the small wheel arm will be attached to the flat side of the control knob, and the servo will be attached to the box. &nbsp;For the wiring, a 2x3 header is used. &nbsp;On one side, the 3 pin connector from the servos will be attached. &nbsp;On the other, wires will be soldered to connect power and ground to the DC power barrel jack, and power, ground, and pwm control signals to the Launchpad. &nbsp;Step by step instructions for this process are as follows:</div><div><ol><li><b><u>Remove the control knobs from each rod:</u></b>&nbsp; Start with the knob on the x-axis. &nbsp;Rotate the y-axis such that the edge of the maze nearest to the knob on the x-axis is tilted down as far as it can go. &nbsp;Use the needle-nose pliers to stabilize the shaft and pull on the x-axis control knob. &nbsp;The control knob is only secured to the shaft by friction, so it should pull off pretty easily. &nbsp;The y-axis control knob is a bit harder to remove, as you can't tilt the board in such a fashion as to make an opening wide enough to get access to the shaft with the pliers. &nbsp;If your pliers are small enough, you can use hole 34 or 36 to get access to the shaft. &nbsp;Again, stabilize the shaft with the pliers and pull off the control knob</li><li><u style="font-weight: bold;">Extend the mounting hole on the control knob:</u>&nbsp; Unfortunately, I didn't record what size drill bit was necessary for this step, but it is easy to determine. &nbsp;Insert a drill bit that looks to be about the right size into the hole that rod used to occupy. &nbsp;When you get to one that fits, use that drill bit on the drill press to extend the hole all the way through the control knob.</li><li><b><u>Secure the small wheel arm to the control knob:</u></b>&nbsp; Use a 1/16" drill bit and the drill press to expand two of the holes that already exist on the small wheel arm. &nbsp;Place the small wheel arm on the flat side of the control knob and use a pencil to mark the location of the expanded holes. &nbsp;Use a drill press and the 1/16" drill bit to drill all the way through the control knob. &nbsp;Use two of the servo mounting screws that came with the 322HD to secure the small wheel arm to the control knob. &nbsp;Be careful with this step, as it is very easy to strip the heads of the servo mounting screws.</li><li><u style="font-weight: bold;">Reattach the control knob to the marble maze:</u>&nbsp; Again starting with the x-axis, use the pliers to stabilize the rod and push the control knob onto the rod with the flat side (which now has the small wheel arm attached) facing outward. &nbsp;Note that there is a flange on the rod that keeps the rod from extending too far outside of the marble maze housing. &nbsp;Use the pliers to make sure this flange is snug against the wall of the maze. &nbsp;Repeat this process for the y-axis, again using hole 34 or 36 to get access to the rod on the y-axis.</li><li><u style="font-weight: bold;">Secure the servo to the marble maze:</u>&nbsp; The small karbonite gear on the HD322 should easily click into the small wheel arm that is now connected to the control knob. &nbsp;Rotate the servo such that the wiring coming out of it is pointed towards the ball return of the marble maze. &nbsp;Attach the 1/16" drill bit to the power drill. &nbsp;Make sure the servo is level, and use one of the four mounting brackets on the servo as a guide to drill a hole into the side of the marble maze, making sure to drill all the way through the wall. &nbsp;This will create a hole that is slightly smaller in width than the #8-32 machine screws. &nbsp;Line the servo up with the hole, then use a hand screwdriver to screw one of the #8-32 machine screws into the hole. &nbsp;It is important to use a 1-1/2" screw here, as you need to get the head of the screw flush with the mounting hole of the servo, but if the screw extends past the wall on the interior of the marble maze, it will limit the range of motion on that axis. &nbsp;Now that the servo is held in place with one screw, use mounting bracket as a guide again and drill a second hole into the box, then a second #8-32 machine screw to affix the servo to the side of the box. &nbsp;Repeat this process for the second servo.</li><li><u style="font-weight: bold;">Wire the servo to the Launchpad and barrel jack:</u>&nbsp; Snip off a 2x3 section of the 0.100" pitch male pin header block. &nbsp;Solder a wire to electrically short the two columns of the first row. &nbsp;This will be used to create a four point node for connecting ground from the DC barrel jack to the launchpad and servos. &nbsp;Solder a wire to electrically short the two columns of the second row. &nbsp;This will be used to create a four point node for connecting 5V from the DC barrel jack to the launchpad and servos. &nbsp;Solder a wire to connect ground from the barrel jack to the column one, row one of the header. &nbsp;Solder a wire with a 0.100" pitch to column two, row one of the header. &nbsp;Solder a wire to connect the tip of the barrel jack (5V) to column one, row two of the header. &nbsp;Solder a wire with a 0.100" pitch to column two, row two of the header. &nbsp;Solder a wire with a 0.100" pitch to column one, row three of the header. &nbsp;Solder a wire with a 0.100" pitch to column two, row three of the header. &nbsp;If possible, use an electrically non-conductive heat shrink on each solder joint to ensure that you don't accidentally short 5V to ground, the two PWM signals to each other, or the PWM signals to 5V. &nbsp;Connect the wire at column two, row one of the header to either pin 1 of J2 or pin 2 of J3 on the bottom (female portion) of the launchpad XL header (GND). &nbsp;Conenct the wire at column two, row two of the header to pin 1 of J3 on the bottom (female portion) of the launchpad XL header (VBUS, which is 5V). &nbsp;Connect the wire at column one, row three of the header to pin 3 of J3 on the bottom of the launchpad (PD0, pwm for y-axis). &nbsp;Connect the wire at column two, row three of the header to pin 4 of J3 on the bottom of the launchpad (PD1, pwm for x-axis). &nbsp;Connect the three pin connector from the servo on the y-axis to column one of the header such that the black wire is on row one, the red wire is on row two, and the yellow wire is on row three. &nbsp;Connect the three pin connector from the servo on the x-axis to column two of the header such&nbsp;that the black wire is on row one, the red wire is on row two, and the yellow wire is on row three.</li><li><u style="font-weight: bold;">Calibrate the servo:</u>&nbsp; Unscrew the machine screws and detach the servo from both control knobs. &nbsp;Plug in the launchpad (and program it if you have not already done so). &nbsp;On startup, the launchpad will set the servo to its 90 degree position. &nbsp;Manually move the control knob for each axis so that the marble maze is level. &nbsp;With the launchpad still on (which will hold the servo at 90 degrees), reattach the karbonite gear of the servo to the control knob, taking care to keep the maze level while doing so. &nbsp;Reattach the servo to the maze using the machine screws. &nbsp;The 90 degree position of the servos should now correlate to a position in which the marble maze is level on each axis.</li></ol><div>You now have a fully functional, launchpad and servo controlled marble maze!</div><div><br /></div></div>http://euphonistihack.blogspot.com/2013/08/marble-maze-makening.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-8446827280631181964Tue, 06 Aug 2013 15:54:00 +00002014-01-22T12:09:19.832-08:00Amazing Sensors!A new project!<br /><br />Lately, I've been working with different sensors as part of my day job at TI. &nbsp;TI happens to have recently released a booster pack for the Stellaris and Tiva launchpads that contains (among other things) an accelerometer, a gyroscope, and a magnetometer. &nbsp;I figured I'd use my recent experience to do something fun with sensors in my free time :)<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-whbpgwT-Uys/Uf75Pi7D6GI/AAAAAAAAAMU/biROTON2ucU/s1600/zoomed+out.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-whbpgwT-Uys/Uf75Pi7D6GI/AAAAAAAAAMU/biROTON2ucU/s320/zoomed+out.jpg" height="320" width="240" /></a></div><br />Using only the highest quality cell phone cameras and engineering studio production values, I made a quick video describing the system and showing it in action<br /><div class="separator" style="clear: both; text-align: center;"><object width="320" height="266" class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="https://ytimg.googleusercontent.com/vi/ZDEgF--3Q-U/0.jpg"><param name="movie" value="https://youtube.googleapis.com/v/ZDEgF--3Q-U&source=uds" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="https://youtube.googleapis.com/v/ZDEgF--3Q-U&source=uds" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h4>Project Overview</h4><br /><ul><li>Accelerometer, gyroscope, and magnetometer fused via direct cosine matrix to observe roll, pitch, and yaw of hand held transmitter</li><li>RemoTI software stack used to pair and transmit data over Zigbee RF4CE protocol between transmitter and receiver</li><li>Software generated PWM signal used to control servos attached to X and Y axis of marble maze based on roll and pitch data received</li><li>Source code and bill of materials can be found on my&nbsp;<a href="https://github.com/EuphonistiHack/marble-maze">github</a><div class="separator" style="clear: both; text-align: center;"><a href="https://github.com/EuphonistiHack/launchpad-freq-analyzer"></a><a href="http://1.bp.blogspot.com/-0kWU3Wn3G0E/Uf75Q7QZKRI/AAAAAAAAAMc/1Nn5fw1xNsE/s1600/closeup.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-0kWU3Wn3G0E/Uf75Q7QZKRI/AAAAAAAAAMc/1Nn5fw1xNsE/s320/closeup.jpg" height="240" width="320" /></a></div></li></ul><h4>Hardware</h4><div>The key piece of hardware that made this project possible was the&nbsp;<a href="http://www.ti.com/tool/boostxl-senshub">SensorHub Booster Pack</a>. &nbsp;The booster pack comes with an MPU 9150, which provides 3-axis data from a gyroscope, an accelerometer, and a magnetometer. &nbsp;The communication between the MCU and these sensors is handled with an interrupt driven I2C interface.</div><div><br /></div><div>An additional bonus of using the SensorHub booster pack is that it was designed to contain a set of EM headers. &nbsp;For this project, I connected a&nbsp;<a href="http://www.ti.com/tool/cc2533emk">CC2533</a>&nbsp;radio transceiver to those headers, which enables short range (~5 meters) radio communication between two modules. &nbsp;It was a bit tricky to set up the CC2533, as there was not a plethora of sample code for using it in conjunction with a Stellaris, but fortunately I was able to find a spare&nbsp;<a href="http://www.ti.com/tool/cc2533dk">development kit</a>&nbsp;that made learning and debugging the radio interface much easier.</div><div><br /></div><div>The final hardware component was the marble maze itself. &nbsp;I actually was able to find this already pre-fabricated. &nbsp;A TI field engineer did a project very similar to this, but using the accelerometer on an msp430 Chronos watch and an lm3s9b96 for the maze control. &nbsp;Unfortunately, that demo was starting to show its age: it used the SimpliciTI stack for wireless communication, which is no longer supported, the 9b96 was from back when the Stellaris line was controlled by Luminary Micro, meaning it had no Texas Instruments branding, and the 9b96 used for the PWM control is part of the now, sadly, NRND'd m3 line. &nbsp;As a result of these factors, the demo was languishing in a marketing office, and hadn't been used for a trade show in years :(</div><div><br /></div><h4>Software</h4><div>Once I procured the hardware, I set about to looking for example code. &nbsp;I came across a project a coworker of mine created, the&nbsp;<a href="http://processors.wiki.ti.com/index.php/Wireless_Air_Mouse_Guide">wireless air mouse</a>&nbsp;demo, that was a great help.</div><div><br /></div><div>The sensor communication was the easiest part to cover (due in no small part to the fact that I've spent the past year and change working with these exact same sensors for a different project within TI). &nbsp;TI has a really great sensor library that is available in the current Tivaware C series release that handles a large part of the sensor functionality. &nbsp;The air mouse demo already handled taking the sensor data and feeding it into a&nbsp;<a href="http://www.starlino.com/dcm_tutorial.html">direct cosine matrix</a>, which generates the eigenvectors needed to determine the roll, pitch, and yaw of the device at a given time. &nbsp;Once I had this data being reliably generated, I moved on to the radio transmission portion of the code.</div><div><br /></div><div>The radio portion of the project was by far the most difficult. &nbsp;I opted to use the RemoTI RF4CE software stack, because a) that's what the air mouse demo used, and b) it sounded like an interesting topic to learn more about. &nbsp;The RF4CE protocol basically classifies each node on the RF network as being either a target or a controller, and sets up a very robust algorithm for pairing and sending data between two devices. &nbsp;Unfortunately, the airmouse demo only covered the controller side of the code; for that demo a&nbsp;<a href="http://www.ti.com/tool/cc2531emk">CC2531 USB dongle</a>&nbsp;was used to handle the target side of the communication, the source code for which I was unable to find. &nbsp;It took a few weeks of reading up on the RemoTI documentation and learning about how the on board MCU on the CC2533 works, but I was eventually able to get a hello world program running, which quickly gave way to transferring roll, pitch, and yaw data from the hand held controller down to the maze controlling target.</div><div><br /></div><div>The servo communication was fairly straightforward: each is controller by a pulse width modulated signal with a base of 20 ms and an active time of between 1 and 2 ms to rotate it to either extreme. &nbsp;I was hoping to use a Tiva Launchpad for this purpose, as it contains a hardware PWM module, but I was not able to find one lying around the lab, so I opted to just use a general purpose timer to create a software based PWM signal for the servos. &nbsp;Once I had roll and pitch data coming over the radio interface, all I had to do was to normalize the rotation between -1 and 1, use that to generate an active time for the PWM, and generate the signal. &nbsp;I ended up adding a multiplier to each axis to get the demo to make a bit more sense, as the launchad is longer along the Y axis than it is the X axis, so it takes a greater effort to change the pitch on the board than it does the roll. &nbsp;As a result, without a different multiplier on each axis, the controller gave the impression that it required more effort to get the board to move in the Y axis than it did to get the same motion on the X axis.</div><div><br /></div><div>As always, the source code for this project can be found on my&nbsp;<a href="https://github.com/EuphonistiHack/marble-maze">github</a>.</div><div><br /></div><h4>Future Steps</h4><div>I have a few ideas for where to go from here, but nothing concrete on the horizon. &nbsp;My first thought was that it would be really easy to modify this code to instead control a POV camera gimble. &nbsp;Something like using the target side MCU to control a gimble that has a go-pro attached to sync the camera movement to the movement of a launchpad attached to a hat. &nbsp;Then maybe finding a way to stream a low res version of that video to an RC car or quadcopter to allow for POV control of the remote vehicle without requiring line of sight. &nbsp;Still thinking about what hardware would be ideal for this, maybe giving me an excuse to buy a beaglebone black. &nbsp;For now, though, I'm quite happy with my little marble maze demo :)</div>http://euphonistihack.blogspot.com/2013/08/amazing-sensors.htmlnoreply@blogger.com (EuphonistiHack)1tag:blogger.com,1999:blog-5712998695777024394.post-444651572240338492Sat, 15 Dec 2012 09:49:00 +00002014-01-22T12:12:39.507-08:00The Writeup 2: Electric Boogaloo<br />Hello everyone! &nbsp;I have some new, very exciting updates to my project!<br /><br />For those who aren't familiar with my previous works, my last big project was a frequency analyzer I made using a Stellaris Launchpad and an 8x8 LED panel MSP430 Launchpad booster pack that I repurposed for Stellaris.<br /><br />Well, I've made a few upgrades since then :)<br /><div class="separator" style="clear: both; text-align: center;"><object width="320" height="266" class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="https://ytimg.googleusercontent.com/vi/C_wptfBkrO4/0.jpg"><param name="movie" value="https://youtube.googleapis.com/v/C_wptfBkrO4&source=uds" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="https://youtube.googleapis.com/v/C_wptfBkrO4&source=uds" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div><br /><br /><div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/2bypQwtkDMg/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/2bypQwtkDMg?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/2bypQwtkDMg?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/1ZK5W2Hotf0/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/X5lFENq9VBQ?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/X5lFENq9VBQ?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/VIKYTA0wko8/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/VIKYTA0wko8?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/VIKYTA0wko8?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/1ZK5W2Hotf0/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/1ZK5W2Hotf0?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/1ZK5W2Hotf0?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div><br /><h4>Project Overview</h4><div><ul><li>Timer triggered ADC for signal capture at user specified frequency (4 KHz - 80 KHz, defaults to 26 KHz)</li><li>Smart uDMA function determines, based on sampling frequency, whether to do signal processing on a fresh array of new data or rotate in a smaller amount of new data for each DSP loop</li><li>2048 point FFT for frequency bin calculation</li><li>ARM CMSIS DSP library used for Hamming Window application and FFT calculation</li><li>Touchscreen used for variable minimum display frequency, maximum display frequency, sampling frequency, number of display elements on screen (aka number of bars), debug information, and visual effects</li><li>Dynamic calculations made based on current sampling frequency and max/min display frequencies to generate a logarithmic scale for frequency axis.</li><li>Timer controlled 18 FPS display rate (adjustable to up to 30 FPS at compile time)</li></ul></div><br /><h4>Hardware</h4><div><h2>Audio Capture</h2></div><div>The first big update to my project is on the signal capture side. &nbsp;I've always been much more of a software guy, but I figured this would be an excellent opportunity to brush the rust off of my board layout skillset.<br /><br />In order to get the Launchpad to be able to read line level audio, it is necessary to add a DC bias to the audio signal. &nbsp;Line level audio is centered at ground and has a ~1.6 V swing, which is a problem because the ADC peripherals on the Launchpad can only take input signals between 3.3 V and ground. &nbsp;To fix this, I use a simple circuit (a capacitor and two resistors) to get the signal centered at 1.65 V with a 1.6 V swing.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-FhUsdrNu0lg/UMlVbCRLAVI/AAAAAAAAAJo/5L6IFgIIyps/s1600/schematic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-FhUsdrNu0lg/UMlVbCRLAVI/AAAAAAAAAJo/5L6IFgIIyps/s400/schematic.png" height="226" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Instead of using a breadboard for this, though, I decided to draw up the schematic using Eagle's schematic capture tool, then do a simple board layout (again using Eagle).</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-6GH82stE9To/UMlVaYPi1KI/AAAAAAAAAJg/M10sYaaCngo/s1600/layout.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-6GH82stE9To/UMlVaYPi1KI/AAAAAAAAAJg/M10sYaaCngo/s400/layout.png" height="400" width="311" /></a></div><br />Once the board layout was done, I uploaded the schematic to&nbsp;<a href="https://batchpcb.com/pcbs/98953">Batch PCB</a>. &nbsp;I was originally planning on ordering a few boards from there, but one of my coworkers found out about what I was doing and offered to pay for my board to be fabricated by&nbsp;<a href="http://www.4pcb.com/">Advanced Circuits</a>&nbsp;instead. &nbsp;As much as I like supporting batch PCB, I'm not one to turn down free fabrication :)<br /><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-2BWEaqS_mjw/UMwrrNzUs9I/AAAAAAAAAJ4/KzUGnZZLhGE/s1600/_DSC9912.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-2BWEaqS_mjw/UMwrrNzUs9I/AAAAAAAAAJ4/KzUGnZZLhGE/s400/_DSC9912.jpg" height="266" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-0lt_C0YyPbA/UMwryGQ5L-I/AAAAAAAAAKI/R3q2SllvtDs/s1600/_DSC9914.jpg" imageanchor="1" style="clear: right; display: inline !important; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://3.bp.blogspot.com/-0lt_C0YyPbA/UMwryGQ5L-I/AAAAAAAAAKI/R3q2SllvtDs/s200/_DSC9914.jpg" height="133" width="200" /></a><a href="http://4.bp.blogspot.com/-JJ9TYaY9OcI/UMwruQiYg7I/AAAAAAAAAKA/oH2EHvd7Xp8/s1600/_DSC9910.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-JJ9TYaY9OcI/UMwruQiYg7I/AAAAAAAAAKA/oH2EHvd7Xp8/s200/_DSC9910.jpg" height="133" width="200" /></a><a href="http://3.bp.blogspot.com/-EZ7AFBifOQs/UMwr1F6ia8I/AAAAAAAAAKY/o-T_xaGUrx8/s1600/_DSC9919.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-EZ7AFBifOQs/UMwr1F6ia8I/AAAAAAAAAKY/o-T_xaGUrx8/s200/_DSC9919.jpg" height="200" width="155" /></a></div><br /><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-NLw1rgy3iqA/UMwrzxhFceI/AAAAAAAAAKQ/-jqRfJceD1o/s1600/_DSC9917.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-NLw1rgy3iqA/UMwrzxhFceI/AAAAAAAAAKQ/-jqRfJceD1o/s400/_DSC9917.jpg" height="266" width="400" /></a></div><br /><br /><br />Overall, I was very happy with how the board ended up turning out. &nbsp;For anyone interested in using/modifying/viewing my raw board design files, they can be found along with the bill of materials for my board in the hardware directory of&nbsp;<a href="https://github.com/EuphonistiHack/launchpad-freq-analyzer">my github</a>.<br /><br /></div><h2>Display</h2><div>The next big change, and arguably the most impressive addition to my project, was the display. &nbsp;Kentec just recently released a 3.5 inch, 320 x 240 16 bit color display, complete with resistive touchscreen overlay on a&nbsp;<a href="http://www.newark.com/kentec-electronics/eb-lm4f120-l35/exp-board-lcd-boosterpack-stellaris/dp/48W2063?Ntt=EB-LM4F120-L35">booster pack</a>&nbsp;for about $50. &nbsp;Needless to say, as soon as I found out about its existence, I incessantly pestered my contact on the Stellaris applications team until he let me have one to play with. &nbsp;I had to make some changes to the sample driver provided for the display. &nbsp;Specifically, the touch screen uses a timer to trigger ADC captures, which interfered with my audio capture functions. &nbsp;With a few modifications, though, I was able to integrate the Kentec booster pack into my project pretty easily.<br /><br /></div><h4>Software</h4><div>The addition of the touch screen allowed for a much, much wider scope of functionality for my software. &nbsp;I had originally designed my code such that the sampling frequency and display parameters were easy to change at compile time via a few pre-processor macros, but it took a bit of work to get those options all configurable at run time in an intuitive fashion. &nbsp;That said, the software I wrote for the LMF120 is now responsible for the following functions:</div><div><ul><li>ADC Sampling at a user defined frequency frequency</li><li>Digital Signal Processing on the captured audio data (2048 point FFT)</li><li>GPIO based communication with the Kentec display</li><li>Graphical User Interface for changing display and DSP parameters at runtime</li></ul>I'm a software developer by day, so I did all that I could to make those steps run as efficiently as possible. &nbsp;I worked primarily in Code Composer Studio, and my source code is available on&nbsp;<a href="https://github.com/EuphonistiHack/launchpad-freq-analyzer">github</a>.</div><div><br /></div><h2>Audio Capture</h2><div>For the ADC interaction, I ended up using three separate peripherals to give me an incredibly software efficient audio capture process. &nbsp;First, I set up a timer to count down at the sampling frequency (defaulted to 26 KHz). &nbsp;I set up the timer to trigger an ADC capture at the hardware level. &nbsp;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. &nbsp;The result is very, very software efficient; One line of code starts the sampling timer, and&nbsp;SAMPLE_SIZE/26000 seconds later a software interrupt occurs, letting you know that the data has been captured and is ready in the sample array. &nbsp;I was very proud of the efficiency of this process :-)<br /><br />I did encounter some difficulty when trying to run my code at low sampling frequencies. &nbsp;The flow of the above method is basically<br /><ol><li>Capture Data</li><li>Halt Capture</li><li>Process Data</li><li>Start Next Capture</li><li>Display Data</li></ol>The problem with this becomes obvious in the example of using 4 KHz for the sampling frequency. &nbsp;The 2048 point FFT requires the use of 2048 samples, and the above method means that every time we want to do a new signal processing loop, we have to get a fresh 2048 samples. &nbsp;If we're sampling at 4 KHz, that means we have at most about 2 sets of fresh samples per second to process. &nbsp;That means we can only update our display at 2 frames per second.<br />To get around this, I have two different uDMA algorithms. &nbsp;The first (fast) method is as described above. &nbsp;The second (slow) method decreases the uDMA size to 256 and uses a ping pong buffer to store the data. &nbsp;When the uDMA transfer for the ping buffer is complete, the uDMA engine switches to storing data in the poing buffer. &nbsp;While the pong buffer is filling, the data in the sample array is shifted left by 1792 samples, then the data from the ping buffer is copied into the top 256 spots of the sample array. &nbsp;The joys of flexibility :)</div><div><br /></div><h2>Digital Signal Processing</h2><div>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. &nbsp;My last DSP class was a long, long time ago, but my interest in audio gave me a decent foundation in audio based DSP. &nbsp;I am also fortunate in that ARM has a DSP library specifically designed to take advantage of the M4F's floating point instructions. &nbsp;TI even has an&nbsp;<a href="http://www.ti.com/lit/an/spma041a/spma041a.pdf">application note</a>&nbsp;detailing how to build that DSP library in Code Composer Studio. &nbsp;From what I've heard, the amazingly handsome, charismatic, genius of an&nbsp;<a href="http://www.blogger.com/profile/13092721954982050109">engineer</a>&nbsp;who wrote the note did an incredible job on it, and it is wonderfully thorough and well written. &nbsp;It might also be a&nbsp;<i>little</i>&nbsp;out of date (as ARM released a new version of CMSIS since the app note was published), but it's still quite useful.</div><div><br />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. &nbsp;Step one is to multiply the samples by a windowing function. &nbsp;I chose a hamming window, because I vaguely remember hearing that that was good for audio. &nbsp;Next, I take the FFT of the input data. &nbsp;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 12.7 Hz wide frequency band. &nbsp;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.</div><div><br /></div><h2>Touch Screen/Configuration</h2><div>Once I had the data captured and processed, I needed a way for the user to configure how it would be displayed. &nbsp;I ended up using three screens total for my project: the main "Frequency Display" screen and two Configuration screens. &nbsp;The first configuration screen is used to modify various frequency parameters and the number of display elements to use. &nbsp;This is done using StellarisWare's graphics library, using slider widgets and pushbutton widgets. &nbsp;Each parameter has a slider that can be used for setting the value, and a + and - button that can be used to fine tune the values. &nbsp;On this screen, the user can change the maximum display frequency, the minimum display frequency, the sampling frequency, and the number of display elements (aka the number of bars) used on the display. &nbsp;Once these have been entered, my code determines which bars need to correspond to which bins in order to best facilitate a logarithmic frequency display. &nbsp;This normally means the lower 1/3 of the bars each represent less than 10 bins worth of FFT data, and the upper bars contain the summation of at times hundreds of bins. &nbsp;If the user ever tries to get the screen to display at a greater granularity than possible (like having only 10 Hz of separation between lower and upper frequency while running at a 40 KHz sampling rate), the display will automatically be set to use as many bars as possible while still not displaying over the user's requested frequency range.<br /><br /></div><h2>Display</h2><div>The communication to the Kentec display is handled via a parallel GPIO interface. &nbsp;I started out using Stellaris' Graphics Library (grlib) for everything, and was quite impressed with it. &nbsp;Going into this project, I had almost no experience working with grlib. &nbsp;Despite that, it ended up only taking me three hours worth of work to go from using my 8x8 LED display to having 8 bars of generic height behaving as desired on the Kentec display. &nbsp;I like to think that I'm a pretty decent programmer, but I honestly feel that getting that far that fast is much more a sign of a well designed library than it is a sign of an intelligent coder.<br /><br />For actually sending data to the display, I was able to use a driver that Kentec provided. &nbsp;Sadly, I had to make a few modifications; the Kentec driver uses the same timer to control the same ADC sequencer that I was using for my audio capture, so I ended up changing to a different timer that performed a processor triggered ADC capture for the Kentec display. &nbsp;Also, the display's draw functions all operated by drawing horizontal lines, which is nonideal if you're trying to draw lots of thin vertical lines, so I added in a function to allow for drawing images and lines by column as opposed to by row. &nbsp;In hindsight, I think the display allows for changing from "portrait" to "landscape" orientation, so I probably could have just rotated my image and set the display to landscaped, but it's a bit late for that realization now.</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-QEoP4SBWV1Q/UMw0o0PZQKI/AAAAAAAAAKs/C4r54y-rsb8/s1600/_DSC9922.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-QEoP4SBWV1Q/UMw0o0PZQKI/AAAAAAAAAKs/C4r54y-rsb8/s320/_DSC9922.jpg" height="320" width="213" /></a><a href="http://3.bp.blogspot.com/-Gz9Lyi7z9w4/UMw0s3-OVPI/AAAAAAAAAK0/jD3ZdAqYxgw/s1600/_DSC9930.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-Gz9Lyi7z9w4/UMw0s3-OVPI/AAAAAAAAAK0/jD3ZdAqYxgw/s320/_DSC9930.jpg" height="320" width="213" /></a></div><br />With my modifications to the Kentec driver, I was able to get around 30-40 frames per second (depending on sampling frequency). &nbsp;I found that the display looked a bit better if I cut it back to 15 frames per second though. &nbsp;The instantaneous nature of the FFT meant that if I updated as fast as data was being processed, impulses in various frequency ranges would not remain on the display long enough for my eye to catch them and make sense of them. &nbsp;I use a simple timer peripheral to keep the frame rate constant over time.<br /><br /></div><h4>Credits, development time, and shaggy hair</h4><div>All photographs on this post were taken by my wonderful wife, whose work can be found on&nbsp;<a href="http://hwillsphotography.wix.com/hwills">her website</a>.<br /><br />In total, this project took about a month and a half to get from my last release to its current state. &nbsp;I could likely have finished it in less time, but I have also been working on another big project in my free time lately; raising my first daughter :)<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-AI7_DbIUHeM/UMw6mjm1OJI/AAAAAAAAALE/NVWr9dLKfNo/s1600/baby.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-AI7_DbIUHeM/UMw6mjm1OJI/AAAAAAAAALE/NVWr9dLKfNo/s400/baby.jpg" height="266" width="400" /></a></div><br />She was born in mid October, meaning she was about 3 weeks old when I started working on the code for this release. &nbsp;She tends to fall asleep pretty easily when she's in a baby bjorn-style harness, so most of my coding was done between 9 and midnight while wearing a ten pound child on my chest :) &nbsp;The unfortunate downside to this has been that between my real 8-5 job at TI (which actually has nothing to do with Launchpad), working on this project, and keeping that little lady happy, I haven't quite been able to maintain&nbsp;the sharp look I&nbsp;<a href="http://i.imgur.com/ceLO9.jpg">used to sport</a>.&nbsp; So my apologies if I look a bit shaggy in the explanation video for this one... it's been a few days since I've had a chance to have a proper shave :)<br /><br /></div>http://euphonistihack.blogspot.com/2012/12/the-writeup-2-electric-boogaloo.htmlnoreply@blogger.com (EuphonistiHack)14tag:blogger.com,1999:blog-5712998695777024394.post-8042538171952911091Sun, 09 Dec 2012 09:23:00 +00002012-12-09T01:23:43.804-08:00Feature Complete!!At 2:30 this morning, the source code for my next update officially hit feature complete. &nbsp;It will take me a few days still to clean up everything for a release, but I should be able to make a public git push, a new writeup, and a few new videos by the end of the week. &nbsp;This is a big one, folks :)http://euphonistihack.blogspot.com/2012/12/feature-complete.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-5794116307156038021Sun, 09 Sep 2012 13:55:00 +00002012-09-09T06:55:46.636-07:00The Secret SauceSource code is up!<br />https://github.com/EuphonistiHack/launchpad-freq-analyzer.git<br /><br />I'm assuming that TI will follow the standard procedure for Stellaris installs for Launchpad, which is to say that when you install StellarisWare, the file structure will be such that you have a root (defaulted to C:/StellarisWare) in which driverlib will be copied. &nbsp;That root will also contain a &nbsp;boards directory, which in turn should contain a directory for the lm4f120. &nbsp;If you do a git clone off of the above address, you'll find a similar file structure. &nbsp;You should just be able to copy the led_equalizer project into the lm4f120 board directory, and copy dsplib into the stellarisWare root.<br /><br />In it's current state, the only thing I've verified working is the Code Composer Studio build. &nbsp;I have included project files that should at least be 95% good for all the other Stellaris supported environments: a makefile for you command line GCC enthusiasts and project files for Keil uVision, IAR Embedded Workbench, and CodeSourcery's IDE. &nbsp;I'm guessing that if you tried to import and build into any of those as is, you will hit two problems. <br /><br />For one, there's a config array that the uDMA engine uses that needs to be stored on a 1024 byte boundary. &nbsp;I have some pragmas in place to handle that for CCS and IAR, but I haven't yet figured out the syntax for handling this in other compilers. &nbsp;Shouldn't be too hard to figure out, and hopefully I'll have that done soon.<br /><br />The second issue will be with the CMSIS library. &nbsp;The current release contains a precompiled .lib and the headers necessary to interface with it. &nbsp;This was compiled with the tms470 compiler, though, so it won't work with anything other than CCS. &nbsp;If you download CMSIS for yourself, it should come with precompiled binaries for use with uVision, GCC, and CodeSourcery. &nbsp;You'll want to add those to dsplib, then edit your project's library include path/list to point to the proper binary. &nbsp;I am considering including these in dsplib, but I'm not really sure what ARM's policy is on redistributing their binaries, and I'd prefer not get sued out of existence :)<br /><br />Enjoy!http://euphonistihack.blogspot.com/2012/09/the-secret-sauce.htmlnoreply@blogger.com (EuphonistiHack)5tag:blogger.com,1999:blog-5712998695777024394.post-5187459941230229450Wed, 05 Sep 2012 19:33:00 +00002012-09-09T06:40:39.732-07:00The writeupHi, everybody!<br /><div><br /></div><div>I like playing with microcontrollers. &nbsp;When I heard that TI was going to release a hobbyist centered Stellaris evaluation kit, the&nbsp;<a href="https://estore.ti.com/Stellaris-LaunchPad.aspx">ek-lm4f120XL</a>, I got excited. &nbsp;When I convinced a coworker to let me have a rev0 board to work with, I got even more excited. &nbsp;I decided that I would combine my love of microcontroller projects with my love of all things audio and make an LED frequency analyzer.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-c1fGJL2FbQ4/UEeo_TOl97I/AAAAAAAAAIM/Z-GHT8zCgJQ/s1600/Jordan+Project.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="http://3.bp.blogspot.com/-c1fGJL2FbQ4/UEeo_TOl97I/AAAAAAAAAIM/Z-GHT8zCgJQ/s320/Jordan+Project.jpg" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><span style="font-size: x-small;">Many thanks to my wife, Heather Wills, for taking a decent photo of the board for me!</span></div></div><div><br /></div><h4>Hardware</h4><div><br /></div><div>The total cost of my setup is about $25 (the majority of which is from the LED display). &nbsp;The Stellaris ADC requires that its input signal be a low impedance signal that is between ground and 3.3V. &nbsp;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. &nbsp;The output of this circuit goes to one of the ADC input pins on the launchpad. &nbsp;I found an <a href="https://www.olimex.com/dev/msp-led8x8.html">MSP430 booster pack</a>&nbsp;that is essentially an SPI controlled 8x8 LED array that I decided to use for the display.</div><div><br /></div><h2>Bill of Materials</h2><div>ek-l4f120XL: $5</div><div>olimex MOD-LED8x8: $15</div><div>Opamp: $2</div><div>Resistors/Capacitors:&nbsp;Negligible&nbsp;(had them lying around in my wiring kit)</div><div><br /></div><div>Total Cost: $22</div><div><br /></div><h2>Implementation</h2><div>The signal condition circuit has to perform two functions. &nbsp;First, it must bias the signal to center at ~1.65V and be limited to a 1.65V swing. &nbsp;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. &nbsp;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. &nbsp;A low grade opamp can be wired up at unity gain to accomplish this. &nbsp;The final result looks something like this:</div><div><br /></div><div><a href="http://4.bp.blogspot.com/-1ce_95q4JEU/UDKoJ018gQI/AAAAAAAAAH0/MIIMAu2sAXg/s1600/jordanckt.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img alt="" border="0" height="255" src="http://4.bp.blogspot.com/-1ce_95q4JEU/UDKoJ018gQI/AAAAAAAAAH0/MIIMAu2sAXg/s400/jordanckt.JPG" title="Hardware conditioning schematic" width="400" /></a></div><div><br /></div><div>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. &nbsp;Were I a more patient engineer, I'm sure I could find something cheaper on digikey or mouser. &nbsp;I slapped all of that together on my breadboard, which gave me the following:</div><div><br /></div><div><a href="http://3.bp.blogspot.com/-cw9fAkDKFM8/UDKptGd7zwI/AAAAAAAAAH8/m0ITMDEpWyY/s1600/IMG_20120820_161538.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="240" src="http://3.bp.blogspot.com/-cw9fAkDKFM8/UDKptGd7zwI/AAAAAAAAAH8/m0ITMDEpWyY/s320/IMG_20120820_161538.jpg" width="320" /></a></div><div><br /></div><div>The last piece of hardware work was connecting the Olimex booster pack to the Launchpad. &nbsp;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. &nbsp;Not a big deal, though, as the interface to the board is only five pins (SPI_CLK, SPI_TX, SPI_LATCH, VCC, GND).</div><div><br /></div><h4>Software</h4><div><br /></div><div>The software running on the Stellaris was responsible for the following functions:</div><div><ul><li>ADC Sampling at a specific frequency</li><li>Digital Signal Processing on the captured audio data</li><li>SPI communication with the LED array</li></ul>I'm a software developer by day, so I did all that I could to make those steps run as efficiently as possible. &nbsp;The source code I'm using is up on github at&nbsp;https://github.com/EuphonistiHack/launchpad-freq-analyzer.git</div><div><br /></div><h2>Audio Capture</h2><div>For the ADC interaction, I ended up using three separate peripherals to give me an incredibly software efficient audio capture process. &nbsp;First, I set up a timer to count down at 44.6 kHz. &nbsp;I set up the timer to trigger an ADC capture at the hardware level. &nbsp;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. &nbsp;The result is very, very software efficient; One line of code starts the sampling timer, and&nbsp;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. &nbsp;I was very proud of the efficiency of this process :-)</div><div><br /></div><h2>Digital Signal Processing</h2><div>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. &nbsp;My last DSP class was a long, long time ago, but my interest in audio gave me a decent foundation in audio based DSP. &nbsp;I am also fortunate in that ARM has a DSP library specifically designed to take advantage of the M4F's floating point instructions. &nbsp;TI even has an <a href="http://www.ti.com/lit/an/spma041a/spma041a.pdf">application note</a>&nbsp;detailing how to build that DSP library in Code Composer Studio. &nbsp;From what I've heard, the amazingly handsome, charismatic, genius of an <a href="http://www.blogger.com/profile/13092721954982050109">engineer</a>&nbsp;who wrote the note did an incredible job on it, and it is wonderfully thorough and well written. &nbsp;It might also be a <i>little</i>&nbsp;out of date (as ARM released a new version of CMSIS since the app note was published), but it's still good.</div><div><br />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. &nbsp;Step one is to multiply the samples by a windowing function. &nbsp;I chose a hamming window, because I vaguely remember hearing that that was good for audio. &nbsp;Next, I take the FFT of the input data. &nbsp;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. &nbsp;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.</div><div><br /></div><h2>LED Display</h2><div>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. &nbsp;I ended up splitting the 8 LEDs into logarithmicly spaced frequency ranges. &nbsp;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). &nbsp;I divide this value by the maximum value I've seen in that frequency range. &nbsp;Then, I just say if the result is greater than 1/8, turn on one LED; Greater than 2/8, turn on 2, etc. &nbsp;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.</div><div><br /></div><div>To actually turn on the LEDs, I used a second timer for an easily configured refresh rate. &nbsp;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. &nbsp;The results are, if I say so myself, pretty impressive.</div><div><br /></div><h4>Future Work</h4><div><br /></div><div>I have a few ideas of where to go from here. &nbsp;For one, the Olimex LED tile has an expansion header that can be used to have multiple tiles connected to the same SPI bus. &nbsp;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). &nbsp;It'd be pretty fun to try expanding my LED tile to 16 columns wide.</div><div><br /></div><div>A coworker of mine built what he calls his LED cube a few months ago. &nbsp;Basically, it's an 8x8x8 cube of LEDs that functions very similarly to the olimex booster pack. &nbsp;I have some cool ideas for how to do 3 dimensional frequency displays :)</div><div><br /></div><div>I also found a booster pack that contains a 160x128 LCD display. &nbsp;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.<br /><br />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.<br /><br /><h4>Showing Off</h4></div><div>For those interested in seeing how this works, I have a rather short video showing off the display! &nbsp;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 :)</div><div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/Vwq0-ocWkYk/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/Vwq0-ocWkYk?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/Vwq0-ocWkYk?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div><div><br /></div><div>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).</div>http://euphonistihack.blogspot.com/2012/08/the-writeup.htmlnoreply@blogger.com (EuphonistiHack)22tag:blogger.com,1999:blog-5712998695777024394.post-1151757060183900532Fri, 31 Aug 2012 16:27:00 +00002012-09-05T12:32:31.927-07:00Show and tell<div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Video time! &nbsp;I figured that it might be helpful to make a few videos explaining how my project works and showing it in action. &nbsp;The first one is pretty basic. &nbsp;It shows off the display handling white noise, a frequency sweep, and a few random songs. <br /><br /><br /><div style="text-align: left;"><div style="text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/Rc8rAVeYoL0/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/Rc8rAVeYoL0?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/Rc8rAVeYoL0?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div></div><div style="text-align: left;"><br /></div><br />This second video is a bit long, but I feel it's a pretty good explanation of my entire project, from signal capture all the way up to figuring out which LEDs to turn on. &nbsp;It contains a brief demonstration of the project running with real input, but it's meant more as a condensed explanation of how everything works.<br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/Vwq0-ocWkYk/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/Vwq0-ocWkYk?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://www.youtube.com/v/Vwq0-ocWkYk?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div><div style="text-align: center;"><br /></div><br /><br /><br /><br /><br /><br />http://euphonistihack.blogspot.com/2012/08/show-and-tell.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-7297644113085604681Sat, 25 Aug 2012 19:26:00 +00002012-09-05T12:32:25.186-07:00All Together Now!My last big step was to combine my signal conditioning circuit, my audio capture functions, and my DSP functions and get them to play nicely together. &nbsp;This was very, very surprisingly easy. &nbsp;I expected to spend days, possibly weeks debugging the integrated code, but surprisingly, that never happened.<br /><br />First, I grabbed an audio splitter from Fry's so I could hear the signal going into my conditioning circuit via my headphones. &nbsp;Next, I modified the main loop of the project that was running my audio capture code to include a signal processing function. &nbsp;This function would check to see if the uDMA interrupt had set the "data ready" flag, and if so, process the data using the method described in my last post, then update my debug UART to display the frequency range of the current peak.<br /><br />Once this was compiled and running, I used a java based web applet (found at&nbsp;<a href="http://web.mit.edu/jorloff/www/beats/beats.html">http://web.mit.edu/jorloff/www/beats/beats.html</a>&nbsp;) to play a sine wave at a frequency I could specify. &nbsp;Much to my amazement, my integration required very little debugging, and in about an hour's time, I was able to see my code properly responding to signals ranging from 40 Hz to 20 kHz. &nbsp;Next, I found a youtube video that contained a frequency sweep, and verified that as the pitch got higher, the observed peak increased as well. &nbsp;Success!<br /><br />The remainder of the necessary code was pretty easy. &nbsp;The shift registers on the Olimex booster pack communicated over SPI, and a coworker of mine already had code running on that board to display an arbitrary character array (each array element is a column, each bit of the char is the row). &nbsp;I copied his code into my project with some minor modifications, and got it working without much issue.<br /><br />The final hurdle was figuring out what data should go into the display matrix. &nbsp;My previous audio experience was extensive enough for me to realize that the frequency should be displayed on a logarithmic scale. &nbsp;I wrote another program in C to calculate which fft frequency bins would correspond to which LED based on the minimum and maximum display frequency, then stored these frequency breakpoints in a static array. &nbsp;I then used the statistics portion of the CMSIS DSP library to find the average power in each LED range.<br /><br />I modified my debug UART to keep track of the maximum magnitude I observed for a frequency bin when running the frequency sweep youtube video, and used that to determine what the maximum "loudness" value would be. &nbsp;I figured I'd display power on a logarithmic scale as well, so I calculated power breakpoints using the value I observed when no audio was being played as the minimum power to display, and the maximum loudness value as the maximum power to display. &nbsp;This gave me an array of power breakpoints. &nbsp;So each time I went through my signal processing loop, I compared the average power observed in each LED range to these power breakpoints, and updated the display array based on that value.<br /><br />The result was a far cry from ideal, but it was good enough to get me very excited. &nbsp;My LED array flickered, &nbsp;the middle and high bands didn't look like they were responding right, but I was able to play a random song and see the lower frequency LEDs light up whenever the kick drum or bass guitar hit. &nbsp;It wasn't good enough to show off yet, but it was certainly good enough for me to call it a finished proof of concept.http://euphonistihack.blogspot.com/2012/08/all-together-now.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-6735120407250009239Tue, 21 Aug 2012 23:48:00 +00002012-09-05T12:31:47.623-07:00Perfection never was a requirement, although some might say we desired it.My proof of concept was complete, and I was excited. &nbsp;Now, it was time to give the display some polish and make it actually look presentable.<br /><br />Step one was getting rid of the LED flicker. &nbsp;The olimex booster pack headers weren't quite compatible with our launchpad (the SPI TX pin to the olimex was sitting on an SPI RX pin on the launchpad), so originally I was having to use our software SPI driver to communicate with the booster pack's shift registers, as the software driver allowed for using generic GPIOs as opposed to SPI specific pins. &nbsp;I already had most of the launchpad&lt;---&gt;olimex connectors blue wired though, so adding three more to allow my project to use the hardware SPI peripheral was no big deal, and made the display refresh function much less software intensive. &nbsp;Next, I decided to allocate one of my unused timers to act as a display timer. &nbsp;I set it up to throw an interrupt based on a predefined refresh rate macro, and update one column of the display every time the interrupt occurred. &nbsp;The result was a much, much better looking display that had no flicker whatsoever. &nbsp;I found that I needed to be careful about setting my refresh rate too high though; my signal processing was able to keep up pretty well, but the fact that I had no smoothing implemented in my software meant that if the refresh rate was too high, any sudden peaks in the frequency spectrum (like that resulting from the attack of a kick drum) would only appear as though the upper LEDs were briefly dimly illuminated.<br /><br />The next step was cleaning up the fft results. &nbsp;The biggest hurdle here was in figuring out how to calculate the "maximum loudness" value. &nbsp;In my proof of concept, the power LED breakpoints were all calculated based on observed values and statically defined. &nbsp;This was a bad approach as it did not account for any variance in &nbsp;the volume of the incoming signal (like switching from a loud song to a soft song) and, more importantly, did not account for the fact that a tone at 200 kHz sounds much, much quieter than a tone of the same power at 12 kHz. &nbsp;Also, the logarithmic frequency display meant that my lower frequency LED was the average of only 2 frequency bins, where my upper frequency LED was the average of about 400 frequency bins. &nbsp;Overall, this caused the lower frequency LEDs to display much more power than the upper frequency LEDs.<br /><br />I played around with a few different possible solutions for this, and finally came up with something that looked good to me. &nbsp;I created an array of eight maximum power values, corresponding to the eight frequency ranges I was displaying. &nbsp;Each time through the processing loop, I divided the current power value for the frequency range by the maximum power I'd observed on that range (updating the maximum power if the current power was bigger than the maximum, of course). &nbsp;This gave me a normalized value between 0 and 1 of how "loud" that frequency range was in relation to what it had been in the past. &nbsp;At that point, I just checked to see if the normalized value was greater than 1/8, 2/8, 3/8, ... and set the number of LEDs to illuminate for that range accordingly. &nbsp;To account for frequency spikes or drastic changes in volume, I multiplied each maximum by a decay factor every time I went through my signal processing loop.<br /><br />The resulting display looked much, much better. &nbsp;The downside to this method was that I was displaying power values relative to previous values for that frequency range, as opposed to the power in one frequency range relative to the current power in all the other frequency ranges. &nbsp;I'm not convinced this is the best method to use for computing the display values, but it is the best that I was able to come up with.<br /><br />The final step to getting a good looking display was to figure out what frequency range I should display. &nbsp;I found that even with my normalized power display method, every time a cymbal crash was made on the drums or a sibilant sound was made by a vocalist, the top three to four frequency LEDs would peak. &nbsp;This was because I was displaying the entire range of human hearing (20 Hz to 22 kHz) scrunched into 8 LED ranges. &nbsp;I probably should have used some scientific method to figure out which ranges to display, but I decided instead to use the trusty "try stuff until it looks good" approach. &nbsp;I ended up finding that using 40 Hz as a minimum and about 12 kHz as a maximum gave me a really good looking LED display. &nbsp;With these values, I was able to finally visually map which LEDs were corresponding to which instruments when a song was playing in real time.<br /><br />After about two months of spending inordinate portions of my weekends and free evenings to blink LEDs, I had finally created something that I was proud of :)http://euphonistihack.blogspot.com/2012/09/perfection-never-was-requirement.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-8432412455097935629Tue, 21 Aug 2012 21:48:00 +00002012-09-05T12:29:49.009-07:00Remember remember, the... whenever your last DSP class wasAfter ensuring that I had a good, valid stream of data coming in, I figured it was time to brush off the DSP textbook that had remained undisturbed on my shelf since college and start doing some analysis on the input audio signal. &nbsp;I started by reading through the documentation and fft example that CMSIS ships with. &nbsp;The fft example showed how to calculate a 1024 point transform on a complex input signal, which was not exactly what I wanted to do, but was close. &nbsp;I added the example code to my project and verified that I was able to properly link to the CMSIS .lib that I had previously created when I wrote my app note on the topic (found at&nbsp;http://www.ti.com/lit/an/spma041a/spma041a.pdf). &nbsp;That done, I switched over to cygwin and wrote a quick c program to emulate what the ADC would see if it was observing a perfect 440 Hz sine wave:<br /><br /><br /><span style="font-size: x-small;">#include &lt;stdio.h&gt;</span><br /><span style="font-size: x-small;">#include &lt;math.h&gt;</span><br /><span style="font-size: x-small;"><br /></span><span style="font-size: x-small;">#define PI 3.141592653589793</span><br /><span style="font-size: x-small;">#define ARR_SIZE 2048</span><br /><span style="font-size: x-small;">#define SAMPLE_FREQ 44600</span><br /><span style="font-size: x-small;">#define FREQ 440</span><br /><span style="font-size: x-small;"><br /></span><span style="font-size: x-small;">int main(int argc, char** argv)</span><br /><span style="font-size: x-small;">{</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; float period = 1.0/SAMPLE_FREQ;</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; float time = 0;</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; float freqRadians = 2*PI*FREQ;</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; unsigned long x[ARR_SIZE*2];</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; int i, j = 0;</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; printf("const float ti_sample_sine_vector[%d] = {\n", ARR_SIZE);</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; for(i=0; i&lt; ARR_SIZE; i++)</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; {</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; j++;</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // ADC sees signal centered at 0x800 w/ 0x280 max swing</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x[i] = (unsigned long)(640 * sin(freqRadians*time) + 0x800);</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("0x%04x,\t", x[i]);</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(j == 8)</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("\n");</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; j = 0;</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time += period;</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; }</span><br /><span style="font-size: x-small;"><br /></span><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; printf("};\n\n\n");</span><br /><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; return 0;</span><br /><span style="font-size: x-small;">}</span><br /><div><br /></div><div>Next, I wrote a similar snippet of code to generate the parameters of a windowing function. &nbsp;I had read in a number of places that an fft is designed to work on a continuous, infinite signal, so when you instead use it for frequency analysis on a finite, non-continuous signal, it is best practice to multiply the samples by a window to reduce the amount of noise across frequency bins. &nbsp;I think. &nbsp;I'm nowhere close to a DSP expert, but this sounded like good information to me, so I wrote another snippet to generate a vector that would represent a hamming window:</div><div><br /></div><div><span style="font-size: x-small;">#include &lt;stdio.h&gt;</span></div><div><span style="font-size: x-small;">#include &lt;math.h&gt;</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">#define PI 3.141592653589793</span></div><div><span style="font-size: x-small;">#define ARR_SIZE 2048</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">int main(int argc, char** argv)</span></div><div><span style="font-size: x-small;">{</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; double x[ARR_SIZE*2];</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; int i, j = 0;</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; printf("const float ti_hamming_window_vector[%d] = {\n", ARR_SIZE);</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; for(i=0; i&lt; ARR_SIZE; i++)</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; {</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; j++;</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x[i] = 0.54 - 0.46*cos(2*PI*i/ARR_SIZE);</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //printf("%.18f,\t%.18f,\t", x[i], x[i]);</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("%.18f,\t", x[i]);</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //if(j == 4)</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(j == 8)</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf("\n");</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; j = 0;</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; }</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; printf("};\n\n\n");</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; return 0;</span></div><div><span style="font-size: x-small;">}</span></div><div><br /></div><div>I copied the output of these two programs into two C files, and used them to write static vectors into the lm4f's flash. &nbsp;Once that was done, I wrote a bit of code in my launchpad project that would multiply the sample by the hamming window, take a 2048 point real fft of that sample (which is implemented in CMSIS by doing a 1024 point complex fft, then running the result through a split fft process. &nbsp;I don't know exactly what that means, but I think the result is a real fft of 2048 points). &nbsp;I then took the complex magnitude of the results, found the maximum value in the result array, determined what frequency range that bin corresponded to, and used my debug UART to print that frequency range. &nbsp;I had to massage the data and play around a bit with the input flags, but eventually I saw what I had been striving for: a message stating that the maximum frequency component of the signal I ran my DSP function on was between 435 and 458 Hz!</div><div><br /></div><div><div><span style="font-size: x-small;">#define NUM_SAMPLES<span class="Apple-tab-span" style="white-space: pre;"> </span>2048</span></div><div><span style="font-size: x-small;">#define&nbsp;SAMPLING_FREQ<span class="Apple-tab-span" style="white-space: pre;"> </span>44600</span></div><div><span style="font-size: x-small;">#define INVERT_FFT<span class="Apple-tab-span" style="white-space: pre;"> </span>0</span></div><div><span style="font-size: x-small;">#define BIT_ORDER_FFT<span class="Apple-tab-span" style="white-space: pre;"> </span>1</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">extern q15_t ti_sample_sine_vector[NUM_SAMPLES];</span></div><div><span style="font-size: x-small;">float32_t nextTest[NUM_SAMPLES];</span></div><div><span style="font-size: x-small;"></span><br /><div><span style="font-size: x-small;">unsigned long g_ulADCValues[NUM_SAMPLES];</span></div><span style="font-size: x-small;"></span><br /><div><span style="font-size: x-small;">float32_t g_fFFTResult[NUM_SAMPLES * 2];</span></div><span style="font-size: x-small;"></span><br /><div><span style="font-size: x-small;"><br /></span></div><span style="font-size: x-small;"></span><div><div><span style="font-size: x-small;">arm_rfft_instance_f32 fftStructure;</span></div><div><span style="font-size: x-small;">arm_cfft_radix4_instance_f32 cfftStructure;</span></div></div><span style="font-size: x-small;"><div><br /></div><div>float HzPerBin;</div><div><br /></div></span></div><div><span style="font-size: x-small;">void</span></div><div><span style="font-size: x-small;">SampleSineTest(void)</span></div><div><span style="font-size: x-small;">{</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>unsigned long i;</span></div><div><span style="font-size: x-small;"><br /></span></div><div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>arm_rfft_init_f32(&amp;fftStructure, &amp;cfftStructure, NUM_SAMPLES, INVERT_FFT, BIT_ORDER_FFT);</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>HzPerBin = (float)SAMPLING_FREQ / (float)NUM_SAMPLES;</span></div></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>for(i=0;i&lt;NUM_SAMPLES;i++)</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>{</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>//</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Recenter the signal at 0... might not be necessary</span></div><div><span style="font-size: x-small;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // </span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>g_fFFTResult[i] = ((float)ti_sample_sine_vector[i] - (float)0x800);</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>}</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>UARTprintf("test processing using sample 440 Hz sine wave\n");</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>arm_mult_f32(g_fFFTResult, ti_hamming_window_vector, g_fFFTResult, NUM_SAMPLES);</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>UARTprintf("Done with Hamming window multiplication\n");</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>UARTprintf("Calculating FFT\n");</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>arm_rfft_f32(&amp;fftStructure, g_fFFTResult, g_fFFTResult);</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>UARTprintf("Calculating complex magnitude of each frequency bin\n");</span></div><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>arm_cmplx_mag_f32(g_fFFTResult, g_fFFTResult, NUM_SAMPLES * 2);</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"></span><br /><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>UARTprintf("Finding largest frequency bin\n");</span></div><span style="font-size: x-small;"></span><br /><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>arm_max_f32(g_fFFTResult, NUM_SAMPLES, &amp;maxValue, &amp;i);</span></div><span style="font-size: x-small;"></span><br /><div><span style="font-size: x-small;"><br /></span></div><span style="font-size: x-small;"></span><div><span style="font-size: x-small;"><span class="Apple-tab-span" style="white-space: pre;"> </span>UARTprintf("Peak is between %06d and %06d Hz: %06d\n\n", (int)(HzPerBin*i), (int)(HzPerBin*(i+1)), (int)maxValue);</span></div><span style="font-size: x-small;"></span></div><div><span style="font-size: x-small;">}</span></div></div><div><br /></div><div><br /></div><div>My next step was to very, very anxiously put everything together...</div>http://euphonistihack.blogspot.com/2012/08/remember-remember-whenever-your-last.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-1208485883282235005Mon, 20 Aug 2012 19:50:00 +00002012-09-05T12:30:45.817-07:00To condense fact from the vapor of nuanceMy first challenge was capturing the audio signal using the analog to digital converter running on lm4f120xl controller. &nbsp;A quick bit of datasheet reading told me that there are two hardware limitations to the ADC that I have to worry about. &nbsp;For one, the ADC expects a signal range of 0 to 3.3V. &nbsp;Any voltages that dip below ground cause problems or are ignored. &nbsp;The second limitation is that the ADC uses a switched capacitor array to implement its successive approximation register. &nbsp;As a result, we have to have a low impedance signal feeding the ADC.<br /><br />I talked with a few hardware knowledgeable friends of mine about this, and eventually came up with the following circuit to handle the above constraints:<br /><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-1ce_95q4JEU/UDKoJ018gQI/AAAAAAAAAH0/MIIMAu2sAXg/s1600/jordanckt.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="255" src="http://4.bp.blogspot.com/-1ce_95q4JEU/UDKoJ018gQI/AAAAAAAAAH0/MIIMAu2sAXg/s400/jordanckt.JPG" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;">The idea here is that the resistors will bias any input signal to between the ADC's maximum input voltage and ground. &nbsp;The opamp will then amplify the signal enough to easily charge the switched capacitor array, giving us a good enough signal for our ADC to work with. &nbsp;I dusted off my wiring kit, made a quick trip to Fry's, fired up the oscilloscope, and poked around enough to get myself confident that my line level input (courtesy of a male to male 1/8 inch audio cable, a stereo headphone jack, and my laptop's audio out) was being properly biased and conditioned.</div><div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-cw9fAkDKFM8/UDKptGd7zwI/AAAAAAAAAH8/m0ITMDEpWyY/s1600/IMG_20120820_161538.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="http://3.bp.blogspot.com/-cw9fAkDKFM8/UDKptGd7zwI/AAAAAAAAAH8/m0ITMDEpWyY/s320/IMG_20120820_161538.jpg" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><span style="font-size: x-small;">I couldn't find a rail-to-rail opamp at Fry's, so I used this little number instead, which has a maximum output of Vcc-1.7. &nbsp;</span><span style="font-size: x-small;">Fortunately, I had a 5V bus I could tie into.</span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both; text-align: left;">Hardware being handled, it was time for software!</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">I had played around with the ADCs on an lm4f232 a bit, so it didn't take much to get me to a point where I could capture the signal and print its level in Volts on one of the UARTs. &nbsp;I figured there was no need to get fancy (yet), so I just used sequencer 3 of the ADC (which is limited to a one sample FIFO) to capture based on a software trigger. &nbsp;Throw it in a while loop and UARTprintf ad nauseum.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Next was figuring out how to set up a controlled sampling frequency. &nbsp;My first thought was to set up a continuous timer to countdown at 44.6 kHz (for some reason I picked 44.6 instead of 44.1... friends don't let friends drink and divide), throwing an interrupt every time it hit 0. &nbsp;I was poking through the driverlib api for the timer getting this set up when I stumbled across the TimerControlTrigger function, which according to the documentation "Enables or disables the ADC trigger output." &nbsp;It turns out that the hardware is already capable of doing what I had planned on doing through software: use a timer to trigger a capture in the ADC module. &nbsp;Happy day!</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">A bit of debugging, a bit of re-reading, and another night of playing around with the launchpad got me to the point where I could use a timer to trigger an ADC capture, then on the ADC interrupt move the data into a sample array, then use a second timer to print out that array once per second. &nbsp;The data looked good, though I couldn't think of a good way to verify that I was capturing with the expected sampling rate.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">A normal person might have called it a day at that, but I long ago decided that the kind of person who spends his weekends and free nights messing about with microcontrollers is a far cry from a normal person. &nbsp;The method of having the ADC interrupt copy the sample from the FIFO to the sample array reeked of inefficiency, especially when the documentation for the uDMA engine was tantalizingly sitting in the periphery of my vision.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">For those not familiar with it, a uDMA engine can automatically move data from one place in memory to another, without needing the main processor to intervene. &nbsp;As a result, you can do things like copying data from a peripheral's register map directly into a variable that's been allocated somewhere on your stack, which happens to be exactly what I wanted to do.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">More datasheet reading, searching through old forum posts, and snooping around random example code led me to the confident belief that I could use the sequencer capture complete signal to trigger a uDMA transfer at the hardware level that would leave me with a much more efficient system. &nbsp;My goal was to have a single "go" bit hit, then receive an interrupt SAMPLE_SIZE/44600 seconds later (44,600 samples/sec, SAMPLE_SIZE samples per signal processing loop), at which time the data would all be nicely arranged in my buffer. &nbsp;Another night spend coding, and I was able to realize this goal. &nbsp;The only limiting factor was that the uDMA engine has a max transaction limit of 1024, meaning that I would have to re-initiate the uDMA transfer after each 1024 samples until my buffer was full. &nbsp;Irritating, but it only took a few lines of code, and was easily managed inside the interrupt handler.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">After all of that, which took me about a week and half to get through (one weekend on the hardware, about another week on the software), I was very efficiently capturing audio data at an easily configured sampling frequency. &nbsp;Woo and hoo!</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">In summation, my signal chain at this point was:</div><div class="separator" style="clear: both; text-align: left;">Timer0 triggers ADC0 capture; ADC0 capture is fed by custom circuit; ADC0 capture complete triggers uDMA transfer; uDMA transfer triggers interrupt, at which point our signal has been captured and moved into our sample buffer.</div>http://euphonistihack.blogspot.com/2012/09/to-condense-fact-from-vapor-of-nuance.htmlnoreply@blogger.com (EuphonistiHack)0tag:blogger.com,1999:blog-5712998695777024394.post-1350836727070915283Mon, 20 Aug 2012 19:50:00 +00002012-09-05T12:28:29.581-07:00The Journey from There to HereI've finally decided to start a blog for the fun little side project I've been playing with lately. &nbsp;So, a condensed history of how I've spent my free time over the past few months follows herein:<br /><div><br /></div><div>February of 2012, I finished my first major, publicly visible task as an engineer on TI's Stellaris Microcontroller team; I published an application note detailing how to build ARM's CMSIS DSP library using our (in the corporate sense) preferred IDE, Code Composer Studio v5. &nbsp;The app note is available at&nbsp;<a href="http://www.ti.com/lit/an/spma041a/spma041a.pdf">http://www.ti.com/lit/an/spma041a/spma041a.pdf</a>&nbsp;and required a decent amount of work between myself and the engineers in Dallas who make the tms470 compiler to get fully running. &nbsp;CMSIS uses a number of direct assembler calls and compiler intrinsic, the support for which required some work on my part.</div><div><br /></div><div>I felt the application note was fairly well done, but I was left with a feeling of unfulfilled remorse, as I spent a very long time writing it, but never actually got to use the CMSIS DSP lib outside of the default test cases that CMSIS ships with. &nbsp;As such, I started brainstorming ideas for projects that would incorporate heavy dsp functions.</div><div><br /></div><div>I spent many years during and after college running soundboards and digital mixing/recording equipment, so I soon decided that I wanted to do something involving audio signal processing with CMSIS. &nbsp;The obvious idea for me was to use the fft functionality to make the display for a graphic equalizer. &nbsp;By April 2012, I had a spare lm4f232 evaluation kit in my office and seven hours' time to kill (compliments of a day of travelling for a friends' wedding), so I spent the time in the air learning how graphics lib worked and exploring the analog inputs of the 232. &nbsp;The experience left me more knowledgeable but discouraged, as the 96x64 OLED just didn't look as visually impressive as I had hoped.<br /><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-0Ot_kG1h8vc/UDKUtqxplfI/AAAAAAAAAHk/DxX6RNaFmOg/s1600/IMG_20120820_144407.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="http://1.bp.blogspot.com/-0Ot_kG1h8vc/UDKUtqxplfI/AAAAAAAAAHk/DxX6RNaFmOg/s320/IMG_20120820_144407.jpg" width="320" /></a></div><br /></div><div><br /></div><div>Around July 2012, several of my coworkers were talking about and working on the Stellaris Launchpad that will be releasing in a few months. &nbsp;It took a bit of finagling, but I managed to secure an early run beta version to play with. &nbsp;A bit of googling and I came across an MSP430 booster pack that Olimex released a few years ago:&nbsp;<a href="https://www.olimex.com/dev/msp-led8x8.html">https://www.olimex.com/dev/msp-led8x8.html</a>&nbsp; In short, it's an 8 by 8 array of LEDs that uses SPI for communication. &nbsp;Not nearly as much resolution as the 232 kit's OLED, but much more interesting to me. &nbsp;Plus, blinking LEDs is what microcontrollers are all about!</div><div><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-WdNPX653RWk/UDKSGXAVeAI/AAAAAAAAAHY/H71JY88RP20/s1600/olimexLED.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://4.bp.blogspot.com/-WdNPX653RWk/UDKSGXAVeAI/AAAAAAAAAHY/H71JY88RP20/s320/olimexLED.jpg" width="292" /></a></div><span id="goog_180315103"></span><span id="goog_180315104"></span><br /></div><div><br /></div><div>Hardware in hand, I started to define the parameters of my project.</div>http://euphonistihack.blogspot.com/2012/08/the-journey-from-there-to-here.htmlnoreply@blogger.com (EuphonistiHack)0