NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » SPI communication code

May 01, 2011
by uml_12
uml_12's Avatar

I'm trying to get a SPI communication between two ATMega168 microcontrollers.

Please Help! It's not doing much of anything. Here is the code I'm using:

define F_CPU 14745600

include <avr/io.h>

include <inttypes.h>

include "../libnerdkits/delay.h"

include "../libnerdkits/lcd.h"

define DDR_SPI PORTB

define DD_MOSI PB4

define DD_SCK PB5

define DD_MISO PB3

// PIN DEFINITIONS: // // PC4 -- LED anode

void SPI_MasterInit(void); void SPI_SlaveInit(void); char SPI_SlaveReceive(void); void SPI_MasterTransmit(char cData); void configurePortDOutput(void);

int main() { //If SlaveSelect (!SS) is high controller is master. if (PB2 == 1) { // Initialize the MASTER Controller SPI_MasterInit();

    while (true)
    {
        char levelOneCubeOne = 0x05; //0000 0101 LEVEL1
        // Transmit a test byte to the slave
        SPI_MasterTransmit(levelOneCubeOne);
    }

}
// Else SlaveSelect (!SS) is low controller is slave.
else
{
    // Initialize the SLAVE Controller
    SPI_SlaveInit();

    // Configure pins of PORTD as output.
    configurePortDOutput();

    // Declare variables
    char * dataRegisterByte;

    while (true)
    {
        // BEGIN NEW CODE   
        // Get the data from the data register
        dataRegisterByte = SPI_SlaveReceive();

        // Check Status of BIT0
        if (dataRegisterByte[0] == 1)
        {
            // Light RED LED
            PORTD |= (1 << PD0);
        }
        else
        {
            // Light GREEN LED
            PORTD |= (1 << PD1);
        }

        // Check Status of BIT1
        if (dataRegisterByte[1] == 1)
        {
            // Light RED LED
            PORTD |= (1 << PD2);
        }
        else
        {
            // Light GREEN LED
            PORTD |= (1 << PD3);
        }

        // Check Status of BIT2
        if (dataRegisterByte[2] == 1)
        {
            // Light RED LED
            PORTD |= (1 << PD4);
        }
        else
        {
            // Light GREEN LED
            PORTD |= (1 << PD5);
        }

        // Check Status of BIT3
        if (dataRegisterByte[3] == 1)
        {
            // Light RED LED
            PORTD |= (1 << PD6);
        }
        else
        {
            // Light GREEN LED
            PORTD |= (1 << PD7);
        }

//END NEW CODE } //END WHILE LOOP } // END ELSE

// Program complete... Return!
return 0;

}

void SPI_MasterInit(void) { // Set MOSI and SCK output, all others input DDR_SPI = (1 << DD_MOSI) | (1 << DD_SCK);

// Enable SPI, Master, set clock rate fck/16
SPCR = (1 << SPE) | (1 << MSTR ) | (1 << SPR0);

}

void SPI_SlaveInit(void) { // Set MISO output, all others input DDR_SPI = (1 << DD_MISO);

// Enable SPI bit
SPCR = (1 << SPE);

}

char SPI_SlaveReceive(void) { // Wait for reception complete while(!(SPSR & ( 1 << SPIF)));

// Return the contents of the Data Register (SPDR)
return SPDR;

}

void SPI_MasterTransmit(char cData) { // Copy cData byte into data register SPDR = cData;

// Wait for transmission complete
while(!(SPSR & (1 << SPIF)));

}

void configurePortDOutput(void) { DDRD |= (1 << PD0); DDRD |= (1 << PD1); DDRD |= (1 << PD2); DDRD |= (1 << PD3); DDRD |= (1 << PD4); DDRD |= (1 << PD5); DDRD |= (1 << PD6); DDRD |= (1 << PD7); }

May 01, 2011
by Ralphxyz
Ralphxyz's Avatar

So is this your Master or Slave code?

You are not using the LCD making PortD available, again on the Master or Slave?

Ralph

May 02, 2011
by uml_12
uml_12's Avatar

I just found out that I need separate codes for master and for a slave.

and yes I made port D availabe for my slave to light up leds.

May 02, 2011
by Ralphxyz
Ralphxyz's Avatar

Yeah, that is what I suspected.

I think you have the right idea in your code so just split it up and lets see what happens.

Ralph

May 02, 2011
by uml_12
uml_12's Avatar

Okay so I split it up.. Can you see anything wrong with this? Thanks

Master Code:

define F_CPU 14745600

include <avr/io.h> // For IO

include <inttypes.h> // For int data types

include "../libnerdkits/delay.h"

define SPI_PORTB PORTB

define SPI_DDR DDRB

define SPI_CS PB2

char SPI_ReadWrite(char data_out);

int main(void){

char data;

DDRD = 0XFF;                // set portd as output 
PORTD = 0X00;

SPI_DDR = (1<<PB3)|(1<<PB5)|(1<<PB2);   // Set MOSI, SCK, !ss

SPI_PORTB |= (1<<SPI_CS);                   // set !ss high to disable slave latch

SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);        // SPI enable, Master, fck/16

//data = SPI_ReadWrite(0);                  // clear the slave buffer

//delay_ms(100);

data = 0x05;

SPI_ReadWrite(data);

while(1);

return 0;

}

