<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.rocketnumbernine.com/~d/styles/itemcontent.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
<channel>
<link>
www.rocketnumbernine.com
</link>
<title>
Rocketnumbernine
</title>
<description>
New Posts from Rocketnumbernine
</description>
<link>http://www.rocketnumbernine.com/</link>
<language>en-us</language>
<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.rocketnumbernine.com/rocketnumbernine" /><feedburner:info uri="rocketnumbernine" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
<pubDate>Fri, 16 Dec 2011 07:17:00 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2011/12/16/installing-avr-gcc-on-osx</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/fhOJgjRmcH0/installing-avr-gcc-on-osx</link>
<title>Installing AVR GCC on OS X</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;I lost my AVR GCC installation on the upgrade to OS X Lion and I can't remember where I got the previous version from.&lt;/p&gt;

&lt;p&gt;There's &lt;a href="http://www.obdev.at/products/crosspack/"&gt;CrossPack&lt;/a&gt; and &lt;a href="http://sourceforge.net/projects/osxavr/"&gt;OSX-AVR&lt;/a&gt; binary packages, and you can compile the tool chain by hand to get the latest version (for example, see &lt;a href="http://www.ladyada.net/learn/avr/setup-mac.html"&gt;Option 3 at ladyada.net&lt;/a&gt; but you'll need to apply the latest gcc patches).&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.macports.org/ports.php?by=name&amp;amp;substr=AVR"&gt;MacPorts&lt;/a&gt; can download, compile, and install AVR GCC 4.0.2, but since moving to &lt;a href="http://mxcl.github.com/homebrew/"&gt;Home Brew&lt;/a&gt; for other application installation I was disappointed to find there isn't an official AVR GCC formula yet, but as luck would have it
&lt;a href="https://github.com/larsimmisch"&gt;Lars Immisch&lt;/a&gt; has written a set of formulas to install GCC 4.6.1, they haven't been pulled into the main Formula repository yet so the simplest way to install is:&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;git clone http://github.com/larsimmisch/homebrew-alt.git
brew install homebrew-alt/avr/avr-libc.rb&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;If you don't have brew check its &lt;a href="http://mxcl.github.com/homebrew/"&gt;homepage&lt;/a&gt; for installation details (a one-liner)&lt;/p&gt;

&lt;p&gt;avrdude is available in the standard formula set:
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;brew install avrdude --with-usb&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/fhOJgjRmcH0" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2011/12/16/installing-avr-gcc-on-osx</feedburner:origLink></item>
<item>
<pubDate>Wed, 14 Dec 2011 12:43:00 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2011/12/14/automating-gerbers-from-eagle</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/P3rbq-t6XI4/automating-gerbers-from-eagle</link>
<title>Automating Gerbers from Eagle</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;I was fed up with creating gerber files through the Eagle GUI and bundling them up manually before sending to get manufactured so created a shell script.
It's not rocket science but I couldn't find that much help on the internet so the following may help others figure it out faster - have briefly tested in Eagle 6.0.0 and appears to work the same as 5.0.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/12/14/gerb274x.jpg" width="550" height="405"&gt;&lt;p class='image-caption'&gt;Eagle gerb274x cam file screen&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;Gerber files, for example the top copper &lt;em&gt;.cmp&lt;/em&gt; gerber file (created using the gerb274x cam file in Eagle pictured above), can be created from the shell using the following:&lt;/p&gt;

&lt;p&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;eagle -X -dGERBER_RS274X -oboard.cmp board.brd Top Pads Vias&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;Where Top, Pads, and Vias etc. are the eagle layers to be exported from board.brd to the board.cmp.  This has to be repeated for each gerber file (silkscreen, solder mask etc.) required.
"eagle -?" will display its command line options and you can assign offsets and mirror the output if you wish.  If you're on windows "eaglecon.exe" should be used so it doesn't detach from the console.&lt;/p&gt;

&lt;p&gt;Excellon drill files can be created using the following:&lt;/p&gt;

