AVR + SPI (yet another!!)

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Guys,

I am trying to use the example from AVR151 for the SPI between two Atmega168's (one master and one slave). Both are using an 8MHz crystal. Here is the master code:


//This is master code; the master has a button input which when pressed on the master side, tells the slave to light LED
//Use internal oscillator for both master and slave
//SPI configured in mode 0 with MSB transmitted first
//SPI mode is polling the interrupt flags

#include 
#include 
#include 
#include 

char* TextStringToSend = "AVR communicating with SPI"+0x00;
char*    PtrToStrChar;                  	// Pointer to certain Character in String


void master_init()

{
   volatile char Data;
   //Make SS,MOSI and SCK as outputs
   DDRB    = (1<<PB2)|(1<<PB3)|(1<<PB5);
   //SPI control register
   //SPIE: SPI interrupt enable needed to poll the interrupt flag
   //SPE: Enable SPI
   //Set SPI in master mode
   //SPR0 sets SCK frequency to SCK/16
   SPCR  = (1<<SPIE)|(1<<SPE)|(1<<MSTR)|(1<<SPR0);
   Data = SPSR;
   Data = SPDR;
   sei();
}

void Master_Send (void)

{
        PtrToStrChar = TextStringToSend;             // Set Pointer to beginning of String
        while   (*PtrToStrChar != 0)    // if not end of String
        {
            SPDR  = *PtrToStrChar;     // send Character
            while (!(SPSR & (1<<SPIF)));    // wait until Char is sent

            PtrToStrChar++;                 // Point to next char in String

        }
}

int main()

  {

     unsigned char KeyPressed;
    //Switch will be on port C
     DDRC = 0x00; 
   //LED will be on Port D
  //PORT D0 will be output
     DDRD = 0xFF; 
     DDRB = 0xFF;
     master_init();
     PORTD = (1 << PD2);//this is to check if AVR is working
       while(1)
        {
         
           if(PINC != 0xFF)
	     {
	        if(KeyPressed == 0x00)
                   {
		  
		       KeyPressed = 0x01;
		       PORTD ^= (1 << PD0); //led will just show that key has been pressed
		       Master_Send();
                    } 
              }

	else KeyPressed = 0x00; 
       } //the LED will toggle only after the switch is released and pressed again

  }

             
			 		  

Here is the slave code:


//Slave code to communicate with M168 Master
//use internal oscillator
//Master sends string to slave; slave compares it with stored string
//If string matches, then slave turns on LED on Port D
#include 
#include 
#include 
#include 

#define ERROR 0x01
#define SUCCESS 0x02

char TransmitStatus = 0x00;
char* ReceivedString = "AVR communicating via the SPI"+0x00;

void init_slave()

{
  volatile char slave_data;
  //Set MISO (PB4) as output

  DDRB = (1 << PB4);

  //Set Slave with clk/(default value for M168)
  
  //Clear the SPSR and SPDR registers by reading them
  slave_data = SPSR;
  slave_data = SPDR;

  DDRD = (1 << PORTD0) | (1 << PORTD1) | (1 << PORTD2);
}


void slave_receive()
{

  while ((*ReceivedString !=0 && TransmitStatus != ERROR))
   
   {

     while (!(SPSR & (1 << SPIF)));
	 if (SPDR != *ReceivedString) //will check one byte at a time till end of string reached
	  {
	    TransmitStatus = ERROR;
       }

	   ReceivedString++;

	 }
	 if(TransmitStatus == ERROR)
	 {
	   PORTD = (1 << PORTD1); //portd1 (pin 3) LED on = transmission error
      
	  }
      else  PORTD = (1 << PORTD0); //Port D0 (pin 2) LED on = success

   while(1);
}
     

int main()
{
  
  init_slave();
  slave_receive();
}
  

What is missing here?

The LED on PD2 on my master lights just fine indicating that the AVR master side is working.
Port C5 is connected to VCC through a 10K resistor by default and has a capacitor to GND. The switch is connected between port C5 and GND.

I have connected the following:
1. MOSI of master to MOSI of slave
2. MISO of slave to MISO of slave
3. SCK of master to SCK of slave
4. Master /SS is connected to VCC
5. Slave SS is connected to GND

Appreciate any help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

how does your slave know the data on the bus is valid and meant fo it and not for another device on the bus?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the slave SS may need to see a 1->0 transistion to get synchronized. Not sure you can just tie it low. Have you seen code that does that (tie SS low)?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I have simplified my code to just transmit one character (U) from master to slave as follows:

master code:

//f_osc=8MHz

#include              
#include          

#define SPI_DDR    DDRB     
#define SPI_PORT   PORTB     
#define SS_PIN     PB2       
#define MOSI_PIN   PB3       
#define MISO_PIN   PB4       
#define SCK_PIN    PB5       


void SPI_MasterInit(void)
{
   SPI_DDR   = (1<<MOSI_PIN)|(1<<SCK_PIN))|(1<<SS_PIN);    
   PORTB = 0xFF;
   SPCR      = ((1<<SPE)|(1<<MSTR)|(1<<SPR0) | (1<<SPR1));  // Enable SPI, master, prescaler 128
   PORTB &= ~(1 << SS_PIN);

}

