2. ATmega644 SPI Configuration

<p>The mega644 SPI module should be configured as follows:</p> <ul> <li>SPI enabled</li> <li>MSB first</li> <li>master mode</li> <li>clock polarity 0 (clock line low when idle)</li> <li>clock phase 0 (sample on leading ege)</li> <li>Maximum frequency: 2.5 MHz (clock/8)</li> </ul> <p>If you have the latest <a href="http://winavr.sourceforge.net/">WinAVR</a> installed (version 20070525 at the time this was written), the following C code will set up the SPI module, assuming you have your device set as atmega644. If you are using an older version of WinAVR, you will need to add zeroes to the ends of all of the SPI register/bit names (e.g. change SPCR to SPCR0, SPSR to SPSR0, SPE to SPE0, etc).</p> <pre name="code" class="c">&#x000A;#include &lt;avr/io.h&gt;&#x000A;&#x000A;// global flag used to help us track when an SPI transmission is in progress&#x000A;unsigned char SPITransmitting;&#x000A;&#x000A;void SPIInit()&#x000A;{&#x000A; // make the MOSI, SCK, and SS pins outputs&#x000A; DDRB |= ( 1 &lt;&lt; PB5 ) | ( 1 &lt;&lt; PB7 ) | ( 1 &lt;&lt; PB4 );&#x000A;&#x000A; // make sure the MISO pin is input&#x000A; DDRB &amp;= ~( 1 &lt;&lt; PB6 );&#x000A;&#x000A; // set up the SPI module: SPI enabled, MSB first, master mode,&#x000A; // clock polarity and phase = 0, F_osc/8&#x000A; SPCR = ( 1 &lt;&lt; SPE ) | ( 1 &lt;&lt; MSTR ) | ( 1 &lt;&lt; SPR0 );&#x000A; SPSR = 1; // set double SPI speed for F_osc/8&#x000A;&#x000A; // the previous settings clear the SPIF bit of SPCR, so we use our global&#x000A; // flag to indicate that this does not mean we are currently transmitting&#x000A; SPITransmitting = 0;&#x000A;}</pre> <p>Most commands require data flow in only one direction: from the mega644 to the mega168. This can be achieved with an SPI transmit command.</p> <pre name="code" class="c">&#x000A;void SPITransmit( unsigned char data )&#x000A;{&#x000A; if ( SPITransmitting ) // if we really are transmitting&#x000A; while ( ! ( SPSR &amp; ( 1 &lt;&lt; SPIF ))) // wait for completion of&#x000A; ; // previous transmission&#x000A; SPDR = data; // begin transmission&#x000A; SPITransmitting = 1; // flag transmission in progress&#x000A;}</pre> <p>Reading data back from the mega168 is only slightly more complicated since we need to give the mega168 time (~3us) to prepare the data byte we&#8217;re requesting. Once it&#8217;s ready we then need to transmit an extra byte since every SPI transaction has data flow in both directions. As our byte is being sent to the mega168, the data we&#8217;re interested is being sent to us. At the end of the transmission, the value from the mega168 will be in the mega644&#8217;s SPI data register, SPDR. We need an extra function to perform our 3 microsecond delay, so we&#8217;ll include an example here:</p> <pre name="code" class="c">&#x000A;static inline void delay_us(unsigned int microseconds) __attribute__((always_inline));&#x000A;void delay_us(unsigned int microseconds)&#x000A;{&#x000A; __asm__ volatile (&#x000A; "1: push r22" "\n\t"&#x000A; " ldi r22, 4" "\n\t"&#x000A; "2: dec r22" "\n\t"&#x000A; " brne 2b" "\n\t"&#x000A; " pop r22" "\n\t"&#x000A; " sbiw %0, 1" "\n\t"&#x000A; " brne 1b"&#x000A; : "=w" ( microseconds )&#x000A; : "0" ( microseconds )&#x000A; );&#x000A;}&#x000A;&#x000A;&#x000A;unsigned char SPIReceive( unsigned char data ) // data is often a junk byte (e.g. 0)&#x000A;{&#x000A; if ( SPITransmitting )&#x000A; while ( ! ( SPSR &amp; ( 1 &lt;&lt; SPIF ))) // wait for completion of&#x000A; ; // previous transmission&#x000A; delay_us( 3 ); // give the mega168 time to prepare&#x000A; // return data&#x000A; SPDR = data; // start bidirectional transfer&#x000A; while ( ! ( SPSR &amp; ( 1 &lt;&lt; SPIF ))) // wait for completion of&#x000A; ; // transaction&#x000A; // reading SPCR and SPDR will clear SPIF, so we will use our global flag&#x000A; // to indicate that this does not mean we are currently transmitting&#x000A; SPITransmitting = 0;&#x000A; return SPDR;&#x000A;}</pre> <p>We can now put wrapper functions around these low-level SPI commands to communicate with the mega168. For instance, here is a command for setting motor 1:</p> <pre name="code" class="c">&#x000A;void setMotor1( int speed )&#x000A;{&#x000A; // first, we'll prepare our command byte&#x000A;&#x000A; unsigned char command;&#x000A;&#x000A; if ( speed &gt; 255 )&#x000A; speed = 255;&#x000A; if ( speed &lt; -255 )&#x000A; speed = -255;&#x000A;&#x000A; if ( speed &gt;= 0 )&#x000A; command = 136; // motor 1, forward&#x000A; else&#x000A; {&#x000A; command = 138; // motor 1, reverse&#x000A; speed = -speed;&#x000A; }&#x000A;&#x000A; // the MSB of the speed gets tacked onto the command byte&#x000A; command |= ( (unsigned char) speed &amp; 0x80 ) &gt;&gt; 7;&#x000A;&#x000A; // now, send the command&#x000A; SPITransmit( command );&#x000A; SPITransmit( (unsigned char) speed &amp; 0x7F );&#x000A;}</pre>

Related Products

Orangutan X2 with VNH3
Log In
Pololu Robotics & Electronics
Shopping cart
(702) 262-6648
Same-day shipping, worldwide
Menu
Shop Blog Forum Support
My account Comments or questions? About Pololu Contact Ordering information Distributors