Master in receive mode in SPI

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

Hi Guys,

I am using the ATmega328 as the master and am trying to transmit some data to a slave (this is not a microcontroller but like a temp. sensor). I want to transmit some data then wait for 500 ms and then start receiving data from the slave. I was trying something like below:


#include              
#include  
#include 

#define ddr_spi    DDRB     
#define port_spi   PORTB     
#define pin_ss     PORTB2       
#define pin_mosi   PORTB3       
#define pin_miso   PORTB4       
#define pin_sck    PORTB5       

unsigned char Data;
unsigned char i;
unsigned char count = 10;
//---------------------------------------------------------------------------
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) | ( 0 << SPI2X) | (0 << SPR1) | (0 << SPR0) ;  

   PORTB &= ~(1 << pin_ss); //toggle slave select line from high to low
   Data = SPSR;
   Data = SPDR;

}


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


//---------------------------------------------------------------------------

void Device_init()

{
     DDRD = 0xFF;
     PORTD = 0x15;
}
     

void SPI_MasterReceive()

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

int main (void)
{   
   
   Device_init();
   SPI_MasterInit();               
 
   while(1)                         
   {
      if(count)

      {       
	  SPI_MasterTransmit(0x55);
		 count--;
       }
      _delay_ms(500);

     SPI_MasterReceive();

   }

}


I expect to receive 10 bytes each with a value of 0x55 from the slave. I have connected the MISO pin of the master to the output pin of the slave.

Will this work? I haven't tried it yet but I am going to give it a shot.

Thanks.

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

Using a single spi() function seems so much more sensible:

//--  Use a single function ----------------------------------
char SPI(char Spi_Data)
{
      SPDR = Spi_Data;              // Load byte to output register
      while(!(SPSR&(1<<SPIF)));     // Wait for byte to be sent
      return SPDR;
}
   while(1)                         
   {
      if(count)
      {       
          SPI(0x55);
          count--;
      }
      _delay_ms(500);
      c = SPI(0x00);          // just receive
   }

Your current void functions are not going to return anything.

David.

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

Thanks for the feedback. Before I change my functions, I am trying a basic test to check and see if the device responds. I am trying to take the SS pin low on the master side so that the master goes into listen mode:

#include              
#include  
#include 

#define ddr_spi    DDRB     
#define port_spi   PORTB     
#define pin_ss     PORTB2       
#define pin_mosi   PORTB3       
#define pin_miso   PORTB4       
#define pin_sck    PORTB5       

unsigned char Data;
unsigned char count = 155;

//---------------------------------------------------------------------------
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 << CPOL) | (1<<SPE) | (1<<MSTR) | ( 0 << SPI2X) | (0 << SPR1) | (0 << SPR0) ;  

   PORTB &= ~(1 << pin_ss); //toggle slave select line from high to low
   PORTB |= (1 << pin_ss);
   Data = SPSR;
   Data = SPDR;

}


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


//---------------------------------------------------------------------------

unsigned char SPI_MasterReceive() 

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


int main (void)
{   
   

   SPI_MasterInit();               
  
   while(1)                         
   {
      
	  if(count)

      {       
	  SPI_MasterTransmit(0x55);
		 count--;
       }

    else
	{
	_delay_us(50);
	PORTB &= ~(1 << pin_ss);
	    
   }
	}
    



}

I checked the SS pin on the scope and it does go low when it is supposed to. I have connected the device's "MISO" pin to the Atmega328's MISO pin.

However I am not seeing anydata on the MISO pin. What am I missing?

I also noticed that when the master had stopped transmitting there is no clock on the SCK pin which is normal. However to see something on the MISO pin, do I need the master to be sending the clock?

Do I have to switch the master over to slave mode?

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

You fail to understand what David told you... SPI is full duplex. The master controls the clock, with a data bit being shifted in both directions on each pulse. So in order to receive a byte, you must transmit a byte. Depending on the protocol, the byte sent [or received] may or may not have any meaning.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Stealing Proteus doesn't make you an engineer.

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

I understand.

Do I have to assert the SS pin low for the slave to respond? Or should I just send some dummy data from the master to keep the clock running and get data back from the slave?

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

Most slaves use the SS pin as a chip select for the slave. When the SS first becomes active low it also synchronizes the slave to receive the first bit of the SPI transfer from the master.

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

You have to read your slave device's data sheet.

As a general rule, SPI slaves have an active low slave select pin /SS. So the slave has to be 'selected' before it will take any notice of anything.

However some slaves need the /SS to go high at the end of a sequence. Typically shift registers read all the data in via SPI, the use the /SS going high to latch the data.

Other devices are happy to have /SS low at all times.

Incidentally, the SPIF flag is cleared by reading the SPDR register. So this is an even more compelling argument for a single SPI() function.

David.

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

Thanks for the link and the info.

Ok so now I am transmitting some dummy bytes after my data transfer is complete.

Attached are the waveforms I got on the scope. Green is data on MOSI pin of master, black is clock on SCK pin of master, red is /SS from master to slave. The cyan is on the MISO line of the slave. I am transmitting all zeroes as dummy bytes from the master after the /SS pin goes low. Here is my code:


#include              
#include 
#include 

#define ddr_spi    DDRB     
#define port_spi   PORTB     
#define pin_ss     PORTB2       
#define pin_mosi   PORTB3       
#define pin_miso   PORTB4       
#define pin_sck    PORTB5       

unsigned char Data;
unsigned char count = 155;
unsigned char test = 155;

//---------------------------------------------------------------------------
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 << CPOL) | (1<<SPE) | (1<<MSTR) | ( 0 << SPI2X) | (0 << SPR1) | (0 << SPR0) ; 

   PORTB &= ~(1 << pin_ss); //toggle slave select line from high to low
   PORTB |= (1 << pin_ss);
   Data = SPSR;
   Data = SPDR;

}


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


//---------------------------------------------------------------------------

unsigned char SPI_MasterReceive()

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


int main (void)
{   
   

   SPI_MasterInit();               
 
   while(1)                         
   {
     
     if(count)

      {       
     SPI_MasterTransmit(0x55);
       count--;
       }

    else
   {
   _delay_us(50);
   PORTB &= ~(1 << pin_ss);
    
    if(test)
     {

       SPI_MasterTransmit(0x00);
       test--;
     }
    
      
   }
   }
   



}

I understand that this could still be a slave device problem (maybe some pin is not set and the slave is not in the right mode) and I will recheck all the inputs.

I am not reading anything back from the slave in my code. I have the scope probe on the MISO pin of the master.

Any suggestions of what may be missing?

Thanks for your patience.

Attachment(s): 

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

Any suggestions of what may be missing?
Quote:

/SS pin is high while the first byte in picture is transmitted.
So the slave will be deselected while transmitting that byte.

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

Ok thanks. I have another question:
With the same code as above, I get the clock generated and the zero transmitted every 50 us. How can I wait for 50 us only once and then start transmitting the zeroes 155 times? As the 50 us is in the else loop it is waiting 50 us before transmitting every group of 8 clock cyles.

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

Anybody?

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

What's the use of that delay?
Is this needed for the slave(stated in its datasheet)?
AVR certainly don't need that delay.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
   PORTB &= ~(1 << pin_ss);   // enable /SS
   spi(0x55);
   _delay_us(50);
   for (i = 0; i < 155; i++)
      reply = spi(0);
   PORTB |= (1 << pin_ss);   // disable /SS

Depending on your Slave, you may need to enable/disable for each spi() transaction. And obviously, you would use the value of the Slave's reply.

David.

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

Quote:

What's the use of that delay?

The delay is needed by the device. I will try your suggestion, David. Thanks.