&lt;p&gt;&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;eagle -X -dEXCELLON -oboard.drd board.brd Drills Holes&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;If the board maker needs a (drl) rack file (you "run drillcfg" from within Eagle), the following lines of sed will produce the same contents from the dri file - I can't figure out how to run drillcfg without prompts without rewriting it. ).  Note this only works as long as you don't adjust the drill sizes when prompted by drillcfg (which you probably shouldn't anyway):&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;cat board.dri | sed -e 's/ *\(T[0-9][0-9]\) *\([0-9.]*..\).*/\1 \2/' | \
    grep -e &amp;quot;^T[0-9][0-9]&amp;quot; &amp;gt; board.drl&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;Finally, here's my script to create a zip of everything ready to send.  Obviously use at your own risk - try creating through both the GUI and command line method and compare the output files to see what the differences are until you've tweaked it to your requirements.&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;a href="#n14" name="n14"&gt;14&lt;/a&gt;
&lt;a href="#n15" name="n15"&gt;15&lt;/a&gt;
&lt;a href="#n16" name="n16"&gt;16&lt;/a&gt;
&lt;a href="#n17" name="n17"&gt;17&lt;/a&gt;
&lt;a href="#n18" name="n18"&gt;18&lt;/a&gt;
&lt;a href="#n19" name="n19"&gt;19&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n21" name="n21"&gt;21&lt;/a&gt;
&lt;a href="#n22" name="n22"&gt;22&lt;/a&gt;
&lt;a href="#n23" name="n23"&gt;23&lt;/a&gt;
&lt;a href="#n24" name="n24"&gt;24&lt;/a&gt;
&lt;a href="#n25" name="n25"&gt;25&lt;/a&gt;
&lt;a href="#n26" name="n26"&gt;26&lt;/a&gt;
&lt;a href="#n27" name="n27"&gt;27&lt;/a&gt;
&lt;a href="#n28" name="n28"&gt;28&lt;/a&gt;
&lt;a href="#n29" name="n29"&gt;29&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n31" name="n31"&gt;31&lt;/a&gt;
&lt;a href="#n32" name="n32"&gt;32&lt;/a&gt;
&lt;a href="#n33" name="n33"&gt;33&lt;/a&gt;
&lt;a href="#n34" name="n34"&gt;34&lt;/a&gt;
&lt;a href="#n35" name="n35"&gt;35&lt;/a&gt;
&lt;a href="#n36" name="n36"&gt;36&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;#! /bin/bash
# andrew@rocketnumbernine.com - Use freely, for whatever, at your own risk.

EAGLE=&amp;quot;/Applications/EAGLE/EAGLE.app/Contents/MacOS/EAGLE&amp;quot;

if [ $# -lt 1 ] ; then
        echo &amp;quot;usage $0 &amp;lt;path-to-board&amp;gt; [output-dir-and-file-prefix]&amp;quot;
        echo &amp;quot; e.g:  $0 my_boards/boardx.brd gerbers/board&amp;quot;
        echo &amp;quot;  will create gerber files with eagle in a directory called gerbers&amp;quot;
        echo &amp;quot;  and bundle into gerbers.zip&amp;quot;
        exit 1
fi

board=&amp;quot;$1&amp;quot;
outputfile=${2:-&amp;quot;$(dirname $board)/$(basename $board .brd)&amp;quot;}
outputdir=$(dirname ${outputfile})

if [ ! -d ${outputdir} ]; then
        mkdir -p ${outputdir}
fi
set -e

# create top copper (cmp), bottom copper (sol), top solder mask (stc), bottom solder mask (sts), {top} silkscreen (plc)
${EAGLE} -X -dGERBER_RS274X -o${outputfile}.cmp ${board} Top Pads Vias
${EAGLE} -X -dGERBER_RS274X -o${outputfile}.sol ${board} Bottom Pads Vias
${EAGLE} -X -dGERBER_RS274X -o${outputfile}.stc ${board} tStop
${EAGLE} -X -dGERBER_RS274X -o${outputfile}.sts ${board} bStop
${EAGLE} -X -dGERBER_RS274X -o${outputfile}.plc ${board} Dimension tPlace

# create drill files
${EAGLE} -X -dEXCELLON -o${outputfile}.drd ${board} Drills Holes
# get drills used from dri file and create drl rack file 
# (like running drillcfg.ulp: ${EAGLE} -N- -C'RUN drillcfg.ulp; QUIT;' -o${outputfile} ${board}
cat ${outputfile}.dri | sed -e 's/ *\(T[0-9][0-9]\) *\([0-9.]*..\).*/\1 \2/' | grep -e &amp;quot;^T[0-9][0-9]&amp;quot; &amp;gt; ${outputfile}.drl

zip -r ${outputdir}.zip ${outputdir}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/P3rbq-t6XI4" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2011/12/14/automating-gerbers-from-eagle</feedburner:origLink></item>
<item>
<pubDate>Mon, 28 Nov 2011 12:00:00 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2011/11/28/smt-table-top-reflow-oven-part-3</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/O7DZ7BIKsEU/smt-table-top-reflow-oven-part-3</link>
<title>SMT Table Top Reflow Oven (part 3): Final Build</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;Two &lt;a href="http://www.rocketnumbernine.com/2009/06/03/smt-table-top-reflow-oven-part-1"&gt;previous&lt;/a&gt; &lt;a href="http://www.rocketnumbernine.com/2009/08/27/smt-table-top-reflow-oven-part-2"&gt;articles&lt;/a&gt; described the my attempts to create a Reflow Oven.
I finally thought it time to document the final construction.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/oven.jpg"&gt;&lt;/p&gt;

&lt;h3&gt;Thermocouple&lt;/h3&gt;


&lt;p&gt;Reading a K-Type Thermcouple from a microcontroller with a MAX6675 chip was described &lt;a href="http://www.rocketnumbernine.com/2009/06/27/using-the-max6675-thermocouple-to-digital-converter"&gt;here&lt;/a&gt;.  I ruined several thermocouple wires when they got snagged/bent and the wire broke.  To reduce the time to replace a wire I wrapped two together with kapton tape, so the backup can be plugged in if the first breaks.  The two wires also makes the cable less likely to bend and break, particularly when further wrapped in some heatshrink tubing.
The thermocouple was inserted through a hole in the oven and position just above the centre of the tray.&lt;/p&gt;

&lt;p&gt;The MAX6675 code was wrapped in a simple C++ class with a read() method to return the temperature multiplied by 4 (as it comes back from the MAX6675) as an integer.
See &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/MAX6675/MAX6675.h"&gt;MAX6675.h&lt;/a&gt; and &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/MAX6675/MAX6675.cpp"&gt;MAX6675.cpp&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Rotary Encoder&lt;/h3&gt;


&lt;p&gt;By removing the existing front panel switches it was possible to mount a rotary encoder using the existing holes and fixtures.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/front.jpg"&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/encoder.jpg"&gt;&lt;p class='image-caption'&gt;Rotary Encoder Fit into the space from the ovens timer and 'on/off bar control' knobs.&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;Decoding was done as described &lt;a href="http://www.rocketnumbernine.com/2010/03/06/decoding-a-rotary-encoder"&gt;here&lt;/a&gt;, except that the Arduino interrupt mechanism was used:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;attachInterrupt(&lt;span class="integer"&gt;0&lt;/span&gt;, knob_turned, CHANGE);
attachInterrupt(&lt;span class="integer"&gt;1&lt;/span&gt;, knob_turned, CHANGE);&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

This will result in the knob&lt;em&gt;turned() method being called when pins 2 and 3 (connected to the rotary encoder) change level.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;a href="#n14" name="n14"&gt;14&lt;/a&gt;
&lt;a href="#n15" name="n15"&gt;15&lt;/a&gt;
&lt;a href="#n16" name="n16"&gt;16&lt;/a&gt;
&lt;a href="#n17" name="n17"&gt;17&lt;/a&gt;
&lt;a href="#n18" name="n18"&gt;18&lt;/a&gt;
&lt;a href="#n19" name="n19"&gt;19&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n21" name="n21"&gt;21&lt;/a&gt;
&lt;a href="#n22" name="n22"&gt;22&lt;/a&gt;
&lt;a href="#n23" name="n23"&gt;23&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; knob_turned()
{
  _delay_ms(&lt;span class="integer"&gt;1&lt;/span&gt;);
  &lt;span class="predefined-type"&gt;int&lt;/span&gt; pin0 = digitalRead(ENCODER_LEFT);
  &lt;span class="predefined-type"&gt;int&lt;/span&gt; pin1 = digitalRead(ENCODER_RIGHT);
  &lt;span class="keyword"&gt;if&lt;/span&gt; (pin0 != pinValues[&lt;span class="integer"&gt;0&lt;/span&gt;]) {
    rotary_encoder_change(&lt;span class="integer"&gt;0&lt;/span&gt;, pin0);
  } &lt;span class="keyword"&gt;else&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (pin1 != pinValues[&lt;span class="integer"&gt;1&lt;/span&gt;]) {
    rotary_encoder_change(&lt;span class="integer"&gt;1&lt;/span&gt;, pin1);
  }
}

&lt;span class="directive"&gt;void&lt;/span&gt; rotary_encoder_change(uint8_t changedPin, uint8_t value)
{
  pinValues[changedPin] = value;
  &lt;span class="comment"&gt;// only increment for each 'click' of the dial - when both pins have gone back to 0&lt;/span&gt;
  &lt;span class="keyword"&gt;if&lt;/span&gt; (value == &lt;span class="integer"&gt;0&lt;/span&gt; &amp;amp;&amp;amp; pinValues[&lt;span class="integer"&gt;0&lt;/span&gt;] == pinValues[&lt;span class="integer"&gt;1&lt;/span&gt;]) {
    &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;long&lt;/span&gt; this_change = millis();
    &lt;span class="comment"&gt;// if the change is within 50ms of the last then move 10 positions rather than 1&lt;/span&gt;
    &lt;span class="predefined-type"&gt;short&lt;/span&gt; multiplier = (last_changed != &lt;span class="integer"&gt;0&lt;/span&gt; &amp;amp;&amp;amp; (this_change - last_changed) &amp;lt; &lt;span class="integer"&gt;50&lt;/span&gt;) ? &lt;span class="integer"&gt;10&lt;/span&gt; : &lt;span class="integer"&gt;1&lt;/span&gt;;
    target_temp += ((changedPin) ? &lt;span class="integer"&gt;1&lt;/span&gt; : -&lt;span class="integer"&gt;1&lt;/span&gt;) * multiplier;
    last_changed = this_change;
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

The &lt;/em&gt;delay_ms(1) is used to debounce the encoder (the Arduino delay() method can't be used from within an interrupt).&lt;/p&gt;

&lt;p&gt;Currently the target maximum temperature is set by the rotary encoder - to speed selection, the time when the last change happened is recorded and if the next change occurs within 50ms the temperature is incremented/decremented by 10 rather than 1.&lt;/p&gt;

&lt;p&gt;The default Arduino interrupt library only supports 2 external interrupts - rather than resorting to AVR lib interrupt routine to capture when the push button is pressed - I simply polled it in the loop() method (which runs every 200ms).  Pushing the button is used to turn the device on and off.&lt;/p&gt;

&lt;h3&gt;LED&lt;/h3&gt;


&lt;p&gt;The original oven had some kind of bulb that's powered by mains when the elements are on, this was replaced with a red LED controlled from the Arduino.  Rather than using a current limiting resistor I just connected to one of the PWM pins (9) and used analogWrite to power for a fraction of the time:
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;analogWrite(&lt;span class="integer"&gt;9&lt;/span&gt;, &lt;span class="integer"&gt;70&lt;/span&gt;);&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;h3&gt;LCD&lt;/h3&gt;


&lt;p&gt;I wanted the oven to be self contained so the screen needs to be big enough to display what's going on and to and display options etc.  A 2x8 LCD was a little small, 2x16 just about big enough for all the information.  There was no way to really house it neatly in the existing front panel so it was positioned vertically and some brushed aluminium sheet used to create a new panel.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/new-panel.jpg"&gt;&lt;p class='image-caption'&gt;New front panel showing LCD, Dial, and on/off LED button.&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;The standard LiquidCrystal Arduino library was used with the LCD connected to the analogue pins:
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;LiquidCrystal lcd(A0, A1, A2, A3, A4, A5);&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;The display was updated every 200 milliseconds.  The current test sketch display is shown below, the display code is in the update_display() method in the &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/TableTopReflow/TableTopReflow.pde"&gt;sketch&lt;/a&gt;.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/lcd.jpg"&gt;&lt;p class='image-caption'&gt;Close up of the Aluminium panel, with LCD showing actual Temperature, rate of change (&amp;deg;C/Second), whether the heating elements are on, the time since heating session started, and the target temperature.&lt;/p&gt;&lt;/div&gt;




&lt;h3&gt;Control&lt;/h3&gt;


&lt;p&gt;After completing the front panel construction I fitted the Arduino into an ABS box on the back of the oven - its cooler there and is easier to get to.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/case.jpg"&gt;&lt;p class='image-caption'&gt;Arduino housed in ABS Box on back of oven.&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;I had originally intended to use a predefined reflow profile and a PID algorithm, but testing showed that with both the heater elements full on the oven reached peak (240&amp;deg;C) in approximately 300 seconds.  The preheat is a little short and the soak and reflow stages a little long:&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/annotated-chart.png"&gt;&lt;p class='image-caption'&gt;Temp/Time chart (for empty oven).&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;Using a PID algorithm won't make the reflow stage any quicker.  To test I've used a small Arduino script that lets the user select the maximum temperature, the heating elements are then turned on until the max is reached.  Usually open the door to increase the cooling rate.&lt;/p&gt;

&lt;p&gt;The source can be found &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/TableTopReflow/TableTopReflow.pde"&gt;here&lt;/a&gt;, a few tests have shown acceptable results.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/attiny13.jpg"&gt;&lt;p class='image-caption'&gt;First Reflow solder test: SOIC 16, perhaps to little solder.&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;I'd bridged a few of the pads with smear of paste but the reflow process has successfully 'sucked' the solder onto the pad apart from a few beads.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/11/28/closeup1.jpg"&gt;
    &lt;p class='image-caption'&gt;Closeup of solder joint.&lt;/p&gt;&lt;/div&gt;




&lt;h3&gt;Conclusions&lt;/h3&gt;


&lt;p&gt;I've been using the oven for awhile now on simple boards with some success - the boards have been fairly simple and haven't had to fix any issues.  I'm still deciding whether to spend the time implementing (and more importantly tuning) a PID controller for the oven.  My feeling is that the ovens elements can't be made to increase/decrease the ovens temperature quick enough to make it worthwhile.&lt;/p&gt;

&lt;p&gt;Others say that applying solder paste by hand and using an oven is as time consuming as using a soldering iron, but I find the process quicker, cleaner (even with a solder fume extractor), and less of an eyestrain - I find it easier to hold a syringe of paste under a microscope than a soldering iron.&lt;/p&gt;

&lt;h3&gt;References/Bibliography&lt;/h3&gt;


&lt;ol&gt;
    &lt;li&gt;&lt;a href="http://www.rocketnumbernine.com/2009/06/03/smt-table-top-reflow-oven-part-1"&gt;SMT Table Top Reflow Oven (part 1)&lt;/a&gt;
    &lt;li&gt;&lt;a href="http://www.rocketnumbernine.com/2009/08/27/smt-table-top-reflow-oven-part-2"&gt;SMT Table Top Reflow Oven (part 2): Controlling the Heater Elements&lt;/a&gt;
    &lt;li&gt;&lt;a href="http://www.rocketnumbernine.com/2009/06/27/using-the-max6675-thermocouple-to-digital-converter"&gt;Using the MAX6675 Thermocouple to Digital Converter&lt;/a&gt;
    &lt;li&gt;&lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/TableTopReflow/TableTopReflow.pde"&gt;Arduino Test Sketch&lt;/a&gt;
&lt;/ol&gt;



&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/O7DZ7BIKsEU" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2011/11/28/smt-table-top-reflow-oven-part-3</feedburner:origLink></item>
<item>
<pubDate>Tue, 25 Oct 2011 06:36:00 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2011/10/25/programming-the-ad9851-dds-synthesizer</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/-gfFXVbk-DA/programming-the-ad9851-dds-synthesizer</link>
<title>Programming the AD9851 DDS synthesizer</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;I don't have and have never really had the desire for a signal generator, but have wanted to play with the &lt;a href="http://www.analog.com/en/rfif-components/direct-digital-synthesis-dds/ad9851/products/product.html" title="AD9851 Product Page"&gt;AD9851 DDS chip&lt;/a&gt; (a digitally controllable 0-180MHz sine wave generator) for awhile and managed to win an &lt;a href="http://shop.ebay.com/i.html?_from=R40&amp;amp;_trksid=p5197.m570.l1313&amp;amp;_nkw=ad9851+circuit&amp;amp;_sacat=See-All-Categories" title="Ebay AD9851"&gt;ebay&lt;/a&gt; auction for a "AD9851 Module DDS Signal Generator with Circuit Diagram" for $23.50 including shipping from China to Europe, that's cheaper than &lt;a href="http://search.digikey.com/scripts/DkSearch/dksus.dll?keywords=ad9851" title="Digikey AD9841 page"&gt;digikey&lt;/a&gt; charge for the chip alone!&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/10/25/board.jpg" alt="" title="AD9851 DDS Board" width="500" height="355" /&gt;&lt;p class='image-caption'&gt;AD9851 DDS Board&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;The board arrived in less than a week and was marked as "valued at $3" so no customs were charged.  The &lt;a href="http://www.kuuco.com/profile/090092_AD9851.rar"&gt;Circuit diagram&lt;/a&gt; included on the ebay auction had no resemblance to the device I got, but it seems the design is the same or similar to most of the barebone AD9851 boards on &lt;a href="http://shop.ebay.com/i.html?_from=R40&amp;amp;_trksid=p5197.m570.l1313&amp;amp;_nkw=ad9851+circuit&amp;amp;_sacat=See-All-Categories" title="Ebay AD9851"&gt;ebay&lt;/a&gt; - just the chip, 30MHz crystal, header pins, bypass caps, and a low pass filter.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/10/25/schematic.png" alt="" title="Schematic" width="500" height="355" /&gt;&lt;p class='image-caption'&gt;Approximate Schematic found on ebay (D7 and GND are swapped on my board but otherwise the components and PCB traces seem to just about match).&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;The device has 3 7-pin headers: 2 control headers (only 1 is needed for serial control), an output header providing the raw outputs provided by the AD09851, and 4 2-pin junction connectors, used to select serial/parallel mode, externally control the output current and for the generated sine wave itself.&lt;/p&gt;

&lt;p&gt;The AD9851 is controlled (see the &lt;a href="http://www.analog.com/static/imported-files/data_sheets/AD9851.pdf" title="AD9851 Datasheet"&gt;datasheet&lt;/a&gt;) by a 40 bit control word consisting of a 32 bit 'frequency tuning word' and an 8 bit 'control' word comprising of 5 bits of phase angle (so that multiple AD9851s can be controlled from the same clock outputing the same sine wave shifted by a different amount.), a shutdown bit, and a flag indicating whether the system clock should be multiplied (allowing the device to generate a 180MHz from a 30MHz oscillator).&lt;/p&gt;

&lt;p&gt; The frequency of the sine wave produced is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;        &amp;lt;system clock&amp;gt; * &amp;lt;frequency tuning word&amp;gt; / 2^32
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;so the 'frequency tuning word' required to send to the device is calculated as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;        &amp;lt;desired frequency&amp;gt; * 4294967296.0 / 180.0e6;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Because the 'frequency tuning word' is a 32 bit integer, when it is incremented by 1 the sine wave increases by .0419Hz - also the minimum frequency - when the tuning word is set to 1 a slow rising signal can be seen on a scope.&lt;/p&gt;

&lt;p&gt;The control word can either be transferred in parallel (byte at a time) or serially.  In serial mode the chip can be controlled with only 3 pins (plus VCC and GND), basically a bit from the control word is written to the DATA pin, W&lt;em&gt;CLK is pulsed high until all 40 bits have been sent and then FQ&lt;/em&gt;UD is pulsed high to transfer the 40 bits from a buffer to the control register as shown below from the datasheet:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/10/25/serial-load.png"/&gt;&lt;/p&gt;

&lt;p&gt;I found the reset pin could be left tied low in these simple tests but i'd do an explicit hardware reset pulse in an application.
The following is some Arduino/C code to transfer the data to an AD9851 on pins DATA, W&lt;em&gt;CLK, and FQ&lt;/em&gt;UD.&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;a href="#n14" name="n14"&gt;14&lt;/a&gt;
&lt;a href="#n15" name="n15"&gt;15&lt;/a&gt;
&lt;a href="#n16" name="n16"&gt;16&lt;/a&gt;
&lt;a href="#n17" name="n17"&gt;17&lt;/a&gt;
&lt;a href="#n18" name="n18"&gt;18&lt;/a&gt;
&lt;a href="#n19" name="n19"&gt;19&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n21" name="n21"&gt;21&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="preprocessor"&gt;#define&lt;/span&gt; pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

&lt;span class="comment"&gt;// transfer a byte a bit at a time LSB first to DATA&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; tfr_byte(byte data)
{
  &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; i=&lt;span class="integer"&gt;0&lt;/span&gt;; i&amp;lt;&lt;span class="integer"&gt;8&lt;/span&gt;; i++, data&amp;gt;&amp;gt;=&lt;span class="integer"&gt;1&lt;/span&gt;) {
    digitalWrite(DATA, data &amp;amp; &lt;span class="hex"&gt;0x01&lt;/span&gt;);
    pulseHigh(W_CLK);
  }
}

&lt;span class="comment"&gt;// frequency of signwave (datasheet page 12) will be:&lt;/span&gt;
&lt;span class="comment"&gt;//   &amp;lt;sys clock&amp;gt; * &amp;lt;frequency tuning word&amp;gt; / 2^32&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; sendFrequency(&lt;span class="predefined-type"&gt;double&lt;/span&gt; frequency) {
  int32_t freq = frequency * &lt;span class="integer"&gt;429496729&lt;/span&gt;&lt;span class="float"&gt;6&lt;/span&gt;&lt;span class="float"&gt;.0&lt;/span&gt; / &lt;span class="integer"&gt;18&lt;/span&gt;&lt;span class="float"&gt;0&lt;/span&gt;&lt;span class="float"&gt;.0e6&lt;/span&gt;;
  &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; b=&lt;span class="integer"&gt;0&lt;/span&gt;; b&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt;; b++, freq&amp;gt;&amp;gt;=&lt;span class="integer"&gt;8&lt;/span&gt;) {
    tfr_byte(freq &amp;amp; &lt;span class="hex"&gt;0xFF&lt;/span&gt;);
  }
  tfr_byte(&lt;span class="hex"&gt;0x01&lt;/span&gt;); &lt;span class="comment"&gt;// control byte - zero phase shift&lt;/span&gt;
  pulseHigh(FQ_UD);
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;Get the full sketch  &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/AD9851/ad9851.pde"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that a microcontoller SPI bus can also be used to talk to the device (SPI Mode 0, LSB first, replace tfr&lt;em&gt;byte() with SPI.transfer()) with SPI SCK and MOSI pins used for W&lt;/em&gt;CLK and DATA pins respectively.  Doing it manually allows any I/O pins to be used, but is a little slower.  Full source of a SPI arduino sketch is &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/AD9851/ad9851-spi.pde"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below shows a quick oscilloscope trace for a 1303Hz wave - it looks reasonable.
&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2011/10/25/waveform.png"/&gt;&lt;/p&gt;

&lt;p&gt;That's it for now - I might spend some more time looking at the output waveforms if I can find a use for the device.&lt;/p&gt;

&lt;h3&gt;References/Bibliography&lt;/h3&gt;


&lt;p&gt;&lt;il&gt;
&lt;li&gt;&lt;a href="http://www.analog.com/en/rfif-components/direct-digital-synthesis-dds/ad9851/products/product.html"&gt;AD9851 Product Page"&lt;/a&gt;
&lt;li&gt;&lt;a href="http://www.analog.com/static/imported-files/data_sheets/AD9851.pdf"&gt;AD9851 Datasheet&lt;/a&gt;
&lt;li&gt;&lt;a href="http://shop.ebay.com/i.html?_from=R40&amp;_trksid=p5197.m570.l1313&amp;_nkw=ad9851+circuit&amp;_sacat=See-All-Categories"&gt;Ebay AD9851 Search&lt;/a&gt;
&lt;li&gt;&lt;a href="http://search.digikey.com/scripts/DkSearch/dksus.dll?keywords=ad9851"&gt;Digikey AD9841&lt;/a&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/AD9851/ad9851-spi.pde"&gt;Example Arduino Sketch&lt;/a&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/AD9851/ad9851-spi.pde"&gt;Example SPI Version Arduino Sketch&lt;/a&gt;
&lt;/il&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/-gfFXVbk-DA" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2011/10/25/programming-the-ad9851-dds-synthesizer</feedburner:origLink></item>
<item>
<pubDate>Thu, 11 Nov 2010 13:24:56 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2010/11/11/using-the-icm7218icm7228-led-driver</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/Pcy5c5ozB0I/using-the-icm7218icm7228-led-driver</link>
<title>Using the ICM7218 LED driver</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;Looking for something interesting to reach the minimum value on a component order, I picked up a couple of &lt;a href="http://datasheets.maxim-ic.com/en/ds/ICM7218-ICM7228.pdf"&gt;Maxim ICM7128&lt;/a&gt; LED drivers, actually I got some Intersil ICM7228 which is interchangeable.  The following shows some experiments to explore its capabilities - an Arduino is used but the C code extracts should be usable on any platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/rocketnumbernine/5166070577/" title="ICM7228 LED Driver Test Setup"&gt;&lt;img src="http://farm5.static.flickr.com/4045/5166070577_77cf4b83c3.jpg" width="500" height="234" alt="ICM7228 LED Driver Test setup" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Overview&lt;/h3&gt;


&lt;p&gt;The two main issues with driving LEDs from a microcontroller is the number of IO pins required often exceeds that available, and the current drawn by the LEDs can exceed the maximum allowed from the device.  Although &lt;a href="http://en.wikipedia.org/wiki/Charlieplexing"&gt;Charlieplexing&lt;/a&gt; and driver circuitry can be used to get around these, an off the shelf chip that takes the burden can simplify a project.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://datasheets.maxim-ic.com/en/ds/ICM7218-ICM7228.pdf"&gt;Maxim ICM7128&lt;/a&gt; can drive up to 8 7-segment displays or an 8 by 8 matrix of individual LEDs with only 2 extra components (2 bypass capacitors).  The 7218 comes in a number of varieties, for example, 7218A and 7218B variants provide support for common anode and common cathode circuits respectively.&lt;/p&gt;

&lt;p&gt;The example circuit below is from Intersil ICM 7228 datasheet (exact pins vary between variants but functionality is same), the pins connected to switches are instead wired to a microcontroller.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/rocketnumbernine/5166701074/" title="ICM7228A example circuit"&gt;&lt;img src="http://farm5.static.flickr.com/4113/5166701074_589ce7ed69.jpg" width="500" height="273" alt="ICM7228A pinout" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Data can be output to the device in raw/undecoded mode which allows each 7-segment segment and the decimal point to be individually controlled (which would be used if an LED matrix were being used), or in Hex (0123456789AbCdEF) or Code-B (0123456789-EHLP ) format which eliminates the need for the microcontroller program to store the segments that need to be lit for each number.&lt;/p&gt;

&lt;p&gt;Once the desired state of the LEDs is sent to the 7218, it takes care of multiplexing the LED matrix,  each segment is lit every 1/250 of a second - which should ensure no flicker is visible.&lt;/p&gt;

&lt;p&gt;The Maxim 7218A can provide a minimum of 30mA per LED.  However, the common cathode version (7218B/7228B) only provides a quater of that.&lt;/p&gt;

&lt;h3&gt;Experiments&lt;/h3&gt;


&lt;p&gt;The 7228 is fairly simple to use, the 8 data bus pins: ID0,..,ID7 and MODE pin are set to desired values and the WRITE is pulsed low for atleast 250ns to indicate the device should read.
Each interaction with the device follows the same pattern: a control byte followed by 0 or more data bytes containing data for display:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;MODE is set to 0 and a byte of control data is written to ID0,..,ID7 and WRITE pulsed low.
  &lt;li&gt;MODE is set to 1 and 0 or more data bytes are written to ID0,...,ID7 each followed by a low pulse of WRITE.
&lt;/ol&gt;


&lt;p&gt;The following shows some Arduino code to test the various features of the 7228, the full Arduino source can be found &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/ICM7218/ICM7218_test.pde"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First the 8 data pins and 2 control pins are connected to the arduino, we'll define some constants to simplify accessing them from code:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="preprocessor"&gt;#define&lt;/span&gt; MODE_PIN &lt;span class="integer"&gt;10&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; WRITE_PIN &lt;span class="integer"&gt;11&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; ID0_PIN &lt;span class="integer"&gt;2&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; ID_PIN(i) ID0_PIN+i&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;So the Mode and Write pins are on Arduino Digital Pins 10 and 11, to simplify the code we assume that pins ID0 to ID7 are connected to consecutive Arduino pins starting at pin 2, so we can define a function ID_PIN() to give us a particular pins number based on an offset, so to set ID4 high we can write:
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;digitalWrite(ID_PIN(&lt;span class="integer"&gt;4&lt;/span&gt;), HIGH);&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;To pulse the write pin low we write a 0 and then 1 (a digitalWrite call takes approximately 5Âµs on a 16MHz Arduino, which is longer than the 250ns required minimum low WRITE pin pulse width - see the write cycle timing in Figure 3 on page 7 of the &lt;a href="http://datasheets.maxim-ic.com/en/ds/ICM7218-ICM7228.pdf"&gt;datasheet&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; writePulse() {
  digitalWrite(WRITE_PIN, LOW);
  &lt;span class="comment"&gt;// 250ns delay not explicitly required&lt;/span&gt;
  digitalWrite(WRITE_PIN, HIGH);
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;In the following examples, we'll store the values to be sent to the ID0,..ID7 pins in the 8 bits of an unsigned char (with the value of ID0 in bit 0, ID1 in bit 1 etc).  The following helper function takes the desired value of the MODE pin and an array of data, each bit is written to its corresponding IO pin and therefore the the 7218 input data line and then a writePulse sent to force the device to read the input.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; sendBytes(&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; data[], &lt;span class="predefined-type"&gt;int&lt;/span&gt; length,
               boolean mode)
{
  &lt;span class="comment"&gt;// set the desired mode&lt;/span&gt;
  digitalWrite(MODE_PIN, mode);

  &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; i=&lt;span class="integer"&gt;0&lt;/span&gt;; i&amp;lt;length; i++) {
    &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; c=&lt;span class="integer"&gt;0&lt;/span&gt;; c&amp;lt;&lt;span class="integer"&gt;8&lt;/span&gt;; c++) { &lt;span class="comment"&gt;// for each bit&lt;/span&gt;
      digitalWrite(ID_PIN(c), data[i] &amp;amp; &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;c);
    }
    writePulse(); &lt;span class="comment"&gt;// tell the 7218 to read the data&lt;/span&gt;
  }
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;Because a command to the 7218 device consists of a byte of control data sent with MODE pin high and then 0 or more data bytes sent with MODE pin low, we can abstract into a function, calling the sendBytes twice, first for the control byte and then any data:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; sendCommand(&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; control,
                 &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; digit[], &lt;span class="predefined-type"&gt;int&lt;/span&gt; length)
{
  sendBytes(&amp;amp;control, &lt;span class="integer"&gt;1&lt;/span&gt;, &lt;span class="integer"&gt;1&lt;/span&gt;);
  sendBytes(digit, length, &lt;span class="integer"&gt;0&lt;/span&gt;);
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;So for example, to send a control byte with ID4, ID6, and ID7 set High (and all others low), and then send 8 display data bytes held in array 'digits', we could use the following:
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;sendCommand(&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;6&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;7&lt;/span&gt;, digits, &lt;span class="integer"&gt;8&lt;/span&gt;);&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

1&amp;lt;&lt;4 sets the 4th bit (00010000 in binary) 1&lt;&lt;6 the 6th, and 1&lt;&lt;7 the 7th and we &lt;em&gt;OR&lt;/em&gt; them together to get 11010000.&lt;/p&gt;

&lt;p&gt;The table below summarises the meaning of the ICM7218 data pins in control mode (when MODE is High), each of the features is described below&lt;/p&gt;

&lt;table border="1"&gt;
&lt;tr&gt;&lt;th&gt;Pin&lt;/th&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;if High&lt;/th&gt;&lt;th&gt;if Low&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID7&lt;/td&gt;&lt;td&gt;Sequential Data Update Select&lt;/td&gt;&lt;td&gt;Data Coming&lt;/td&gt;&lt;td&gt;No Data Coming&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID6&lt;/td&gt;&lt;td&gt;Decoding Scheme Selection (if ID5 Low)&lt;/td&gt;&lt;td&gt;Hex&lt;/td&gt;&lt;td&gt;Code B&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID5&lt;/td&gt;&lt;td&gt;Decode/No Decode Selection&lt;/td&gt;&lt;td&gt;No Decode&lt;/td&gt;&lt;td&gt;Decode&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID4&lt;/td&gt;&lt;td&gt;Lower Power Mode Select&lt;/td&gt;&lt;td&gt;Normal Operation&lt;/td&gt;&lt;td&gt;Display disabled&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID3&lt;/td&gt;&lt;td&gt;RAM Bank Select&lt;/td&gt;&lt;td&gt;Bank A&lt;/td&gt;&lt;td&gt;Bank B&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID2&lt;/td&gt;&lt;td&gt;Single Digit Address Mode Select (if ID7 Low)&lt;/td&gt;&lt;td colspan="2"&gt;Digit Address Bit 2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID1&lt;/td&gt;&lt;td&gt;Single Digit Address Mode Select (if ID7 Low)&lt;/td&gt;&lt;td colspan="2"&gt;Digit Address Bit 1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;ID0&lt;/td&gt;&lt;td&gt;Single Digit Address Mode Select (if ID7 Low)&lt;/td&gt;&lt;td colspan="2"&gt;Digit Address Bit 0&lt;/td&gt;&lt;/tr&gt;
  &lt;caption&gt;ICM7228 Control Pin Descriptions&lt;/caption&gt;
&lt;/table&gt;


&lt;h3&gt;Lower Power Mode Select - ID4&lt;/h3&gt;


&lt;p&gt;Sending a control byte with ID4 = 0, puts the ICM7128 into low power 'shutdown' mode: the digits are blanked and the multiplexing oscillator is stopped.  the datasheet claims the devices will use 10ÂµA when shutdown but about 3ÂµA was observed.
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;sendCommand(&lt;span class="hex"&gt;0x00&lt;/span&gt;, &lt;span class="integer"&gt;0&lt;/span&gt;, &lt;span class="integer"&gt;0&lt;/span&gt;);&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

If the device remains on but the the segments are turned off the device uses about 200ÂµA.&lt;/p&gt;

&lt;h3&gt;Decoding - ID5 &amp; ID6&lt;/h3&gt;


&lt;p&gt;ID5 and ID6 control how the ICM7218 will treat the display data that follows, which can be in one of three modes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;No Decode (ID5 High) - each segment of the digits can be controlled individually by a bit of ID0-ID7.  This mode would be used when a matrix of LEDs are being controlled rather than 7-Segment displays.
  &lt;li&gt;Hex Mode (ID5 Low, ID6 High) - the device will treat the input on pins ID0-ID4 as a number and display the according Hexadecimal character on the digit: "0123456789AbCdEF".
  &lt;li&gt;Code-B Mode (ID5 Low, ID6 Low) - the device will treat the input on pins ID0-ID4 as a number and display the according Code B character on the digit: "0123456789-EHLP " (the last digit is a space and can be used to blank a digit)
&lt;/ol&gt;


&lt;p&gt;Note, that in all modes the decimal point is controlled by ID7 - &lt;b&gt;a 0 value will turn it on and 1 value turn it off&lt;/b&gt; - the other way around would have seemed more natural.
When Hex and Code-B mode is being used only 4 bits of data (excluding the decimal point) need to be stored - the 7228 makes use of this by providing ability to store two values for each digit, selected by ID3 - RAM Bank Select - the decimal point is set for both banks however.  I can't see of many practical uses for this.&lt;/p&gt;

&lt;h3&gt;Sequential Data Update Select - ID7&lt;/h3&gt;


&lt;p&gt;After the control byte, display data is sent to the device, this can be a sequence of bytes for each digit (from digit 0, to digit 7) or a data for a specific digit:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;ID7 High - Data Coming, the device will expect a succession of display data on each following pulse of the WRITE pin.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; digit[] = { &lt;span class="hex"&gt;0x80&lt;/span&gt;, &lt;span class="hex"&gt;0x81&lt;/span&gt;, &lt;span class="hex"&gt;0x82&lt;/span&gt;, &lt;span class="hex"&gt;0x83&lt;/span&gt;, &lt;span class="hex"&gt;0x84&lt;/span&gt;, &lt;span class="hex"&gt;0x85&lt;/span&gt;, &lt;span class="hex"&gt;0x86&lt;/span&gt;, &lt;span class="hex"&gt;0x87&lt;/span&gt; };
sendCommand(&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;6&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;7&lt;/span&gt;, digit, &lt;span class="integer"&gt;8&lt;/span&gt;);&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;


  &lt;li&gt;ID7 Low - Data not coming, bits ID0, ID1, and ID2 form the 3 digit address of the single digit data that follows, so we can repeat the behaviour of the example above by looping over each digit:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; i=&lt;span class="integer"&gt;0&lt;/span&gt;; i&amp;lt;&lt;span class="integer"&gt;8&lt;/span&gt;; i++) {
  &lt;span class="comment"&gt;// i will fill the 3 low bits of the command byte(ID0, ID1, and ID2)&lt;/span&gt;
  sendCommand(i | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;6&lt;/span&gt;, &amp;amp;i, &lt;span class="integer"&gt;1&lt;/span&gt;);
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;/ol&gt;


&lt;p&gt;An example of using no decode mode is shown below, it loops around lighting each LED segment (stored in the 'segment' array) on each digit in.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; segment[] = { &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;6&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;, &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;5&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;, &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;2&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;,
       &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;3&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;, &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;0&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;, &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;, &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;2&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;, &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;1&lt;/span&gt; | &lt;span class="hex"&gt;0x80&lt;/span&gt;};
  &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; i=&lt;span class="integer"&gt;0&lt;/span&gt;; i&amp;lt;&lt;span class="integer"&gt;3&lt;/span&gt;; i++) {
    &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; digit=&lt;span class="integer"&gt;0&lt;/span&gt;; digit&amp;lt;&lt;span class="integer"&gt;8&lt;/span&gt;; digit++) {
      &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; j=&lt;span class="integer"&gt;0&lt;/span&gt;; j&amp;lt;&lt;span class="integer"&gt;8&lt;/span&gt;; j++) { 
        sendCommand(digit | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;5&lt;/span&gt;, &amp;amp;segment[j], &lt;span class="integer"&gt;1&lt;/span&gt;);
        delay(&lt;span class="integer"&gt;50&lt;/span&gt;);
      }
    }
  }&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;Although the examples above assume a 7 segment display, the ICM7218 could be used to control a 8x8 matrix of LEDs, here the advantage of offloading multiplexing to an external device is evident - if the desired on/off values are held in an 8 byte array, then the microcontroller can send the data to the device and continue processing:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; data[&lt;span class="integer"&gt;8&lt;/span&gt;] = ... 
  sendCommand(&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;5&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;7&lt;/span&gt;, data, &lt;span class="integer"&gt;8&lt;/span&gt;);&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;Finally, an example that counts up from zero. Each time the count is incremented the 8 digits are filled with the appropriate decimal digit value and the entire set of digits rewritten:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; digit[&lt;span class="integer"&gt;8&lt;/span&gt;];
 &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; count=&lt;span class="integer"&gt;0&lt;/span&gt;; count&amp;lt;&lt;span class="integer"&gt;999999&lt;/span&gt;; count++) {
   &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; power=&lt;span class="integer"&gt;1&lt;/span&gt;, i=&lt;span class="integer"&gt;0&lt;/span&gt;; i&amp;lt;&lt;span class="integer"&gt;8&lt;/span&gt;; i++, power*=&lt;span class="integer"&gt;10&lt;/span&gt;) {
      digit[i] = (count/power % &lt;span class="integer"&gt;10&lt;/span&gt;) | &lt;span class="hex"&gt;0x80&lt;/span&gt;;
    }
    sendCommand(&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;4&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;6&lt;/span&gt; | &lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;7&lt;/span&gt;, digit, &lt;span class="integer"&gt;8&lt;/span&gt;);
    delay(&lt;span class="integer"&gt;50&lt;/span&gt;);
  }&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;