void SPI_MasterTransmit(char SPI_Data)
{
      SPDR =   SPI_Data;               // Load byte to output register
      while(!(SPSR&(1<<SPIF)));     // Wait for byte to be sent
}


int main (void)
{   
   DDRC = 0xFF;
   SPI_MasterInit();               

   while(1)                         
   {
      PORTC ^= 0xFF;                 // Led
      _delay_ms(500);
      SPI_MasterTransmit('U');     
   }
}


slave code:

//Slave code to communicate with M168 Master
//use 8MHz crystal oscillator
//Master sends char to slave; slave compares it with stored char
//If char matches, then slave turns on LED on Port D
#include 
#include 
#include 
#include 


void init_slave()

{
  volatile char slave_data;
  //Set MISO (PB4) as output

  DDRB = (1 << PB4);

  //Set Slave with clk/(default value for M168)
  
  //Clear the SPSR and SPDR registers by reading them
  slave_data = SPSR;
  slave_data = SPDR;

  DDRD = (1 << PORTD0) | (1 << PORTD1);
}


void slave_receive()
{

     while (!(SPSR & (1 << SPIF)));
	 if (SPDR == 'U') //will check one byte at a time till end of string reached
	  {
	    
		PORTD = (1 << PORTD1); //portd1 (pin 3) LED on = transmission success

       }

      else  PORTD = (1 << PORTD0); //Port D0 (pin 2) LED on = error

}
     

int main()
{
  
  init_slave();
  while(1)
  {
  slave_receive();
  }
}
  

I have toggled the SS pin from high to low in the master init().

The LED is blinking on the master but on the slave side, the LED on port D0 is switching on not the LED on port D1.

What could be wrong? Do I need a delay?

edit:

Got it working at least partially with the following code:(master code is same as above)

slave code:


//Slave code to communicate with M168 Master

#include 
#include 
#include 
#include 


void init_slave()

{
  //Set MISO (PB4) as output

  DDRB = (1 << PB4);
  SPCR = (1 << SPE);//ENABLE SPI 

  //Set Slave with clk/(default value for M168)
  
  DDRD = (1 << PORTD0) | (1 << PORTD1) | (1 << PORTD2);
}


unsigned char slave_receive()
{

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

}
     

int main()
{
  unsigned char SPI_char;
  init_slave();
  while(1)
  {

     SPI_char = slave_receive();
  	 if (SPI_char == 'U') //will check one byte at a time till end of string reached
    
		PORTD = (1 << PORTD1); //portd1 (pin 3) LED on = transmission success

       else  PORTD = (1 << PORTD0); //Port D0 (pin 2) LED on = error

  }
}


I did not have the SPI enabled on the slave.

Now the problem is when I put the LED on port D0 in my circuit, it gets turned on but when I remove the LED on port D0,the LED on port D1 is turned on indicating success. In other words, the port D1 led turns on only when there is no led on port d0.

Last Edited: Tue. Mar 2, 2010 - 11:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So on the slave why not use 8 LEDs and output the entire SPDR byte to the port then you can see exactly what was received.

But you do understand the purpose of _SS don't you? Don't just pull the slave low once and for all at the start. Instead you normally leave _SS high to all slaves. Then to select one to communicate with, just while byte(s) are being sent you take it low and then take it high again afterwards. It is the high to low transition of the slave's _SS pin that resets its SPDR shift register. So if a previous transmission missed an SCK pulse or there was a glitch seen as an extra clock pulse the two shift registers could be out of sync but the high->low reset gets both ends back in sync again. So pay more attention to _SS.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the idea,Cliff.

I sent the output to LED's and I am getting 10101010 (U) on the slave. Will now move on to the switch based example.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In which case your if() [which is missing braces - very bad style!] should take the "PORTD = (1 << PORTD1); " path so PD1 will be '1' and PD0 (and all other 6 bits) will be '0'

Are you suggesting the "== 'U'" comparison is not working?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok one more question; I noticed that when I was transmitting 0xF0 the SPI slave read it as 0xF0; but the LED's showed it as 0x0F. I am using PORTD with LED's on PD0-PD7.Why are the nibbles getting swapped?

slave code:

//Used 8MHz crystal on both slave and master side
//The byte is transmitted with MSB first
//This program is for two M168 
//Need to try with switch
//Important: toggle slave select line from high to low to select M168 slave only on master side

#include 
#include 
#include 
#include 


void init_slave()

{
  //Set MISO (PB4) as output

  DDRB = (1 << PB4);
  SPCR = (1 << SPE);// Enable SPI on slave side

  //Set Slave with clk (default value for M168)
  //SPI2X,SPR1 and SPR0 are all 0; but SPR1 and SPR0 have no effect on slave
  
  DDRD = 0xFF;
  DDRC = 0xFF;

}