char SPI_ReadWrite(char data_out){

char data_in;

SPDR = data_out;

while(!(SPSR & (1<<SPIF)));

data_in = SPDR;

SPI_PORTB &= ~(1<<SPI_CS);

delay_us(1);

SPI_PORTB |= (1<<SPI_CS);

return data_in;

}

Slave Code:

define F_CPU 14745600

include <avr/io.h> // For IO

include <inttypes.h> // For int data types

include "../libnerdkits/delay.h"

define SPI_PORTB PORTB

define SPI_DDR DDRB

define SPI_CS PB2

char SPI_WriteRead(char data_out);

int main(void) {

char *data_in, data_out;

SPI_DDR |= (1<<PB4);                        // MISO output. others = input

SPCR = (1<<SPE);                            // enabling SPI but not selecting master mode

DDRD |= (1<<PD7)|(1<<PD6)|(1<<PD5)|(1<<PD4);

data_out = 0x00;

data_in = SPI_WriteRead(data_out);      // data_in = data shifted in by master

    //delay_us(1);
for(;;){

    if(data_in[0] == 1) { PORTD |= (1<<PD7);} 
    if(data_in[1] == 1) { PORTD |= (1<<PD6);}
    if(data_in[2] == 1) { PORTD |= (1<<PD5);}
    if(data_in[3] == 1) { PORTD |= (1<<PD4);}

}

// remember in the real code that the slave must take the data input from the SPDR buffer

return 0;

}

char SPI_WriteRead(char data_out){

SPDR = data_out;                    // shifting is circular, so data_out is req.

while(!(SPSR & (1<<SPIF)));     // wait for transmission

return SPDR;                        // Return the new data shifted in by the master

}

May 02, 2011
by Hexorg
Hexorg's Avatar

uml_12, in master's code SPI_ReadWrite(), why are you toggling the SS pin at all?

In master code, set SS pin to ouput, set it to 1, and never change it.

In slave code everything seems correct. You only need to use SS pin on SLAVES when you have more then 1 slave.

May 02, 2011
by uml_12
uml_12's Avatar

well .. okay, i will change it for now. but the future of our project is to control multiple slaves. Are you saying that ss should be pulled the entire time only when one slave is actually connected ?

May 03, 2011
by uml_12
uml_12's Avatar

pulling !ss low (active) for the duration of the program did absolutely nothing. Can anyone see anything ever so slightly wrong with that code ? our next step is to implement debugging leds which should have probably been in there already. but im fairly new to this sort of thing. Basically, I plug it in and nothing at all occurs. I'll post my schematic when I get some spare time .. and my setup as well.

but are you sure you can't see anything wrong at all with the above code or general flow? I can't even count the hours I've spent researching, and testing spi code (more than 100 by now). we are at the tipping point. everyone I talk to is suggesting we move to aurdino just to get the project over with. I'm not 100% that will solve our problems without providing us with new ones..

May 03, 2011
by Noter
Noter's Avatar

Could be the SPI is working. Be sure you select the slave before you try to read or write, not after as shown in your code above. Maybe just keep it selected as Hexorg suggests.

Otherwise, I think your problem may be declaring data_in as a pointer and then setting it to the value of the received byte. A pointer is not necessary for a single byte but when you do use a pointer you must set it to the correct address.

Another problem may be that you mean to test bits in the recieved data byte instead of different bytes in an array.

May 03, 2011
by uml_12
uml_12's Avatar

Thanks ! I will look into that.

May 03, 2011
by Hexorg
Hexorg's Avatar

Well, in master mode, SS has to be configured as output, because (ATmega168 datasheet p.167):

If SS is configured as an input, it must be held high to ensure Master SPI operation. If the SS pin
is driven low by peripheral circuitry when the SPI is configured as a Master with the SS pin
defined as an input, the SPI system interprets this as another master selecting the SPI as a
slave and starting to send data to it.

And for the slave:

When the SPI is configured as a Slave, the Slave Select (SS) pin is always input. When SS is
held low, the SPI is activated, and MISO becomes an output if configured so by the user. All
other pins are inputs. When SS is driven high, all pins are inputs, and the SPI is passive, which
means that it will not receive incoming data. Note that the SPI logic will be reset once the SS pin
is driven high.

Post a Reply

Please log in to post a reply.

Did you know that you can connect a pushbutton to a microcontroller with only one wire? Learn more...