&lt;p&gt;The ICM7218 is a good all-rounder, in summary:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Simplifies control of upto 8 7-segment displays or a 8x8 grid of LEDs by reducing the components required and simplifying microcontroller code.
  &lt;li&gt;Fairly widely available in small quantities, although not cheap - the price varies widely between suppliers so its worth shopping around.
  &lt;li&gt;Available in through hole and SMD versions.
  &lt;li&gt;Incredibly simple to use API.
  &lt;li&gt;Common Cathode versions (B &amp; D) have limited current output.
  &lt;li&gt;Not possible to adjust the power sent to the LEDs (either digitally or through a single trim resistor).
  &lt;li&gt;Uses 10 controller IO pins - although if using Hex or Code-B without decimal point only need to connect 6 pins (ID0-ID3, MODE, and WRITE), the others could be hardwired.  See &lt;a href="&lt;a href="http://ardugonic.blogspot.com/2010/06/icm7218a-combined-with-pcf8574-to.html"&gt;here&lt;/a&gt; for an example of fronting the ICM7218 with an I2C interface chip to reduce the number of pins to 2.
&lt;/ol&gt;


&lt;p&gt;Other LED driver chips are available (for example see &lt;a href="http://para.maxim-ic.com/en/results.mvp?fam=disp_driv"&gt;Maxim's range&lt;/a&gt; - with better LED intensitiy, or SPI/I2C control to reduce the number of IO pins required, but the 7218 has its uses.&lt;/p&gt;

&lt;h3&gt;References/Bibliography&lt;/h3&gt;


&lt;p&gt;&lt;il&gt;
&lt;li&gt;Example &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/ICM7218/ICM7218_test.pde"&gt;Arduino Sketch&lt;/a&gt;
&lt;li&gt;&lt;a href="http://datasheets.maxim-ic.com/en/ds/ICM7218-ICM7228.pdf"&gt;Maxim ICM7218/7228 Datasheet&lt;/a&gt;
&lt;li&gt;&lt;a href="http://www.intersil.com/data/fn/fn3160.pdf"&gt;Intersil ICM7228 datasheet&lt;/a&gt;
&lt;li&gt;&lt;a href="http://ardugonic.blogspot.com/2010/06/icm7218a-combined-with-pcf8574-to.html"&gt;Use of ICM7218 with an I2C chip and only 2 IO pins&lt;/a&gt;
&lt;/il&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/Pcy5c5ozB0I" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2010/11/11/using-the-icm7218icm7228-led-driver</feedburner:origLink></item>
<item>
<pubDate>Sat, 06 Mar 2010 22:03:02 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2010/03/06/decoding-a-rotary-encoder</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/Ty4Kyi8QCns/decoding-a-rotary-encoder</link>
<title>Decoding a Rotary Encoder</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;This article details a simple approach to decoding the output of a 2 bit incremental &lt;a href="http://en.wikipedia.org/wiki/Rotary_encoder"&gt;Rotary Encoder&lt;/a&gt;.  The example uses interrupts with AVR GCC on an Arduino platform, but the same interrupt setup can be used on most AVR chips and the approach will of course work on any device.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2010/03/EncoderOnArduino.png" alt="" title="Encoder On Arduino" width="500" height="357" class="size-full wp-image-419" /&gt;&lt;p class='image-caption'&gt;Rotary Encoder on Arduino prototype shield&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;Rotary Encoders can offer high precision, long life and a simpler/digital interface over analogue devices, such as potentiometers, for reading a rotational movement on a computer.  They're often fairly expensive, but I recently picked up a bargain pack on eBay and Mouser also have a &lt;a href="http://www.mouser.com/Passive-Components/Encoders/_/N-6g7nx?Keyword=rotary+encoder&amp;Ns=Pricing|0"&gt;selection&lt;/a&gt; for around a dollar.&lt;/p&gt;

&lt;p&gt;There are a number of interface and encoding techniques used by encoders, ranging from devices that provide a digital output of the angle of the shaft - with resolutions ranging from 8 to over 8000 counts per revolution, to simple 2 bit incremental encoders which report clockwise or counterclockwise movement of the shaft, this article looks at decoding the latter.&lt;/p&gt;

&lt;p&gt;The rotary encoder indicates movement by toggling the output of two pins from high to low or vice-versa, the direction of rotation is indicated by which line goes high (or low first) as shown in the following diagram from wikipedia:
&lt;a href="http://en.wikipedia.org/wiki/File:Quadrature_Diagram.svg" class="image"&gt;&lt;img alt="" src="http://upload.wikimedia.org/wikipedia/en/thumb/6/68/Quadrature_Diagram.svg/300px-Quadrature_Diagram.svg.png" width="300" height="100" class="aligncenter" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The waveform produced from the encoder I tested with when rotating clockwise then counter clockwise is shown below:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2010/03/RotaryEncoder-Output.bmp" alt="Rotary Encoder Waveform" title="Rotary Encoder Waveform" class="aligncenter size-full wp-image-415" /&gt;&lt;/p&gt;

&lt;h3&gt;Decoding the Encoder&lt;/h3&gt;


&lt;p&gt;There are many ways of decoding the changes in pin logic level to direction of rotation, but I think the following is the simplest.  If you look at the waveforms above, a clear pattern emerges: travel from left to right over the waveform above (simulating rotation the shaft in one direction), when the inputs are the same (both high or both low) the last input to have changed will be A, if the inputs are different then the last input to have changed will be B.  If the shaft is rotated in the reverse direction (travelling from right to left over the waveform) the opposite is true.  If we write these conditions in binary expressions: A is equal to B, last changed pin is A, direction is left we can create a truth table:&lt;/p&gt;

&lt;p&gt;&lt;p&gt;
&lt;table border="1"  align="center"&gt;
&lt;tr&gt;&lt;th&gt;A==B&lt;/th&gt;&lt;th&gt;changedPin==A&lt;/th&gt;&lt;th&gt;direction==left&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;true&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;td&gt;false&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;true&lt;/td&gt;&lt;td&gt;false&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;false&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;td&gt;true&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;false&lt;/td&gt;&lt;td&gt;false&lt;/td&gt;&lt;td&gt;false&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/p&gt;

&lt;p&gt;From this we see that the we can use a simple &lt;a  href="http://en.wikipedia.org/wiki/Exclusive_or"&gt;XOR&lt;/a&gt; to calculate the  direction of travel (direction == left) = (A == B) ^ (changedPin == A)&lt;/p&gt;

&lt;p&gt;Note, that the above generates 4 rotation signals on each complete quadrature rotation - with some encoders this is too high a resolution (the mechanical ones I've tested this with are 'stepped' - they click as the shaft is turned, resting with with both pins at zero).  So it may be better to just record movement when both pins pins have fallen to the low state (A == 0 &amp;amp;&amp;amp; B == 0) and ignore other changes.
&lt;h3&gt;Detecting Pin Changes&lt;/h3&gt;
The two pins from the rotary encoder are attached to digital IO pins on the microcontroller and held to ground with a resistor until rotation causes them to be go high.  Rather than polling for changes, interrupts are used to trigger execution of a handler function every time the line state changes.  This allows for other operations to be done whilst waiting for input.&lt;/p&gt;

&lt;p&gt;In this example AVR external pin change interrupts are used.  These can be used to detect changes on nearly all of the devices IO pins.  PCINT18 and PCINT19 are used below, these are triggered by changes to the AVR IO pins PD2 and PD3 - Arduino pins D2 and D3 respectively, see &lt;a href="http://arduino.cc/en/Hacking/PinMapping168"&gt;here for a mapping&lt;/a&gt;.  Pin change interrupts are handled in 3 groups, PCINT0..7, PCINT8..14, PCINT16...23 and are controlled by the PCICR and PCMSK0-PCMS2 registers, we're using pins in the same group to simplify setup.  Note that we have to use the same interrupt handler to receive events for both pins, the pin value is queried to see which has changed:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; setup()
{
  Serial.begin(&lt;span class="integer"&gt;19200&lt;/span&gt;);
  pinMode(&lt;span class="integer"&gt;2&lt;/span&gt;, INPUT); &lt;span class="comment"&gt;// 2 Encoder pins as inputs&lt;/span&gt;
  pinMode(&lt;span class="integer"&gt;3&lt;/span&gt;, INPUT);
  &lt;span class="comment"&gt;// enable interrupts on those two pins:&lt;/span&gt;
  PCICR |= (&lt;span class="integer"&gt;1&lt;/span&gt; &amp;lt;&amp;lt; PCIE2); 
  PCMSK2 |= (&lt;span class="integer"&gt;1&lt;/span&gt; &amp;lt;&amp;lt; PCINT18) | (&lt;span class="integer"&gt;1&lt;/span&gt; &amp;lt;&amp;lt; PCINT19);
  sei();
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;h3&gt;Debouncing&lt;/h3&gt;
The mechanical encoder I tested with create multiple triggers on a single transition due to bouncing.  The encoders datasheet claims a maximum bounce of 5ms, but I didn't see more than 1ms.  The waveform below shows a very much worse case of a pin transitioning from high to low.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2010/03/RotaryEncoder-Bounce.bmp" alt="" title="Rotary Encoder Output Bounce" class="aligncenter size-full wp-image-408" /&gt;&lt;/p&gt;

&lt;p&gt;This is simply debounced in software by waiting for 1ms and then reading the pin values - if neither has changed from the stored value we ignore the trigger.  Note, that this technique will fail if the shaft is being rotated to fast and the width of the cycle approaches 1ms or the two signals are less than 1ms apart.&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;a href="#n14" name="n14"&gt;14&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;volatile&lt;/span&gt; uint8_t pinValues[&lt;span class="integer"&gt;2&lt;/span&gt;] = {&lt;span class="integer"&gt;0&lt;/span&gt;,&lt;span class="integer"&gt;0&lt;/span&gt;};
&lt;span class="directive"&gt;volatile&lt;/span&gt; &lt;span class="predefined-type"&gt;int&lt;/span&gt; position = &lt;span class="integer"&gt;0&lt;/span&gt;;

ISR(PCINT2_vect)
{
  _delay_ms(&lt;span class="integer"&gt;1&lt;/span&gt;);
  &lt;span class="predefined-type"&gt;int&lt;/span&gt; pin0 = digitalRead(&lt;span class="integer"&gt;2&lt;/span&gt;);
  &lt;span class="predefined-type"&gt;int&lt;/span&gt; pin1 = digitalRead(&lt;span class="integer"&gt;3&lt;/span&gt;);
  &lt;span class="keyword"&gt;if&lt;/span&gt; (pin0 != pinValues[&lt;span class="integer"&gt;0&lt;/span&gt;]) {
    rotary_encoder_change(&lt;span class="integer"&gt;0&lt;/span&gt;, pin0);
  } &lt;span class="keyword"&gt;else&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (pin1 != pinValues[&lt;span class="integer"&gt;1&lt;/span&gt;]) {
    rotary_encoder_change(&lt;span class="integer"&gt;1&lt;/span&gt;, pin1);
  }
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;The previous values are stored in a two value byte array and we refer to pins A and B as 0 and 1 in the code to simplify things.  The Rotary Encoder decode logic is the simple boolean expression ((pinValues[0] == pinValues[1]) ^ changedPin) which returns true or false to indicate clockwise or counter clockwise shaft rotation.  This is used to increment or decrement an integer counter, which the main loop prints back to the host computer.&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; rotary_encoder_change(uint8_t changedPin, uint8_t value)
{
  pinValues[changedPin] = value;
  position += ((pinValues[&lt;span class="integer"&gt;0&lt;/span&gt;] == pinValues[&lt;span class="integer"&gt;1&lt;/span&gt;]) ^ changedPin) ? &lt;span class="integer"&gt;1&lt;/span&gt; : -&lt;span class="integer"&gt;1&lt;/span&gt;;
}

&lt;span class="directive"&gt;void&lt;/span&gt; loop()
{
  Serial.println(position);
  delay(&lt;span class="integer"&gt;100&lt;/span&gt;);
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

&lt;h3&gt;Conclusions&lt;/h3&gt;
This is a fairly simple technique that works well for slow rotations of the shaft, but fast rotations of the shaft get lost or be reported in the wrong direction - good for replacing potentiometers as instrumentation dials but not for recording the exact movement of a motor etc.&lt;/p&gt;

&lt;p&gt;Full source is &lt;a href="http://code.google.com/p/rocketnumbernine/source/browse/trunk/AVR/rotary_encoder/rotary_encoder.pde"&gt;here&lt;/a&gt;.
&lt;h3&gt;References/Bibliography&lt;/h3&gt;&lt;il&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Rotary_encoder"&gt;Wiki Rotary Encoder Page&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="http://arduino.cc/en/Hacking/PinMapping168"&gt;Arduino/Atmega168 pin mapping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf"&gt;Atmega 168/328 datasheet&lt;/a&gt; - see chapter 11 and 12 for interrupt details.&lt;/li&gt;
&lt;/il&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/Ty4Kyi8QCns" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2010/03/06/decoding-a-rotary-encoder</feedburner:origLink></item>
<item>
<pubDate>Sun, 22 Nov 2009 15:24:00 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2009/11/22/external-time-capsule-fan</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/bIFy3wIsLPM/external-time-capsule-fan</link>
<title>External Time Capsule Fan</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;&lt;a href="http://www.flickr.com/photos/rocketnumbernine/4124951592/" title="External Time Capsule Fan"&gt;&lt;img src="http://farm3.static.flickr.com/2721/4124951592_86fafe62f0.jpg" width="500" height="234" alt="External Time Capsule Fan" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My &lt;a href="http://www.apple.com/timecapsule/"&gt;Apple Time Capsule&lt;/a&gt; runs hot - approaching 50°C to the touch - this isn't helped by the lack of space it has and the &lt;a href="http://www.apple.com/appletv/"&gt;Apple TV&lt;/a&gt; stacked on top (I've added some legs to give some space below and above for air to flow.   On large backups/restores during the summer - the device often went into over heat warning mode.  There's also worrying &lt;a href="http://www.guardian.co.uk/technology/blog/2009/nov/04/apple-time-capsule-failures-early"&gt;reports&lt;/a&gt; of the power supply overheating and Time Capsules &lt;a href="http://timecapsuledead.org/"&gt;dying&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I originally thought of a standard PC fan with some kind of thermistor /thermocouple/sensor and a simple control circuit to activate the fan when temperature gets too hot, but stumbled across &lt;a href="http://www.arctic-cooling.com/catalog/product_info.php?mID=279&amp;cPath=3_47_80"&gt;Arctic Cooling Pro TC F9 fan&lt;/a&gt; which has a temperature sensor on a 40cm cable to turn up the fan speed when the temperature rises above 32°C.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/rocketnumbernine/4124949764/" title="Fan with stand attached  (click for annotations)"&gt;&lt;img src="http://farm3.static.flickr.com/2566/4124949764_f8bdb763ba.jpg" width="500" height="333" alt="Fan with stand attached" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Replacing the plug with a 2.1mm power jack to fit a 12V wall wart PSU and creating a stand with a loop of wire coat hanger sleeved in heat shrink to give it a nice black cover to match the fans case and the unit was ready to go.  Taping the end of the temperature sensor to the top of what felt the hottest part of the Time Capsule (over the power supply next to the power inlet) reduced the temperature to less than 30Â°C.  I though the fan might be too low powered initially but it manages to cool all the devices on the shelf it's on.&lt;/p&gt;

&lt;p&gt;The unit is silent - at least quieter than the ambient noise and hopefully will extend the life of the Time Capsule.&lt;/p&gt;

&lt;h3&gt;References/Bibliography&lt;/h3&gt;


&lt;ol&gt;
&lt;li&gt;&lt;a href="http://www.arctic-cooling.com/catalog/product_info.php?mID=279&amp;cPath=3_47_80"&gt;Arctic Cooling Pro TC F9 fan&lt;/a&gt;
&lt;li&gt;&lt;a href="http://timecapsuledead.org/"&gt;The Apple Time Capsule Memorial Register&lt;/a&gt;
&lt;li&gt;&lt;a href="http://www.guardian.co.uk/technology/blog/2009/nov/04/apple-time-capsule-failures-early"&gt;Guardian: What's killing Apple's Time Capsules after 18 months?&lt;/a&gt;
&lt;/ol&gt;

&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/bIFy3wIsLPM" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2009/11/22/external-time-capsule-fan</feedburner:origLink></item>
<item>
<pubDate>Thu, 27 Aug 2009 20:24:16 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2009/08/27/smt-table-top-reflow-oven-part-2</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/ePrxU6eZW5o/smt-table-top-reflow-oven-part-2</link>
<title>SMT Table Top Reflow Oven (part 2): Controlling the Heater Elements</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;&lt;a href="http://www.flickr.com/photos/rocketnumbernine/3735221789/" target="images"title="Oven Heater Element Control Circuit"&gt;&lt;img src="http://farm3.static.flickr.com/2444/3735221789_014454f8f2.jpg" width="500" height="267" alt="Oven Heater Element Control Circuit" /&gt;&lt;/a&gt; In a &lt;a href="http://www.rocketnumbernine.com/2009/06/03/smt-table-top-reflow-oven-part-1/"&gt;previous article&lt;/a&gt; I described insulation of a mini/toaster oven with the aim of using it for table top reflow soldering.  Some manual &lt;a href="http://www.rocketnumbernine.com/2009/06/13/bidirectional-level-converter-pcb/"&gt;tests&lt;/a&gt; have been encouraging.  This article documents creation of the circuitry required to turn the ovens heating elements on and off under control of a microcontroller.  I'm still not convinced that the oven can increase its temperature fast enough to warrant attempting to recreate a 'proper' reflow profile, but even if not,  using a microcontroller to log and display the temperature and turn off the heating elements when the peak is reached will still be worthwhile.&lt;/p&gt;

&lt;h3&gt;The circuitry below involves mains electricity, don't try this unless you are sure you know what you are doing - check, test and insulate repeatedly and thoroughly.&lt;/h3&gt;


&lt;p&gt;  There are a number of commercial relay control boards available, for example &lt;a href="http://www.futurlec.com/Relay_4.shtml"&gt;Futurlec&lt;/a&gt; and &lt;a href="http://www.elektor.com/products/e-blocks/modules/relay-board.530431.lynkx"&gt;Elector&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Hinari HTP033 oven has two elements and a total power rating of 630W - so each draws just over 1.3A at 240V (UK mains voltage), this obviously can't be provided directly from a microcontroller IO pin but requires some kind of &lt;a href="http://en.wikipedia.org/wiki/Relay"&gt;relay&lt;/a&gt;.  There's a huge range of relays available - I choose a &lt;a href="http://www.digchip.com/datasheets/download_datasheet.php?id=920665&amp;part-number=SRRHN-1C-S-5VDC"&gt;IMO SRRHN-1C-S-5VDC&lt;/a&gt;, this can operate at 5Volts, draws 100mA (I actually found it took under 75mA) and is fairly cheap (a few Â£).  Although a 5V device can be driven from a microcontroller IO pin, 100mA is too high a load, 40mA is the maximum for most AVR Microcontroller IO pins, although the maximum current drain for the entire device is around 200mA, so 40mA can only be driven on a few IO pins at once.&lt;/p&gt;

&lt;h3&gt;Driving the relay&lt;/h3&gt;


&lt;p&gt;A simple transistor/resistor circuit can be used to control the relay with much lower power drain.  Basically the transistor (I used a 2N2222A) is being used as a current amplifying switch. &lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2009/07/relay-control-300x224.png" alt="Relay Board for Heater Element Control" title="Relay Board for Heater Element Control" width="300" height="224" class="alignright size-medium wp-image-366" /&gt; When the transistor is being used in on/off mode - only approximate mental/back of the envelope maths are needed to calculate the values of the resistors (&lt;a href="http://www.google.co.uk/search?q=using+transistor+as+power+switch"&gt;Search Google for more background&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;From the transistors &lt;a href="http://www.st.com/stonline/products/literature/ds/9288.pdf"&gt;datasheet&lt;/a&gt;, when the 100mA of current required to power the relay flows from collector to emittor the transistor will provide a minimum gain of just under 100 - so atleast 1mA  needs to flow into the transistors base, if the IO pin is high at 5V and there's a 1V voltage drop from the transistors base to emitter, a resistor of 4K will provide this (R = V/I = (5V-1V)/1mA).  To cope with less gain and a lower Vbe voltage, it's best to provide much more current to I used a 1330R and measured 3.5mA going into the base of the transistor when on.&lt;/p&gt;

&lt;p&gt;Resistor R2 connects the base to ground when the input is unconnected.  This ensures the relay will be safely turned off during microcontroller startup.  The 10K resistor ensures the current flowing through it when the input is high will be low.&lt;/p&gt;

&lt;p&gt;The relay is just a coil of wire which acts as an electromagnet when current flows through it, unfortunately when the power is switched off it will act as an inductor pumping current back into the circuit - the diode protects the transistor when this happens by providing a 'return path' to the 5V supply.&lt;/p&gt;

&lt;h3&gt;Installation&lt;/h3&gt;


&lt;p&gt;The circuit (2 of them) was simple enough to build on a &lt;a href="http://www.flickr.com/photos/rocketnumbernine/3862249901/" title="Oven Relay Box" &gt;&lt;img src="http://farm3.static.flickr.com/2611/3862249901_17b03ddfd3_m.jpg" class="alignleft" width="240" height="187" alt="Oven Relay Box" /&gt;&lt;/a&gt;Prototype/Vero board (pictured above) and housed in a plastic box to protect from earthing.  I bypassed the ovens element and timer controls and passed the live mains wire to the relay box with an output to each of the elements.&lt;/p&gt;

&lt;p&gt;Some brief tests with an Arduino turning on the relay control pins showed that the relevant oven element came on perfectly, with a reasuring click from the relay as it switched.
Now I have more control over the ovens elements the plan is to do more extensive testing to see how controllable the temperature is.&lt;/p&gt;

&lt;h3&gt;References/Bibliography&lt;/h3&gt;


&lt;ol&gt;
&lt;li&gt;&lt;a href="http://www.digchip.com/datasheets/download_datasheet.php?id=920665&amp;part-number=SRRHN-1C-S-5VDC"&gt;IMO SRRHN-1C-S-5VDC&lt;/a&gt; &lt;li&gt;&lt;a href="http://www.google.co.uk/search?q=using+transistor+as+power+switch"&gt;Google Search: Using transistor as a power switch&lt;/a&gt;
&lt;li&gt;&lt;a href="http://www.st.com/stonline/products/literature/ds/9288.pdf"&gt;2N2222A transistor datasheet&lt;/a&gt;
&lt;li&gt;Commercial Relay boards: &lt;a href="http://www.futurlec.com/Relay_4.shtml"&gt;Futurlec&lt;/a&gt; and&lt;a href="http://www.elektor.com/products/e-blocks/modules/relay-board.530431.lynkx"&gt;Elector&lt;/a&gt;.
&lt;li&gt; Some other relay control articles on the web &lt;a href="http://www.sparkfun.com/commerce/tutorial_info.php?tutorials_id=119"&gt;SparkFun&lt;/a&gt;
&lt;/ol&gt;

&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/ePrxU6eZW5o" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2009/08/27/smt-table-top-reflow-oven-part-2</feedburner:origLink></item>
<item>
<pubDate>Fri, 03 Jul 2009 21:08:29 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2009/07/03/using-spi-on-an-avr-3</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/jg4FCd5O8t8/using-spi-on-an-avr-3</link>
<title>Using SPI on an AVR (3)</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;This is a follow up to &lt;a href="http://www.rocketnumbernine.com/2009/04/26/using-spi-on-an-avr-1/"&gt;Using SPI on an AVR (1)&lt;/a&gt; and &lt;a href="http://www.rocketnumbernine.com/2009/05/12/using-spi-on-an-avr-2/"&gt;Using SPI on an AVR (2)&lt;/a&gt; articles which illustrated using an AVR Microcontroller to communicate with some simple SPI Slave devices.
This entry shows two AVR Micro controllers communicating with each other through SPI - one acting as Master and one as Slave.  An interrupt handler is used in the slave to notify it when data is being received.  One device is an at90usb162 on a &lt;a href="http://www.prjc.com/teensy"&gt;Teensy Board&lt;/a&gt; running AVR GCC code, the other an Arduino (with ATmega328) although the code should run on most AVR microcontrollers and Arduino versions that are SPI capable.&lt;/p&gt;

&lt;h3&gt;AVR SPI Slave Mode&lt;/h3&gt;


&lt;p&gt;When a device is operating as the SPI slave device it receives data under control of the master:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The slave devices slave select (SS) pin is brought low by the master.
&lt;li&gt;The master oscillates the SCK clock as it places bits onto the MOSI line and reads from the MISO line.
&lt;li&gt;It's the master's responsibility to use the SPI mode the slave is expecting (whether data is written and read on the rising or falling edges of the clock) and to make sure the clock rate is not too high for the slave.
&lt;/ol&gt;


&lt;p&gt;The AVR's built in SPI hardware takes care of receiving and processing the SPI data and placing the received data a byte at a time onto the SPI data register buffer, the program simply needs to consume the read bytes from the buffer fast enough.&lt;/p&gt;

&lt;p&gt;There are two ways for the program to ascertain when data is ready, it can either poll the SPI status register to see when a byte has been received (SPSR  = (1&amp;lt;&amp;lt;SPIF)) - the same send_spi() helper function that was developed in a previous article can be used.
Alternatively the SPI  system can be configured to generate an interrupt when a byte has been received and is ready to read from the buffer.  This is achieved by setting the SPIE bit in the SPI control register (SPCR = (1&amp;lt;&amp;lt;SPIE)), I encapsulated this as a parameter to the spi setup helper function developed earlier.
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;setup_spi(SPI_MODE_1, SPI_MSB, SPI_INTERRUPT, SPI_SLAVE);&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;h3&gt;Example&lt;/h3&gt;


&lt;p&gt;To reduce the amount of code to be written, each program (the AVR GCC and Arduino) acts as both master and slave, and either part can be replaced with the other (ie. two Arduinos can be used etc.).  The circuit is shown below, apart from the SPI connections each device has an LED and a push button connected.
&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2009/06/spi-masterslave.png" alt="spi-mqasterslave" title="spi-masterslave" width="500" height="257" class="aligncenter size-full wp-image-343" /&gt;&lt;/p&gt;

&lt;p&gt;When the push button on one device is pressed, the LED on the other flashes.  This is achieved using the following logic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The device is put into SPI slave mode so that an interrupt is raised when data is received on the SPI bus.
&lt;li&gt;When the button is pressed the program puts the AVR into SPI master mode and sends the slave a message (a number of bytes) then goes back into slave mode.
&lt;li&gt;The program's SPI interrupt handler simply stores the bytes received in a buffer until a terminating null byte (0x00) is received, it then parses and processes the message.
&lt;li&gt;Currently a single message type is supported - FLASH_LED (0x00) which is followed by a byte containing the number of times the slave should flash its LED - so to make the slave flash its LED 5 times the master would send 3 bytes - 0x01, 0x05, 0x00.
&lt;/ol&gt;


&lt;p&gt;With the SPI system in slave mode and with interrupts enabled the interupt service handler for SPI&lt;em&gt;STC&lt;/em&gt;Vect will be called when a byte has been received.  All the handler implementation does (below) is save the byte received from SPI in a buffer (incoming) and checks if the byte received is null or we've filled the buffer, if so the parse_message() is called to process the data received.  As stated previously the interrupt handler mu st return fast enough to handle the next received byte.  Whilst the handler is running interrupts will be disabled and if it takes too long data will be lost.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;ISR(SPI_STC_vect)
{
  incoming[received++] = received_from_spi(&lt;span class="hex"&gt;0x00&lt;/span&gt;);
  &lt;span class="keyword"&gt;if&lt;/span&gt; (received &amp;gt;= BUFSIZE || incoming[received-&lt;span class="integer"&gt;1&lt;/span&gt;] == &lt;span class="hex"&gt;0x00&lt;/span&gt;) {
    parse_message();
    received = &lt;span class="integer"&gt;0&lt;/span&gt;;
  }
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;The received&lt;em&gt;from&lt;/em&gt;spi() function simply reads the byte read from the SPI pipeline and sets the value of the next byte to be sent - note the SPDR buffer is double buffered so the value can be written before being read.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; received_from_spi(&lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; data)
{
  SPDR = data;
  &lt;span class="keyword"&gt;return&lt;/span&gt; SPDR;
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

Using an interrupt handler to receive and process the SPI data allows the AVR to perform other tasks, or sleep, until all data is ready.&lt;/p&gt;

&lt;h3&gt;Sending a SPI message on button press&lt;/h3&gt;


&lt;p&gt;We also use an interrupt to trigger sending of a message when a button is pressed in the SPI master.  We configure the AVR to  trigger an interrupt when a pin goes low due to the button being pressed.  The Arduino has a helper function to set this up:
&lt;div class="CodeRay"&gt;
  &lt;div class="code"&gt;&lt;pre&gt;attachInterrupt(&lt;span class="integer"&gt;0&lt;/span&gt;, button_pressed, FALLING);&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;

This will result in the function "button&lt;em&gt;pressed" being called when external interrupt 0 (Connected to Arduino pin D2) goes from high to low (FALLING).  In native AVR GCC this takes a little more effort - we set the relevant bits in the EICRB register to receive an interrupt on a falling signal (ISCO1 and ISC00), enable external interrupts in EIMSK register,  and then enable interrupts (sei()).  See the External Interrupt Channel of the relevant AVR &lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc7707.pdf"&gt;datasheet&lt;/a&gt; for details of the registers.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;EICRB = (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;ISC01) | (&lt;span class="integer"&gt;0&lt;/span&gt;&amp;lt;&amp;lt;ISC00);
 EIMSK |= (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;INT0);
 sei();&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

The above configuration results in the handler named ISR(INT0&lt;/em&gt;vect) being called when the INT0 pin goes from high to low, both this and the Arduino's button&lt;em&gt;pressed() just call the send&lt;/em&gt;message function to transmit a message.  This attempts to put the device into SPI master mode, checks that it was successful (If its select pin isn't low) and if so selects the other device, sends 3 bytes, then deselects the other device and goes back into slave mode:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; send_message()
{
  setup_spi(SPI_MODE_1, SPI_MSB, SPI_NO_INTERRUPT, SPI_MSTR_CLK8);
  &lt;span class="keyword"&gt;if&lt;/span&gt; (SPCR &amp;amp; (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;MSTR)) { &lt;span class="comment"&gt;// if we are still in master mode&lt;/span&gt;
    SELECT_OTHER; &lt;span class="comment"&gt;// tell other device to flash LED twice&lt;/span&gt;
    send_spi(FLASH_LED_COMMAND); send_spi(&lt;span class="hex"&gt;0x02&lt;/span&gt;); send_spi(&lt;span class="hex"&gt;0x00&lt;/span&gt;);
    DESELECT_OTHER;
  }
  setup_spi(SPI_MODE_1, SPI_MSB, SPI_INTERRUPT, SPI_SLAVE);
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;The AVR spec says the SPI clock frequency in the slave device shouldn't exceed the MCU frequency/4 - I've found frequency/8 is safer.&lt;/p&gt;

&lt;p&gt;We've seen that the slave device just stores byte received until it receives the terminating null byte, it then calls parse&lt;em&gt;message().  This checks the value of the first byte in the buffer and parses the remaining bytes accordingly.  The current implementation just supports the single FLASH&lt;/em&gt;LED_COMMAND which reads the next byte and flashes an LED that number of times.  If we receive an unexpected message the LED is flashed repeatedly:&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="directive"&gt;void&lt;/span&gt; parse_message()
{
 &lt;span class="keyword"&gt;switch&lt;/span&gt;(incoming[&lt;span class="integer"&gt;0&lt;/span&gt;]) {
 &lt;span class="keyword"&gt;case&lt;/span&gt; FLASH_LED_COMMAND:
   flash_led(incoming[&lt;span class="integer"&gt;1&lt;/span&gt;]);
   &lt;span class="keyword"&gt;break&lt;/span&gt;;
 &lt;span class="keyword"&gt;default&lt;/span&gt;:
   flash_led(&lt;span class="integer"&gt;20&lt;/span&gt;);
 }
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;The complete programs are shown below and are also included as examples in the SPI helper library zip below
AVR GCC:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;a href="#n14" name="n14"&gt;14&lt;/a&gt;
&lt;a href="#n15" name="n15"&gt;15&lt;/a&gt;
&lt;a href="#n16" name="n16"&gt;16&lt;/a&gt;
&lt;a href="#n17" name="n17"&gt;17&lt;/a&gt;
&lt;a href="#n18" name="n18"&gt;18&lt;/a&gt;
&lt;a href="#n19" name="n19"&gt;19&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n21" name="n21"&gt;21&lt;/a&gt;
&lt;a href="#n22" name="n22"&gt;22&lt;/a&gt;
&lt;a href="#n23" name="n23"&gt;23&lt;/a&gt;
&lt;a href="#n24" name="n24"&gt;24&lt;/a&gt;
&lt;a href="#n25" name="n25"&gt;25&lt;/a&gt;
&lt;a href="#n26" name="n26"&gt;26&lt;/a&gt;
&lt;a href="#n27" name="n27"&gt;27&lt;/a&gt;
&lt;a href="#n28" name="n28"&gt;28&lt;/a&gt;
&lt;a href="#n29" name="n29"&gt;29&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n31" name="n31"&gt;31&lt;/a&gt;
&lt;a href="#n32" name="n32"&gt;32&lt;/a&gt;
&lt;a href="#n33" name="n33"&gt;33&lt;/a&gt;
&lt;a href="#n34" name="n34"&gt;34&lt;/a&gt;
&lt;a href="#n35" name="n35"&gt;35&lt;/a&gt;
&lt;a href="#n36" name="n36"&gt;36&lt;/a&gt;
&lt;a href="#n37" name="n37"&gt;37&lt;/a&gt;
&lt;a href="#n38" name="n38"&gt;38&lt;/a&gt;
&lt;a href="#n39" name="n39"&gt;39&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n40" name="n40"&gt;40&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n41" name="n41"&gt;41&lt;/a&gt;
&lt;a href="#n42" name="n42"&gt;42&lt;/a&gt;
&lt;a href="#n43" name="n43"&gt;43&lt;/a&gt;
&lt;a href="#n44" name="n44"&gt;44&lt;/a&gt;
&lt;a href="#n45" name="n45"&gt;45&lt;/a&gt;
&lt;a href="#n46" name="n46"&gt;46&lt;/a&gt;
&lt;a href="#n47" name="n47"&gt;47&lt;/a&gt;
&lt;a href="#n48" name="n48"&gt;48&lt;/a&gt;
&lt;a href="#n49" name="n49"&gt;49&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n50" name="n50"&gt;50&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n51" name="n51"&gt;51&lt;/a&gt;
&lt;a href="#n52" name="n52"&gt;52&lt;/a&gt;
&lt;a href="#n53" name="n53"&gt;53&lt;/a&gt;
&lt;a href="#n54" name="n54"&gt;54&lt;/a&gt;
&lt;a href="#n55" name="n55"&gt;55&lt;/a&gt;
&lt;a href="#n56" name="n56"&gt;56&lt;/a&gt;
&lt;a href="#n57" name="n57"&gt;57&lt;/a&gt;
&lt;a href="#n58" name="n58"&gt;58&lt;/a&gt;
&lt;a href="#n59" name="n59"&gt;59&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n60" name="n60"&gt;60&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n61" name="n61"&gt;61&lt;/a&gt;
&lt;a href="#n62" name="n62"&gt;62&lt;/a&gt;
&lt;a href="#n63" name="n63"&gt;63&lt;/a&gt;
&lt;a href="#n64" name="n64"&gt;64&lt;/a&gt;
&lt;a href="#n65" name="n65"&gt;65&lt;/a&gt;
&lt;a href="#n66" name="n66"&gt;66&lt;/a&gt;
&lt;a href="#n67" name="n67"&gt;67&lt;/a&gt;
&lt;a href="#n68" name="n68"&gt;68&lt;/a&gt;
&lt;a href="#n69" name="n69"&gt;69&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n70" name="n70"&gt;70&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n71" name="n71"&gt;71&lt;/a&gt;
&lt;a href="#n72" name="n72"&gt;72&lt;/a&gt;
&lt;a href="#n73" name="n73"&gt;73&lt;/a&gt;
&lt;a href="#n74" name="n74"&gt;74&lt;/a&gt;
&lt;a href="#n75" name="n75"&gt;75&lt;/a&gt;
&lt;a href="#n76" name="n76"&gt;76&lt;/a&gt;
&lt;a href="#n77" name="n77"&gt;77&lt;/a&gt;
&lt;a href="#n78" name="n78"&gt;78&lt;/a&gt;
&lt;a href="#n79" name="n79"&gt;79&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n80" name="n80"&gt;80&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n81" name="n81"&gt;81&lt;/a&gt;
&lt;a href="#n82" name="n82"&gt;82&lt;/a&gt;
&lt;a href="#n83" name="n83"&gt;83&lt;/a&gt;
&lt;a href="#n84" name="n84"&gt;84&lt;/a&gt;
&lt;a href="#n85" name="n85"&gt;85&lt;/a&gt;
&lt;a href="#n86" name="n86"&gt;86&lt;/a&gt;
&lt;a href="#n87" name="n87"&gt;87&lt;/a&gt;
&lt;a href="#n88" name="n88"&gt;88&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="preprocessor"&gt;#include&lt;/span&gt; &lt;span class="include"&gt;&amp;lt;avr/interrupt.h&amp;gt;&lt;/span&gt;
&lt;span class="preprocessor"&gt;#include&lt;/span&gt; &lt;span class="include"&gt;&amp;lt;util/delay.h&amp;gt;&lt;/span&gt;
&lt;span class="preprocessor"&gt;#include&lt;/span&gt; &lt;span class="include"&gt;&amp;lt;spi.h&amp;gt;&lt;/span&gt;

&lt;span class="preprocessor"&gt;#define&lt;/span&gt; FLASH_LED_COMMAND &lt;span class="hex"&gt;0x01&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; OTHER_SELECT_PIN PB6
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; SELECT_OTHER PORTB &amp;amp;= ~(&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;OTHER_SELECT_PIN)
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; DESELECT_OTHER PORTB |= (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;OTHER_SELECT_PIN)

&lt;span class="preprocessor"&gt;#define&lt;/span&gt; BUFSIZE &lt;span class="integer"&gt;20&lt;/span&gt;
&lt;span class="directive"&gt;volatile&lt;/span&gt; &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; incoming[BUFSIZE];
&lt;span class="directive"&gt;volatile&lt;/span&gt; &lt;span class="predefined-type"&gt;short&lt;/span&gt; &lt;span class="predefined-type"&gt;int&lt;/span&gt; received=&lt;span class="integer"&gt;0&lt;/span&gt;;


&lt;span class="comment"&gt;// flash led that's connected to pin PD7&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; flash_led(&lt;span class="predefined-type"&gt;int&lt;/span&gt; count)
{
 DDRD |= (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;PD7);
 &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; i=&lt;span class="integer"&gt;0&lt;/span&gt;; i&amp;lt;count*&lt;span class="integer"&gt;2&lt;/span&gt;; i++) {
   PORTD ^= (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;PD7);
   _delay_ms(&lt;span class="integer"&gt;75&lt;/span&gt;);
 }
}

&lt;span class="comment"&gt;// send a SPI message to the other device - 3 bytes then go back into&lt;/span&gt;
&lt;span class="comment"&gt;// slave mode&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; send_message()
{
 setup_spi(SPI_MODE_1, SPI_MSB, SPI_NO_INTERRUPT, SPI_MSTR_CLK8);
 &lt;span class="keyword"&gt;if&lt;/span&gt; (SPCR &amp;amp; (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;MSTR)) { &lt;span class="comment"&gt;// if we are still in master mode&lt;/span&gt;
   SELECT_OTHER; &lt;span class="comment"&gt;// tell other device to flash LED twice&lt;/span&gt;
   send_spi(FLASH_LED_COMMAND); send_spi(&lt;span class="hex"&gt;0x02&lt;/span&gt;); send_spi(&lt;span class="hex"&gt;0x00&lt;/span&gt;);
   DESELECT_OTHER;
 }
 setup_spi(SPI_MODE_1, SPI_MSB, SPI_INTERRUPT, SPI_SLAVE);
}

&lt;span class="comment"&gt;// called when the button pushed and pin INT0 goes from 1 to 0&lt;/span&gt;
ISR(INT0_vect)
{
 send_message();
 _delay_ms(&lt;span class="integer"&gt;500&lt;/span&gt;); &lt;span class="comment"&gt;// 'debounce'&lt;/span&gt;
}

&lt;span class="comment"&gt;// parse the data received from the other device&lt;/span&gt;
&lt;span class="comment"&gt;// currently just knows about the FLASH_LED_COMMAND&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; parse_message()
{
 &lt;span class="keyword"&gt;switch&lt;/span&gt;(incoming[&lt;span class="integer"&gt;0&lt;/span&gt;]) {
 &lt;span class="keyword"&gt;case&lt;/span&gt; FLASH_LED_COMMAND:
   flash_led(incoming[&lt;span class="integer"&gt;1&lt;/span&gt;]);
   &lt;span class="keyword"&gt;break&lt;/span&gt;;
 &lt;span class="keyword"&gt;default&lt;/span&gt;:
   flash_led(&lt;span class="integer"&gt;20&lt;/span&gt;);
 }
}

&lt;span class="comment"&gt;// called by the SPI system when there is data ready.&lt;/span&gt;
&lt;span class="comment"&gt;// Just store the incoming data in a buffer, when we receive a&lt;/span&gt;
&lt;span class="comment"&gt;// terminating byte (0x00) call parse_message to process the data received&lt;/span&gt;
ISR(SPI_STC_vect)
{
 incoming[received++] = received_from_spi(&lt;span class="hex"&gt;0x00&lt;/span&gt;);
 &lt;span class="keyword"&gt;if&lt;/span&gt; (received &amp;gt;= BUFSIZE || incoming[received-&lt;span class="integer"&gt;1&lt;/span&gt;] == &lt;span class="hex"&gt;0x00&lt;/span&gt;) {
   parse_message();
   received = &lt;span class="integer"&gt;0&lt;/span&gt;;
 }
}

&lt;span class="predefined-type"&gt;int&lt;/span&gt; main(&lt;span class="directive"&gt;void&lt;/span&gt;)
{
 &lt;span class="comment"&gt;// make sure other device is unselected (pin is HIGH) and setup spi&lt;/span&gt;
 DESELECT_OTHER;
 DDRB |= (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;OTHER_SELECT_PIN);
 setup_spi(SPI_MODE_1, SPI_MSB, SPI_INTERRUPT, SPI_SLAVE);

 &lt;span class="comment"&gt;// raise interrupt when the button is pushed and INT0 pin goes&lt;/span&gt;
 &lt;span class="comment"&gt;// from 1 to 0 (pin PD0 at AT90usbXXX, pin PD2 on ATmegaXXX,&lt;/span&gt;
 &lt;span class="comment"&gt;// arduino pin 2)). The code in ISR(INT0_vect) above will be called&lt;/span&gt;
 EICRB = (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;ISC01) | (&lt;span class="integer"&gt;0&lt;/span&gt;&amp;lt;&amp;lt;ISC00);
 EIMSK |= (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;INT0);
 sei();

 &lt;span class="comment"&gt;// flash LED at start to indicate were ready&lt;/span&gt;
 flash_led(&lt;span class="integer"&gt;1&lt;/span&gt;);

 &lt;span class="keyword"&gt;while&lt;/span&gt; (&lt;span class="integer"&gt;1&lt;/span&gt;); &lt;span class="comment"&gt;// do nothing&lt;/span&gt;
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;Arduino:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;a href="#n14" name="n14"&gt;14&lt;/a&gt;
&lt;a href="#n15" name="n15"&gt;15&lt;/a&gt;
&lt;a href="#n16" name="n16"&gt;16&lt;/a&gt;
&lt;a href="#n17" name="n17"&gt;17&lt;/a&gt;
&lt;a href="#n18" name="n18"&gt;18&lt;/a&gt;
&lt;a href="#n19" name="n19"&gt;19&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n21" name="n21"&gt;21&lt;/a&gt;
&lt;a href="#n22" name="n22"&gt;22&lt;/a&gt;
&lt;a href="#n23" name="n23"&gt;23&lt;/a&gt;
&lt;a href="#n24" name="n24"&gt;24&lt;/a&gt;
&lt;a href="#n25" name="n25"&gt;25&lt;/a&gt;
&lt;a href="#n26" name="n26"&gt;26&lt;/a&gt;
&lt;a href="#n27" name="n27"&gt;27&lt;/a&gt;
&lt;a href="#n28" name="n28"&gt;28&lt;/a&gt;
&lt;a href="#n29" name="n29"&gt;29&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n31" name="n31"&gt;31&lt;/a&gt;
&lt;a href="#n32" name="n32"&gt;32&lt;/a&gt;
&lt;a href="#n33" name="n33"&gt;33&lt;/a&gt;
&lt;a href="#n34" name="n34"&gt;34&lt;/a&gt;
&lt;a href="#n35" name="n35"&gt;35&lt;/a&gt;
&lt;a href="#n36" name="n36"&gt;36&lt;/a&gt;
&lt;a href="#n37" name="n37"&gt;37&lt;/a&gt;
&lt;a href="#n38" name="n38"&gt;38&lt;/a&gt;
&lt;a href="#n39" name="n39"&gt;39&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n40" name="n40"&gt;40&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n41" name="n41"&gt;41&lt;/a&gt;
&lt;a href="#n42" name="n42"&gt;42&lt;/a&gt;
&lt;a href="#n43" name="n43"&gt;43&lt;/a&gt;
&lt;a href="#n44" name="n44"&gt;44&lt;/a&gt;
&lt;a href="#n45" name="n45"&gt;45&lt;/a&gt;
&lt;a href="#n46" name="n46"&gt;46&lt;/a&gt;
&lt;a href="#n47" name="n47"&gt;47&lt;/a&gt;
&lt;a href="#n48" name="n48"&gt;48&lt;/a&gt;
&lt;a href="#n49" name="n49"&gt;49&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n50" name="n50"&gt;50&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n51" name="n51"&gt;51&lt;/a&gt;
&lt;a href="#n52" name="n52"&gt;52&lt;/a&gt;
&lt;a href="#n53" name="n53"&gt;53&lt;/a&gt;
&lt;a href="#n54" name="n54"&gt;54&lt;/a&gt;
&lt;a href="#n55" name="n55"&gt;55&lt;/a&gt;
&lt;a href="#n56" name="n56"&gt;56&lt;/a&gt;
&lt;a href="#n57" name="n57"&gt;57&lt;/a&gt;
&lt;a href="#n58" name="n58"&gt;58&lt;/a&gt;
&lt;a href="#n59" name="n59"&gt;59&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n60" name="n60"&gt;60&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n61" name="n61"&gt;61&lt;/a&gt;
&lt;a href="#n62" name="n62"&gt;62&lt;/a&gt;
&lt;a href="#n63" name="n63"&gt;63&lt;/a&gt;
&lt;a href="#n64" name="n64"&gt;64&lt;/a&gt;
&lt;a href="#n65" name="n65"&gt;65&lt;/a&gt;
&lt;a href="#n66" name="n66"&gt;66&lt;/a&gt;
&lt;a href="#n67" name="n67"&gt;67&lt;/a&gt;
&lt;a href="#n68" name="n68"&gt;68&lt;/a&gt;
&lt;a href="#n69" name="n69"&gt;69&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n70" name="n70"&gt;70&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n71" name="n71"&gt;71&lt;/a&gt;
&lt;a href="#n72" name="n72"&gt;72&lt;/a&gt;
&lt;a href="#n73" name="n73"&gt;73&lt;/a&gt;
&lt;a href="#n74" name="n74"&gt;74&lt;/a&gt;
&lt;a href="#n75" name="n75"&gt;75&lt;/a&gt;
&lt;a href="#n76" name="n76"&gt;76&lt;/a&gt;
&lt;a href="#n77" name="n77"&gt;77&lt;/a&gt;
&lt;a href="#n78" name="n78"&gt;78&lt;/a&gt;
&lt;a href="#n79" name="n79"&gt;79&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n80" name="n80"&gt;80&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n81" name="n81"&gt;81&lt;/a&gt;
&lt;a href="#n82" name="n82"&gt;82&lt;/a&gt;
&lt;a href="#n83" name="n83"&gt;83&lt;/a&gt;
&lt;a href="#n84" name="n84"&gt;84&lt;/a&gt;
&lt;a href="#n85" name="n85"&gt;85&lt;/a&gt;
&lt;a href="#n86" name="n86"&gt;86&lt;/a&gt;
&lt;a href="#n87" name="n87"&gt;87&lt;/a&gt;
&lt;a href="#n88" name="n88"&gt;88&lt;/a&gt;
&lt;a href="#n89" name="n89"&gt;89&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n90" name="n90"&gt;90&lt;/a&gt;&lt;/strong&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="preprocessor"&gt;#include&lt;/span&gt; &lt;span class="include"&gt;&amp;lt;spi.h&amp;gt;&lt;/span&gt;
&lt;span class="preprocessor"&gt;#include&lt;/span&gt; &lt;span class="include"&gt;&amp;lt;util/delay.h&amp;gt;&lt;/span&gt;

&lt;span class="preprocessor"&gt;#define&lt;/span&gt; BUTTON_PIN &lt;span class="integer"&gt;2&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; LED_PIN &lt;span class="integer"&gt;3&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; FLASH_LED_COMMAND &lt;span class="hex"&gt;0x01&lt;/span&gt;

&lt;span class="preprocessor"&gt;#define&lt;/span&gt; OTHER_SELECT_PIN &lt;span class="integer"&gt;7&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; SELECT_OTHER digitalWrite(OTHER_SELECT_PIN, LOW);
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; DESELECT_OTHER digitalWrite(OTHER_SELECT_PIN, HIGH);

&lt;span class="preprocessor"&gt;#define&lt;/span&gt; BUFSIZE &lt;span class="integer"&gt;20&lt;/span&gt;
&lt;span class="directive"&gt;volatile&lt;/span&gt; &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; incoming[BUFSIZE];
&lt;span class="directive"&gt;volatile&lt;/span&gt; &lt;span class="predefined-type"&gt;short&lt;/span&gt; &lt;span class="predefined-type"&gt;int&lt;/span&gt; received=&lt;span class="integer"&gt;0&lt;/span&gt;;

&lt;span class="comment"&gt;// flash led that's connected to pin PD7&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; flash_led(&lt;span class="predefined-type"&gt;int&lt;/span&gt; count)
{
 pinMode(LED_PIN, OUTPUT);
 &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="predefined-type"&gt;int&lt;/span&gt; i=&lt;span class="integer"&gt;0&lt;/span&gt;; i&amp;lt;count*&lt;span class="integer"&gt;2&lt;/span&gt;; i++) {
   digitalWrite(LED_PIN, i%&lt;span class="integer"&gt;2&lt;/span&gt;==&lt;span class="integer"&gt;0&lt;/span&gt;);
   _delay_ms(&lt;span class="integer"&gt;75&lt;/span&gt;);
 }
}

&lt;span class="comment"&gt;// send a SPI message to the other device - 3 bytes then go back into&lt;/span&gt;
&lt;span class="comment"&gt;// slave mode&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; send_message()
{
 setup_spi(SPI_MODE_1, SPI_MSB, SPI_NO_INTERRUPT, SPI_MSTR_CLK8);
 &lt;span class="keyword"&gt;if&lt;/span&gt; (SPCR &amp;amp; (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;MSTR)) { &lt;span class="comment"&gt;// if we are still in master mode&lt;/span&gt;
   SELECT_OTHER; &lt;span class="comment"&gt;// tell other device to flash LED twice&lt;/span&gt;
   send_spi(FLASH_LED_COMMAND); send_spi(&lt;span class="hex"&gt;0x02&lt;/span&gt;); send_spi(&lt;span class="hex"&gt;0x00&lt;/span&gt;);
   DESELECT_OTHER;
 }
 setup_spi(SPI_MODE_1, SPI_MSB, SPI_INTERRUPT, SPI_SLAVE);
}


&lt;span class="comment"&gt;// called when button pushed and pin INT0 goes from 1 to 0&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; button_pressed()
{
 send_message();
 _delay_ms(&lt;span class="integer"&gt;500&lt;/span&gt;);  &lt;span class="comment"&gt;// 'debounce'&lt;/span&gt;
}

&lt;span class="comment"&gt;// parse the data received from the other device&lt;/span&gt;
&lt;span class="comment"&gt;// currently just knows about the FLASH_LED_COMMAND&lt;/span&gt;
&lt;span class="directive"&gt;void&lt;/span&gt; parse_message()
{
 &lt;span class="keyword"&gt;switch&lt;/span&gt;(incoming[&lt;span class="integer"&gt;0&lt;/span&gt;]) {
 &lt;span class="keyword"&gt;case&lt;/span&gt; FLASH_LED_COMMAND:
   flash_led(incoming[&lt;span class="integer"&gt;1&lt;/span&gt;]);
   &lt;span class="keyword"&gt;break&lt;/span&gt;;
 &lt;span class="keyword"&gt;default&lt;/span&gt;:
   flash_led(&lt;span class="integer"&gt;20&lt;/span&gt;);
 }
}

&lt;span class="comment"&gt;// called by the SPI system when there is data ready.&lt;/span&gt;
&lt;span class="comment"&gt;// Just store the incoming data in a buffer, when we receive a&lt;/span&gt;
&lt;span class="comment"&gt;// terminating byte (0x00) call parse_message to process the data received&lt;/span&gt;
ISR(SPI_STC_vect)
{
 incoming[received++] = received_from_spi(&lt;span class="hex"&gt;0x00&lt;/span&gt;);
 &lt;span class="keyword"&gt;if&lt;/span&gt; (received == BUFSIZE || incoming[received-&lt;span class="integer"&gt;1&lt;/span&gt;] == &lt;span class="hex"&gt;0x00&lt;/span&gt;) {
     parse_message();
     received = &lt;span class="integer"&gt;0&lt;/span&gt;;
  }
}

&lt;span class="directive"&gt;void&lt;/span&gt; setup()
{
 &lt;span class="comment"&gt;// make sure other device is unselected (pin is HIGH) and setup spi&lt;/span&gt;
 DESELECT_OTHER;
 pinMode(OTHER_SELECT_PIN, OUTPUT);
 setup_spi(SPI_MODE_1, SPI_MSB, SPI_INTERRUPT, SPI_SLAVE);

 &lt;span class="comment"&gt;// raise interrupt when INT0 pin falls (arduino pin 2))&lt;/span&gt;
 &lt;span class="comment"&gt;// the function button_pressed will be called&lt;/span&gt;
 attachInterrupt(&lt;span class="integer"&gt;0&lt;/span&gt;, button_pressed, FALLING);

 &lt;span class="comment"&gt;// flash LED at start to indicate were ready&lt;/span&gt;
 flash_led(&lt;span class="integer"&gt;1&lt;/span&gt;);
}


&lt;span class="directive"&gt;void&lt;/span&gt; loop()
{
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;h3&gt;References&lt;/h3&gt;


&lt;ul&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/rocketnumbernine/downloads/list"&gt;AVI SPI library&lt;/a&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus"&gt;Serial Peripheral Interface&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc7707.pdf"&gt;AVR AT90USB82/162 Datasheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc8025.pdf"&gt;AVR ATmega48/88/168/328 Datasheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc2585.pdf"&gt;AVR151: Setup And Use of The SPI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/jg4FCd5O8t8" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2009/07/03/using-spi-on-an-avr-3</feedburner:origLink></item>
<item>
<pubDate>Sat, 27 Jun 2009 18:51:55 +0000</pubDate>
<guid isPermaLink="false">http://www.rocketnumbernine.com/2009/06/27/using-the-max6675-thermocouple-to-digital-converter</guid>
<link>http://feeds.rocketnumbernine.com/~r/rocketnumbernine/~3/OE9gCFytkyc/using-the-max6675-thermocouple-to-digital-converter</link>
<title>Using the MAX6675 Thermocouple-to-Digital Converter</title>
<author>Andrew</author>
<description>&lt;div id="post"&gt;&lt;div class="text"&gt;
    &lt;p&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2009/06/p1010187.jpg" alt="MAX6675 on Breakout Board" title="MAX6675 on Breakout Board" width="250" height="167" class="alignright size-full wp-image-375" /&gt;I've been asked for the code used to read the &lt;a href="http://datasheets.maxim-ic.com/en/ds/MAX6675.pdf"&gt;MAX6675&lt;/a&gt; Thermocouple-to-Digital Converter used to measure the temperature in my ongoing &lt;a href="http://www.rocketnumbernine.com/2009/06/03/smt-table-top-reflow-oven-part-1/"&gt;SMD Reflow Oven&lt;/a&gt; project.  The following is based on an Arduino (with a home built SPI helper library) but is fairly generic and should be applicable to most platforms.&lt;/p&gt;

&lt;p&gt;Maxim's MAX6675 8-pin SOIC chip is a compact and simple to use solution for converting the output of a type K thermocouple to a digital temperature value read over SPI.  It provides Digital to Analogue conversion of the voltage produced by the thermocouple, cold-junction compensation, noise reduction and processing of the result into a binary temperature value at a precision of 0.25Â°C.&lt;/p&gt;

&lt;div class='captioned-image'&gt;&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2009/06/max6675-circuit.png" alt="SO - MISO (Arduino D12), SCK (Arduino D13), CS (Arduino D8)" title="MAX6675 Circuit" width="285" height="250" class="size-full wp-image-288" /&gt;&lt;p class='image-caption'&gt;SO - MISO (Arduino D12), SCK (Arduino D13), CS (Arduino D8)&lt;/p&gt;&lt;/div&gt;


&lt;p&gt; Circuit used (from the datasheet sheet) is shown to the right.  I used a breadboard for the circuit but created a &lt;a h ref="http://www.batchpcb.com/product_info.php?products_id=20538&amp;check=1586acac99a545c865a8a0ae59868ddc"&gt;breakout board&lt;/a&gt; for the (SMD) MAX6675 chip and decoupling capacitor with &lt;a href="http://www.batchpcb.com/"&gt;BatchPCB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The thermocouple is connected to pins 2 and 3 - if connected the wrong way around the temperature output from the MAX6675 will go down as the end of the thermocouple is heated.  Connecting the MAX6675 to the Arduino is straightforward - just power and the SPI MISO and SCK (clock) and chip select line (^CS).  Pin D8 was used to select the device.&lt;/p&gt;

&lt;h3&gt;SPI Setup&lt;/h3&gt;


&lt;p&gt;From the &lt;a href="http://datasheets.maxim-ic.com/en/ds/MAX6675.pdf"&gt;datasheet&lt;/a&gt; we see that the MAX6675 transmits the temperature in 2 bytes - most significant bit first.  The clock is expected to be idle low and data can be read when it is falling (SPI mode 1).  The MAX6675 can take a maximum SPI clock rate of 4.3MHz - as the Arduino was running at 16MHz a SPI data rate of Clock/8 (2MHz) was specified so the device was operating well within its tolerances.
The following sets up up the MAX6675 chip select pin (Arduino Pin D8) control pin, SPI and the Arduino Serial line:
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="preprocessor"&gt;#define&lt;/span&gt; MAX6675_SELECT_PIN &lt;span class="integer"&gt;8&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; DESELECT_MAX6675 digitalWrite(MAX6675_SELECT_PIN, HIGH)
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; SELECT_MAX6675 digitalWrite(MAX6675_SELECT_PIN, LOW)

&lt;span class="directive"&gt;void&lt;/span&gt; setup()
{
  pinMode(MAX6675_SELECT_PIN, OUTPUT);
  DESELECT_MAX6675;
  setup_spi(SPI_MODE_1, SPI_MSB, SPI_NO_INTERRUPT, SPI_MSTR_CLK8);
  Serial.begin(&lt;span class="integer"&gt;19200&lt;/span&gt;);
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

 &lt;h3&gt;Reading the Temperature&lt;/h3&gt;
Reading the temperature value is equally straightforward.  Select the device, wait atleast 100ns (this is close to the time that it will take the AVR to start sending data on the SPI bus anyway but I put in a microsecond delay to be certain), read the two bytes from SPI, and deselect the device.
&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;SELECT_MAX6675;
 delayMicroseconds(&lt;span class="integer"&gt;1&lt;/span&gt;);
 &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; highByte = send_spi(&lt;span class="integer"&gt;0&lt;/span&gt;);
 &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; lowByte = send_spi(&lt;span class="integer"&gt;0&lt;/span&gt;);
 DESELECT_MAX6675;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;The 16-bits read from the device in two bytes need to be processed slightly:
&lt;img src="http://rocketnumbernine.s3.amazonaws.com/uploads/2009/06/max6675-bitfield.png" alt="MAX6675 Bitfield" title="MAX6675 Bitfield" width="504" height="70" class="alignright size-full wp-image-310" /&gt;&lt;/p&gt;

&lt;p&gt;Bit 2 in the 2nd byte will be high if the device cannot detect an attached thermocouple, otherwise the 12 temperature value bits spread across the 2 bytes (6-0 of the hight byte, and 7-3 of the low byte) need to be shifted into a single numeric value (highByte &amp;lt;&amp;lt; 5 | lowByte&gt;&gt;3).&lt;/p&gt;

&lt;p&gt;The program simply prints the bytes received on an error and the temperature (divided by 4 to get the value in degrees) if it has been read successfully.&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; (lowByte &amp;amp; (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;2&lt;/span&gt;)) {
   Serial.print(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;span class="content"&gt;Not connected &lt;/span&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
   Serial.print(highByte, HEX); Serial.print(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;span class="content"&gt; &lt;/span&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
   Serial.println(lowByte, HEX);
 } &lt;span class="keyword"&gt;else&lt;/span&gt; {
   &lt;span class="predefined-type"&gt;short&lt;/span&gt; value = (highByte &amp;lt;&amp;lt; &lt;span class="integer"&gt;5&lt;/span&gt; | lowByte&amp;gt;&amp;gt;&lt;span class="integer"&gt;3&lt;/span&gt;);
   Serial.print(value/&lt;span class="integer"&gt;4&lt;/span&gt;); Serial.print(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;span class="content"&gt;.&lt;/span&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
   Serial.println(value%&lt;span class="integer"&gt;4&lt;/span&gt; *&lt;span class="integer"&gt;25&lt;/span&gt;);
 }&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;
&lt;/p&gt;

&lt;p&gt;The MAX6675 has a maximum conversion time of 0.22 seconds. If the device is sampled faster than this it will continue to output the temperature that was first read without any indication that it is being read too fast - I found a delay of atleast 250ms between readings the device worked well.&lt;/p&gt;

&lt;p&gt;The complete sampling code is shown below:&lt;/p&gt;

&lt;p&gt;&lt;table class="CodeRay"&gt;&lt;tr&gt;
  &lt;td class="line-numbers" title="double click to toggle" ondblclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"&gt;&lt;pre&gt;&lt;a href="#n1" name="n1"&gt;1&lt;/a&gt;
&lt;a href="#n2" name="n2"&gt;2&lt;/a&gt;
&lt;a href="#n3" name="n3"&gt;3&lt;/a&gt;
&lt;a href="#n4" name="n4"&gt;4&lt;/a&gt;
&lt;a href="#n5" name="n5"&gt;5&lt;/a&gt;
&lt;a href="#n6" name="n6"&gt;6&lt;/a&gt;
&lt;a href="#n7" name="n7"&gt;7&lt;/a&gt;
&lt;a href="#n8" name="n8"&gt;8&lt;/a&gt;
&lt;a href="#n9" name="n9"&gt;9&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n10" name="n10"&gt;10&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n11" name="n11"&gt;11&lt;/a&gt;
&lt;a href="#n12" name="n12"&gt;12&lt;/a&gt;
&lt;a href="#n13" name="n13"&gt;13&lt;/a&gt;
&lt;a href="#n14" name="n14"&gt;14&lt;/a&gt;
&lt;a href="#n15" name="n15"&gt;15&lt;/a&gt;
&lt;a href="#n16" name="n16"&gt;16&lt;/a&gt;
&lt;a href="#n17" name="n17"&gt;17&lt;/a&gt;
&lt;a href="#n18" name="n18"&gt;18&lt;/a&gt;
&lt;a href="#n19" name="n19"&gt;19&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n20" name="n20"&gt;20&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n21" name="n21"&gt;21&lt;/a&gt;
&lt;a href="#n22" name="n22"&gt;22&lt;/a&gt;
&lt;a href="#n23" name="n23"&gt;23&lt;/a&gt;
&lt;a href="#n24" name="n24"&gt;24&lt;/a&gt;
&lt;a href="#n25" name="n25"&gt;25&lt;/a&gt;
&lt;a href="#n26" name="n26"&gt;26&lt;/a&gt;
&lt;a href="#n27" name="n27"&gt;27&lt;/a&gt;
&lt;a href="#n28" name="n28"&gt;28&lt;/a&gt;
&lt;a href="#n29" name="n29"&gt;29&lt;/a&gt;
&lt;strong&gt;&lt;a href="#n30" name="n30"&gt;30&lt;/a&gt;&lt;/strong&gt;
&lt;a href="#n31" name="n31"&gt;31&lt;/a&gt;
&lt;a href="#n32" name="n32"&gt;32&lt;/a&gt;
&lt;a href="#n33" name="n33"&gt;33&lt;/a&gt;
&lt;a href="#n34" name="n34"&gt;34&lt;/a&gt;
&lt;a href="#n35" name="n35"&gt;35&lt;/a&gt;
&lt;a href="#n36" name="n36"&gt;36&lt;/a&gt;
&lt;a href="#n37" name="n37"&gt;37&lt;/a&gt;
&lt;a href="#n38" name="n38"&gt;38&lt;/a&gt;
&lt;/pre&gt;&lt;/td&gt;
  &lt;td class="code"&gt;&lt;pre&gt;&lt;span class="preprocessor"&gt;#include&lt;/span&gt; &lt;span class="include"&gt;&amp;lt;spi.h&amp;gt;&lt;/span&gt;

&lt;span class="comment"&gt;// MAX6675 connected to pin D9&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; MAX6675_SELECT_PIN &lt;span class="integer"&gt;8&lt;/span&gt;
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; DESELECT_MAX6675 digitalWrite(MAX6675_SELECT_PIN, HIGH)
&lt;span class="preprocessor"&gt;#define&lt;/span&gt; SELECT_MAX6675 digitalWrite(MAX6675_SELECT_PIN, LOW)

&lt;span class="directive"&gt;void&lt;/span&gt; setup()
{
 pinMode(MAX6675_SELECT_PIN, OUTPUT);
 DESELECT_MAX6675;
 setup_spi(SPI_MODE_1, SPI_MSB, SPI_NO_INTERRUPT, SPI_MSTR_CLK8);
 Serial.begin(&lt;span class="integer"&gt;19200&lt;/span&gt;);
}

&lt;span class="directive"&gt;void&lt;/span&gt; loop()
{
 delay(&lt;span class="integer"&gt;500&lt;/span&gt;);

 &lt;span class="comment"&gt;// select the device, wait &amp;gt; 100nS, read two bytes, deselect&lt;/span&gt;
 SELECT_MAX6675;
 delayMicroseconds(&lt;span class="integer"&gt;1&lt;/span&gt;);
 &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; highByte = send_spi(&lt;span class="integer"&gt;0&lt;/span&gt;);
 &lt;span class="predefined-type"&gt;unsigned&lt;/span&gt; &lt;span class="predefined-type"&gt;char&lt;/span&gt; lowByte = send_spi(&lt;span class="integer"&gt;0&lt;/span&gt;);
 DESELECT_MAX6675;

 &lt;span class="comment"&gt;// if bit 3 is high thermocouple is unconnected&lt;/span&gt;
 &lt;span class="keyword"&gt;if&lt;/span&gt; (lowByte &amp;amp; (&lt;span class="integer"&gt;1&lt;/span&gt;&amp;lt;&amp;lt;&lt;span class="integer"&gt;2&lt;/span&gt;)) {
   Serial.print(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;span class="content"&gt;Not connected &lt;/span&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
   Serial.print(highByte, HEX); Serial.print(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;span class="content"&gt; &lt;/span&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;);
   Serial.println(lowByte, HEX);
 } &lt;span class="keyword"&gt;else&lt;/span&gt; {
   &lt;span class="comment"&gt;// temperature value is in bits 6-0 of highByte and 7-2 of lowByte&lt;/span&gt;
   &lt;span class="predefined-type"&gt;short&lt;/span&gt; value = (highByte &amp;lt;&amp;lt; &lt;span class="integer"&gt;5&lt;/span&gt; | lowByte&amp;gt;&amp;gt;&lt;span class="integer"&gt;3&lt;/span&gt;);
   &lt;span class="comment"&gt;// 1 bit is 0.25 degree 'divide' by 4 to show degrees&lt;/span&gt;
   Serial.print(value/&lt;span class="integer"&gt;4&lt;/span&gt;); Serial.print(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;span class="content"&gt;.&lt;/span&gt;&lt;span class="delimiter"&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;); Serial.println(value%&lt;span class="integer"&gt;4&lt;/span&gt; *&lt;span class="integer"&gt;25&lt;/span&gt;);
 }
}&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;

 &lt;h3&gt;Conclusions&lt;/h3&gt;
Compared with manual reading and conversion of the thermocouple voltage to temperature the MAX6675 is a delight to use.  Although it is not a cheap device it is comparable to other thermocouple processing devices (for example the &lt;a href="http://www.analog.com/en/other/ios-subsystems/ac1226/products/product.html"&gt;AC1226&lt;/a&gt; and &lt;a href="http://www.linear.com/pc/productDetail.jsp?navId=H0,C1,C1010,C1073,P1181"&gt;LTK001&lt;/a&gt;) but is fairly unique in having a digital/SPI output rather than a cleaned up voltage per Centigrade value that also requires a D/A converter.
 &lt;h3&gt;References/Resources&lt;/h3&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="http://code.google.com/p/rocketnumbernine/downloads/list"&gt;AVR SPI Helper Library&lt;/a&gt;
&lt;li&gt;&lt;a href="http://datasheets.maxim-ic.com/en/ds/MAX6675.pdf"&gt;MAX6675 Spec sheet&lt;/a&gt;
&lt;li&gt;&lt;a href="http://www.batchpcb.com/index.php/Products/20538"&gt;Break out board PCB&lt;/a&gt;
&lt;/ol&gt;

&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/rocketnumbernine/~4/OE9gCFytkyc" height="1" width="1"/&gt;</description>
<feedburner:origLink>http://www.rocketnumbernine.com/2009/06/27/using-the-max6675-thermocouple-to-digital-converter</feedburner:origLink></item>
</channel>
</rss>