unsigned char slave_receive()
{

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

}
     

int main()
{
  unsigned char SPI_char;
  PORTC = 0x00;
  init_slave();
  while(1)
  {

     SPI_char = slave_receive();

	 PORTD = SPI_char; // send master output to slave on portd with 8 LED's for debugging; this is showing lower nibble on PD0-PD3 and upper nibble on PD4-PD7
  	 if (SPI_char == 0xF0) 
        PORTC = (1 << PORTC5); //master sent 0xF0

  }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How can you tell it's just a nybble swap using 0xF0 (11110000)? I'd suggest it's a bit swap so it becomes 00001111. Try changing the DORD control bit and see what happens.

If you use 0xE0 then a nybble swap would be 0x0E but a bit swap would be 11100000 becomes 00000111 - that is 0x07

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I checked and there was no swapping or inverting. I was just interpreting the LED's wrong.

Now that I have got the character transmission working, I am trying to send a string.

master:

//Used 8MHz crystal on both slave and master side
//This program is for two M168 
//Need to try with switch
//Important: toggle slave select line from high to low to select M168 slave only on master side

#include              
#include  
#include 

#define ddr_spi    DDRB     
#define port_spi   PORTB     
#define pin_ss     PB2       
#define pin_mosi   PB3       
#define pin_miso   PB4       
#define pin_sck    PB5       

char* TextStringToSend = "AVR communicating with SPI"+0x00;
char*    PtrToStrChar;                  	// Pointer to certain Character in String
unsigned char Data;
//---------------------------------------------------------------------------
void SPI_MasterInit(void)
{
   ddr_spi   = ((1<<pin_mosi)|(1<<pin_sck))|((1<<pin_ss));    // MOSI, SCK = 1
   PORTB = 0xFF; //This is very important; need to toggle Slave select line from high to low
   SPCR      = (1<<SPE)|(1<<MSTR)|(1<<SPR0) | (1<<SPR1);  // Enable SPI, master, prescaler 128
   PORTB &= ~(1 << pin_ss); //toggle slave select line from high to low
   Data = SPSR;
   Data = SPDR;

}

void Master_Send (void)

{

        PtrToStrChar  = TextStringToSend;             // Set Pointer to beginning of String

        while   (*PtrToStrChar != 0)            // if not end of String

        {

                SPDR  = *PtrToStrChar;          // send Character

                while (!(SPSR & (1<<SPIF)));    // wait until Char is sent

                PtrToStrChar++;                 // Point to next char in String

        }

}
//---------------------------------------------------------------------------
int main (void)
{   
   DDRD = (1 << PD0); //Blinking LED on port D0 (pin 2) all others input

   SPI_MasterInit();               

   while(1)                         
   {
      PORTD ^= (1 << PD0);                 // Led
      _delay_ms(500);

		Master_Send();
   }

}

slave:

//Used 8MHz crystal on both slave and master side
//The byte is transmitted with MSB first
//This program is for two M168 
//Need to try with switch
//Important: toggle slave select line from high to low to select M168 slave only on master side

#include 
#include 
#include 
#include 

char* ReceivedString = "AVR communicating via the SPI"+0x00;

void init_slave()

{
  //Set MISO (PB4) as output

  DDRB = (1 << PB4);
  SPCR = (1 << SPE);// Enable SPI on slave side

  //Set Slave with clk (default value for M168)
  //SPI2X,SPR1 and SPR0 are all 0; but SPR1 and SPR0 have no effect on slave
  
  DDRD = 0xFF;
  DDRC = 0xFF;

}
 

int main()
{
//  unsigned char SPI_char;
  PORTC = 0x00;
  init_slave();
  while(1)
  {

     while(*ReceivedString != 0)
	   
	    {
          
		   while (!(SPSR & (1 << SPIF)));
// send master output to slave on portd with 8 LED's for debugging

			   PORTD = SPDR;
			   if (SPDR == *ReceivedString)
			     
				 {
    
	            	PORTC ^= (1 << PORTC5); //keep this blinking to show correct transmission
                    
                  }
              
			  
         }
		 ReceivedString++;
   }

}
  

The LED on the slave side is continuously blinking. So to test if this is really working, I truncated the "AVR communicating with SPI"+0x00 string on the slave side to "AVR communicating"+0x00. However, the LED still kept blinking. I expect the blinking to stop after the "g" character on the slave side.

I also tried this with the while(1) loop removed and the LED on port C5 is still blinking. It does stop when I remove the MOSI connection so I know SPI is working.
What could be wrong here? Is it the way I am collecting the string on the slave side?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why not have the master send the string one character every 2-3 seconds. Have the slave output the received bytes on the LEDs and you have 2-3 seconds each time to verify that the bit pattern is the next expected ASCII character from the string. No need for a copy of the string in the slave. You could even have the master hold off sending the next character until a button is pressed - giving you a virtually limitless time to check each byte on the slave.

(or you could invest in an OCD interface and check everything within seconds - or how about putting a UART link or an LCD on the slave to "see" what's going inside?)