tag:blogger.com,1999:blog-19339577396568164862024-02-19T03:10:16.468-06:00Electron IncantationIs it still a cargo cult if the chanting works?benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.comBlogger29125tag:blogger.com,1999:blog-1933957739656816486.post-27706272251635276422013-05-22T18:58:00.000-05:002013-05-22T18:58:00.621-05:00How to export vector figures from MATLAB (or, the saddest thing is how elated I was when I finally figured this out)<div dir="ltr" style="text-align: left;" trbidi="on">
Lately, I've been producing a lot of figures for my dissertation in MATLAB (my scientific computing platform of choice). Once the figures are created, they need to be transferred into a word processor (e.g, Microsoft Word) to become part of my contribution to the advancement of science.<br />
<br />
The problem is that this is a more complicated procedure than one might think. The most straightforward method would be to save the figures as some sort of bitmap format (png, jpg, etc.); however, this means that the resolution is set and the figure labels, annotations, trace widths, etc. cannot be changed without going back to MATLAB, changing those things in the figure, then re-exporting it. The alternative is to save the figure in a vector format; .eps would be ideal, but Word's support is spotty, so I use .emf. A vector format saves a description of the shapes that make up the figure (lines, letters, patches of color) rather than a raster of pixels; this way, you can unpack the figure after-the-fact and change things in the word processor (line widths, fonts, labels) without having to go back to MATLAB.<br />
<br />
The problem I had was that the vector images that MATLAB exported were... jagged. Disjointed. Weird. Specifically, I need to plot EMG traces (ElectroMyoGrams, the electrical activity of muscles); some examples of this are shown immediately below. The two figures immediately below are screenshots of the same data from within MATLAB; the left is just a zoomed-in subset of the right figure. As you zoom in, MATLAB re-renders the image on the screen based on the actual data that was plotted.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBCyqmcSOXvGAcy8MRGzWw2XVideJdYx2_jX-ECb2WEtdV_jqBLL8uptDO17K2mcq6VWe7cyeFsZbCJdN6YF6mxPWHaGXgBpj5Eitw-f2P2V0r38QFzL6e5eMFc5gvNLw8D5cPGdF0qD8/s1600/nativeFigure.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBCyqmcSOXvGAcy8MRGzWw2XVideJdYx2_jX-ECb2WEtdV_jqBLL8uptDO17K2mcq6VWe7cyeFsZbCJdN6YF6mxPWHaGXgBpj5Eitw-f2P2V0r38QFzL6e5eMFc5gvNLw8D5cPGdF0qD8/s320/nativeFigure.png" width="191" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Several traces of EMG, as seen natively in MATLAB</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg0RSE3G_K20K5aLkrIV6_YoJCSRk2r43uNCXrs6sH7JQqFda7q5vOlI26dlkSMn0fb8lcmUU12vt7mU4_FkRf8RVRj7OORzrcNwQznGTY6d_Wl0RwltM6TB8FIDmdNaw8dvtroFqKpvw/s1600/nativeZoomed.png" imageanchor="1" style="clear: right; display: inline !important; margin-bottom: 1em; margin-left: auto; margin-right: auto; text-align: center;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg0RSE3G_K20K5aLkrIV6_YoJCSRk2r43uNCXrs6sH7JQqFda7q5vOlI26dlkSMn0fb8lcmUU12vt7mU4_FkRf8RVRj7OORzrcNwQznGTY6d_Wl0RwltM6TB8FIDmdNaw8dvtroFqKpvw/s320/nativeZoomed.png" width="232" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Zoom in of EMG traces, natively in MATLAB, showing normal spikiness of EMG</td></tr>
</tbody></table>
<div style="text-align: left;">
The problem happened why I tried to export these figures into a vector format that I could insert into a Word document. I first tried exporting as .eps; this did not work AT ALL. When I exported to .emf, things appeared to work better (see the screenshot immediately below). However, when zoomed in, the traces were 'jagged'. Additionally, smoother traces (joint angles corresponding to the EMG) appeared 'quantized' far more than in the original figure.</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigQ4pF1oViYZubEAHltcL8e1XREjPGXK2wQedbPYUyBB1pVhisOkSGgw6opBHEG8Maeu4cVZSJ9teFcLkFNhEhxgVsD5hEZe3up96JOoOUBIWXVT4OdYyujE1jKZToBqgZVOAXAkp7GFc/s1600/autoFigure.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigQ4pF1oViYZubEAHltcL8e1XREjPGXK2wQedbPYUyBB1pVhisOkSGgw6opBHEG8Maeu4cVZSJ9teFcLkFNhEhxgVsD5hEZe3up96JOoOUBIWXVT4OdYyujE1jKZToBqgZVOAXAkp7GFc/s320/autoFigure.png" width="195" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Screenshot of exported figure in PowerPoint</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjj4_9_yUvNlkKlZR_izqt3uVMvZzbzUmrj4AwIiOeLxCpFMBKidIUP_AURE4QkXOvQ9X0SWBMBUro4849IxJqs-j9l-1LGXth0it8KmfQhdW1qpAvtpTRIL5hkIS-vgO05UcykWKIpKM/s1600/autoZoomed.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjj4_9_yUvNlkKlZR_izqt3uVMvZzbzUmrj4AwIiOeLxCpFMBKidIUP_AURE4QkXOvQ9X0SWBMBUro4849IxJqs-j9l-1LGXth0it8KmfQhdW1qpAvtpTRIL5hkIS-vgO05UcykWKIpKM/s200/autoZoomed.png" width="160" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Screenshot within Powerpoint of the figure above, zoomed in, showing 'jagged' trace</td></tr>
</tbody></table>
This was very confusing; why would the vectors be quantized? they represent the underlying data from the figure, right? Searching online was largely in vain; the keyword 'jagged' was of no help, and most of what turned up was just generic 'how do I export MATLAB' how-tos. After trying a series of progressively more esoteric and unnatural transformations (export to eps, open in ghostscript, export to emf, import into Powerpoint...), I finally found <a href="http://www.mathworks.com/matlabcentral/newsreader/view_thread/18641">this thread</a> on the MATLAB Central forum. A few posts in, one person notes that "<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 17.59375px;"> the resolution of the metafile is limited to screen resolution. This</span><br style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 9px; margin: 0px; padding: 0px;" /><span style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 17.59375px;">means when we draw a line segment the endpoints are on a grid at screen</span><br style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 9px; margin: 0px; padding: 0px;" /><span style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 17.59375px;">resolution. So even though the line is drawn at device resolution the</span><br style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 9px; margin: 0px; padding: 0px;" /><span style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 17.59375px;">endpoints could be at a much lower resolution. This problem usually shows up</span><br style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 9px; margin: 0px; padding: 0px;" /><span style="background-color: white; color: #191919; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 17.59375px;">when you have lots of short line segments -- </span><a href="http://www.mathworks.com/matlabcentral/newsreader/author/9536" style="background-color: #edeeef; color: #004aa0; font-family: Arial, Helvetica, sans-serif; font-size: 11px; line-height: 17.59375px; margin: 0px; padding: 0px; word-wrap: break-word;">Ben Hinkle</a></blockquote>
AHA! I had my answer. The problem was not bad emf support in MATLAB or Powerpoint, but the fact that the default renderer creates the ends of the line segments in the vector image export at the resolution of the screen. However, that post was from 2000, so MATLAB has advanced slightly since then; I was able to find an export setting that allowed me to force the resolution of the renderer to be higher than the screen resolution.<br />
<br />
So, in the end, my MATLAB-to-Word (by way of Powerpoint for manipulation, because I am too cheap for Corel et. al) is:<br />
<br />
<br />
<ol style="text-align: left;">
<li>Create the figure in MATLAB</li>
<li>In the MATLAB figure window, File -> Export Setup; under the Rendering tab, choose 'Painters' as the renderer (the other two are rasterizers) and 600 as your DPI (or 300 or 100, whatever floats your boat). Presumably, 'Auto', which it <i>was</i> set to, had somewhat too-low standards for my needs.</li>
<li>Apply to Figure</li>
<li>Export -> set your file type as .emf.</li>
<li>In Powerpoint, Insert -> Picture -> choose your figure</li>
<li>Right click on the figure -> ungroup -> yes (this 'unpacks' the figure in Powerpoint, allowing you to manipulate the vector objects that make it up)</li>
<li>Manipulate the figure, then directly copy/paste into Word (don't forget to re-group the figure elements before copying)</li>
</ol>
<br />
The end result is seen below; unfortunately, Powerpoint wouldn't let me zoom in any further, but it's clear that the figure as exported is far more faithful to the original than before the 600DPI setting. Additionally, smoother traces no longer suffer from 'quantization'.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPBO4XUiG57-JccSqA3MQ718wqjZpdjC0Ax82wfZvIeBPYI82Gpz3ImvBoxv6FWetBfp8sBbwatrJzt5hs6zC_HMyXADrZIcq2cGW5uA0ZwtWPxXB7V3cCI9sVzYcqJDGda3OkHktIG7E/s1600/600figure.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPBO4XUiG57-JccSqA3MQ718wqjZpdjC0Ax82wfZvIeBPYI82Gpz3ImvBoxv6FWetBfp8sBbwatrJzt5hs6zC_HMyXADrZIcq2cGW5uA0ZwtWPxXB7V3cCI9sVzYcqJDGda3OkHktIG7E/s320/600figure.png" width="195" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy7giCkR18-oRZY41mCCeP6P6iYa125Q3xZRBgXWCJWCbsdDMa0m0stZY0uqHuI08xbmFSup1feT9TGANsyKQhhlcCpuItbMZaLBqQ_kGdqK2auJntNHrtQSWSxflERgrBogcTOzB6Lm0/s1600/600Zoomed.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy7giCkR18-oRZY41mCCeP6P6iYa125Q3xZRBgXWCJWCbsdDMa0m0stZY0uqHuI08xbmFSup1feT9TGANsyKQhhlcCpuItbMZaLBqQ_kGdqK2auJntNHrtQSWSxflERgrBogcTOzB6Lm0/s200/600Zoomed.png" width="200" /></a></div>
<br />
<br />
<br />
<br />
<br /></div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-80991488982264873362013-03-07T16:57:00.000-06:002013-03-07T16:57:10.854-06:00Toward triangular-tesselated Game of Life wrapped onto regular polyhedra (or, MATLAB's handle graphics interface is simultaneously terrible and the best solution possible)<div dir="ltr" style="text-align: left;" trbidi="on">
As I outlined in a previous post, I want to build a device which will display Conway's Game of Life. To make things a little more interesting, it will do this using a triangular tesselation (rather than the much more common square pattern), and faces of 25 pixels will be folded and assembled to form a regular polyhedron.<br />
<br />
Since building the device will be a large undertaking and the firmware will be very complicated (in a way which would make small errors generally difficult to isolate), I have implemented the device in simulation using the MATLAB environment. This has allowed me to verify that my mapping of the pixels and implementation of the game update rules is correct.<br />
<br />
<span style="font-size: large;"><b><u>Tetrahedron</u></b></span><br />
<br />
The first shape is the tetrahedron, show below. Since the tetrahedron is difficult to visualize in a 2D projection, I've posted two images of the same pattern from two different angles. It's a period-3 oscillator under the rule {4,6,4,4}. Implementing this form will only require one of the four-triangle circuit sets I've designed.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIJNX8rW5UtM6oh8Eo_w90Xcc3BKTlHcni0a7H_SNBuCmD1eLIIbHIq-nK8Mo3-GOJUr7nJCNUC-4QxFqC_FLFIhO-mk9b0rqqMWk7vZT78Dw7lKppxY69PYP5JDjOFZl10HAVTxqFvS8/s1600/4644oscTetra1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="194" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIJNX8rW5UtM6oh8Eo_w90Xcc3BKTlHcni0a7H_SNBuCmD1eLIIbHIq-nK8Mo3-GOJUr7nJCNUC-4QxFqC_FLFIhO-mk9b0rqqMWk7vZT78Dw7lKppxY69PYP5JDjOFZl10HAVTxqFvS8/s200/4644oscTetra1.gif" width="200" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKEw4Krhh2Rdy3Hyn59ly4eSHgGKJ4UrGivPc1rUyl_fA60ghksjO7OT-DLRAme90J1u20AqnmOWsWJAmy6OrH-0lYhlkclq2fRYvEQJbNsB09oI9_YgTzQ2uTYsf4ZkEV2nF1XzatSbc/s1600/4644oscTetra2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKEw4Krhh2Rdy3Hyn59ly4eSHgGKJ4UrGivPc1rUyl_fA60ghksjO7OT-DLRAme90J1u20AqnmOWsWJAmy6OrH-0lYhlkclq2fRYvEQJbNsB09oI9_YgTzQ2uTYsf4ZkEV2nF1XzatSbc/s200/4644oscTetra2.gif" width="151" /></a></div>
<br />
<b><u><span style="font-size: large;">Octahedron</span></u></b><br />
<br />
Below, I show show the evolution of a glider pattern, also under rule {4,6,4,4}. The period of its circumnavigation of the device is 45.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnuzkHShUUqQiySG8mFXpRC_fk7T1RS06vqdB4VKrfThzlDjN0TbLyMRSUx5fMk7kzQJYsm2MM0xAebgy5n-9SmKERYjuP15jnM6LsX6E9VuXYHBjp5wjidIxG1QPiteKt2UB5b44Y34s/s1600/4644gliderOcta.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="252" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnuzkHShUUqQiySG8mFXpRC_fk7T1RS06vqdB4VKrfThzlDjN0TbLyMRSUx5fMk7kzQJYsm2MM0xAebgy5n-9SmKERYjuP15jnM6LsX6E9VuXYHBjp5wjidIxG1QPiteKt2UB5b44Y34s/s320/4644gliderOcta.gif" width="320" /></a></div>
<br />
<br />
<b><u><span style="font-size: large;">Icosahedron</span></u></b><br />
<br />
The same glider pattern, also rule {4,6,4,4}. Circumnavigation takes 75 generations.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjehyupsYue47jOftSj_R5JVYatk2MG3YAqChdleUgmCtgmOmcZpZQLykuh3q3PkrAG-cMSyDb8sXDx-v_G7dZs0l88rAF1jliwIVqipKHtK5QQSCUVnl3EpjSDDi4hCOkXnCg-XTSnSlQ/s1600/4644gliderIcosa.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="252" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjehyupsYue47jOftSj_R5JVYatk2MG3YAqChdleUgmCtgmOmcZpZQLykuh3q3PkrAG-cMSyDb8sXDx-v_G7dZs0l88rAF1jliwIVqipKHtK5QQSCUVnl3EpjSDDi4hCOkXnCg-XTSnSlQ/s320/4644gliderIcosa.gif" width="320" /></a></div>
<br />
<br />
<b><u><span style="font-size: large;">Source</span></u></b><br />
<br />
The MATLAB files I used are <a href="https://docs.google.com/file/d/0B9psp2BpNroQRE8tLWc5WHFkdDQ/edit?usp=sharing">here</a>. To instantiate one of the above forms, just run createIcosaTRI([4,6,4,4]), with your desired rule in the brackets. The other shape creation functions are createTetraTRI and createOctaTRI.</div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-84659623067779984652013-02-23T18:07:00.002-06:002013-02-23T18:07:35.509-06:00Initial validation of joint sensor glove board (or, at least I didn't put the USB jack in backwards this time)<div dir="ltr" style="text-align: left;" trbidi="on">
As I related in a past post, I recently designed and built a device which could be built into a joint-sensor glove; it is able to measure the resistance of 25 sensors and store that data to a microSD card or stream it over a serial link.<br />
<br />
I started this project because I've long wanted to play around with using an instrumented glove which would allow free-form hand gestures to be used as a human interface. Additionally, I work with people who do research into physical rehab, who sometimes need access to hand movement data. Currently, they have to use a bulky device which is wirelessly tethered to a PC; this restricts the circumstances under which data can be taken. This device, being able to be contained in a glove and being able to store data internally, may allow for experiments which are not currently viable (e.g., a full-day recording to examine the structure of normal hand use).<br />
<br />
<b><u><span style="font-size: large;">Hardware</span></u></b><br />
<br />
A front image of the board is below. The front only has two buttons, two status LEDs, and the low-frequency, high-accuracy clock oscillator.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivFyt5WgQb0Xn7Bu29sQYT-56LbdFlM1peEfOChDROg68-di5mt6CKI2-YsDMA7xCaer2pp_HltZz1U7T2ougdtr1tt3sqE2ImSgIyEDYnDPFhRYz6wnAXUAm4lvCEwUjq6bMSI6Sm334/s1600/sensor_glove_top.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="325" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivFyt5WgQb0Xn7Bu29sQYT-56LbdFlM1peEfOChDROg68-di5mt6CKI2-YsDMA7xCaer2pp_HltZz1U7T2ougdtr1tt3sqE2ImSgIyEDYnDPFhRYz6wnAXUAm4lvCEwUjq6bMSI6Sm334/s400/sensor_glove_top.JPG" width="400" /></a></div>
<br />
The back of the board is below. The resistive sensors would be connected to the header at the top; the top row of holes all connect to +3.3V and the bottom row lead to the FET switch array. Currently, I've got a potentiometer connected for first-round debugging.<br />
<br />
The actual resistance-sensing circuitry uses a FET and an opamp to sink a set amount of current through the selected sensor channel. The sensor channel is selected by turning ON the appropriate FETs in the line of ICs just below the sensor header at the top; it is a 5-by-5 array, allowing one of 25 sensors to be selected by the appropriate use of 10 outputs. The voltage at the bottom of this switch array is sampled to measure the resistance; since the current sink is controlled, the voltage difference between 3.3V and the measured voltage determines the resistance.<br />
<br />
The board also includes the microSD slot (bottom left) and the power circuitry (charger and 3.3V buck converter, bottom right). All of the circuitry has been tested except the microSD slot.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1AOcdADqy_07gCMd46U6u5mTbYihE8WrNug6gYtz9h8dbPI23enSnQ1pNobRI4YkVwWgcoO2DimkJA3PTtUvlIclsC8FuUwgKqbVNGGqZn4WkX8HbnOs6sbo02wS5XRL8Bmj7jUeojHs/s1600/sensor_glove_bottom.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="343" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1AOcdADqy_07gCMd46U6u5mTbYihE8WrNug6gYtz9h8dbPI23enSnQ1pNobRI4YkVwWgcoO2DimkJA3PTtUvlIclsC8FuUwgKqbVNGGqZn4WkX8HbnOs6sbo02wS5XRL8Bmj7jUeojHs/s400/sensor_glove_bottom.JPG" width="400" /></a></div>
<br />
<b><u><span style="font-size: large;">Software + Testing</span></u></b><br />
<br />
To start testing the hardware, I developed a simple firmware which will sample the 25 sensors and transmit the resulting voltages over the serial channel that I have broken out for debugging. The firmware source is <a href="https://docs.google.com/file/d/0B9psp2BpNroQWDhweW5qZW1vWUU/edit?usp=sharing">here</a>; it is very simple, and applies a constant sink current across all of the sensors and over time. In the future, it may make sense to adjust the sink current to match the sensitivity and range of the device to the magnitude and range of resistances exhibited by each channel.<br />
<br />
The firmware sets the switch array and samples each 'sensor' in turn; the data is then partitioned and inserted into a transmit buffer. Once all of the channels have been sampled, I use the DMA controller to 'automatically' transmit the buffer contents over the serial link.<br />
<br />
To watch the data as it comes in, I use an upgraded version of my general-purpose MATLAB serial data 'scope function (<a href="https://docs.google.com/file/d/0B9psp2BpNroQZTdaV3k3T2l1VFU/edit?usp=sharing">here</a>). First, I verified that all of the channels were working by shorting each channel in turn (shown below); as you can see, all of the channels were working.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdLCpJDEUmX6aIFSeJMf7gEsIVYDeAygFRTLISHiSTH4XUgLThAqPCgc135iGLdonLYB5TvrKB6gmqABqOntJnYCAlkqyqog8g8FHhnIlUNjkXnJLs6QapqwrPJKhBcJ7PCLUKRAuYpp0/s1600/short_test_all.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdLCpJDEUmX6aIFSeJMf7gEsIVYDeAygFRTLISHiSTH4XUgLThAqPCgc135iGLdonLYB5TvrKB6gmqABqOntJnYCAlkqyqog8g8FHhnIlUNjkXnJLs6QapqwrPJKhBcJ7PCLUKRAuYpp0/s400/short_test_all.png" width="400" /></a></div>
<br />
I then wiggled the potentiometer at either end of its range, to cause wiggles in the scope for the appropriate channels (below).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNXDwZ2_FmJzUN0J0WwNt-8ADkhqZbCSbWjFdvRZy1nUvgtBOluSqM0rbgo3hSbDcG_8j9gbXgs7qt8DcUFEtQvdlTL4PsDCyMUQbRl2NpbwJwrdK_IeAOJAtSga4kRF0t6C0rjjbqWf4/s1600/wiggle_test.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNXDwZ2_FmJzUN0J0WwNt-8ADkhqZbCSbWjFdvRZy1nUvgtBOluSqM0rbgo3hSbDcG_8j9gbXgs7qt8DcUFEtQvdlTL4PsDCyMUQbRl2NpbwJwrdK_IeAOJAtSga4kRF0t6C0rjjbqWf4/s400/wiggle_test.png" width="400" /></a></div>
<br />
This means that, with the exception of the SD card slot, all of the hardware has been tested and verified.<br />
<br />
<u><b><span style="font-size: large;">Moving Forward</span></b></u><br />
<br />
Since I know that the resistance sensor circuit works (in a broad sense), there are a number of things to do:<br />
<br />
-<u>Implement the SD-card logging</u>. This will involve testing out the hardware and its ability to interface with SD cards, looking up the specs for SD card partitioning and FAT32, and using that information to implement some simple code that finds a long stretch of unused space on the card to fill up with logged sensor data. It would be possible to code something that does not require a contiguous segment of the drive; however, this would require a much more complicated algorithm. Since this is a very purpose-built device, that seems like overkill; one merely needs to make sure that the SD cards used are relatively empty and absolutely defragmented.<br />
<br />
-<u>Implement a limited USB serial port</u>. At present, the hardware is able to communicate over USB (verified by flashing the device with a USB-based bootloader). However, I do not use this facility; further, to get debug or other data out of my prototypes, I use a separate USB-to-serial cable connected to a debug USART. This extra cable is somewhat cumbersome; it would be nice to only need to plug in one cable to power the device and allow for debugging and reprogramming.<br />
<br />
-<u>Make an example resistive joint sensor; develop the algorithms to read it</u>. At present, I just set the sensor current sink to a constant level. However, for certain ranges of resistances to-be-measured, it will saturate, while others will barely register. Additionally, an increase in resolution may be possible by sampling the sensor output single-ended, then also sampling it differentially with a bias input set equal-ish to the single-ended value.<br />
<br />
-<u>Make a glove; fill it with joint sensors</u>. Obviously, I eventually need to actually make a glove to sew sensors into, and then connect to the device.</div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-84585241219922680812013-02-23T00:29:00.001-06:002013-02-23T00:29:13.856-06:00Migration to sleep mask v2 (or, the device now lacerates the user's face in only 12% of use cases)<div dir="ltr" style="text-align: left;" trbidi="on">
So, I finally got the hardware for the second version of the sleep mask to work. Turns out, if your firmware waits for the real-time clock to stabilize the external crystal oscillator, it helps to have one attached.<br />
<br />
The device front and back are pictured below; it is running on its own power, and all of the main subsystems are working.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQKwHOnN7unGTrXmQsI4Qc6ri_olKtE3ir7Fky5DarZjdKwTuYj3ofRbAafFobsmQksEcDEdvZZkdNF4CzEtpwfTOi3MZn7wcsYADXMZBwaeUnwfeuRIUSXETdvl6ZZ3VpIwm50XyL7CU/s1600/sleep_mask_back.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="162" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQKwHOnN7unGTrXmQsI4Qc6ri_olKtE3ir7Fky5DarZjdKwTuYj3ofRbAafFobsmQksEcDEdvZZkdNF4CzEtpwfTOi3MZn7wcsYADXMZBwaeUnwfeuRIUSXETdvl6ZZ3VpIwm50XyL7CU/s400/sleep_mask_back.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1yS8T4i97pq13cmuIblYWcZDj1rbF7e8F4uIr_edmteF2g1WVqWvtoieVAiKWqDKYNebTVjS8Yek5j8b3546qSJF3kfBWnhLtoGJaUG3CIJmHZCRpYAnwBQGG_u2nyfu7J-pwanV2Bqg/s1600/sleep_mask_front.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1yS8T4i97pq13cmuIblYWcZDj1rbF7e8F4uIr_edmteF2g1WVqWvtoieVAiKWqDKYNebTVjS8Yek5j8b3546qSJF3kfBWnhLtoGJaUG3CIJmHZCRpYAnwBQGG_u2nyfu7J-pwanV2Bqg/s400/sleep_mask_front.JPG" width="400" /></a></div>
<br />
Of course, I forgot to add a line from the battery to the ADC to allow for charge status to be monitored; that's one definite change for V3.<br />
<br />
Moving forward, I need to:<br />
<br />
-Take a few nights' worth of data, to make sure that the REM detector still works<br />
-Develop a filter/classifier to determine whether REM was happening based on the detector output.<br />
-Assess power usage again, and implement the lowpass in front of the headphone amplifier to see if that reduces power use.<br />
-See what's going on with the headphone detection circuit; I cribbed this from an application note, and it was implied that it would just give a digital output; this has not been the case.<br />
<br />
After the classifier is determined, I'll start to design the overall structure of the working firmware; it'll have to take into account the needs of the REM classifier and the headphone noise generator while also keeping track of current time, alarms and current and past REM states.<br />
<br />
<br />
<br /></div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com1tag:blogger.com,1999:blog-1933957739656816486.post-70674438175193100902013-02-22T16:40:00.001-06:002013-02-22T16:40:46.329-06:00First steps for unnecessarily complicated paperweight (or, inconsequential blinking-light projects evolved)<div dir="ltr" style="text-align: left;" trbidi="on">
For a long time, I've wanted to make a project that implemented <a href="http://en.wikipedia.org/wiki/Conway's_Game_of_Life">Conway's Game of Life</a> in electronics, as a kind of creating-for-its-own-sake thing. The Game of Life (GoL) is a program that computes the 'evolution' of a population of cells on a square, 2-D plane. It has some interesting computational properties, but I was mostly interested in it because, when observed over time, the cells have a kind of ordered chaos aesthetic.<br />
<br />
My original yen (more than a decade ago) was to just build a HUGE matrix of LEDs and use them to display the ongoing evolution via GoL of a random starting seed state. However, this is A) expensive, in both space and currency; and B) not novel any more. There have been a number of physical GoL implementations; in fact, you can <a href="http://www.adafruit.com/category/24">buy a kit for a four-by-four matrix </a>which can be tiled with other kits to make an arbitrarily large GoL implementation.<br />
<br />
Playing around with the idea, I recently decided to make something out of the GoL with a triangular tessellation, rather then the usual square grid. Fortunately, there was some literature on this subject (Bays, Carter. Cellular Automata in the Triangular Tesselation. Complex Systems 8 (1994) 127-150), so I didn't have to reinvent the wheel; additionally, his work specified which GoL rules would lead to interesting behavior. He also discovered several glider patterns, so that I could incorporate them into non-random, pre-specified seed states if I so wanted.<br />
<br />
I made a quick-and-dirty triangular tessellation display, as seen in the video below; I hooked it up to an ATXMEGA32A4U with a firmware that would start with a pattern, then allow it to evolve over time until there are no cells left, at which point it would reset to the original test state.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/uy3gUC7MpK8?feature=player_embedded' frameborder='0'></iframe></div>
<br />
My goal was to create independent 5-by-5 triangular units which could be used as the faces of an icosahedron. However, this ended up requiring a LOT of microcontrollers and a LOT of by-hand jumper soldering, owing to difficulties in routing one tiny board with a big microcontroller on one side. I've since settled on a design which uses one microcontroller per four faces; this made the routing easier (in only have to solder the edges of the triangles together, with on jumpers) and significantly reduced the overall project cost, since I would only need five microcontrollers instead of 20.<br />
<br />
At present, I have the set of boards for the matched four triangular faces designed, and the bill of materials for the faces set. I just need to double-check those boards, design the power supply circuitry for the center of the icosahedron, add a board for V2 of the turn signal gloves, finish my doctoral thesis, sit for the USPTO registration examination and start my new job. i expect to resume work on this project sometime after Labor Day. 2014.<br />
<br /></div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-87990504745230991472012-12-16T00:03:00.001-06:002012-12-16T00:03:36.992-06:00Replacing the middle button on the Logitech M570 Wireless Trackball (or, yes, I really do love this trackball that much)<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
I've been using a wireless trackball for a while now, and it's turned out to be one of the best purchases I've ever made. It's especially excellent for working on the train, since i don't like the trackpad and there isn't room for a normal mouse.<br />
<br />
The wireless trackball I'm using is the Logitech M570. It has a combination scroll wheel/middle button; however, over time this button began to get flaky. There would be weeks where the button wouldn't respond at all, or would only respond to exceptional force. Looking online, this seems to be a common problem; the device is somewhat cheaply made, and the middle button is not the usual high-performance switch, but some lower-quality part.<br />
<br />
I set out to determine whether the button could be replaced; I was successful, and my procedure for replacing it is outlined below. The new button is slightly stiffer than the old, but it works consistently.<br />
<br />
<span style="font-size: large;"><b><u>Button Replacement Procedure</u></b></span><br />
<br />
<span style="font-size: large;">Disassembly</span><br />
<br />
First, pop the blue trackball out. Then, there are five screws holding the shell together; remove them. Note that one of these screws is beneath the sticker in the battery compartment (shown below).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8cwKo5LcIBnrbsBml6FA57pEi9VFLvdOl1FrClwOJIrcb1MXaZfNzvkKUiaYxGPoX2v9DQZ-4LNrZUhkYvTWiGEj6qsrWeyJazI35mowcDuGIdVk9YXImvraWtrjvl0GT688XyvzDd4U/s1600/remove_screws.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="259" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8cwKo5LcIBnrbsBml6FA57pEi9VFLvdOl1FrClwOJIrcb1MXaZfNzvkKUiaYxGPoX2v9DQZ-4LNrZUhkYvTWiGEj6qsrWeyJazI35mowcDuGIdVk9YXImvraWtrjvl0GT688XyvzDd4U/s320/remove_screws.JPG" width="320" /></a></div>
<br />
With the shell off, you'll need to remove the circuit boards. First, detach the ribbon cable leading to the trackball reader (the gold bit in the middle of the image below); to do this, you'll have to pull up the plastic locking connector. Then, remove the four screws which keep the circuit boards on the lower shell, and pick up the circuit board assembly. Take care not to bend the battery wires as you remove them from the lower shell. The middle/scroll wheel button we're going to replace is just in front of the ribbon cable.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlcDuclE40GfH9zZvyTIBuMi-ZbKAY6SZI14juzPJumrILaTIXjFMEpAYNDtXkoQu9TfvOXGaHUlDLn12HS-eAXAY2gamD6yD_0d_R1i8FiP4YCxHprrHfgED8skm0cisGrTstMGmwYqA/s1600/shell_off.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlcDuclE40GfH9zZvyTIBuMi-ZbKAY6SZI14juzPJumrILaTIXjFMEpAYNDtXkoQu9TfvOXGaHUlDLn12HS-eAXAY2gamD6yD_0d_R1i8FiP4YCxHprrHfgED8skm0cisGrTstMGmwYqA/s320/shell_off.JPG" width="320" /></a></div>
<br />
<br />
<span style="font-size: large;">Old Button Removal</span><br />
<br />
In removing the old button, we need to be careful not to damage the rest of the circuitry. Note that our task is made easier by the fact that we don't care what state the old button ends up in. The method I used to avoid damage to the surrounding circuit is shown below; first, I used a diagonal cutter to cut the two forward leads on the button. Once this was done, I gently rocked the button up and down to weaken and then break the remaining two leads of the button and remove it (I did not cut these leads, as there were difficult to access without risking damage to the rest of the circuit).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhQM8CZ4pXO-q3Qa-USW8-AD6ws_F5P4BN01_QR8GGJOa8Nwz84uDlOcrlIiYVJCzt7M9yDqY44r7agX9CV6766JZaAXWTde2W0Wq8g_G0JkCcS6jV3mUWqfdxm_lPiAOAWy1d81mCP40/s1600/cut_old_button_leads.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhQM8CZ4pXO-q3Qa-USW8-AD6ws_F5P4BN01_QR8GGJOa8Nwz84uDlOcrlIiYVJCzt7M9yDqY44r7agX9CV6766JZaAXWTde2W0Wq8g_G0JkCcS6jV3mUWqfdxm_lPiAOAWy1d81mCP40/s320/cut_old_button_leads.JPG" width="296" /></a></div>
<br />
Once the button is mostly removed, I used copper solder wick to clean up the four holes that the leads of the button formerly inhabited.<br />
<br />
<span style="font-size: large;">New Button modification and installation</span><br />
<br />
If you were paying close attention during the old button removal, you'll notice that the leads were pretty much vertical. The buttons I had on hand were very similar in size and function (normally open, leads paired length-wise); however, their leads were gull-wing SMD style (<a href="http://www.mouser.com/Search/ProductDetail.aspx?R=3-1437565-0virtualkey50660000virtualkey506-3-1437565-0">MOUSER link</a>). Before I can install it, I need to manipulate the leads into a more vertical configuration, detailed in pictures below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1xfZL1_f4phV2PXymciLJEji5XwQExSa9sDNbq25FxC_HDyq47D692F69bDVkHypZFGsy8zT0PguEstSAKQFLr324fGVxhw3SE0s0Ps-BKUGgddHRV6nq29fx9YrBAGkz1IEJAw6qg-M/s1600/button_closeup_unaltered.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="276" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1xfZL1_f4phV2PXymciLJEji5XwQExSa9sDNbq25FxC_HDyq47D692F69bDVkHypZFGsy8zT0PguEstSAKQFLr324fGVxhw3SE0s0Ps-BKUGgddHRV6nq29fx9YrBAGkz1IEJAw6qg-M/s320/button_closeup_unaltered.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
First, push the leads down.</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1docQJly3tIxW84z1exSGlmQKHv9aTzJDHKRwSSWtW7omfNjNZ4JUPqQmKkBfk6KBnIm9gVdjkRiU6BYv4303bzmqduh2NyBP6g8JrmaFxTo4pjUTjZWDP7PSUZnKYwT_e2QGGxRz0T4/s1600/bed_leads.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="314" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1docQJly3tIxW84z1exSGlmQKHv9aTzJDHKRwSSWtW7omfNjNZ4JUPqQmKkBfk6KBnIm9gVdjkRiU6BYv4303bzmqduh2NyBP6g8JrmaFxTo4pjUTjZWDP7PSUZnKYwT_e2QGGxRz0T4/s320/bed_leads.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Then, use pliers to straighten the leads downward.</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS5NQYo0MWuxCZHZ4giYjnplkQuqeKc_JPw9u3HBWJwLxll_do8zuNwWULP1K-BEvWK3AGNllq9VHeBEa-oeFwH2ZqIWvT2Tu4RaVmzdRQMFdb5juGUsXwsMxVrjJfh6S-2YPqDvMHOGQ/s1600/straighten_leads.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS5NQYo0MWuxCZHZ4giYjnplkQuqeKc_JPw9u3HBWJwLxll_do8zuNwWULP1K-BEvWK3AGNllq9VHeBEa-oeFwH2ZqIWvT2Tu4RaVmzdRQMFdb5juGUsXwsMxVrjJfh6S-2YPqDvMHOGQ/s320/straighten_leads.JPG" width="227" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Then, gently insert the button into the cleared holes and solder it into place; be sure that it is flush with the surface of the circuit board.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiwe8ePAhqjmMbsZPYVNbyI3R146UgTpSchMsTf3sAmYKhAxpYFxdNrL18sCco17JWYZnmLjG_NmRcfxKFg6cJ4CijM30C1fOOP7hCzga2ob4y9a4dPkWvzLiajOC6aYysEoCsscbnnhA/s1600/end_solder_new.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiwe8ePAhqjmMbsZPYVNbyI3R146UgTpSchMsTf3sAmYKhAxpYFxdNrL18sCco17JWYZnmLjG_NmRcfxKFg6cJ4CijM30C1fOOP7hCzga2ob4y9a4dPkWvzLiajOC6aYysEoCsscbnnhA/s320/end_solder_new.JPG" width="320" /></a></div>
<br />
<br />
<br />
<span style="font-size: large;">Re-Assembly</span><br />
<br />
Just reverse the steps in Disassembly. It is important to make sure that the little plastic power button in the lower shell is lined up with the switch on the circuit board before screwing everything back together (the switch is the silver-and-black affair on the lower right of the picture above, just in front of where the battery clips attach to the circuit board).<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
It is interesting to note that Logitech uses <a href="http://en.wikipedia.org/wiki/Nordic_Semiconductor">Nordic Semiconductor</a> radios for their wireless links (at least for the new-ish unified receiver).<br />
</div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com14tag:blogger.com,1999:blog-1933957739656816486.post-88613117608959398252012-12-11T11:00:00.001-06:002012-12-11T11:16:49.378-06:00My initial investigation of the MARLOK key (or, how many times do I have to mess up using a pipelined ADC before I'll learn my lesson permanently?)<div dir="ltr" style="text-align: left;" trbidi="on">
As I mentioned in the previous post, my university uses a somewhat rare access control technology called 'MARLOK': each user is issued a key, whose identity is encoded in a series of holes in the shank of the key (kind of like an old punch-card). The key is inserted into a reader next to a door, and if the user has privileges to the door, it is momentarily unlocked.<br />
<br />
I was interested in reading the key (and possibly eventually building something to emulate the key, in a reader), so I had a couple of little boards made to mount infrared emitters and detectors at the appropriate distances to read the three tracks of the key.<br />
<br />
<span style="font-size: large;"><b>Initial Research</b></span><br />
<br />
Before doing all this, I tried to look up the MARLOK system on the internet. Details were sparse; the only information of any substance is <a href="http://www.lockpicking101.com/viewtopic.php?f=8&t=46972">here</a>, where someone reports that each key encodes 24 bits of ID. Otherwise, there's very little info out there; nothing on the structure of the encoding, how the clock is embedded/recovered, etc.<br />
<br />
<b><span style="font-size: large;">The Current Test</span></b><br />
<br />
<span style="font-family: inherit;">The sensor (with key inserted) is shown below. The emitters are Kignbright <span style="background-color: white;">APT2012F3C</span>; they are wired in parallel with a 460Ohm current limiting resistor, leading to a total of 5mA passing through them collectively. The detectors are Everlight <span style="background-color: white;">PT19-21C/L41/TR8</span> phototransistors; they are connected to the positive rail, and then through 2.2kOhm resistors to ground. The voltage is sampled at the top of the resistors by a microcontroller, then sent out at 100Hz through the serial port.</span><br />
<span style="font-family: inherit;"><br /></span>
The key has three tracks; the black plastic is infrared-transparent. The metal of the key is punched with square holes to allow for IR light to travel through the key.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_ldUuZZ4fbrWacPvmEbYuicZywrHgzX2KVvY25x9j0cuURFTxahR7zM13ZetYcs-lXxJznUHzXiT1WDDHVxa9LNmHfTUY6LKZcxkRzW1AzVCg2InyWzFMol5KxDTerXGCzag6bqRAyig/s1600/MARLOK_sensor.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="307" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_ldUuZZ4fbrWacPvmEbYuicZywrHgzX2KVvY25x9j0cuURFTxahR7zM13ZetYcs-lXxJznUHzXiT1WDDHVxa9LNmHfTUY6LKZcxkRzW1AzVCg2InyWzFMol5KxDTerXGCzag6bqRAyig/s320/MARLOK_sensor.JPG" width="320" /></a></div>
<br />
The ends of two of the tracks were totally accidentally exposed, to show the structure of the holes (shown below). As you can see, the holes are square. From the data I show later, the middle track is a clock signal; this makes sense, as the middle hole is offset from the exposed side track by a half-hole-width, so that transition on the clock track signals when to sample the data tracks.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHuFlAwgrXZJ2pv_IRUygSD0VQrvan0NXQsUapsoccyG7yj6Ol0g_4OWhyphenhyphenHvC2ECkkUGNgodN6HtrbHZ_grIdjRdp14F3W6LX6qo5w6BWopK-f3m9tUPYOChV-1ULdDjiHTzswYRUw-W4/s1600/MARLOK_key_visible_holes.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="155" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHuFlAwgrXZJ2pv_IRUygSD0VQrvan0NXQsUapsoccyG7yj6Ol0g_4OWhyphenhyphenHvC2ECkkUGNgodN6HtrbHZ_grIdjRdp14F3W6LX6qo5w6BWopK-f3m9tUPYOChV-1ULdDjiHTzswYRUw-W4/s320/MARLOK_key_visible_holes.JPG" width="320" /></a></div>
<br />
Below is a plot of the three track traces; the left is when the sensor was seated at the base of the key, and the right occurred as I slid the sensor off the key. Green is the middle track, red and blue are the side tracks. Note that it took a little messing around to get this data; originally, the LED current was three times what it was for these trials, and it saturated the clock channel (since, being in the middle, it got illumination from all three LEDs).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhICVgrAhBfTcwS8hMVfssaRCz5rK94P1k4DbighczyndgLG2sv7cRwGFiInepB6CZbAk3QMP9Ka9TbGn3klpAolZ98TF7LcUD3KoSXKDifNYo6rAER_n1Zc3-8UdreVajfcFCOb_8reVE/s1600/keyRemovingBegin.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhICVgrAhBfTcwS8hMVfssaRCz5rK94P1k4DbighczyndgLG2sv7cRwGFiInepB6CZbAk3QMP9Ka9TbGn3klpAolZ98TF7LcUD3KoSXKDifNYo6rAER_n1Zc3-8UdreVajfcFCOb_8reVE/s320/keyRemovingBegin.png" width="320" /></a></div>
<br />
This next is the same information, but at the end of the sensor removal, as the sensor comes of the end of the key.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTU9NTZiY3ffMf1-ipFa7ep17BnEhsvDqiew-c5vLHfIgMUHJsql7-I-2ccwee8N1SEpk-X5uM5QfMBFrU96yJjEXKsYuKJhL0AUkeMiyS-Cjljn_cxby-G2jTQdATL2EgqmhDf8EFYhg/s1600/keyRemovingEnd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTU9NTZiY3ffMf1-ipFa7ep17BnEhsvDqiew-c5vLHfIgMUHJsql7-I-2ccwee8N1SEpk-X5uM5QfMBFrU96yJjEXKsYuKJhL0AUkeMiyS-Cjljn_cxby-G2jTQdATL2EgqmhDf8EFYhg/s320/keyRemovingEnd.png" width="320" /></a></div>
<br />
From the data, I saw that the middle track was the only one that was always on; from this (and the fact that its holes were offset from the other tracks' holes), I assume it is the clock signal.<br />
<br />
Looking at the traces over a full sensor-removal, I saw the following patterns of holes:<br />
<br />
01111110011101<br />
11111111111111<span class="Apple-tab-span" style="white-space: pre;"> </span>END OF KEY>><br />
00001001110111<br />
As i said above, the middle track was the only one that was always 'on'. Additionally, since the first clock has both 'on', and the last has both 'off', and the internet says these keys encode 24 bits, and there are 14 total clock pulses, I assume that the end-of-key data track bits are always 'on', and the base-of-key data track bits are always 'off', which likely helps in level determination and data recovery.<br />
<br />
<span style="font-size: large;"><b>Moving Forward</b></span><br />
<br />
As evidenced in the data traces, I'm having difficulty keeping the key straight in the sensor; I'll need to do something to mechanically narrow the passage. Once this is done, it'll be possible to recover sampling times from the clock transitions. It might also be possible to improve the signal level by masking the LEDs so that they don't bleed over into the other channels as much.<br />
<br />
<span style="font-size: large;"><b>Data Recording Software</b></span><br />
<br />
To get this data, I created a microcontroller firmware that samples the three channels, then packs each 12bit sample into a message packet (which includes a start sentinel nibble for frame alignment) and sends it over the serial port. I then created a general-purpose serial port data-extractor and -viewer for MATLAB, which is available <a href="https://docs.google.com/open?id=0B9psp2BpNroQcjRFbEI5clhqN28">here</a>.</div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-4659404520927285262012-12-06T20:20:00.000-06:002012-12-06T20:20:14.005-06:00Final bugs and V2 (or, yes, I actually labeled the USB pins in reverse order. Stop laughing)<div dir="ltr" style="text-align: left;" trbidi="on">
I've finally figured out what was wrong with the sleep mask charger and USB circuits: the pins on the connector were reversed. So, hilariously, every time I plugged it in, i was connecting V+ and GND backwards, leading to some considerable heating in the charger IC. Remarkably, the whole thing worked flawlessly once I flipped the connector.<br />
<br />
Since I worked that out, I've designed version 2 of the sleep mask and helmet boards. Additionally, I designed boards for a few other projects, including an instrumented glove, and MARLOK key reader and a pulse oximeter. I've begun to populate the boards, but I'm out of some things, so I'm waiting for another Mouser order. Unfortunately, the component names ended up on the silkscreen, making it thoroughly messy.<br />
<br />
<span style="font-size: large;"><b>Sleep Mask V2</b></span><br />
<br />
After figuring out what I'd done wrong with the USB connector (as well as playing around with the REM detector), I moved forward with redesigning the sleep mask controller board. Most importantly, I significantly shrunk the board (the old version was very... clunky). I used smaller versions of the microcontroller, FET, headphone jack and opamp. Additionally, I added the option for a lowpass between the microcontroller DACs and the headphone amplifier, in the hopes that I can reduce power consumption.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLLcTzCpKt-bp-_QcufkkX1KvlODsEt2I3Q7F_A8waW7D0PaZQt94S2wCQ0I3iRs2QmPPXQm1SFenNrK9wi7P98sGQ1_XpzHnz0ms1lr6G4NsNrI2xIg43U323aoqNY10KN80unUavF_E/s1600/sleep_mask_reverse.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="172" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLLcTzCpKt-bp-_QcufkkX1KvlODsEt2I3Q7F_A8waW7D0PaZQt94S2wCQ0I3iRs2QmPPXQm1SFenNrK9wi7P98sGQ1_XpzHnz0ms1lr6G4NsNrI2xIg43U323aoqNY10KN80unUavF_E/s320/sleep_mask_reverse.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
I also wired up a new mask (the old one was... clunky. Also gaudy... very gaudy).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw1KbvZCbq9KlBuD2OYuOxk-TJt2z1AKdP_g_pKUPpH-fRbto75xXMSczhVN_O9cOo4FZkMIOzwQTIL8bQgbXsVB79ojpjvWQy7-UZxl8WwnoSzsDK8MadVIQYp2EO6K6-yRxMExz947k/s1600/100_4836.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="243" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw1KbvZCbq9KlBuD2OYuOxk-TJt2z1AKdP_g_pKUPpH-fRbto75xXMSczhVN_O9cOo4FZkMIOzwQTIL8bQgbXsVB79ojpjvWQy7-UZxl8WwnoSzsDK8MadVIQYp2EO6K6-yRxMExz947k/s640/100_4836.JPG" width="640" /></a></div>
<br />
<span style="font-size: large;"><b>Helmet V2</b></span><br />
<br />
The new version of the helmet implemented the analog-power-off changes, in addition to adding a charger circuit. However, it ends up being about the same size of the old board, owing to the use of smaller FET, opamp and microcontroller.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdhhfctCUFL9O9TArO3Fa5K1yFHsh9t0E5u4mTNAxe7VLYpGe7rkoGWT_dIg5oNnBx3NsSqfDFx19tRsSKwCkprYCDclWRZ9yEG56KzKHTp7Rel1jghWqLdQewFQfIsOQLLATs3ybFXz8/s1600/helmet_board.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdhhfctCUFL9O9TArO3Fa5K1yFHsh9t0E5u4mTNAxe7VLYpGe7rkoGWT_dIg5oNnBx3NsSqfDFx19tRsSKwCkprYCDclWRZ9yEG56KzKHTp7Rel1jghWqLdQewFQfIsOQLLATs3ybFXz8/s320/helmet_board.JPG" width="320" /></a></div>
<br />
<br />
<span style="font-size: large;"><b>Instrumented Glove</b></span><br />
<br />
I've long been interested in the idea of a joint-angle-sensing glove for computer interface. Additionally, I overheard someone around the lab implying that they would like to take such joint data over a whole day of normal hand-use. So, I thought that I could use some of the spare board-space on a circuit which would read at least 22 channels of joint angle data, and record them to an SD card. The flex sensors will be home-made, and will change their resistance with flexion (like in <a href="http://hackaday.com/2009/07/25/custom-flex-sensors/">this article</a>).<br />
<br />
I decided to go for a controlled-current topology for resistor-sensing (rather than a divider) to get a linear read on the resistance. The resistor sensors are connected to the header at the top of the board; they connect to V+ on one side, and to the constant-current sink on the other side, through a matrix of FET switches; 5 banks of 5 sensors each give me 25 potential sensor channels for a mere 10 digital outputs. This circuit also includes a battery charger.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYMgxQ48WUSa63cj9dkq6KnId9ojRtEMoEsc2Xt6L7mr07dBN-UDz28vtGc-muU11e9-W6BtJsJA7euFZiEmG3G-lD3bU2dxYOoB0eCIDreiHB74RtG9vsQO6C6Gh2flq9EMRPxl6nxbY/s1600/glove_board.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYMgxQ48WUSa63cj9dkq6KnId9ojRtEMoEsc2Xt6L7mr07dBN-UDz28vtGc-muU11e9-W6BtJsJA7euFZiEmG3G-lD3bU2dxYOoB0eCIDreiHB74RtG9vsQO6C6Gh2flq9EMRPxl6nxbY/s320/glove_board.JPG" width="320" /></a></div>
<br />
<br />
<b><span style="font-size: large;">MARLOK key reader</span></b><br />
<br />
I've long hated the MARLOK key; it seems like a perfect storm of high-cost and low-convenience and I don't understand why you would use it instead of an iButton system or RFID. In any event, I have a MARLOK key and I was curious about how the information (and clock) were encoded in it. There isn't much information online about the format; all I was able to find was that the information is encoded in the sequence of holes drilled in the shaft of the key.<br />
<br />
I designed a pair of board upon which I could mount IR emitters and IR phototransistors to read all three tracks of the key; I added a few spare holes to weld the two sides together using wire scraps.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_DfzcC6W9_Y_m_CXY3WzTlLzBwlV-arEz2l0egKMCoGWwAyC-bwsoemAJXVfDaL1BQTahXQ9JH7Q_KKpr2-Rxor8hvhsTEJcMg072GV0BsYPIPSPSHErPGzZ7atbBJhqnjVZ7A2a6HhU/s1600/MARLOK_sensor.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="307" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_DfzcC6W9_Y_m_CXY3WzTlLzBwlV-arEz2l0egKMCoGWwAyC-bwsoemAJXVfDaL1BQTahXQ9JH7Q_KKpr2-Rxor8hvhsTEJcMg072GV0BsYPIPSPSHErPGzZ7atbBJhqnjVZ7A2a6HhU/s320/MARLOK_sensor.JPG" width="320" /></a></div>
<br />
<b><span style="font-size: large;">Pulse Oximeter Sensor</span></b><br />
<br />
Finally, I wanted to try out reading pulse (and possibly blood oxygenation) non-invasively. I created a sensor board based on "A wireless reflectance pulse oximeter with digital baseline control for unfiltered photoplethysmograms" by Kejia Li and Steve Warren. The sensor is reflectance-mode, which means that you only need access to the surface of the body; in opposition to the transmission mode, where the emitter is opposite a protruding bit of anatomy (like a finger) from the detector. The emitters and detectors are in the upcoming order, so it's just the board and cable for now.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgegEU8FYaTW3TICqjKexcbWtuIGYvIdTB_tE0KjfGsH0UQrPnx8ozkxgIzhMOJIdQAknQd4UL0vlfUz4V94PjCT5u6eaUPRUPg2XaZQcTxhk7V2api_74P0AUI1XXzWGi-UZ1uDNhoBc/s1600/pulseox_sensor.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="129" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgegEU8FYaTW3TICqjKexcbWtuIGYvIdTB_tE0KjfGsH0UQrPnx8ozkxgIzhMOJIdQAknQd4UL0vlfUz4V94PjCT5u6eaUPRUPg2XaZQcTxhk7V2api_74P0AUI1XXzWGi-UZ1uDNhoBc/s320/pulseox_sensor.JPG" width="320" /></a></div>
<br /></div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-91815717062800394162012-10-25T22:47:00.002-05:002012-10-26T11:30:19.840-05:00REM detector + display updates (or, scrolling displays are almost as cool as fezzes)<div dir="ltr" style="text-align: left;" trbidi="on">
As I remarked in a recent post, I've nailed down the cause of my pesky REM detector noise issue. Additionally, I started sprucing up the display subsystem, using a DMA channel to significantly reduce the processor load needed. I've taken another couple of steps forward, in that I've implemented a fix to the REM noise issue, and I've refined the display subsystem so that it can easily produce text and primitive graphics; these refinements are shown immediately below.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi46X1cdl3cvY5kNFQrUlrV7IpiiUp7y8x15GUQf-4J55O__zkMkVl6Oq5H-bFk8JgeNFMugTeH3L_CzuYsQDXMHxv4KZO2qElCzJhaTImgECRobaPqmImRH_nITdnhorM2a3hWdroqrzA/s1600/display_scrollplustime.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi46X1cdl3cvY5kNFQrUlrV7IpiiUp7y8x15GUQf-4J55O__zkMkVl6Oq5H-bFk8JgeNFMugTeH3L_CzuYsQDXMHxv4KZO2qElCzJhaTImgECRobaPqmImRH_nITdnhorM2a3hWdroqrzA/s400/display_scrollplustime.JPG" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The bottom 3/4 of the display is the REM differential signal level, scrolling from left to right. The numbers on top are the 32-bit integer holding the device's seconds counter (left) and the IR illuimnator level (right).</td></tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjs_BakXoitOjPrll343yaUZmqh4ymjmZ1QDjmTUN5dYBQuofiMJ8DhSJYCorv0JZV2Uw9nII-1O6JU45G60vXn5AJjS9neBCC1XDBSNOlKheeH9k8JuHS8_qSSG91nI5SU_whpBQKbEys/s1600/100_4801.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="287" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjs_BakXoitOjPrll343yaUZmqh4ymjmZ1QDjmTUN5dYBQuofiMJ8DhSJYCorv0JZV2Uw9nII-1O6JU45G60vXn5AJjS9neBCC1XDBSNOlKheeH9k8JuHS8_qSSG91nI5SU_whpBQKbEys/s400/100_4801.JPG" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Not very interested waveform-wise, but a clearer picture.</td></tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/WybgcId6rBk?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
The video shows the scrolling display in action; since the sensor is pointed at the couch arm, there's not much going on, except when I rotate it.<br />
<br />
<b><span style="font-size: large;">Implementation details, vaguely</span></b><br />
<br />
On the display side, this is really only a slight complication of what I outlined previously; there's a local buffer that I draw on, and periodically I set the DMA channel to transmit the contents of that buffer to the display via the serial transmitter peripheral. There's a little bit of trickery translating the signed ADC inputs into the bytes that get painted into the display buffer, but it's overall quite straightforward. A zipped archive of the project files is <a href="https://docs.google.com/open?id=0B9psp2BpNroQRk5oYjFYNGRQTGc">here</a>.<br />
<br />
On the REM detector side, I'd determined that the reason I was getting 'bias noise' every four samples was that the PWM switching transients were making it into the ADC signal, and because the PWM and REM counters were not integer multiples of each other, they would only 'line up' every four samples. To rectify this scenario, I modified the code to make sure that the sample timer is always set up to be an integer multiple of the PWM timer period. I also set up the clocking of the timers to go through the XMEGA's event system, so that I could keep the timers in sync. A plot of the signal below shows no abnormal patterns in the noise, so I think this has taken care of the problem.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKjJJpBCpPpzN3sH9KLxmfPvBUm7PpoX96XbBjhw26P3Q4_1CRhSA3J6RR6zm6UqMavdNmRUGdc4SqO9ZGNOE9JipyG28sHWl2EiGUbxllEoVfgWEuIk67jRPfMk2ry1-BuyybSKnTKlI/s1600/post_sync_events.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKjJJpBCpPpzN3sH9KLxmfPvBUm7PpoX96XbBjhw26P3Q4_1CRhSA3J6RR6zm6UqMavdNmRUGdc4SqO9ZGNOE9JipyG28sHWl2EiGUbxllEoVfgWEuIk67jRPfMk2ry1-BuyybSKnTKlI/s400/post_sync_events.png" width="400" /></a></div>
<br />
<br />
<b><span style="font-size: large;">Moving Forward again</span></b><br />
<br />
So, my next step has to be nailing down the REM detector. Specifically, I'm going to slightly modify the hardware so that I can quarter the PWM duty cycle. This way, I can maximize the settling time between the PWM switching time and the ADC sample time. After this is done, I'm going to assess whether this has had the effect of decreasing the noise; either way, I'm going to collect some real-life sleep data, and get cracking on whether I can assemble a filter/classifier that can detect REM.<br />
<br />
Of course, this completely avoids the issue of the busted charger circuit; unfortunately, I still haven't secured access to a hot-air rework station...<br />
<br /></div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-50927829592142095492012-10-24T19:58:00.002-05:002012-11-28T15:52:25.423-06:00Using DMA to automatically transfer display data (or, it's RTFM, not 'Haphazardly Cast Your Eyes Left to Right Over The Manual While Thinking of Something Else')<div dir="ltr" style="text-align: left;" trbidi="on">
As an aside from the process of perfecting the REM detector, I decided to start to clean up the display-painting parts of the firmware.<br />
<div>
<br /></div>
<div>
It takes half a kilobyte of data to completely fill the display (128-by-32 off-or-on pixels) and the data is sent through one of the chip's serial peripherals. Since there is so much data to transmit per display update, it takes a LOT of processor cycles to do so; it takes even more cycles to do so by waiting and polling the serial peripheral instead of setting up an interrupt (as I did to debug the display).</div>
<div>
<br /></div>
<div>
Since the process of getting that data out to the display could be fairly straightforward, it seemed like the perfect excuse to take advantage of the AVR XMEGA microcontroller's onboard DMA feature. And since I made a couple of errors along the way (and couldn't find a straightforward explanation of my problem online), it seemed appropriate to describe the situation here.</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">What is DMA?</span></div>
<div>
<br /></div>
<div>
Briefly, <a href="http://en.wikipedia.org/wiki/Direct_memory_access">DMA (direct memory access)</a> is a way of taking the load off of the processor by 'automating' data transfers. Specifically, the DMA controller transfers a block of data from one location in memory to another while the processor executes other tasks. Without a DMA controller, any large data transfer would take up processor time as the processor accessed and copied each byte of data individually.</div>
<div>
<br /></div>
<div>
There are many situations where you would want to transfer a lot of data from one location to another, or a small amount of data from (or to) one location a great many times. In my scenario, I have a buffer in local memory that represents what the display will look like; the buffer is local so that manipulating it (compositing the image, blanking regions, adding text) is easier. However, that buffer data needs to be periodically sent (serially) to the display hardware. Ideally, the DMA will automate the process of taking each byte of the buffer in turn and sending it to the serial peripheral when it's ready to accept the bytes.</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Implementation on the XMEGA</span></div>
<div>
<br /></div>
<div>
The XMEGA A microcontroller that I'm using has four independent DMA channels. Each of them has a lot of configuration, including the ability to specify the source and destination data addresses and the ability to set what triggers the data transfers.</div>
<div>
<br /></div>
<div>
My problem was that I didn't read the manual closely enough; I thought that setting the channel trigger to the serial peripheral meant that every time the send register was empty (the trigger source), that a single byte would be sent. In the default mode, however, the trigger causes the DMA to transfer an entire block of data as quickly as possible; since the serial peripheral sends out bytes a LOT slower than the rate that the DMA controller pushes bytes, this meant that each transfer only resulted in a few randomly-selected bytes of the buffer actually being sent to the display. This caused me some consternation until I re-read the manual and realized my error; this fast operation is useful when copying data into SRAM or other fast destinations, but completely inappropriate for slow, single-byte destinations like the USART peripheral.</div>
<div>
<br /></div>
<div>
For slower destinations which will need to signal the transmission of each byte (or each burst of 2, 4, or 8 bytes) one at a time, the 'Single-Shot Data transfer' mode is used. This mode completes a single burst, instead of a whole block, with each DMA channel trigger activation.</div>
<div>
<br /></div>
<div>
Since I want to transfer the complete contents of the display buffer to a single address on the serial peripheral, I need the destination address to be fixed and the source address to increment during the transmission, and reset at the end for the next update.</div>
<div>
<br /></div>
<div>
The actual C code I used to initialize the DMA controller and channel on the ATXMEGA128A4U is shown below; <span style="font-family: Courier New, Courier, monospace;">USARTC1</span> is the serial peripheral I'm using as my transmitter (it's been set up in master SPI mode for my display module, and then used to initialize the display module) and <span style="font-family: Courier New, Courier, monospace;">debugBuffer</span> is the 512-byte-long stretch of internal memory that I'm using as my display buffer.</div>
<div>
<span style="color: #6aa84f;"><br /></span></div>
<div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;">//set up a DMA channel</span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;">//enable the DMA controller</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CTRL = DMA_ENABLE_bm;</span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;">//set the burst length to 1 byte</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_CTRLA = ( DMA_CH_SINGLE_bm | DMA_CH_BURSTLEN_1BYTE_gc );</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span style="color: #6aa84f;">//set the following: source address incremented, reload after each block; </span>destination address fixed (reload after each block)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_ADDRCTRL = ( DMA_CH_SRCRELOAD_TRANSACTION_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTRELOAD_TRANSACTION_gc | DMA_CH_DESTDIR_FIXED_gc );</span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;">//now set the DMA trigger source to the USART data register being empty</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_TRIGSRC = DMA_CH_TRIGSRC_USARTC1_DRE_gc;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><span style="color: #6aa84f;">//load the block transfer count register with the number of bytes in our blocks</span> <span style="color: #38761d;">(that is, 128*4 = 512)</span></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_TRFCNT = 512;</span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;">//now put in the initial source address; should be the memory address of the first byte of the display buffer</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_SRCADDR0 = ( (uint16_t) debugBuffer >> 0 ) & 0xFF;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_SRCADDR1 = ( (uint16_t) debugBuffer >> 8 ) & 0xFF;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_SRCADDR2 = 0x00;</span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;">//now specify the destination address; the transmit register of the USARTC1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_DESTADDR0 = (( (uint16_t) &USARTC1_DATA ) >> 0) & 0xFF;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_DESTADDR1 = (( (uint16_t) &USARTC1_DATA ) >> 8) & 0xFF;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_DESTADDR2 = 0x00;</span></div>
</div>
<div>
<br /></div>
<div>
Once the DMA channel is set up (and assuming the USART and display have been initialized), I can update the display with the current contents of the buffer by simply enabling the DMA channel:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">DMA_CH0_CTRLA |= 0b10000000;</span></div>
<div>
<br /></div>
</div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com1tag:blogger.com,1999:blog-1933957739656816486.post-26648334578163276672012-10-11T19:06:00.000-05:002012-10-12T14:52:35.306-05:00Project Updates (or, I am probably not dead)<div dir="ltr" style="text-align: left;" trbidi="on">
So, it recently came to my attention that I haven't posted in several months. This is due, in large part, to my actually getting work done on my PhD. This is also due to the fact that the next steps are relatively... un-glamorous and un-postable. Specifically, I have a small issue with the REM detector, and I need to figure out what's wrong with the battery charger circuit. On the up side, I was able to significantly reduce the standby power consumption of my helmet flasher so... little victories.<br />
<br />
<b><span style="font-size: large;">Charger Debugging</span></b><br />
<br />
As it stands, the Lithium-Polymer battery charging circuit does not work. This is ironic, as it was the only sub-circuit that I did not mock up and test before ordering the circuit boards (I even mocked up the 3.3V buck converter, which only had three components). I did this because, well... I was just using the reference implementation. I've gone over the design (and the physical artifact's correspondence to it) with a fine-toothed comb; at this point, I'd like to swap the chip to see if that's the issue. Unfortunately, I do not own a hot-air rework station, so that is easier said than done. Additionally, the chip gets REALLY hot and sources a bunch of current when you plug it in, which complicates debugging, as I can only leave it plugged in for short periods. Further, even if I could simply replace the chip, I'd be leery of doing it, as the new chip could easily fry as well. This is triply annoying, as I would like to add the charger to my helmet and run signal glove in their next iteration, but can't do that until I know I've got the circuit right.<br />
<br />
<b><span style="font-size: large;">REM Detector Hiccups</span></b><br />
<br />
The specific problem with the detector is that, for certain levels of illumination, the the detector output follows a 'negative bias' for every fourth sample; this is illustrated below. Looking at the 'noise' and mean of the signal after separating it into four down-sampled signals (that is, the first sub-signal is every four samples of the original, starting with the first sample of the whole record; the second is every four starting with the second, etc.), it appears that this really is just a constant 'bias' term added only to every fourth sample.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-9M1wuSmRJ9JSY86DpHPduVs8AbLI51JpbqCSR6X4nafV76l-Z4XlCrPUql7pSuYHQ2oWIqqyIlzfvj-dheov3EWdtp95JWalX4CNTJ0N78gpxN0Ln_SvQtOnbwx_rrOHIaSkYFlIHS8/s1600/tester_handOnOff.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-9M1wuSmRJ9JSY86DpHPduVs8AbLI51JpbqCSR6X4nafV76l-Z4XlCrPUql7pSuYHQ2oWIqqyIlzfvj-dheov3EWdtp95JWalX4CNTJ0N78gpxN0Ln_SvQtOnbwx_rrOHIaSkYFlIHS8/s640/tester_handOnOff.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">This is an example of the detector output against my hand; green is the differential signal, dark blue is the single-ended signal, and light blue/cyan is the illuminator amplitude. As you can see, the signal appears to get much more 'noisy' as the illumination level increases, until it abruptly decreases at a certain level.</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2_GjI3lJNQJaP7AOHHWox2kO6O_GqY4gw0zvwmO_o0QW1apeCXQpi4x-Ya6ljOFuwng8hEsMa3oTj09AASEbTIaW_OlyL__tZH8inT1rq5TJ8iiK7S5OvCulhSOnJoF_7J-6m_PvC18s/s1600/tester_handOnOff_zoomEveryFour.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2_GjI3lJNQJaP7AOHHWox2kO6O_GqY4gw0zvwmO_o0QW1apeCXQpi4x-Ya6ljOFuwng8hEsMa3oTj09AASEbTIaW_OlyL__tZH8inT1rq5TJ8iiK7S5OvCulhSOnJoF_7J-6m_PvC18s/s640/tester_handOnOff_zoomEveryFour.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Zoomed in view of the 'noisy' segment from above; we see that the extra 'noise' is due to every fourth sample being much lower than the other three. This pattern is also borne out in the single-ended signal.</td></tr>
</tbody></table>
My current hypothesis is that this is due to my use of low-passed PWM outputs to give myself some extra low-bandwidth analog outputs to control the REM detector illumination level and the differential signal bias level. Specifically, since the sample-specific noise only occurs at certain levels of illumination, and then suddenly stops once the level raises above that level, I am lead to believe that it's something to do with the PWM switching time lining up with the ADC sampling time.<br />
<br />
If that's the case, I'll have to decrease the passing of switching transients by reducing the corner frequency of the low-pass and/or increasing the carrier frequency of the PWM. Of course, increasing the carrier frequency will reduce the resolution of the LP-PWM channels; however, since I don't use the full resolution anyway, it wouldn't be much of a sacrifice.<br />
<br />
<b><span style="font-size: large;">Helmet flash controller standby power reduction</span></b><br />
<br />
Going back to the helmet flasher project, I noticed that the AA batteries were getting drained in a manner that was less-than-consistent with usage. I recalled that I had been less than diligent in regards to standby power usage, so I figured I could shave off a few milliamps by taking a closer look.<br />
<br />
There were two main methods I figured I could use to reduce standby power use: put the controller into a deeper sleep state while in standby, and modify the hardware so that I could remove power from the op-amps used int he constant-current LED drivers.<br />
<br />
The first approach, going into a deeper sleep state, was the first I implemented since it didn't involve modifying the hardware. Before making my changes, I inserted a 1-Ohm 1% precision resistor to measure the standby current draw. I set the device to go into "power down" when going into the standby state, with wake-up accomplished by state change on any of the buttons. Additionally, I set the device to go into "standby" while the timers count and wait to move on to the next flasher state. The source is available <a href="https://docs.google.com/open?id=0B9psp2BpNroQSGpsQ0swZV9fYUk">here</a>, in case it is useful to someone wanting to see how a non-human primate would implement the preceding.<br />
<br />
After implementing the sleep state changes, the standby power consumption went from 2.1mA to 1.7mA. Good, but we're not finished. Since the on-state power consumption is in the range of 100-200mA, it was impossible to detect if my changes had an effect on on-state power consumption.<br />
<br />
The next step was to switch off the power to the linear constant-current LED driver block. The op-amps used in that block draw current even when the LEDs are off, so it made sense to try to switch the power off when the device was in standby. Since the analog block was currently fed directly from the power rail, it was necessary to cut that source first.<br />
<br />
Since the op-amps only draw a few milliamps (far less than the 30mA the controller pins are rated for), it made sense to feed them from one of the controllers output pins; this way, I didn't need to add any additional FETs. After doing this, and making the appropriate changes in the firmware, the standby power consumption fell from 1.7mA to less than 0.1mA.<br />
<br />
Overall, these changes reduced standby power consumption from 2.1 to 0.1mA; a more than 95% reduction. This makes me much more comfortable with leaving the batteries in when I'm not using it.<br />
<br />
<b><span style="font-size: large;">Next Steps</span></b><br />
<br />
So, the most immediate next steps involve fixing the REM detector problem outlined above and making the charger circuit work. After that, though, there are a couple of immediate next steps:<br />
<br />
<b>Finally finalize the sleep mask hardware</b><br />
<br />
The current version of the sleep mask hardware is... clunky. Pointy. Eye-pokey, even. Since it was a first prototype, I didn't put all the effort in the world into optimizing the layout, or the parts (I just used the components I had on hand, instead of sourcing the absolute smallest I could find). Once I'm confident that the hardware is up to snuff, I can source the absolute smallest components (especially the controller and FETs/op-amps and the passives) and re-design the board. To do that, though, I need to rectify the problems listed above (REM four-sample noise, charger broken). Additionally, I'll need to take a few nights' worth of data and specify a classifier/filter that can detect 'REM' to my satisfaction; if the hardware needs revision to get to that point, I'd rather do so before the next hardware revision.<br />
<br />
On a somewhat unrelated point, I'm going to hack up the headphone amplifier hardware to see if I can't make it more efficient. As it stands, the white noise component consumes the lion's share of the power for the device. Looking at the output of the headphone amp, the 'square edges' of the DAC output are preserved to the output; this might be causing greater power consumption than necessary. If I introduce a high-pass before the headphone amp and shave those square edges off, I might be able to significantly reduce my power consumption.<br />
<br />
<b>Update the helmet flasher and glove turn signal hardware; make a decision about the helmet</b><br />
<br />
As I've said above, I'd like to make the turn signal glove and helmet flasher run on rechargeable lithium-polymer batteries. To do that, I need to make sure that the charger chip and circuit work as advertised.<br />
<br />
Additionally, I need to make some decisions about the helmet flasher. As it stands, it steps the battery voltage up to 16V to drive the LEDs in series. This was done partially since I originally had intended to add some EL wire to the helmet; EL wire requires ~150V AC, and my intent was to switch current through some step-up transformers. The transformers could be smaller/have a lower turns ratio if my switched DC voltage was larger.<br />
<br />
This step-up is expensive (3$ for the controller alone, to say nothing of the related caps and inductor). However, the series wiring of the LEDs allows the brightness/current of the LEDs to be more consistent. Maintaining the step-up would also mean that I wouldn't have to re-wire my existing helmet.<br />
<br />
<b>Design the sleep mask PCB version 2</b><br />
<br />
As I said above, once the sleep mask hardware is finalized, I'll source smaller components, and then redesign to the circuit board. I figure it will be installed above the nose in the mask, with leads going down to the REM detector and red LEDs. I'll also incorporate power control of the analog block, as I did for the helmet above, to reduce standby power consumption.<br />
<br />
<b>Order new boards + components; assemble + test</b><br />
<br />
Just what it says: pick the new smaller components, order them and the boards, then build everything.<br />
<br />
<b>Software development of the sleep mask</b><br />
<br />
At this point, the hardware for the sleep mask should be more-or-less finalized. All that will be left is putting together the firmware.<br />
<br />
I haven't given a lot of thought to the design of the interface or the overall design of the firmware. However, I have given some thought to potential features I can try out, including:<br />
<ul style="text-align: left;">
<li>REM-relative wake-up alarm: only wakes you up if you are at the tail end of an REM cycle (or you've reached some no-later-then-this time). I haven't checked the science behind this, but I've heard that you wake up more refreshed if you wake up at the end of a cycle, rather than during the deep sleep in the middle.</li>
<li>Sleep induction using entrainment: again, I haven't looked into how rigorous the science is behind this is, but some advocate the use of binaural beats or isochronous pulses to induce lower-frequency EEG states, assisting the user away from consciousness.</li>
<li>External cues for lucid dreaming induction: the original purpose.</li>
<li>Slowly increasing LED illumination to ease wake-up: just what I said, improve wake-up by gradually increasing illumination within the mask along with the natural dawn.</li>
<li>REM logging: save the timestamps of REM periods, allow them to be transferred over USB. Share on facebook?</li>
<li>USB bootloader: I know that Atmel provides one, I just need to see where it's hosted and play with it.</li>
</ul>
<br />
<br />
<br />
<br /></div>
benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-40051267266055619282012-07-29T19:40:00.000-05:002012-07-29T19:44:55.917-05:00Bicycle helmet complete (or, the afore-promised inconsequential blinking-light projects)<div dir="ltr" style="text-align: left;" trbidi="on">
I have finally completed the bicycle helmet flasher project (that I added as an afterthought to the REM sleep mask PCB). The design and fabrication of the controller module was outlined in a <a href="http://rellinger.blogspot.com/2012/06/project-updates-or-trading-few.html">previous post</a><span style="background-color: white;">; here I show the results of finally installing the lights into my existing helmet.</span><br />
<span style="background-color: white;"><br /></span><br />
<span style="background-color: white;">Additionally, the source has been slightly modified (available </span><a href="https://docs.google.com/open?id=0B9psp2BpNroQYmxJaTh2UzdQLVk">here</a>)<span style="background-color: white;">; the device now has three buttons, one each for brightness of the forward and rear lights and the third setting the mode (off, both flashing, rear flashing with forward steady on).</span><br />
<span style="background-color: white;"><br /></span><br />
<span style="background-color: white;">The outside of the helmet is shown below; first from ahead, then from behind. The LEDs are mounted within the hollows of the helmet, using folded-over staples as an initial anchor, then doused with two-part epoxy.</span><br />
<span style="background-color: white;"><br /></span><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGIdZEZQj_lFJMHb3xVUXe5ficuhMBZ1ORCMvQaHs57LGCLrKwCF97siKMIeDyuHOK__yrGPg0lmtkQQJLInAdl_6URaBcCCJdmTqm22osQopIgn1zygrySf86uyzrzf2rBD4FkuJvFig/s1600/outside_front.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="270" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGIdZEZQj_lFJMHb3xVUXe5ficuhMBZ1ORCMvQaHs57LGCLrKwCF97siKMIeDyuHOK__yrGPg0lmtkQQJLInAdl_6URaBcCCJdmTqm22osQopIgn1zygrySf86uyzrzf2rBD4FkuJvFig/s400/outside_front.JPG" width="400" /></a>
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLmj-KZWBhzI43ZHrgX-DQN52xzC-rQdY8TrehTvkh8-nfOB7mcLSw2XtwBD8qiz3b-4QCR5FknoNS9IE-u27goYfWRqIuVDZrOphYp60p-GCRYVq14W24EsW111NoF_OqPiWLJDXSh7s/s1600/outside_back.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="291" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLmj-KZWBhzI43ZHrgX-DQN52xzC-rQdY8TrehTvkh8-nfOB7mcLSw2XtwBD8qiz3b-4QCR5FknoNS9IE-u27goYfWRqIuVDZrOphYp60p-GCRYVq14W24EsW111NoF_OqPiWLJDXSh7s/s400/outside_back.JPG" width="400" /></a>
<br />
<br />
The helmet in action is shown below:<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/Yb-xcXQIUZQ?feature=player_embedded' frameborder='0'></iframe></div>
<br />
The helmet is shown from the inside below; the LEDs are wired in series. The leads of the LEDs go through tough plastic strips which are anchored to the helmet with bent-up office staples; everything is further stabilized with a healthy helping of two-part epoxy.<br />
<div style="text-align: right;">
<br />
<div style="text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-I_xXzr_jP2nTgOJfca1s0EdO2yJ8ol9ENlM9kG-GnVtvb01mCt8SSZOtRHprH2PmLfCbEOuN6_xC8YIIYcWtPzChLmGCSZI3wYRyl6tHEURS0pNB2in_cek_LJo2w1n-25v_rnxs4y0/s1600/inside_front.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-I_xXzr_jP2nTgOJfca1s0EdO2yJ8ol9ENlM9kG-GnVtvb01mCt8SSZOtRHprH2PmLfCbEOuN6_xC8YIIYcWtPzChLmGCSZI3wYRyl6tHEURS0pNB2in_cek_LJo2w1n-25v_rnxs4y0/s400/inside_front.JPG" width="400" /></a>
</div>
</div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqy_M65lgHhjiahFTtdMigsglSFPM0jnLbSxtlSfVQ8s5bJox0bXz0eMnYUSxppqC2iXZMvefAvmeql9mAqvMu5z2lxknS77fBPoIz1hK9UFKHbOc2FcPdAMcCvcIi1BcdOHLFbzkCCa8/s1600/inside_back.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqy_M65lgHhjiahFTtdMigsglSFPM0jnLbSxtlSfVQ8s5bJox0bXz0eMnYUSxppqC2iXZMvefAvmeql9mAqvMu5z2lxknS77fBPoIz1hK9UFKHbOc2FcPdAMcCvcIi1BcdOHLFbzkCCa8/s400/inside_back.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
The interface consists of three buttons adhered to the side of the helmet (visible in the video above). Everything on the helmet should be water-proof (though the controller module and battery pack are not currently water-proofed, as they are very much prototypes).<br />
<br />
Moving forward, I would want to optimize/shrink the controller module, and incorporate a lithium-polymer battery pack with USB charging; hopefully, this would allow the whole battery/controller module to fit within the hollows of the helmet (rather than having a tethered AA battery pack hanging off of the back, as it is currently)</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com1tag:blogger.com,1999:blog-1933957739656816486.post-64444887086239328502012-06-23T19:07:00.001-05:002012-06-23T19:07:58.486-05:00Sleep mask prototype assembly and initial testing (or, now that the hardware's built, it's only 90% of the project left to do)<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: justify;">
<span style="background-color: white;">The next step in the development of the Sleep Mask was fabricating the 'final' prototype, and doing some initial testing to make certain that there were no shorts.</span></div>
<div>
<br /></div>
<div style="text-align: justify;">
The front and back of the assembled board (with battery and display attached) are shown below. The smallest surface-mount parts (including the charger and headphone amplifier ICs) were placed on syringe-applied solder paste and 'baked' into place using a cheap electric skillet; the remainder of the parts were applied manually, using a conventional iron (the reason for this roundabout assembly method is detailed in a previous post; long story short, I bought the wrong paste).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Being extra-cautious, I checked each of the solder joints on the tight-pitch ICs before adding the rest of the components. Being extra-paranoid, I introduced cuts into the power traces, so that I could monitor current usage as I re-connected different subsystems (this is evident in the backside image).</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5-gyD-W25Drp6nDnEKu3MhmPJQ-u9C_1e30_1TlKSF3u9Ls5za0-FETUfyc3OT81I2ffvThEZ45b8NKTVL0LDabZDfZwb5cUlrMmUiSWNCLWo5lz4S8K_6geTjeSGqZFb_xVFsh1uTXU/s1600/100_4185.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5-gyD-W25Drp6nDnEKu3MhmPJQ-u9C_1e30_1TlKSF3u9Ls5za0-FETUfyc3OT81I2ffvThEZ45b8NKTVL0LDabZDfZwb5cUlrMmUiSWNCLWo5lz4S8K_6geTjeSGqZFb_xVFsh1uTXU/s320/100_4185.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Front of assembled board; display not yet mechanically fixed</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTZ8KBa7O9x15F7kcFxK61yoR7YjriXUriEclHCZqeoJlQD6FjgkubNZIGh_6O9RHgOL7deajLdxN1hyphenhyphenenbkdqO5Sp7UGpkKHjoivEXMxm3Rj6FXRxAKvA6abRmxi6iwTjSpTyvv4WABQ/s1600/100_4183.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTZ8KBa7O9x15F7kcFxK61yoR7YjriXUriEclHCZqeoJlQD6FjgkubNZIGh_6O9RHgOL7deajLdxN1hyphenhyphenenbkdqO5Sp7UGpkKHjoivEXMxm3Rj6FXRxAKvA6abRmxi6iwTjSpTyvv4WABQ/s320/100_4183.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Back of assembled board; display not fixed; connector to mask lights/sensor at bottom</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
With everything reconnected and no programming loaded into the controller, the device drew 4.6mA; with phones plugged in (and, again, no programming and thus no signal being output), the device drew 44.5mA. Even with the tiny battery I have connected now (450mAh), this is low enough to allow for a full night's use on a charge; of course, this doesn't take into account the power used by the IR REM sensor illuminator, the display or the extra power which may be expended to generate actual sounds with the headphones. However, power use appears to be dominated by the audio system, so I am not too concerned right now.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
I connected the assembled board to the mask (shown twice below); additionally, the display is scotch-taped to the board to secure it mechanically (but reversibly so).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlBnlbgq9zf_oH4F4gkTa_yBdIMo4tRyIBwxC9bgPMRj9F5vY2gCnrtqYYDWti_bwFxEyfBQSDURHKPCqA2QPE_X00bhsI0Zyj7shtQTOeduhBTsdwVcMJklw4J6eMrbqwHCGLUEvIknQ/s1600/100_4187.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlBnlbgq9zf_oH4F4gkTa_yBdIMo4tRyIBwxC9bgPMRj9F5vY2gCnrtqYYDWti_bwFxEyfBQSDURHKPCqA2QPE_X00bhsI0Zyj7shtQTOeduhBTsdwVcMJklw4J6eMrbqwHCGLUEvIknQ/s320/100_4187.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0lc3gOWDyj5VbPGtCJ9D2B0flbqViX-uHyMW3IyJ1mvBpdT4nJDA6YGCVUwTKlVUqPRGqGis004A0HaYfV2g9yJDcC5-k7ayegQmtixuzwcfPV-GbR3M5v21LG9SI5os-mX7yPFgTRVo/s1600/100_4186.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0lc3gOWDyj5VbPGtCJ9D2B0flbqViX-uHyMW3IyJ1mvBpdT4nJDA6YGCVUwTKlVUqPRGqGis004A0HaYfV2g9yJDcC5-k7ayegQmtixuzwcfPV-GbR3M5v21LG9SI5os-mX7yPFgTRVo/s320/100_4186.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The only things left for the hardware are to fix the board and battery to the mask and to install the red LEDs in the eyecups (these will be used to flash at the user during alarm conditions). I'll also need to install a header to allow for repeated programming. All that's left for the software... is everything.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Or course, this is the roughest sort of prototype, meant to prove the concept and develop the basic REM detection algorithms and the framework of the eventual overall program architecture. In addition to about a million changes to the overall mechanics of the mask (formed neoprene base? injection-molded face for the buttons/display), the main board itself would undergo a lot of beneficial changes, mostly to decrease its size. As I've commented before, the components I've used are ones I have a stock of locally; as such, they are rated for far more current/voltage/power/dissipation than needed for the current application. Additionally, the controller is the easy-to-hand-solder TQFP, rather than the absolute smallest package available. As a result, I suspect that a future version of the device, with all the same functionality but reduced part size, could be as small as one quarter of the area of this version.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<br /></div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com1tag:blogger.com,1999:blog-1933957739656816486.post-39969192741243752052012-06-10T00:57:00.002-05:002012-06-10T01:01:06.232-05:00Project updates (or, trading a few milligrams of epidermis for a few milligrams of reflowed solder)<div dir="ltr" style="text-align: left;" trbidi="on">
Due to travel (and actual, legitimate research), I've not been able to progress on these projects much in the last few weeks. Additionally, getting the boards from <a href="http://www.seeedstudio.com/depot/">Seeed</a> took a little while (though it was worth it, 10 boards for 15$ is nothing to sneeze at; they're shown immediately below).<br />
<br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOHRCgeuTVpHxqwKc9aDur-EEa-6B1VjWcZXOK4e5cRhDjhST3mZWA47UO12XIFmLrffRzVsL8U6e0GDKCQR2xPpCju81h-ogB6_NsuhFFctpqKhH-miI61wihxLDA2Aohyag2fIQS_Sk/s1600/raw_boards.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOHRCgeuTVpHxqwKc9aDur-EEa-6B1VjWcZXOK4e5cRhDjhST3mZWA47UO12XIFmLrffRzVsL8U6e0GDKCQR2xPpCju81h-ogB6_NsuhFFctpqKhH-miI61wihxLDA2Aohyag2fIQS_Sk/s400/raw_boards.jpg" width="400" /></a></div>
<br />
<div>
Today, I got back into things by trying out a little hot skillet reflow. Going off of resources at <a href="http://www.sparkfun.com/tutorials/59">SparkFun</a> and <a href="http://www.instructables.com/id/Extreme-Surface-Mount-Soldering/step8/Preparing-for-Soldering/">this instructable</a>, it seemed the cheapest method available to me. To apply the paste, I didn't have the time, money or patience to do solder paste stencilling (shown in the previous links); so, I applied the paste manually, as at<a href="http://store.curiousinventor.com/guides/Surface_Mount_Soldering/Solder_Paste_and_Toaster_Oven"> this site</a>. Unfortunately, I didn't realize that the paste formulations are different between stencil and syringe application; I loaded some stencil paste from Sparkfun (<a href="http://www.sparkfun.com/products/10448">here</a>) into a syringe and it was very tough to get it to come out.</div>
</div>
<div>
<br /></div>
<div>
One thing to be aware of with the stencil-type solder paste: it behaves a lot more like wet sand than any sort of easily-coaxed gel. Syringe-type paste might behave a little better/differently.</div>
<div>
<br /></div>
<div>
In any event, I was able to reflow the majority of the components on the (hastily thrown together) helmet flasher board, shown below (apologies for the poor picture quality). I have seen heating-element control boards for toaster ovens and skillets to get the perfect heat profile; in my case, cranking the thing up to max temp and waiting for the solder to turn shiny sufficed. Note the blue wire fix; I forgot to connect the enable line for the step-up to a free pin on the controller.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNSVWMdiPSxtBS6rBWKkHo2jQ47MqQeg__hfVDG8R0ETi-bVjT-E2UQ_wkDFgVrd7ASkmEh6dJeNlekyW94Oy719Wj7MdcQAGwQL3r81yD3zfkplpeLTndnKRDcBDhgkCZ7R-gVQKO76M/s1600/helmet_populated.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="258" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNSVWMdiPSxtBS6rBWKkHo2jQ47MqQeg__hfVDG8R0ETi-bVjT-E2UQ_wkDFgVrd7ASkmEh6dJeNlekyW94Oy719Wj7MdcQAGwQL3r81yD3zfkplpeLTndnKRDcBDhgkCZ7R-gVQKO76M/s400/helmet_populated.jpg" width="400" /></a>
<br />
<br />
Debugging revealed only two small errors in the reflow; two of the pins on the stepup controller were bridged (easily separated) and one of the resistors in the current controller didn't reflow (also easily rectified). The step-up produces 'high voltage' (16.5V), the pots have all been manually set (one to set the high voltage level, the other two to set the maximum constant-current levels) and the controller talks to my programmer.<br />
<br />
The next steps for this quick project are A) create a simple program for this thing, and B) assemble the in-helmet parts of the project (lights, switches and 2xAA battery pack installed, wiring routed). There's also the more pie-in-the-sky goal of implementing the EL drivers (but I haven't quite sourced the transformers yet; not enough of my CFL bulbs have gone out yet).<br />
<br />
Of course, just because the project has barely started doesn't mean I'm not already thinking about version 2; specifically, I'd want to implement the following changes:<br />
A: source smaller components, with specs sized more appropriately for this project.<br />
B: add a LiPolymer battery and charger circuit to allow the controller module to be more monolithic and allow it to be charged over micro USB.<br />
C: figure out a better connector solution between the helmet and the controller; the 0.1" headers I'm using were chosen for inventory convenience.</div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-31871385863178477242012-05-12T14:44:00.002-05:002012-05-12T14:44:39.390-05:00Implementation of a supervised discretization algorithm (or, I'm almost certain that I had a reason for this when I started)<div dir="ltr" style="text-align: left;" trbidi="on">
Oftentimes, you have a set of data and want to use it to make a prediction about the future (e.g., predicting tomorrow's weather, based on today's weather) or about an unobserved system (e.g., predicting the presence or absence of metal ores underground, based on the local magnetic field).<br />
<br />
In order to make that prediction, we need to set up some sort of mathematical framework describing the possible relationships between the data and the prediction. If we have first-principles knowledge of the system under question, we can make a model of the system and use the available data to set any free parameters the model has. However, we often have no idea about the system between the data and the prediction. In these cases, we need to propose a sufficiently complicated and unbiased model so that, after setting the model's parameters according to the observed prediction/data relationships, the model accurately reflects the unknown system between the data and the variables to be predicted.<br />
<br />
There is an enormous literature describing many different structures for generic predictors. However, many of these rely on the input data being discretely-valued; that is, instead of taking on any value in a continuous range (like a distance, 12m, 5.43m, 1m, 0.333205m), they can only take on a discrete number of values (like 'number of fingers' is an integer between 0 and 12, inclusive). In order to leverage these discrete-input predictors, it is necessary to 'discretize' continuous inputs; that is, to assign ranges of values to a single class (e.g., temperatures 0 to 10 degrees are now '0', 10 to 25 are now '1', 25 to 40 are now '2', etc.).<br />
<br />
There is a smaller literature describing and analyzing methods for chosing how to perform this discretization. In the future, I might put together a post reviewing these methods (at least, from a layman's perspective, as I am not a member of the machine learning research community). Here, I will share my implementation, in MATLAB, of the method created/communicated by Marc Boulle in 2006; this was the method I found the most compelling after performing a review of the discretization literature.<br />
<br />
<b><u><span style="font-size: large;">The implementation</span></u></b><br />
<br />
The algorithm is described (and derived, and analyzed, and experimentally compared with other methods) in "<a href="http://sci2s.ugr.es/keel/pdf/algorithm/articulo/2006-Boulle-ML.pdf">Boulle, Marc (2006). MODL: A Bayes optimal discretization method for continuous attributes. <i>Machine Learning</i>, 65:131-165.</a>" The algorithm consists of a criterion which allows one to compare different potential partitionings of the data and a method for attempting to find the best partitioning, based on this criterion.<br />
<br />
My implementation uses two 'linked lists' (in quotation marks, because they are implemented as the MATLAB generic array data type, instead of a distinct linked-list data type). The first list contains the data on the partition intervals in the data; this information includes the total number of instances of the sample data in the interval, the number of instances of the sample data from each output class in the data, and the identity of the first an last member of the data set in each interval. The second list points to adjacent pairs of the intervals in the first list, and is sorted according to how much the criterion would be improved by the merger of the pointed-to intervals. The algorithm proceeds by merging the 'best' pair of adjacent intervals, then updating the lists to reflect that merger. The algorithm is called 'greedy', as it always chooses the most immediately obvious 'best' merger, even though that may not lead to a universally optimum solution.<br />
<br />
My implementation of this algorithm (including some support functions) is included in <a href="https://docs.google.com/open?id=0B9psp2BpNroQT3l6RHczdUlQTEU">this archive</a>.<br />
<br />
<b><u><span style="font-size: large;">A quick test</span></u></b><br />
<br />
Here's some example data I generated, along with the optimal discretization returned by the algorithm (as implemented in MATLAB). The 1000 sample data points were drawn from equal-variance gaussian distributions whose means were different and determined by their output class value. The 1000 sample data points are plotted below; the y-axis is an individual data point's continuous value, and the point's color corresponds to its output class value. The horizontal lines show the edges of the intervals determined by the algorithm; as you can see, the classes (colors) are well-segregated by the edges. Intuition suggests that there would be four edges (separating the five output classes); in the case below, there is an additional edge separating the contentious transition region between the dark blue and cyan classes.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmkKIPGNf8qDurrTRp6h4SHRPKEgtZj_zF3fmNDhnv2MKxG_yf54bVIT12JeZIP6dTO5m4KoXkGZhyphenhyphenfAH5pblXwWdCHfFj6o0iPSWDuDlJqVI2MDa7gUDJe1qzvbYBTO3Is1sFuHNqCQI/s1600/discrtization_5classes_gaussian_MODL.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmkKIPGNf8qDurrTRp6h4SHRPKEgtZj_zF3fmNDhnv2MKxG_yf54bVIT12JeZIP6dTO5m4KoXkGZhyphenhyphenfAH5pblXwWdCHfFj6o0iPSWDuDlJqVI2MDa7gUDJe1qzvbYBTO3Is1sFuHNqCQI/s640/discrtization_5classes_gaussian_MODL.png" width="640" /></a></div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-68783715041068157102012-05-06T16:54:00.001-05:002012-05-09T10:15:33.703-05:00Project updates (or, why is it that the least interesting parts of a project make up most of the effort?)<div dir="ltr" style="text-align: left;" trbidi="on">
In the last few weeks (since I tested out the OLED display), I've been getting all of the last little details in place to move forward with the sleep mask project. Specifically, since the circuits have been finalized, I have been laying out the printed circuit board and making certain that I have all of the necessary components on hand (and putting together an order for those I do not).<br />
<br />
The cheapest service I could find is <a href="http://www.seeedstudio.com/depot/fusion-pcb-service-p-835.html">Seeed Studio's Fusion PCB service</a>. For a mere 10$ you get 10 5x5cm boards; an extra 15$ gets you 5x10cm.<br />
<br />
After completing the board layout (shown below), I found that it was more than 5x5cm. It is larger than I had hoped, but still reasonable relative to the size of the sleep mask. A large part of its... largeness... is due to the fact that I was designing based on the parts I already had in my inventory. Those parts, in turn, were chosen to be usable across many projects; as a result, they are usually rated for much higher voltages, currents, and dissipated energies than are strictly necessary for this project. This is okay for a prototype, but any future hardware revisions will involve specifying more appropriately-sized resistors and capacitors.<br />
<br />
Since I was going to have to pay for an extra 5cm of board, I decided to make the best of it and add a circuit for a project I've had on the back burner for a while. Specifically, I want to build some flashing lights into my bicycle helmet for safety; some of the lights will be ordinary LEDs, but eventually I want to build some EL wire into the helmet to give a real Vegas feel. To do this, I need 'high voltage' (about 20V) to step up to 120V using transformers. While I am still collecting the transformers (I take them out of burned-out CFL lightbulbs, as transformers or even bare magnetics of appropriate size have proven difficult to source), I am going to move forward with getting this board, including the 20V step-up and LED constant-current drivers, layed out and ordered. It is also shown in the image below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2PEdaT9hMcSRX72iMlVEJxyQKmFe7oD5hBYeQTNBh3NT0bgIDRwsiavBotUCQolxI1DwYZVOf8QrtC1Vd_aFy3oxjseFuaM_WiYu1ihzAZdaON3qtRdk8APlMFmkvflQW70cwLalqoIQ/s1600/board.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2PEdaT9hMcSRX72iMlVEJxyQKmFe7oD5hBYeQTNBh3NT0bgIDRwsiavBotUCQolxI1DwYZVOf8QrtC1Vd_aFy3oxjseFuaM_WiYu1ihzAZdaON3qtRdk8APlMFmkvflQW70cwLalqoIQ/s400/board.png" width="400" /></a></div>
<br />
The REM sleep mask board is on the left; the microcontroller is in the middle, with the micro USB connector above, battery charger above right, buck converter right, REM detector bottom left, headphone amplifier left and OLED display top left. The helmet flasher/boost board is on the right; boost top left, EL wire switches bottom left, microcontroller bottom right and LED drivers top right.</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-76367165618180573282012-04-21T21:23:00.001-05:002012-05-06T16:57:54.951-05:00SPI OLED A-OK (or, I would like to apologize to my readers for the preceding title)<div dir="ltr" style="text-align: left;" trbidi="on">
I've reached another milestone in the development of my sleep mask: the OLED module works.<br />
<br />
This section of the project was relatively straightforward: basically nothing more complicated than establishing a serial connection to the device and starting it up appropriately.<br />
<br />
<b><u><span style="font-size: large;">The Hardware</span></u></b><br />
<br />
This is identical to my original design, which was, in turn, copied from the <a href="https://docs.google.com/open?id=0B9psp2BpNroQcml6TnhLbFMxNTQ">module datasheet</a>. This is the same hardware as is sold by <a href="http://www.adafruit.com/products/661">Adafruit Industries</a>; I acquired mine from another source, without the breadboard-friendly PCB attached. It has an on-board capacitor charge pump to provide the high-voltage (~7.5V) necessary to drive the OLED pixels. The serial interface is identical to the Serial Peripheral Interface (SPI) with a Command/Data select line and a Reset line in addition to the usual Chip Select line.<br />
<br />
<b><u><span style="font-size: large;">The Software</span></u></b><br />
<br />
The stripped-down testing firmware I used is posted<a href="https://docs.google.com/open?id=0B9psp2BpNroQbkJ3SVVObTIycnM"> here</a>. It's not much to look at; it just starts up the SPI on-chip peripheral and sends the necessary command bytes (while manipulating the control lines appropriately) to start up and activate the display module. It then starts sending out data bytes to change what is shown on the display. The commands sent are outlined in the module controller's data sheet (the <a href="https://docs.google.com/open?id=0B9psp2BpNroQWXYwdUsyVmtGaE0">Solomon Systech SSD1306</a>).<br />
<br />
The controller continuously updates the pixels in the display by reading from an internal display memory. When data is written to the device, it is used to update the contents of this display memory.<br />
<br />
The folks at Adafruit have also implemented <a href="https://github.com/adafruit/Adafruit_SSD1306">some software</a> to drive this display module. My software is largely the same, with one noticeable difference. The controller contains twice as much display memory as needed for the module (to make it capable of driving larger displays); when writing to this memory over the serial link, the memory is all written over in turn before restarting at the beginning. The Adafruit software just writes zeros onto that second half of the driver memory; however, there is a command which allows you to set the limits of the memory to be written. By setting this command to only write over the usable half of the memory, my software doesn't need to write the entire memory every time, only the half that is actually visible. In this way, I don't need to devote as much of my computational resources to updating the display.<br />
<br />
<b><u><span style="font-size: large;">The Goods</span></u></b><br />
<br />
The test hardware setup is shown below. As usual, I used my oh-so-refined soldering and fabrication skills to gain access to the tiny pads on the end of the display's ribbon connector. The connections are relatively simple; a few decoupling caps between power pins to ground, some pass-throughs for the serial link, and the two capacitors for the charge pump.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw9u75XqeGeArG8X1pqt2CAmeOm0R3hAdQlirBeOTWjRpME1X0l34izryf6ED7XZTEDBwl27ucumpd-yJc8PoJcIEY1B3r6StlMAvotsRMWUh_RiNJAratk2SfsxVm_I2jC2RE1wrYyaY/s1600/display_still.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw9u75XqeGeArG8X1pqt2CAmeOm0R3hAdQlirBeOTWjRpME1X0l34izryf6ED7XZTEDBwl27ucumpd-yJc8PoJcIEY1B3r6StlMAvotsRMWUh_RiNJAratk2SfsxVm_I2jC2RE1wrYyaY/s400/display_still.JPG" width="400" /></a></div>
<br />
To prove that I actually got this to work, here is a short video of the device (and ATXMEGA controller) as power is applied; first the display is told to turn all the pixels on, then it displays from the display memory (which is, initially, full of noise; this is in contrast with the data sheet, which states that the RAM should be blanked after a reset cycle). Then, the controller starts sending alternating frames of display data.<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/A1MC_YzO3q4/0.jpg" height="266" width="320"><param name="movie" value="http://www.youtube.com/v/A1MC_YzO3q4?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" />
<param name="bgcolor" value="#FFFFFF" />
<embed width="320" height="266" src="http://www.youtube.com/v/A1MC_YzO3q4?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash"></embed></object></div>
<br />
<br />
The firmware source containing the specification of the alphabet/symbols is <a href="https://docs.google.com/open?id=0B9psp2BpNroQUDVXby1jVlkyMVE">here</a>.</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com1tag:blogger.com,1999:blog-1933957739656816486.post-19791910870450724602012-04-15T19:19:00.003-05:002012-04-18T15:04:48.068-05:00REM detection hardware, firmware and software tests (or, I honestly had some doubts that this would work so well)<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: justify;">
On the REM-detecting, white-noise-generating, potato-julienne-ing project front, I have finalized the hardware (and toyed with the firmware) for the REM detector subsystem.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
To recap this project: I am developing a sleep mask which will be able to detect the REM (rapid eye movement) phase of sleep, and wake the user up at the 'optimal' point in their sleep cycle. Additionally, it will be able to record the timing and duration of REM sleep phases (potentially useful for improving sleep) and it will be able to generate white, pink or red noise through speakers at the ears to improve sleep.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
I mocked up the hardware and some firmware which sends acquired REM detector samples over a serial channel; I also had to put together some software to acquire, interpret and plot this serial information. The results of this effort have allowed me to finalize the hardware design for the REM detector subsystem. As it stands, all I have left to finalize of the hardware is the display and the USB interface hardware; once these two things are nailed down, I can design and order the boards and fabricate the hardware, moving to the firmware-only phase of the project.</div>
<div style="text-align: left;">
<u><br /></u></div>
<div style="text-align: left;">
<b><span style="font-size: large;"><u>
Finalized hardware</u></span></b></div>
<div style="text-align: left;">
<u><br /></u></div>
<div class="separator" style="clear: both; text-align: justify;">
The hardware has been modified from the schematics I presented originally. These changes are due primarily to two factors: the microcontroller has an internal gain (negating the need for a second external gain stage) and the desire to used a switched-emitter topology (that is, the illumination of the eye for REM detection will only be on for a small percent of the time to save power).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
As seen in the schematic below (the left op-amp), the current output from between the phototransistors on the mask is fed into a transimpedance amplifier whose output is fed directly into an ADC pin on the microcontroller. This single-ended signal can eventually be used to set the emitter amplitude. This signal is also fed into the positive side of a differential ADC (with gain); the negative side is fed from a lowpassed PWM output. This negative input can be used to bias the differential ADC channel to maximize dynamic range; the lowpass converts the oscillating PWM signal into a DC signal whose level is the rail voltage times the Duty Cycle of the PWM waveform. The transimpedance amplifier feedback resistor is set to 40kOhm to maximize signal amplitude while preventing saturation under normal (and even some abnormal) usage conditions.</div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6DeFaSo8QDocxCMBMH24W5dyxJLxJaGf7sCVSRMUFFNdZcgGup-bl4zjVwakhWv0AI1LXSx1_6022cEuqOA9Dgos__1GsW_dI7QV5LfbtuyY7CxpVcHfJip1cPdKe78SHmiaivmikmDY/s1600/hardware.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="394" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6DeFaSo8QDocxCMBMH24W5dyxJLxJaGf7sCVSRMUFFNdZcgGup-bl4zjVwakhWv0AI1LXSx1_6022cEuqOA9Dgos__1GsW_dI7QV5LfbtuyY7CxpVcHfJip1cPdKe78SHmiaivmikmDY/s640/hardware.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
The driver circuitry has been significantly improved in this schematic. Specifically, an op-amp is used to 'linearize' the current control. Above, a sense resistor is used in negative feedback to set the current through the infrared emitter; the set point is determined by a lowpassed PWM fed through a voltage divider. Without the 'linearization' of the sense resistor and negative feedback, the highly nonlinear nature of the emitter's current/voltage characteristic meant that only a few of the possible PWM output levels were 'useful' (that is, corresponding to levels of current we would want to drive our emitter with).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Additionally, the op-amp makes driving the emitter simpler; its high input impedance simplifies the design process for the lowpass-PWM easier. Additionally, it makes it simple to add an enhancement N-FET to allow for switching the emitter on and off (by tying the control input of the op-amp to ground).</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<b><u><span style="font-size: large;">
Firmware/Software for debugging</span></u></b></div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
To debug the REM detector hardware, I needed to implement a firmware to sample the REM detector input channel and transmit that data to my laptop. I also needed to create software on my computer to acquire and plot the transmitted serial data. The firmware and software are contained in <a href="https://docs.google.com/open?id=0B9psp2BpNroQZDBwbl81bW5iaUE">this zip archive</a>.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The big questions I needed to answer were: what do I need to do to keep the differential channel biased appropriately, and how long does the emitter need to be on to ensure that the phototransistor signal is stable before sampling.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The firmware samples the single-ended and differential ADC channels. It then updates the PWM bias setting on the negative input of the differential channel to keep the differential signal centered. It then formats a couple of serial bytes according to the ADC data and sends them to the computer.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
To plot this serial stream, I made a function in MATLAB that opens the serial port and continuously samples the incoming bytes, breaking up the stream into sequences, translating them into floating-point numbers and displaying them on the screen, as seen below. The blue trace is the single-ended ADC channel, the green trace is the differential channel, and the red is a moving-average of the green.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
I waggled my eyes at the beginning and middle of the plotted waveform; you can see that the signal is well-modulated by eye-waggling, which is necessary for detection of REM.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPxO87yXVEeM8S3gM3QF7r58dK1VYoGtr18iZD9GJHs8gE_NUkRhpm8NkD6mMdB_F34PGNDYqHpId9OSELenzFGI2WuTRn19VQiCwp3ApcMS07xajEkUCjyvZcFXF-HsZY-JmKbPWnsPk/s1600/example_output.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPxO87yXVEeM8S3gM3QF7r58dK1VYoGtr18iZD9GJHs8gE_NUkRhpm8NkD6mMdB_F34PGNDYqHpId9OSELenzFGI2WuTRn19VQiCwp3ApcMS07xajEkUCjyvZcFXF-HsZY-JmKbPWnsPk/s640/example_output.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
I used a USB oscilloscope (the Hantek DSO-2090, highly recommended) to check out the settling time for the phototransistor signal in response to switching the emitter. At the end of the day, I established that a switched emitter could be timed to allow for detection of REM using the specified circuit, so I have finalized that circuit and am moving on to validating the USB and display subsystems.</div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-8741257352025669642012-04-04T15:25:00.000-05:002012-04-04T15:25:30.370-05:00Plotting bars, whiskers and bridges in MATLAB (or, too much time spent getting the spacing just right)<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: justify;">
I needed to plot the mean and variance of some data, <i>and </i>indicate pairs of data points which were significantly different. I searched and searched, but there was no easy plot function in my environment of choice (MATLAB), so I made one myself.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The look of the plot is shown below; basically, you've got clusters of mean+variance data (or whatever you want to represent with a bar and whisker). Within each cluster, there are pairwise relationships you want to indicate. Relationships can be further specified by a variable number of marks above the bridges.</div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0m5zihEbvo-opUViLaPnU8Gg9u0QoxMhpv0RdWGHzN5s2s2ahXwPFamYOOSA1lj9k6DH0BYRnLm2Szfg_AA_VXZEI3CKyaNhwKnn4doDUnkUbid8uAGbetW03GNzbYYT3IyC74oSpewY/s1600/untitled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0m5zihEbvo-opUViLaPnU8Gg9u0QoxMhpv0RdWGHzN5s2s2ahXwPFamYOOSA1lj9k6DH0BYRnLm2Szfg_AA_VXZEI3CKyaNhwKnn4doDUnkUbid8uAGbetW03GNzbYYT3IyC74oSpewY/s640/untitled.png" width="640" /></a></div>
<div style="text-align: justify;">
The function is <a href="https://docs.google.com/open?id=0B9psp2BpNroQMHBUcHUxLWVSMXFMMEd4Xy1YTTN3Zw">here</a>. It takes three arguments: the first two are (number of clusters)-by-(number of bars per cluster), and represent (respectively) the height of the bars and the length of the whiskers. The third argument is (number of bars per cluster)-by-(number of bars per cluster)-by-(number of clusters) and indicates whether a bridge should be drawn between a pair of bars; a nonzero value indicates that a bar should be drawn, and integers greater than one indicate that extra indication (in the form of circular marks) should be made above the bridges.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
It's not the prettiest, but it works and it got the job done well enough for me. Besides, any fine-tuning is going to be done in your vector editor of choice, so all that's important is getting the actors arranged on the stage.</div>
<div style="text-align: justify;">
<br /></div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-5723056365912976042012-03-30T07:24:00.001-05:002012-04-18T15:05:55.144-05:00Reverse engineering a motion-capture file format (or, the answer to my prayers... a week ago)<div dir="ltr" style="text-align: left;" trbidi="on">
So, in a surprising turn of events, I am posting about something that I actually did for my research. Part of the work I do involves motion capture; I use cameras and strobes and markers affixed to bony landmarks on the rat hindlimb to record the motion of the limb in space during behavior. One of the motion capture files that I recorded was corrupted with noise, and could not be un-corrupted using the programs and tools from the system manufacturer. Being the clever and industrious fellow that I am (read: I didn't want to do the analysis that I actually had scheduled), I spent a day to completely reverse-engineer the motion capture data file format and use that knowledge to create a program which completely removed the corruption from the file in question and allowed normal data analysis to occur.<br />
<div style="text-align: left;">
<b><u><br /></u></b></div>
<div style="text-align: left;">
<b><u><span style="font-size: large;">The Problem</span></u></b></div>
<div style="text-align: left;">
<b><u><br /></u></b></div>
<div>
As I said above, part of the analysis I am doing for my research involves recording the kinematics of hindlimb locomotion. The system that the lab purchased to get this data is passive and camera-based; that is, bits of shiny stuff (markers) are affixed to the subject and illuminated, and the grayscale images of the shiny stuff are used to infer the location of bony landmarks in space over time.</div>
<div>
<br /></div>
<div>
Since the shiny stuff is so shiny, it's usually easy to set a threshold on the grayscale images to get rid of non-marker sources in the image. However, sometimes there is something else similarly shiny in the image; in those cases you 'mask' the offending pixels (always setting them to zero). Of course, that also means that, if a legitimate marker moves into the masked area, it will not be detected.</div>
<div>
<br /></div>
<div>
My situation was that I had masked the offending reflections in the image... but then had to shift the treadmill around a bit. As a result, some large reflections were present in the data. They were such that the post processing (converting the grayscale images into labeled 3-D trajectories) just wasn't working; bits of whiteness from the reflections were being erroneously labeled.</div>
<div>
<br /></div>
<div>
Unfortunately, the system we use does not have a native facility for re-masking data after it's been recorded. So, I needed to roll my own. To do this, I needed to understand the native data file format.</div>
<div>
<br /></div>
<div>
DISCLAIMER: The system we use is the Vicon Nexus. This is NOT RECOMMENDED by them. DO NOT USE THIS INFORMATION TO DO ANYTHING. In fact, stop reading now. I make no guarantees as to the usability or safety of the software provided here.<br />
<br /></div>
<div style="text-align: left;">
<u><b><span style="font-size: large;">The Solution</span></b></u></div>
<div>
<br />
I opened my my trusty hex editor (<a href="http://mh-nexus.de/en/programs.php">HxD by <span style="background-color: white; color: #3c3232; font-family: 'Trebuchet MS', 'Comic Sans MS', Verdana, Helvetica, Arial, sans-serif; font-size: 14px; text-align: -webkit-auto;">Maël Hörz</span></a>) and took a look at a couple of the raw data files. Long story short, they all shared almost identical initial segments (the first 770 bytes, specifically) which I assume are header information and contain ASCII sub-strings with the camera type and specs in plain text. There were also two 4-byte-long sections of this header which described A: the number of images frames in the file and B: the offset (number of bytes from the beginning of the file) at which the 'index' began. This header was followed by the second section (the largest by far) which contained the grayscale image and blob center data. The final section was the 'index', which contained a series of 12-byte-long records describing the frame numbers (first four bytes) and the offset at which each frame began in the file (last eight bytes).</div>
<div>
<br /></div>
<div>
Sections of the data segment were arranged hierarchically; each object on a given level started with two bytes of 'start sentinel', four bytes describing the length of the object, and four bytes giving some other important number (e.g., camera number, number of blobs in a frame, number of grayscale scan lines in a blob). The top level for each frame was, well, the frame; that is, all of the data taken during one sample. Below the frame level was the camera subframe level; each of those contained the data for the given frame from one of the cameras.</div>
<div>
<br /></div>
<div>
A bit of indirection below the camera subframe, and we come to the meat of the file: the grayscale image data. Each subframe specifies how many bytes long it is, and how many 'blobs' it contains. Blobs are just contiguous sections of non-zero in the grayscale image. Each blob then specifies how many bytes it contains, and how many horizontal scan lines of grayscale data it is made up of. These lines then specify the X- and Y-coordinates of their left-most pixel, how many pixels long they are, and then proceed to actually post the grayscale data. Using the file read and write commands makes traversing this hierarchy simpler, because the file pointer helps to keep track of where you are.</div>
<div>
<br /></div>
<div>
I keep things vague, because I don't want to ruin the fun for anyone else, and because I am a coward.<br />
<br /></div>
<div style="text-align: left;">
<u><b><span style="font-size: large;">The Goods</span></b></u></div>
<div>
<br />
Using my detailed notes on the structure of the data file and its many headers and start sentinel codes, I implemented several useful functions to make it possible to quickly and painlessly re-mask my data. These functions are included in <a href="https://docs.google.com/open?id=0B9psp2BpNroQb2ZmVGZ6dG1SUEMwc2tvYTdzUEVZQQ">this .zip archive</a>; the files are MATLAB m-files and use mostly generic, easily-ported syntax. One of the functions uses the MATLAB sparse matrix data type; I have kept the non-sparse version of the code in the comments, so porting should be straightforward.</div>
<div>
<br /></div>
<div>
I reiterate from above: DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING. This is in no way endorsed by Vicon. Always make a backup. Et cetera. <a href="mailto:rellinger AAATTTTTTT u DDDDOT northwestern DOOOOTTTT edu">Contact me</a> if you have concerns and absolutely need to re-mask some bad Vicon data. I post this only in the spirit of giving, in the hope that someone in the future, in the same spot that I was in a week ago, will be helped by my efforts.</div>
<div>
<br /></div>
<div>
The functions are as follows:</div>
<div>
<ul style="text-align: left;">
<li>extractFrameIndices: This function extracts the offsets for all of the frames in the record.</li>
<li>makeSparseFrameRaster: This function extracts a specified frame from the record, for viewing.</li>
<li>remaskInPlace: This function dances through the record, zeroing out all of the pixels in the record the user desires</li>
<li>testFunc: An example masking function that I used to test this out (also, coincidentally, exactly the masking function that I needed applied to my data)</li>
</ul>
</div>
<div>
The re-masking function is designed to be as mutable as possible; the form of the masking can be as complicated as the user desires, since a handle (MATLAB version of pointer) to the masking function is passed to remaskInPlace, rather than parameters defining a restricted domain of masks. The masking function receives as inputs from the calling function the X- and Y-location of the pixels in question as well as the index of the current frame, allowing the masks to be functions of time as well as space. Additionally, user-specified parameters can be passed transparently through remaskInPlace to the masking function.</div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-18191876753659836282012-03-11T16:23:00.000-05:002012-04-18T15:06:24.461-05:00Generating and outputting white, pink and red noise (or, all that effort just to generate signals you'd usually rather be without)<div dir="ltr" style="text-align: left;" trbidi="on">
I've completed the design and verification of the noise generation software and hardware. I have finalized the software for generating colored noise from white noise samples and I have mocked up the headphone amplifier circuit and verified that it works and performs as expected<br />
<br />
<i>CPU use by noise generation algorithms</i><br />
<br />
One important last step in implementing the colored noise generators is determining how long they take to execute. If it takes 300 cycles to generate a single sample of pink noise, and you've only got 200 cycles to do it per sample, you're going to fall behind. Alternatively, the algorithm could take on average 100 cycles to complete, but 210 cycles in the worst case; what do you do to handle that worst case? Additionally, how many extra cycles do you need per sample to do housekeeping tasks and run other subroutines?<br />
<br />
In my situation, I need to generate a noise sample at regular intervals. If the generator, on average, costs more cycles than you've got, you can increase your clock frequency, decrease your sample rat or try desperately to increase the efficiency of your code. If the average execution time is good, but the worst-case takes too long, you can perform the above interventions or write some sort of wrapper function that keeps a sample or two generated ahead of time, to allow for the worst-case samples.<br />
<br />
Looking at the actual execution statistics, however, leads me to believe that I have a more than acceptable margin even in the worst cases. To get real, live cycle costs, I coded up a firmware that looks at a counter immediately before and after use of the algorithms, then transmits the difference over the serial port. I then left the thing to run for about a second's worth of data (about 44100 samples for each of the three generators); the summary stats are:<br />
<br />
<ul style="text-align: left;">
<li>White Noise: 10 cycles average, 10 cycles worst case, 10 cycles best case</li>
<li>Pink Noise: 90 cycles average, 99 cycles worst case, 88 cycles best case</li>
<li>Red Noise: 56 cycles average, 70 cycles worst case, 52 cycles best case</li>
</ul>
<br />
Since I'm running the CPU at 32MHz, and my sample rate is 44100Hz, that give me about 725 cycles per sample to get thing done; several times more than a comfortable margin even in the worst cases. Since I am likely to only be generating the samples in a mode where other interactions/tasks are minimized (that is, not much else should be going on while the user has the device on), it may even be possible to reduce the CPU speed to improve battery life.<br />
<br />
<i>Verification of amplifier hardware design</i><br />
<br />
I'm using Maxim's MAX9724B fixed-gain headphone amplifier IC. I chose this part because:<br />
<br />
<ul style="text-align: left;">
<li>It's monolithic: feedback resistors are internal and issues with stability and such have been taken care of by Maxim's engineers.</li>
<li>It contains a charge-pump voltage inverter to allow for bipolar output simply and easily from a positive rail; this allows for louder maximum output and removes the need for large output DC-blocking capacitors. Additionally, if I end up needing a negative rail for other things, the device is designed to provide a little more current than it needs.</li>
<li>Click-and-pop suppression, RF noise rejection, very small package size.</li>
</ul>
<br />
Here's the chip, connected to the necessary capacitors and headphone jack. I soldered onto that extra-tiny 12-pin TQFN by hand (this was a ridiculous experience).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9j2P1eKp36SUFnTHT0YzFF0kYtu6DPhR_qg77I7-8OH5TkVWAj3bz7xY3rKL6FjlIisB9T4pwAZL-3V8qMq0t8gE0BVRGU3k4UiHIVstl6hj_yAzG58zVewOveZ0kOgA5HeTNSzC5pYU/s1600/headphone_amp_implementation.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9j2P1eKp36SUFnTHT0YzFF0kYtu6DPhR_qg77I7-8OH5TkVWAj3bz7xY3rKL6FjlIisB9T4pwAZL-3V8qMq0t8gE0BVRGU3k4UiHIVstl6hj_yAzG58zVewOveZ0kOgA5HeTNSzC5pYU/s640/headphone_amp_implementation.jpg" width="640" /></a></div>
<br />
I plugged everything in, and it worked. The voltage on the charge pump output was -3.3V, and the headphone outputs sounded good. I hooked up the scope to the outputs to see if the amplifier was rounding out the DAC switching transitions. They were slightly rounded, but still very obvious. However, as I just said, the outputs <i>sounded</i> okay, so I don't feel the need to alter the design to include lowpassing to get rid of the last of the switching transitions.<br />
<br />
<b><u><span style="font-size: large;">Next Steps</span></u></b><br />
<br />
The only bits of hardware left to verify are the display and the REM detector; of course, these are likely going to be more difficult than the preceding hardware and software. The display should be fairly straightforward in hardware, but coding the drivers will be more involved; It may be possible that someone has already implemented such software and I can crib off of that.<br />
<br />
From the little bit I've already done prototyping the REM detector, it's likely to be somewhat temperamental. The level of illumination provided by the IR LED has to be large enough to result in a big REM signal, but small enough that the zero-frequency current offset saturates the transimpedance amplifier. It seems likely that the optimal illumination level will change between users and possibly even for single users between uses. As a result, I'll likely need to code in a feedback controller to optimize the illumination level for whatever the current operating conditions are. I also need to nail down the properties of the REM differential current signal, to determine how often I need to sample the signal <i>and</i> whether it will be possible to gate the illumination signal in time with the sampling to minimize power usage.<br />
<br />
(There is still the matter of the battery charge circuit; however, I trust the part data sheets and the IC has about a million pins, so manual soldering is going to suck if I try to mock it up.)</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-11164363751703095702012-03-08T19:35:00.001-06:002012-03-09T11:24:00.596-06:00A few things to know about using interrupts on the XMEGA (or, RTFM, seriously)<div dir="ltr" style="text-align: left;" trbidi="on">
In my further adventures in XMEGA migration, I hit a couple of roadblocks (which will likely be hilariously obvious to most). They relate to the use of interrupts, and they are as follows:<br />
<div>
<br /></div>
<div>
A: the XMEGA has a multi-level interrupt handler. This is pretty neat; it means that you can set different interrupts to different priorities, with higher-priority interrupts able to interrupt lower-priority ones. Of course, this means that enabling interrupts is slightly more involved than just setting the global interrupt enable bit (sei()); specifically, you need to enable each of the levels of interrupt (Low, Medium and High) by setting the appropriate bits in PMIC_CTRL. This one is an obvious case of 'didn't read the manual'; I assumed that the default behavior of the interrupt controller would be identical to the TINY/MEGA controller.</div>
<div>
<br /></div>
<div>
B: don't EVER enable interrupts without specifying the corresponding interrupt routines. This one is a little less certain than the last bit of advice, because it is borne out of my investigation of some flaky behavior I observed.</div>
<div>
<br /></div>
<div>
Specifically, I was trying to verify that I understood/could correctly use the real-time clock (RTC) peripheral. My test would consist of initializing the RTC (and clocks, and ports, and USARTs) and transmitting a few bytes over the serial port every time the RTC overflow interrupt occurred. To make sure that the device was working otherwise, every 250ms it would output another character to prove that any problems were with the RTC implementation, and not the device setup code.</div>
<div>
<br /></div>
<div>
The check bytes were being sent... but the RTC interrupt was not being called. To check that interrupts in general were working, I changed the interrupt routine vector to another counter's overflow, which I had not disabled from a previous bit of investigation. It worked; and when I added another interrupt handler (one for both the RTC overflow and the counter overflow), both worked. All I can figure is that the lack of a valid interrupt routine for the counter overflow (in the case where I had programmed a routine for the RTC overflow) meant that the interrupt handler was never apprised of the completion of the counter overflow interrupt routine (though a RETI instruction), so any subsequent interrupts were not handled because the controller thought that the original interrupt was still being executed. This is predicated on the assumption that all unspecified interrupt vectors are by default handled by a simple RET instruction (which would leave the interrupt flag set, which makes a certain sort of sense as a default behavior); if true, it explains the behavior I observed.</div>
<div>
<br /></div>
<div>
The simplest solution is (at least for the AVR Studio/AVR-GCC environments) is to always specify a BADISR routine (that is, a default routine for otherwise unspecified interrupts). This seems to take care of my problems, even with the counter overflow interrupt still enabled.</div>
<div>
<br /></div>
<div>
<br /></div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-4632825684845209592012-03-03T17:29:00.002-06:002012-03-06T09:35:44.516-06:00Colored noise generation using a hardware CRC (or, 'it sounds right' is basically as good as mathematical rigor, right?)<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: justify;">
Toward the 'noise maker' aspect of the sleep mask I'm designing, I've done some work toward generating white and colored noise on the AVR XMEGA hardware. My goal was to generate white, pink and red/brown noise so that any of the three could be chosen by a user to aid their sleep. So far, I have generated <a href="https://docs.google.com/open?id=0B9psp2BpNroQLWktTzVBXzhRb21CcWNvRnpuWUVsUQ">white</a>, <a href="https://docs.google.com/open?id=0B9psp2BpNroQT3RPRXdiR2NUc0N4dXQyWlRmU21jUQ">pink</a> and <a href="https://docs.google.com/open?id=0B9psp2BpNroQMTB3Mk9CM1RTcXViNWR3OFFQOWhoUQ">red</a> noise in simulation (using a model of the XMEGA's CRC generator) and have translated these simulations successfully to the actual microcontroller for the <a href="https://docs.google.com/open?id=0B9psp2BpNroQWmd4M0dZUkJSU0thaU90U0RfMzZXdw">white</a> and <a href="https://docs.google.com/open?id=0B9psp2BpNroQV3FKOGVQb2ZRLUNkSHhVYmFtRzBodw">red</a> cases. UPDATE: <a href="https://docs.google.com/open?id=0B9psp2BpNroQUGJ6bDJudk1SSS1melBQYWVXVUNJQQ">pink</a> case also successfully implemented.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b><u><span style="font-size: large;">Background</span></u></b></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Many devices and applications exist which generate various sounds/noises to aid sleep and drown out the irregular sounds which can interrupt deep sleep. Three commonly provided sounds are white, pink and red noise; <a href="http://en.wikipedia.org/wiki/White_noise">white noise</a> has the most high-frequency content, <a href="http://en.wikipedia.org/wiki/Pink_noise">pink</a> less and <a href="http://en.wikipedia.org/wiki/Brownian_noise">red</a> the least.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<i>Colored noise</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="http://en.wikipedia.org/wiki/White_noise">White noise</a> refers, generally, to noise whose samples are totally independent of each other; knowledge of one sample gives no information about any other sample, previous or subsequent. As a result, a white noise signal's <a href="http://en.wikipedia.org/wiki/Autocorrelation">autocorrelation</a> is unity at zero lag and zero elsewhere. Additionally, a white noise signal has equal power at all frequencies. Different types of noise are commonly referred to by their 'color': white is so-called because it contains all frequencies, analogous to the way that white light contains all colors. Pink noise is similar to white noise, except that it has a decreasing amount of power as frequency increases; where white noise has a constant power level (P(f) = 1, let's say), pink noise has 1/f power (P(f) = 1/f). Red noise takes it a step further, having even less power at high frequencies (P(f) = 1/f^2).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Pink and red noise can be generated from white noise. Red noise can be generated by integrating white noise (this is where its other name, brown/brownian noise, comes from: the path of the signal in time is a <a href="http://en.wikipedia.org/wiki/Brownian_motion">Brownian walk</a>). Pink is more difficult to generate; where red noise can be generated using integration or a first-order lowpass, generating pink noise would require some sort of half-order filter. From my researches, there are two commonly-implemented methods for generating pink noise computationally from white noise: using a specially-specified higher-order filter, and the Voss-McCartney method. These methods are quite capably and completely explained and expounded upon <a href="http://home.earthlink.net/~ltrammell/tech/pinkalg.htm">here</a> and <a href="http://www.firstpr.com.au/dsp/pink-noise/">here</a>; since the special filter method involved a lot of multiplication and tweaking and maybe even floating point math, I chose the Voss-McCartney method. It is very clever, and only relies on addition, subtraction and keeping track of a very small buffer of past-generated white noise samples. I will go over it further when I describe my implementation below (I assure you, though, the sites above are quite top-notch).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<i>Random and no-so-random numbers</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<div class="separator" style="clear: both; text-align: justify;">
The difficulty in generating white noise is finding a signal source whose output at one point in time is totally independent of its output at a subsequent time. Or course, this is impossible with any normal computer system; its state at one clock cycle is deterministically derived from its state at the previous cycle. It is possible to <a href="http://en.wikipedia.org/wiki/Hardware_random_number_generator">access randomness in the form of analog signals in the environment around the computer</a>; if we have a bit of radioisotope, its decay will serve as a great random process. A reverse-biased diode can also be used, as can the randomness in the timing of user interactions (if we happen to have a user close at hand). The problems with the above hardware random number generators (and hardware random number generators in general) are twofold: first, the extra hardware can be expensive; second, the rate of random bit generation can be very slow (especially true in the case of the user input).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Faced with these costs, it would be nice if we could, <a href="http://en.wikipedia.org/wiki/Pseudorandom_number_generator">deterministically within the computer, generate numbers which are random-like</a>. While the computer's state completely predicts the next pseudorandom number, the string of numbers in question may appear random enough for a specific purpose. Since our success criterion is 'does it sound good', even a poor pseudorandom number generator (PRNG) will be good enough. There are a variety of algorithms extant; <a href="http://en.wikipedia.org/wiki/Linear_feedback_shift_register">Linear Feedback Shift Register</a>s (LFSR) are one such class of PRNG. They work by shifting a register and exclusive-OR-ing some of its members every clock cycle. The <a href="http://en.wikipedia.org/wiki/Xorshift">XORshift</a> algorithm is a cheap and capable method invented by George Marsaglia. I implemented and tested a version of it (for the 8-bit platform, developed by <a href="http://www.donnelly-house.net/programming/cdp1802/8bitPRNGtest.html">William Donnelly</a>) before I realized that the <a href="http://en.wikipedia.org/wiki/Cyclic_redundancy_check">Cyclic Redundancy Check </a>hardware on the XMEGA, intended to check the integrity of the program flash memory and USB transmissions, was also an LFSR and might afford me four bytes of decent pseudorandom numbers every clock cycle.</div>
<div class="separator" style="clear: both; text-align: justify;">
<i><br /></i></div>
<div class="separator" style="clear: both; text-align: justify;">
<b><u><span style="font-size: large;">Hardware and software implementation</span></u></b></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The central question going into this was 'will the hardware CRC peripheral be able to produce samples of data that sound sufficiently white?'. I had already implemented the 8-bit Xorshift algorithm provided by <a href="http://www.donnelly-house.net/programming/cdp1802/8bitPRNGtest.html">William Donnelly</a>, and it performed well. However, it took over 70 cycles to generate one sample, so the possibility of ONE cycle samples was too good not to investigate.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<i>Setting up and using the CRC to generate pseudorandom numbers</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The CRC peripheral on the USB-capable XMEGAs is fairly straightforward: a control byte, a status byte, a data input byte and four output bytes. It can take as input the flash memories, the DMA channels or a 'manual' single-byte IO channel, so that user applications can more easily take advantage of the hardware. I had some difficulty with getting the thing to take my IO data and getting it to perform the full 32-bit CRC (instead of getting stuck in the 16-bit mode), so here's my sequence for initializing it for my nefarious purposes:</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
</div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">//set up the CRC thingy to accept data from the IO bus, CRC-32</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">//also set the current state of the CRC generator to all 1's (if //it resets with all 0's, this will go nowhere)</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">CRC_CTRL |= CRC_RESET_RESET1_gc;</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">//set up the input to be the data byte</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">CRC_CTRL &= CRC_SOURCE_gm;</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">CRC_CTRL |= CRC_SOURCE_IO_gc;</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">//set to 32 bit width; first set busy byte to allow the change</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">CRC_STATUS |= CRC_BUSY_bm;</span></div>
<div class="separator" style="clear: both;">
<span style="font-family: 'Courier New', Courier, monospace;">CRC_CTRL |= CRC_CRC32_bm;</span></div>
<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Every time I want to get a new random number set in the outputs, I feed all zeros into the CRC (other bytes may be valid), and read out the changed checksum a mere single cycle later.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: 'Courier New', Courier, monospace;">//change the checksum</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: 'Courier New', Courier, monospace;">CRC_DATAIN = 0b00000000;</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: 'Courier New', Courier, monospace;">//get the bytes</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: 'Courier New', Courier, monospace;">uint8_t crc_out3 = CRC_CHECKSUM3;</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: 'Courier New', Courier, monospace;">uint8_t</span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">crc_out2 = CRC_CHECKSUM2;</span>
</div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: 'Courier New', Courier, monospace;">uint8_t</span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">crc_out1 = CRC_CHECKSUM1;</span>
</div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="font-family: 'Courier New', Courier, monospace;">uint8_t</span><span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">crc_out0 = CRC_CHECKSUM0;</span>
</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Bear in mind that this set-up only works for my situation of steady-state generation of pseudo-random checksums by passing more and more constant bytes into the CRC hardware. If the CRC hardware is required for other tasks (like, for example, checking out USB packets), it will be necessary to save the current state and reset the generator (including, possibly, changing the data source and bit width) before using it. Before going back to using the CRC as a PRNG, it is necessary to pause the generator, load it with the saved state, and set the input and bit width as above.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
To verify that I understood how the CRC hardware worked and how to access it, I created a firmware which generates a new CRC checksum (by adding an all-zero byte to the input) and transmits it over the serial peripheral. The source is available <a href="https://docs.google.com/open?id=0B9psp2BpNroQM0JhNEJTNC1RZWlxRHFlSExPUTlWUQ">here</a> (that component is actually commented out in a big block in the middle).</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<i>Simulating a sequence of random numbers; implemented coloration</i></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Using the data output over the serial channel above, I tested a MATLAB function which implemented the activity of the CRC as detailed in the device datasheet (available in <a href="https://docs.google.com/open?id=0B9psp2BpNroQYWhsQ3ZER2hSdC1VQmFZLWg2WjBBZw">this zip file</a>, 'benCRC32guess.m'). Once I was satisfied that my model of its working was correct (bear in mind, that file assumes that the input to the CRC is all zeros), I used it to generate a sequence of bytes as the hardware would. I then did the simplest test of white-ness possible: auto- and cross-correlation.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
The first plot is the autocorrelation for the first byte of the CRC checksum; It is high for only one sample and low elsewhere (not sure why the scaling was all wonky).</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPz4kTpo_yFo55ewr1bSSUeYbLM81XWRxwwFVJ_Vw9mzMFoF-eTycfmSgrFeEN0-DSj7-3gy3dk9o3tDo4rdYLpMeJ-gWhf53YPZSEzFr3thGc2jAbJoaXXVBV2qVhQnO3JfZm3QSILrY/s1600/autocorr_CRC_byte0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPz4kTpo_yFo55ewr1bSSUeYbLM81XWRxwwFVJ_Vw9mzMFoF-eTycfmSgrFeEN0-DSj7-3gy3dk9o3tDo4rdYLpMeJ-gWhf53YPZSEzFr3thGc2jAbJoaXXVBV2qVhQnO3JfZm3QSILrY/s320/autocorr_CRC_byte0.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
The second test was cross-correlation between the bytes; even getting only one random byte per cycle would be great, but if I could use all four (that is, if they were also independent of each other) would be super-great. As shown below, the cross-correlations were all nada (same weird scaling as above).</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGOeH9Qd7U26subvVI0eAoa0XSOw1EDzclsIwfWXQDKted6NX248tta3FKQnS2DEou_H_9VHw_Ctu4gqlt4TAMI_nfo_AL1xOR4_L5B-5i3Nkk1Lifdxy0kEPizWl_KSywN4TvBfGXqz0/s1600/crosscorr_CRC_byte01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGOeH9Qd7U26subvVI0eAoa0XSOw1EDzclsIwfWXQDKted6NX248tta3FKQnS2DEou_H_9VHw_Ctu4gqlt4TAMI_nfo_AL1xOR4_L5B-5i3Nkk1Lifdxy0kEPizWl_KSywN4TvBfGXqz0/s320/crosscorr_CRC_byte01.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
Note that the above two plots are exemplars (top was for byte 1, bottom was betwen bytes 1 and 2); the autocorrelations of the other 3 bytes and cross-correlations between the other 5 pairs were similarly acceptable. The final (and most important) test was the by-ear test; does it sound right? As linked above (and <a href="https://docs.google.com/open?id=0B9psp2BpNroQLWktTzVBXzhRb21CcWNvRnpuWUVsUQ">here</a>), the white noise from the simulation sounded right.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Now that I was confident that the CRC PRNG method was acceptable, I moved on to implementing the red and pink noise generators. The brown was the simplest; simply integrate the input from the white sample generated above (taking care to prevent values outside a finite range). This first pass was <a href="https://docs.google.com/open?id=0B9psp2BpNroQNHo1TF9fWGhTX080Z3VhZDE3SW1sQQ">okay... but exhibited some nasty clipping-ish atrifacts</a>, due to the fact that I just put a hard limit on the valid range (that is... once you hit the ceiling/floor, you hit hard, and saturate). To soften this, I set up 'buffer' ranges within the valid range. The middle range allowed for integration as normal; however, progressively farther out ranges scale down input samples which would move the integrand away from the middle of the range. This was implemented in the MATLAB file 'brownMakerSoftedge.m' in the zip file linked above (and <a href="https://docs.google.com/open?id=0B9psp2BpNroQYWhsQ3ZER2hSdC1VQmFZLWg2WjBBZw">here</a>). While I have no idea what this does to the statistics of the signal, it makes it <a href="https://docs.google.com/open?id=0B9psp2BpNroQMTB3Mk9CM1RTcXViNWR3OFFQOWhoUQ">sound better</a>, and thus is awesome.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Implementing the Voss-McCartney algorithm for pink noise generation was slightly more involved (but only slightly). While the <a href="http://home.earthlink.net/~ltrammell/tech/pinkalg.htm">links</a> <a href="http://www.firstpr.com.au/dsp/pink-noise/">above</a> explain in more detail the ins and outs of the algorithm and its statistical properties, I will describe it briefly here. Basically, the output of the generator is the sum of 8 white noise generators. Each generator is sampled progressively more slowly; the first generator is updated every sample, the second every two samples, the third every four samples... Since each generator exhibits more and more low-frequency power, the sum of the set ends up having a more-or-less 1/f power spectrum. By scheduling the updates appropriately, only two new white noise bytes are required every sample. My implementation is in the MATLAB <a href="https://docs.google.com/open?id=0B9psp2BpNroQYWhsQ3ZER2hSdC1VQmFZLWg2WjBBZw">ZIP file</a>, as 'pinkBuffer.m'; the sample sound is <a href="https://docs.google.com/open?id=0B9psp2BpNroQT3RPRXdiR2NUc0N4dXQyWlRmU21jUQ">here</a>. As above, it sounds right, so it <i>is</i> right.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="" style="clear: both; text-align: justify;">
<i>Porting the colorizers to the hardware</i></div>
<div style="text-align: justify;">
<br />
The MATLAB business above was more-or-less directly ported to C for compilation and loading onto the ATXMEGA32A4U. The source is in <a href="https://docs.google.com/open?id=0B9psp2BpNroQM0JhNEJTNC1RZWlxRHFlSExPUTlWUQ">these</a> <a href="https://docs.google.com/open?id=0B9psp2BpNroQd1NYWlJVREJSOGlLRllENVhGVVJDdw">three</a> <a href="https://docs.google.com/open?id=0B9psp2BpNroQYWxqN3d1dE9UX1diT19RVTdvbDVKUQ">files</a> (various bits of code will need to be commented/uncommented). I set up the generators to continuously transmit samples over the serial channel; these samples were read into MATLAB using the 'getSerialCRCtest.m' file in the <a href="https://docs.google.com/open?id=0B9psp2BpNroQYWhsQ3ZER2hSdC1VQmFZLWg2WjBBZw">ZIP archive</a>. They were then converted to floating point values, zero-meaned and scaled so that they could be exported as WAV files. The hardware-generated white noise is <a href="https://docs.google.com/open?id=0B9psp2BpNroQWmd4M0dZUkJSU0thaU90U0RfMzZXdw">here</a>, and the hardware-generated red noise is <a href="https://docs.google.com/open?id=0B9psp2BpNroQV3FKOGVQb2ZRLUNkSHhVYmFtRzBodw">here</a>. They sound good, so I'm satisfied. Currently, the hardware pink output does not sound good (and, upon visual analysis, also does not <i>look</i> good), so I am still working on that. UPDATE: I changed the source slightly, so the <a href="https://docs.google.com/open?id=0B9psp2BpNroQUGJ6bDJudk1SSS1melBQYWVXVUNJQQ">pink</a> works now (and sounds right). Modified source is <a href="https://docs.google.com/open?id=0B9psp2BpNroQdDZFNEd5XzRSbkNoNkhoT04zZUVBUQ">here</a>.<br />
<br /></div>
<div style="text-align: justify;">
<b><u><span style="font-size: large;">Next steps</span></u></b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The first step is to figure out whats going wrong with the XMEGA implementation of the Voss-McCartney algorithm. Probably some sort of overflow issue.<br />
<br />
The next step is to read up on the XMEGA DACs and start passing the generator outputs to the outside world. After achieving that, I'll mock up the headphone amplifier and see if lowpassing is necessary to eliminate DAC switching transients in the output (if not, I can use the internal-resistor amplifiers, which would reduce overall parts count).<br />
<br />
Once all of that is settled, I'll tidy up the code to make it more modular/portable, probably including a save/load state feature to allow CRC hardware use by other functions to be interleaved with the use here.<br />
<br />
UPDATE: The pink noise generator works now. Instead of updating the buffer sum, I completely re-calculate it with each iteration. The modified source is <a href="https://docs.google.com/open?id=0B9psp2BpNroQdDZFNEd5XzRSbkNoNkhoT04zZUVBUQ">here</a>.</div>
<div style="text-align: justify;">
<br /></div>
</div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-13617196400771125972012-03-01T21:27:00.000-06:002012-03-03T19:37:48.585-06:00Getting started with Atmel's XMEGA (or, making all the old mistakes all over again)<div style="text-align: justify;">
Normally, I wouldn't communicate such a small change, but I had a few little issues in the migration from MEGA/TINY to XMEGA, so this might conceivably help someone somewhere somehow.</div>
<div>
<div style="text-align: justify;">
<br /></div>
</div>
<div>
<div style="text-align: justify;">
Because the XMEGAs use the PDI interface for programming rather than ISP, it was necessary to get a new programmer. I settled on the <a href="http://www.mattairtech.com/index.php/development-boards/zeptoprog-board.html">Zeptoprog</a>; it was cheap (~25USD, including postage) and MADE IN AMERICA which, beyond jingoism, means that it arrived almost instantaneously. Furthermore, the programmer/designer/manufacturer replied very quickly to my emails. Furthermost, the device can also enumerate as an ISP/TPI programmer, GPIO/SPI/frequency counter and serial bridge as well as allowing for firmware upgrades over USB using Atmel's FLIP application. As a bonus, the device shows up as an AVRISP mkII.</div>
</div>
<div>
<div style="text-align: justify;">
<br /></div>
</div>
<div>
<div style="text-align: justify;">
To test out the XMEGA, I soldered one onto a header and broke out the programming and power pins along with two GPIO pins, the two DACs and a USART. It is pictured below in all its kludgy glory.</div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyXYoWswzhr5CmgEGKepXgr9ERwvG78BGZ9LyAcfwYiXpgDgRw_cPo3GlJULoPORR9_k2_ex5TrbnrZjRa85tHySiCYo8ch0JRGO4BiDJGbpXRceKfkETPPvWHn1DRqAVEHUnKa6ieKdM/s1600/XMEGA_KLUDGE_CONNECTOR.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="288" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyXYoWswzhr5CmgEGKepXgr9ERwvG78BGZ9LyAcfwYiXpgDgRw_cPo3GlJULoPORR9_k2_ex5TrbnrZjRa85tHySiCYo8ch0JRGO4BiDJGbpXRceKfkETPPvWHn1DRqAVEHUnKa6ieKdM/s320/XMEGA_KLUDGE_CONNECTOR.JPG" width="320" /></a></div>
<div>
<br /></div>
<div>
<div style="text-align: justify;">
And now, to the whole point of this post: DON'T MAKE YOUR LEADS TOO LONG FOR PDI PROGRAMMING. I had a good foot-and-a-half between programmer and device, and I wasn't able to talk to the device. After a prompt set of emails with the Creator (of the Zeptoprog), I tried shortening the leads to the extra-shortness shown in the pic below, and it worked. I could have made longer leads, but I wasn't taking any chances (I was in debug mode). In fairness to the Zeptoprog, its drivers are stronger even than those in the Atmel-made AVRISP mkII; so any situation the official programmer can handle, the Zeptoprog can, and then some.</div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_3JqWRaSJDkYK_hiZDvUKJPAwmQS0oNKMV23gHulB5HX1YVi9q55eMy14DGEtjzXQSkNIAMXkdwAB4P-LdsskX9TStX_iuqtbr36FECjIks08Xu-uqbpL02Lk4DvtLI14Ewkc6MaiFxM/s1600/zeptoprog_plus_programming_harness.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="306" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_3JqWRaSJDkYK_hiZDvUKJPAwmQS0oNKMV23gHulB5HX1YVi9q55eMy14DGEtjzXQSkNIAMXkdwAB4P-LdsskX9TStX_iuqtbr36FECjIks08Xu-uqbpL02Lk4DvtLI14Ewkc6MaiFxM/s400/zeptoprog_plus_programming_harness.JPG" width="400" /></a></div>
<div>
<br /></div>
<div>
<div style="text-align: justify;">
Regrading the XMEGA itself: I love it. It's so beefy, and has so many peripherals, and everything is quadruply configurable. My chosen device (ATXMEGA32A4U) has two 12-bit DACs, a whole mess of PWMs and ADC channels, a couple of SPI and USART ports AND a built-in USB peripheral. Best of all, it's got a built-in hardware CRC peripheral, which I'll be using to great effect in generating pseudo-random numbers in almost no cycles at all.</div>
</div>
<div>
<br /></div>benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com0tag:blogger.com,1999:blog-1933957739656816486.post-34218843009631490582012-02-19T17:03:00.000-06:002012-03-03T19:40:26.251-06:00REM-detecting white-noise-generating sleep mask (or, biting off more than you can chew)<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">For my next project, I'm going to design and build a sleep mask that has the ability to detect rapid eye movement (REM, which indicates dreaming sleep) and present sounds and lights to the wearer to enhance the sleeping experience.</span></div>
<br />
<div style="text-align: justify;">
First, the device will sound an alarm when the user is at the 'optimal' time to wake up, relative to their sleep cycle. It's asserted in various places on the internet that the ideal time to wake up is at the end of an REM cycle; I'm still searching for peer-reviewed research to this effect, but there already exist devices on the market that trade on this possibly-more-substantial-than-folk wisdom. Specifically, there is a <a href="http://www.myzeo.com/sleep/">headband that records EEG (brainwaves) to detect REM sleep</a>, and a<a href="http://www.sleeptracker.com/"> wristwatch which detects REM through arm/body movement and/or elevated heart rate</a> (I can't tell how exactly it detects REM from the wrist, but those are my guesses). Additionally, there are several smartphone applications and <a href="http://www.axbo.com/axbo/CMS/leichter_aufwachen.aspx?ClientID=wfb4b52fcd-22bd-4dee-a2d9-075a40710b88&SiteID=0&GroupID=8&Status=328EA69CA4B0A3&Language=E">this alarm clock</a> that detect REM through the amount of body movement a person exhibits. I will detect REM directly, by illuminating the eye with pulsed infrared light and recording the changes in the reflected power. The EEG headband device is very expensive, and my device will end up with a far wider range of features than both devices, so I don't think I'm duplicating any existing products. Additionally, the timing and duration of REM cycles will be recorded and accessible by USB.</div>
<br />
<div style="text-align: justify;">
Second, the device will present noise to the wearer to assist in sleep. I more-or-less need a 'red' noise generator running in the room to sleep well; devices on the market exist that produce white, pink and red noise as well as other 'nature' sounds to improve sleep. By generating the noise in the mask, it becomes a sort of 'best sleep in a box'; with the alarm and noise generator, you can easily have as good a night's sleep in a hotel room as you do at home. While there exist myriad smartphone programs to generate these noises (<a href="http://simplynoise.com/">here</a> is a site for a company that makes a good, no-nonsense one that also works for free on a computer), I don't own a smartphone. So there. Also, these features wrapped up into one common device elevates the mask to the status of a general sleep appliance that would be useful as a discrete device with one interface, one battery to charge and no monthly fee.</div>
<br />
<div style="text-align: justify;">
Third, the device will be able to deliver light and sound to the wearer during REM sleep to help induce 'lucid dreaming'. Lucid dreaming is a state where you are dreaming, but consciously aware of that fact and thus able to influence the contents and progression of the dream. The ability to dream lucidly is a talent that can be developed; one method is to train yourself to periodically perform 'reality checks' so that if you are dreaming, you will become aware of it as soon as your next 'reality check'. There are also devices which attempt to present flashes of light or sound pulses during REM sleep which can indicate to the dreamer that they are asleep. There already exists a 'commercial' product that attempts to do this called the <a href="http://www.lucidity.com/novadreamer.html">NovaDreamer</a>; it uses a simple delay to hopefully flash lights at the user during REM. Since this device is no longer being made, and since it uses such a simple REM prediction method (that doesn't take into account any real-time information about the user), this feature is treading in a more-or-less untapped market.</div>
<br />
<div style="text-align: justify;">
Together, these features make a device that will, hopefully, allow the user to have more enjoyable wake-ups, better sleep and possibly troubleshoot their sleep by analyzing the recorded sleep cycle data. Additionally, I will design the hardware with 'future-proofing' in mind: I will attempt to make features software-configurable and extensible so that A) features can be progressively added an debugged on the prototype hardware and B) extra features can be added beyond the immediate scope outlined above.</div>
<br />
<b><u><span style="font-size: large;">Overall system scope</span></u></b><br />
<br />
<br />
<ul>
<li style="text-align: justify;">The most important design constraint, considering our culture's current level of microelectronic and battery technology, is device size and shape. I want this device to basically be a slightly bulky sleep mask; the battery and electronics/interface board will be mounted on the front of the eye-cups and small speakers for the noise generator will be built into the band (EDIT: this speaker-in-the-band approach has apparently been <a href="http://www.sleepphones.com/">done before</a>).</li>
<li style="text-align: justify;">The device should be able to run for at least three nights without recharging, and should be able to be used while charging (I absolutely loathe cordless devices that can't charge while being used; burn in hell, cheap knock-off cordless drill!).</li>
<li style="text-align: justify;">The interface should be quick and simple for common tasks but allow for more detailed settings to be accessed relatively easily; additionally, common during-sleep interactions like volume control and snooze should be easy to perform while wearing the device.</li>
<li style="text-align: justify;">Charging, sleep data downloading and firmware updating should occur through micro USB port due to the ubiquity of micro-USB cables and chargers for cellphones (and even for dumb devices, of late).</li>
</ul>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b><u><span style="font-size: large;">Overall system design</span></u></b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b><u>Mechanical</u></b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The mechanical design will consolidate as much of the electronics as possible onto a single board over the left eye; REM detection will occur through emitter/detector pair whose leads go through the left eyecup; the battery will be mounted over the right eyecup; and the speakers will be built into the sleep mask's band. Eventually it will make sense to design a hard plastic enclosure/casing for the electronics in and around the mask, but for now I'm going to focus on the electronic side of the design.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b><u>Electronic</u></b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The electronics will consist of several subsystems:</div>
<ul>
<li style="text-align: justify;">Power management: a Microchip LiPo battery charger IC will sit between the USB V+ rail, the LiPo battery and the application circuit. Everything downstream of the battery will run off a +3.3V rail provided by a high-efficiency buck converter.</li>
</ul>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy5bebUVSGOnp1dBxN7g0Zpl8sNHBMkgTwOklRSfgPq07oDNRj9qBA-ThAcuWU9in4c_qsU35xzOW0KkQikzqsH57fz7R2vpTtIZ-I5iMR2IubfPh0nIZ16dP04X9fja1AoYwP5a0jPuw/s1600/power_lipo_charger.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="395" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy5bebUVSGOnp1dBxN7g0Zpl8sNHBMkgTwOklRSfgPq07oDNRj9qBA-ThAcuWU9in4c_qsU35xzOW0KkQikzqsH57fz7R2vpTtIZ-I5iMR2IubfPh0nIZ16dP04X9fja1AoYwP5a0jPuw/s640/power_lipo_charger.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWYe4Z_K7hGtxkOaTfF7_WdlrG-IL0GzUka8Oc7rtLirctuGZxLc3Uq5J18ThYCC90NbsBf2UiWaXfmuGyiNiGA8VVv6ujyTMm7nWeUix8Aoz1j8axPis1iAKD30HiK5NECyh8y4qfPDI/s1600/power_buck_usb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="404" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWYe4Z_K7hGtxkOaTfF7_WdlrG-IL0GzUka8Oc7rtLirctuGZxLc3Uq5J18ThYCC90NbsBf2UiWaXfmuGyiNiGA8VVv6ujyTMm7nWeUix8Aoz1j8axPis1iAKD30HiK5NECyh8y4qfPDI/s640/power_buck_usb.png" width="640" /></a></div>
<ul>
<li style="text-align: justify;">REM detection: the center of the detector will be an IR emitter and a pair of IR phototransistors. the detectors will be stacked to allow for differential current measurement, which will be amplified by a transimpedance amplifier cascaded with an inverting gain stage. All signals will be 'unsigned' (0 to +3.3V) and full dynamic range ensured by proper biasing. This circuit is cribbed fairly liberally from an <a href="http://digitalcommons.calpoly.edu/cgi/viewcontent.cgi?article=1086&context=eesp&sei-redir=1&referer=http%3A%2F%2Fwww.google.com%2Furl%3Fsa%3Dt%26rct%3Dj%26q%3Dir%2520detection%2520rem%26source%3Dweb%26cd%3D8%26ved%3D0CFUQFjAH%26url%3Dhttp%253A%252F%252Fdigitalcommons.calpoly.edu%252Fcgi%252Fviewcontent.cgi%253Farticle%253D1086%2526context%253Deesp%26ei%3DdDL9TvaAA6GK2QWl-aGXCA%26usg%3DAFQjCNHkJspX9Bi9pU5syPFq6nSIaRmPzQ#search=%22ir%20detection%20rem%22">undergraduate project to implement REM detection for a sleep alarm</a> (design document mirrored <a href="https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B9psp2BpNroQZTc0ODc2ZjEtMzI0OC00NWZhLTg1Y2ItYWMyYzRlNmVkYzI5">here</a>). A similar project is
<a href="http://www.bbelmont.com/pdfs/DreamMachine.pdf">here</a>. Since the signal of interest is very low bandwidth, the emitter will be pulsed in time with ADC sampling of the output; this is a significant difference from the previous work and should result in significant power savings.</li>
</ul>
<div style="margin-left: 1em; margin-right: 1em; text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjfQGAEIcriuadrTXwbrLBE8FtDX8evAMFraoPOnfmHL8ndBgoASwMweGZjsjKu7G5HVCUoIym98LLLqtqq-eMYD32a9vFiFG_7rKUQbSrZQPuPgL512iddLwuE252rAxdv2gXuOgDuqk/s1600/REM_linear.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="506" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjfQGAEIcriuadrTXwbrLBE8FtDX8evAMFraoPOnfmHL8ndBgoASwMweGZjsjKu7G5HVCUoIym98LLLqtqq-eMYD32a9vFiFG_7rKUQbSrZQPuPgL512iddLwuE252rAxdv2gXuOgDuqk/s640/REM_linear.png" width="640" /></a></div>
<ul>
<li style="text-align: justify;">Noise generation: an attractively-featured Maxim headphone amplifier will be fed with the DAC outputs of the microcontroller. The chosen amplifier breaks out the feedback path, allowing me to design in a suitable bandpass to cut off the high-frequency DAC switching transients and also block the +3.3/2V DC component in the DAC outputs.</li>
</ul>
<div style="margin-left: 1em; margin-right: 1em; text-align: justify;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrlu7sAnYpFXPLutdpkBotHwwSSyITmEhR9MD3uVzEYCvPmqhTX31jtOK8QfoLaNXRW8glOSrVrYWhE0T7VOa1u6Jsy2vaEkTMOzJ21teG4Hm7GzfQ3S4PJo40uv-fXh09c1_JZB-UPZQ/s1600/audio.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrlu7sAnYpFXPLutdpkBotHwwSSyITmEhR9MD3uVzEYCvPmqhTX31jtOK8QfoLaNXRW8glOSrVrYWhE0T7VOa1u6Jsy2vaEkTMOzJ21teG4Hm7GzfQ3S4PJo40uv-fXh09c1_JZB-UPZQ/s640/audio.png" width="640" /></a></div>
<ul>
<li style="text-align: justify;">Interface: the interface will consist of four appropriately labeled pushbuttons and a nifty ultra-tiny OLED display manufactured by Univision Technology Inc.. I was able to find two for cheap off eBay; I found the module and controller datasheets on the Adafruit website (<a href="http://www.adafruit.com/datasheets/UG-2832HSWEG04.pdf">here</a> and <a href="http://www.adafruit.com/datasheets/SSD1306.pdf">here</a>). The module contains its own driver stepup and is controlled over SPI.</li>
<li style="text-align: justify;">Controller: ATXMEGA32A4U. I wanted built-in DACs and USB support; additionally, the timers for the ATXMEGA (along with most everything else) are very expansively featured and allow for 32MHz operation down to 2.7V. Migrating to ATXMEGA from the ATTINY and ATMEGA I've played with in the past means that I needed to find a cheap PDI programmer; I found the <a href="http://www.mattairtech.com/index.php/development-boards/zeptoprog-board.html">ZeptoProg</a> on eBay for reasonable cheap and it plays well with AVR Studio 6 (I tried to get it to play well with AVRDUDE and the open source toolchain I used to use... but the problems have not yet been resolved).</li>
</ul>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
The minimum usable endurance for the device is one night; assuming a middle-of-the-line, appropriately sized LiPo battery of 1000mAH capacity, this means that our device needs to draw about 100mA in the mean. Tallying up the current usage of the various subsystems in theory yields a number far less than this, but I want to plan for the worst in putting together the prototype. The buck can handle more than 100mA, and the battery charger circuit was designed with trimpots to allow for an appropriately-sized battery to be chosen <i>after</i> the completed device has been tested for real-life current usage.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b><u>Software</u></b></div>
<div style="text-align: justify;">
<b><u><br /></u></b></div>
<div style="text-align: justify;">
I've only designed the software in the broadest strokes; there will be a 'mission' mode, wherein the device keeps time, generates noise and detects REM, and an 'interface' mode, where the user is navigating menus and setting things up. The reason I plan to segregate this way is twofold: first, the user can't navigate most of the menus while wearing the device, so we'll never have to do both (generated noise/sense REM <i>and</i> generate the interface display); second, generating the random numbers for the noise might take up a significant fraction of the available processing power, so I want to leave as many cycles spare as I can during 'mission' operation.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
For particular computational tasks, I have put together some research/notes and even performed some simulations to verify cycle costs and correct theoretical behavior. Specifically, I've collected a number of online resources toward the task of generating reasonably white noise from pseudorandom noise generators on a microcontroller substrate (<a href="http://www.donnelly-house.net/programming/cdp1802/8bitPRNGtest.html">8-bit XORshift</a>, <a href="http://www.arklyffe.com/main/2010/08/29/xorshift-pseudorandom-number-generator/">wider XORshift</a>, <a href="http://www.firstpr.com.au/dsp/rand31/">linear congruential generators</a>). Additionally, there are a couple of good online treatments of the problem of generating pink noise from white noise (<a href="http://home.earthlink.net/~ltrammell/tech/pinkalg.htm">here</a> and <a href="http://www.firstpr.com.au/dsp/pink-noise/">here</a>; while red noise is easy (just integrate), pink noise falls off as though passed through a 'half-order' filter, making the whole proposition interesting). In a future post, I'll go over these methods and the results of my simulations/implementations.</div>
<div style="text-align: justify;">
<b><u><br /></u></b></div>
<div style="text-align: justify;">
<b><u>Futureproofing</u></b></div>
<div style="text-align: justify;">
<b><u><br /></u></b></div>
<div style="text-align: justify;">
I cut off the ballooning feature list as outlined above; however, there is a lot left I want to implement. Additionally, since this is a prototype, I wanted the design to be able to be implemented incrementally with the same printed circuit board; I am a poor, struggling, starving, barely-making-the-rent artist type so I really only want to order one version of the board.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
For debug-proofing, the following features will be added, to allow for incremental feature roll-out</div>
<div style="text-align: justify;">
<br /></div>
<ul>
<li style="text-align: justify;">Test pads allowing easy access to the programming port and a simple UART. This will allow for ease of programming before I figure out how to work Atmel's super nifty <a href="http://www.atmel.com/Images/doc8429.pdf">firmware-update-over-usb bootloader</a>. The UART will allow for debug information to pass easily from the device, and allow for proto-interfaces to be coded for the UART before I get the real SPI-OLED interface up.</li>
<li style="text-align: justify;">Trimpots to set the charger IC's constant-current charge level and constant-voltage current threshold. Since the overall power usage is not 100% nailed down at this point, I wanted easy options for the battery. Additionally, this allows me to eventually make the weight/cost/benefit tradeoff without having to pull up and replace set resistors on the board.</li>
<li style="text-align: justify;">Pads and jumpers inline with the REM and noise signals paths. This will allow me to troubleshoot problems with those subsystems (though I hope to have the circuits finalized through practical testing before I order the boards).</li>
<li style="text-align: justify;">The option of a manual trimpot <i>or</i> microcontroller PWM control of the bias level for the REM circuit. My current plan is to use the ongoing signal levels in the REM circuit to set the bias level, allowing for greater signal amplification without running into the edge of my dynamic range. However, this may not be the best solution (or it may just take some finite time to implement), so I'm giving myself the option of manual setting via a trimpot.</li>
</ul>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
As for future-proofing, there is really only one thing that I am altering the hardware for to possibly allow in the future, and that is a pulse oxygenation sensor. This sort of a sensor would allow the device to have access to both the user's pulse rate (potentially improving the REM detection) and the user's blood oxygenation (a decent measure of the efficacy/rate of their breathing). Being able to record these two things would make sleep-problem diagnosis with the device significantly more powerful. Admittedly, my current knowledge of the practical tribulations involved in building a robust pulseox sensor is minimal; however, I know that I will need an analog input, two 180-degree out-of-phase PWM signals and two analog outputs to set the emitter gains (which I will implement with lowpassed PWM signals).</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Additional futureproofing would involve adding a micro SDHC card slot to allow for datalogging of more user sleep data (especially if the pulseox is ever added). Since, in terms of harware, this is as simple as connecting to an SPI port, I will add the footprint and pullup resistors at the end of the board design process if the added device size seems reasonable.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
All of my other potential future features are software changes; for example, I might want to add some code to slowly bring up the visible LED levels to simulate sunrise, leading to better wake-ups. By having the LEDs driven by PWM from the start, this should not require any hardware futureproofing.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<b><u><span style="font-size: large;">Work to date</span></u></b></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Beyond the general design work, I've begun to mock up sub-circuits to test them and refine the design. Additionally, I've begun to modify a sleep mask with the various bits and bobs necessary to detect rapid eye movement.<br />
<br />
I've mocked up the 3.3V buck circuit (shown below). It is able to source more than 100mA, as detailed in the data sheet. In fairness, it probably wasn't necessary to verify this simple circuit, but I had a bad time with a similar boost as part of a project during college, so I'm paranoid about switched supplies.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK_hGIKQVU8q3s8yXlunLfv3nYokYRVoVTbBosxxgsgmhDxQRW8o0RnH6hatLU2HPu1llr_5vQSi5V65WWdzw7RD7J2R9nR_W4Q8jsqs2-GC-BFhpPi9aLlOP9FY80AUFzkhYAreIgcU0/s1600/mask_buck_test_1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK_hGIKQVU8q3s8yXlunLfv3nYokYRVoVTbBosxxgsgmhDxQRW8o0RnH6hatLU2HPu1llr_5vQSi5V65WWdzw7RD7J2R9nR_W4Q8jsqs2-GC-BFhpPi9aLlOP9FY80AUFzkhYAreIgcU0/s400/mask_buck_test_1.JPG" width="400" /></a></div>
<br />
Here is the sleep mask with the infrared REM emitter/detectors installed; I've tried to keep as little as possible of the circuit installed on the mask. The leads from the emitter and detectors (emitter in the middle) are folded over to keep them all in place. The wires are sewn onto the mask to keep things tidy. The longer unconnected wires will eventually be trimmed and connected to a visible LED in each eyecup.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVru1Qbhm29EBbhnaV4WtEq3k7UJMQZ1oxGcvGLBnJySPsWd3EH8NY6zv3phNhUiU1Ku8KJkFkfEx3hdTYesKjFG4-CLdZeFc7we6Pp_hpGWkXbccY_3xCEuW7GQfcJ2dS1irE-5kaHYY/s1600/mask_mask_front_1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVru1Qbhm29EBbhnaV4WtEq3k7UJMQZ1oxGcvGLBnJySPsWd3EH8NY6zv3phNhUiU1Ku8KJkFkfEx3hdTYesKjFG4-CLdZeFc7we6Pp_hpGWkXbccY_3xCEuW7GQfcJ2dS1irE-5kaHYY/s640/mask_mask_front_1.JPG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdAJuM2XSXwMsaWoaKur8W_eaCAK_UwmQ_1EIOyItUCcBPnBZXLpvTr9ZBLUzWBswECTc2Jwvh6PKjvrv2W_l9-7bT7qMkio58A-673r0nePpO0pN8Oc2YY_tvuX3974RzXjkOn7APGos/s1600/mask_mask_back_1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdAJuM2XSXwMsaWoaKur8W_eaCAK_UwmQ_1EIOyItUCcBPnBZXLpvTr9ZBLUzWBswECTc2Jwvh6PKjvrv2W_l9-7bT7qMkio58A-673r0nePpO0pN8Oc2YY_tvuX3974RzXjkOn7APGos/s640/mask_mask_back_1.JPG" width="640" /></a></div>
<br />
Finally, I've put together the first stage of the REM detector amplifier (shown below). I've already had to reduce the value of the transimpedance feedback resistor from what had been used before to prevent output saturation.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2dNPMo9pB5MsvUsFNvwnOSkLb3pTByFmuc5z_VpRZex6yj1HMlcUX27znjkJBdNwveaz1eh0dmvCc7QCObX7KFozaPXc4ARQwgFHllxLROIvS43XJJSr4uzn4XxIfPKf_TUE-ytea8WI/s1600/mask_transimpedance_test_1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2dNPMo9pB5MsvUsFNvwnOSkLb3pTByFmuc5z_VpRZex6yj1HMlcUX27znjkJBdNwveaz1eh0dmvCc7QCObX7KFozaPXc4ARQwgFHllxLROIvS43XJJSr4uzn4XxIfPKf_TUE-ytea8WI/s640/mask_transimpedance_test_1.JPG" width="492" /></a></div>
<br />
<b><u><span style="font-size: large;">Next steps</span></u></b><br />
<br />
The immediate next step is to take a look at the output of the mocked-up amplifier to determine the characteristics of the REM reflectance signal. My first priority is to nail down the ideal value of the transimpedance feedback resistor to allow for the largest gain without being unable to maintain the voltage between the phototransistors at 3.3V/2. Additionally, this investigation should reveal what the effective bandwidth of the signal is, informing my choice of sampling rate.<br />
<br />
After that, I will completely specify and prototype the whole REM detector circuit. Specifically, I will try out the pulsed-emitter idea I had to reduce power consumption. It will be necessary to see how long the rise time is for the output once the emitter is activated; also, I've got to make certain that the differential output current isn't affected (beyond the obvious) by being pulsed.<br />
<br />
Soon after (and during) those steps, I'm going to mock up the power and programming and USB lines for the ATXMEGA controller, programming it with Atmel's USB-based bootloader to make subsequent reprogramming easier. I'll also make sure that I can talk to the OLED display.<br />
<br />
At that point, I think I'll be ready to design and purchase the boards. I still won't have prototyped the LiPo charger and headphone amplifier circuits, but these are very straightforward (and adequately datasheet-ed) and shouldn't present any surprises.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br /></div>
<br />benhttp://www.blogger.com/profile/03060279023200342569noreply@blogger.com4