Polling SPI between Microcontrollers - Problem - SOLVED

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

Please find attached (The codes are new)

(1) main_MST.c
(2) main_SLV.c
(3) my_SPI.h

The codes are for polled master slave data exchange by SPI. The codes "work" without the SynchWithSlave() / SynchWithMaster(), [respective codes], but only when first Slave is hard-reset and then "Master hard-reset, less than 500ms, and Slave is hard-reset". If the delay between the Master to Slave hard-resets are greater than 500 ms, no data exchange takes place.

The SynchWithSlave() / SynchWithMaster(), [in respective codes] was introduced to avoid the hard-resets which is not acceptable for normal operation. No data exchange takes place with these two sub-routines. Using step-wise LED-o/p-diagnostics, hard-reset is still required to achive "SPI_SendByte", but SynchWithSlave() / SynchWithMaster() is not executed.

Humbly requesting guidance, where have I missed some thing. This may require re-creating this setup.

Connections :
MOSI---MOSI
MISO---MISO
/SS /SS
RST-SW-GND RST--SW-GND

Many thanks in advance

India_AVR

<< attachments deleted as same code included in post later >>

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

Last Edited: Thu. Mar 6, 2008 - 07:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't looked at the code yet, But I see that you have /SS "open" on the master. Make sure this pin is set as an output, or otherwise pulled high. If this pin is left floating, or somehow pulled low, while set as an input, the masters SPI hardware will enter slave mode.

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

glitch wrote:
I haven't looked at the code yet, But I see that you have /SS "open" on the master. Make sure this pin is set as an output, ....

The pin has been set as output.

I think people find it difficult to open attachment hence I am
including the code in the post.

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

I am trying to learn about using SPI data exchange between two ATMega's. I humbly approach the fraternity to please guide me, where I may be making mistake as the code does not seem to work. Many thanks in advance.
India_AVR

MASTER :

/* MST_sync_condensed.c  */

#include 
#include 
#include "my_SPI.h"

/*
	                SPI MASTER EXAMPLE - Dean
 		      /SS was left open - India_AVR
*/

char SPI_SendByte(char Byte);
void SynchWithSlave(void);
void delay_ms(uint16_t ms);

int main (void)
{
	/* Set MOSI and SCK as output, MISO and /SS as input with pullups */
	SPI_MST_DDR = (1 << SPI_MST_MOSI_DD) | (1 << SPI_MST_SCK_DD);
	SPI_MST_PORT = (1 << SPI_MST_MISO_PORT) | (1 << SPI_MST_U_SS_PORT);
	
	/* Set debug PORTD as all outputs */
	LED_DDR = 0xFF;

	/* SPI On, Master, Fcpu/16 */
	SPCR  = ((1 << SPE) | (1 << MSTR) | (1 << SPR0));
	
	/* Set debug LEDs to 0xFF */
	LED_PORT = ~0x55; 
	delay_ms(1000);
	SynchWithSlave();
	LED_PORT = SPI_SendByte(0b00100010);
	
	/* Do nothing until AVR reset */
	for (;;);
}

char SPI_SendByte(char Byte)
{
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));
	return SPDR;
}

void SynchWithSlave(void)
{
 const uint8_t Sequence[] = {0xAA, 0xBB, 0xCC}; 
 uint8_t CorrectBytes     = 0;
 uint8_t EchoedByte       = 0x00;
 
 /* Send some NULLs to the slave at startup to flush the SPI buffer */
 for (uint8_t SynchNulls = 0; SynchNulls < 5; SynchNulls++)
   SPI_SendByte(0x00);
   
 /* Loop until the entire sequence is correctly echoed */
 while (CorrectBytes != sizeof(Sequence))
 {
  /* Reset correctly echoed sequence bytes counter for next attempt */
  CorrectBytes = 0;
 
  /* Echo out the sequence bytes, plus one garbage byte at the end to grab the last echoed byte */
  for (uint8_t SeqByte = 0; SeqByte <= sizeof(Sequence); SeqByte++)
  {
   /* Send next byte of the sequence, store echoed byte */
   EchoedByte = SPI_SendByte(Sequence[SeqByte]);
   
   /* Check if the echoed byte matches the next byte in the sequence */
   if (EchoedByte == Sequence[CorrectBytes])
     CorrectBytes++;
   else
     CorrectBytes = 0;
  }
  
  /* Turn off SPI */
  SPCR  &= ~(1 << SPE);
 
  /* Manually clock the CLK pin once to shift the slave's SPI register one bit */
  SPI_MST_PORT ^=  (1 << 7);
  _delay_us(20);
  SPI_MST_PORT ^=  (1 << 7);
  
  /* Turn the SPI system back on ready for next attempt */
  SPCR  |=  (1 << SPE);  
 } 

}

/* function for long delays  */
void delay_ms(uint16_t ms)
{
   while(ms)
   {
    _delay_ms(1);
	ms--;
   }
}

SLAVE :

/*  SLV_sync_condensed.c  */
#include 
#include 
#include "my_SPI.h"

/*
	                  SPI SLAVE EXAMPLE - Dean
			/SS was left open - India_AVR
*/

char SPI_SendByte(char Byte);
void SynchWithMaster(void);
void delay_ms(uint16_t ms);

int main (void)
{
	/* Set MOSI and SCK as input with pullups, MISO and /SS as output */
	SPI_SLV_DDR = (1 << SPI_SLV_MISO_DD) | (1 << SPI_SLV_U_SS_DD);
	SPI_SLV_PORT = (1 << SPI_SLV_MOSI_PORT) | (1 << SPI_SLV_SCK_PORT);
	
	/* Set debug PORTD as all outputs */
	LED_DDR  = 0xFF;

	/* SPI On, Slave, Fcpu/16 */
	SPCR  = ((1 << SPE) | (1 << SPR0));
	
	/* Set debug LEDs to 0xFF */
	LED_PORT = ~0x33;
	delay_ms(500);
	SynchWithMaster();
	LED_PORT = SPI_SendByte(0b10001000);
	
	/* Do nothing until AVR reset */
	for (;;);
}

char SPI_SendByte(char Byte)
{
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));
	return SPDR;
}

void SynchWithMaster(void)
{
 const uint8_t Sequence[] = {0xAA, 0xBB, 0xCC}; 
 uint8_t CorrectBytes     = 0;
 uint8_t PrevByte         = 0x00;
 
 /* Loop until the entire sequence is properly recieved */
 while (CorrectBytes != sizeof(Sequence))
 {
  /* Send the previously recieved byte, save the newly recieved byte */
  PrevByte = SPI_SendByte(PrevByte);
  
  /* Check if the received byte matches the next in the sequence */
  if (PrevByte == Sequence[CorrectBytes])
    CorrectBytes++;
  else
    CorrectBytes = 0;
 }
}

/* function for long delays  */
void delay_ms(uint16_t ms)
{
   while(ms)
   {
    _delay_ms(1);
	ms--;
   }
}

my_SPI.h :

/*      my_SPI.h    */

/*      2135 : 29-Jan-2008 - India_AVR
	To ensure DDRx / PORTx / PINx correctly defined
        Some defines may never be used
*/

/* Master */
#define SPI_MST_DDR		DDRB
#define SPI_MST_PORT		PORTB

#define SPI_MST_DD_MOSI		DDB5
#define SPI_MST_DD_MISO		DDB6
#define SPI_MST_DD_SCK		DDB7
#define SPI_MST_DD_U_SS		DDB4

#define SPI_MST_PORT_MOSI	PB5
#define SPI_MST_PORT_MISO	PB6
#define SPI_MST_PORT_SCK	PB7
#define SPI_MST_PORT_U_SS	PB4

/* Slave */
#define SPI_SLV_DDR		DDRB
#define SPI_SLV_PORT		PORTB

#define SPI_SLV_DD_MOSI		DDB5
#define SPI_SLV_DD_MISO		DDB6
#define SPI_SLV_DD_SCK		DDB7
#define SPI_SLV_DD_U_SS		DDB4

#define SPI_SLV_PORT_MOSI	PB5
#define SPI_SLV_PORT_MISO	PB6
#define SPI_SLV_PORT_SCK	PB7
#define SPI_SLV_PORT_U_SS	PB4

/* PORT_LED */
#define LED_DDR			DDRD
#define LED_PORT		PORTD

<< Condensed original code to essential minimum >>

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

Last Edited: Sun. Feb 10, 2008 - 05:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is the same point applicable viz /SS high initially - low before send - high after receive, to my case also as guided by John Samperi to Atif in :-

https://www.avrfreaks.net/index.p...

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Quote:
The pin has been set as output

But it hasn't been - in the very most recent post of your code you don't appear to be setting SPI_MST_DD_U_SS which means the pin is an input (though you do appear to pull it up). Wouldn't it be better to deliberately set it as output and drive it appropriately (that usually means take it low before SPI transfer, take it high afterwards while "quiescent")

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

By the way have you read (and looked at the associated code for) Atmel's app note AVR151:

http://www.atmel.com/dyn/product...

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

Clawson :
Ref to your suggestions, I will make following changes in Master and Slave as shown below. I had earlier come across a reference that for Master the SPI_MST_SCK_DD should be set seperately first then SPI_MST_MOSI_DD and SPI_MST_SCK_DD. Your comments would be highly appreciated. Incidently the AVR151 was my base reference along with 2466N-AVR-10/06.

Dean :
Ref your original code, Master Initialisation, the DDRB is being changed as shown below, but the PORTB initial value was not clear to me. Is it okay to drop the PORTB initial value. Similarly DDRB in Slave is being changed and PORTB is to be dropped. Would appreciate your comments.

Master Initialisation was :
   /* Set MOSI and SCK as output, MISO and /SS as input with pullups */
   SPI_MST_DDR = (1<<SPI_MST_MOSI_DD)|(1<<SPI_MST_SCK_DD);
   SPI_MST_PORT = (1<<SPI_MST_MISO_PORT)|(1<<SPI_MST_U_SS_PORT);

Master Initialisation should be :
   /* Set MOSI, SCK and /SS as output */
   SPI_MST_DDR = (1<<SPI_MST_MOSI_DD)|(1<<SPI_MST_SCK_DD)|(1<<SPI_MST_U_SS_DD);

Slave Initialisation was : 
   /* Set MOSI and SCK as input with pullups, MISO and /SS as output */
   SPI_SLV_DDR = (1<<SPI_SLV_MISO_DD)|(1<<SPI_SLV_U_SS_DD);
   SPI_SLV_PORT = (1<<SPI_SLV_MOSI_PORT)|(1<<SPI_SLV_SCK_PORT);

Slave Initialisation should be :
   /* Set MISO as output */
   SPI_SLV_DDR = (1<<SPI_SLV_MISO_DD);

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

I have condensed the code to bare minimum. Hope this makes it easier to read the codes now. Subsequently I have made changes in initialisations. Please if anyone can help why SynchWithSlave() and SynchWithMaster() is not working.
India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Quote:
is not working

and that means?

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

Clawson :-
What the full program was expected to do :-
The Master and Slave should synchronise and exchange the data of respective SPDR and display on the PORTD LEDs.

What is not happening (Feb 04, 2008) post :-

Quote:

The SynchWithSlave() / SynchWithMaster(), [in respective codes] was introduced to avoid the hard-resets ..... . No data exchange takes place with these two sub-routines. Using step-wise LED-o/p-diagnostics, hard-reset is still required to achive "SPI_SendByte", but SynchWithSlave() / SynchWithMaster() is not executed.

The above problem still exists even after SPI_MST_SCK_DD was made output :-
    Master Initialisation should be :
    /* Set MOSI, SCK and /SS as output */
    SPI_MST_DDR = (1<<SPI_MST_MOSI_DD)|(1<<SPI_MST_SCK_DD)|(1<<SPI_MST_U_SS_DD);
The check was done by introducing PORTD LEDs at different steps of the code.
I hope I am able to clarify what the problem is.
India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

In your code I do not see anywhere where your master asserts the /SS line of the slave. This is required for the slave to start receiving data over SPI. Sending NULL characters will not synch the two units, if the receiver is one bit off, it will continually be one bit off until the /SS line is released and re-asserted. in fact sending NULL characters really does nothing for SPI itself, it's only use would be for a higher level protocol.

The easiest implementation here is to tie the /SS lines of the two micros together, set the MASTER one as an output, and the slave one as an input. Just before starting your transmission, you set the /SS line low. And after the last byte has been transferred, you bring /SS back high. This ensures that your master will never enter slave mode accidentally, and that the slave is synchronized with the master at the byte level.

Looks to me like your code was written by someone else, I suggest you spend some time in the datasheet familiarizing yourself with how the SPI unit of the AVR operates.

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

Glitch :

Many deep thanks for your kind observations. I did start off with the code snippets in ATMega16 datasheets and got myself thoroughly confused. Hence Mr Dean was very very kind to find some time from his busy schedules to quickly writeup a code for polling byte echange between Master and Slave. It did require some more working and he permitted me to post it on the forum.

Incidently ATMEL's AVR151 gives code example for a string to be sent from Master to Slave only (both by polling and interupt). I have succeeded in its polling method after changing IAR header to WinAVR headers. I need to do byte exchange between Master and Slave.

I will implement your suggestions and revert.

Thank you once again.

India_AVR

PS : I thought creating my_SPI.h was to make me certain what I was defining and using, but hope this has not caused people who want to help me being put-off; I could go back to normal usage in posting, if it makes it easier. Comments.

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Quote:

The easiest implementation here is to tie the /SS lines of the two micros together, set the MASTER one as an output, and the slave one as an input.

I wrote the code.

Yes, true -- the easiest way would be to have the master control the slave's /SS pin. However, it seemed India_AVR didn't want that, so I used the alternative solution. It's the same system ButtLoad uses to synch with an AVR's ISP interface, where there *is* no /SS pin.

If the sequence comes back corrupted, the master manually clock out a single bit (to advance the slave's SPI register) via the following code:

  /* Manually clock the CLK pin once to shift the slave's SPI register one bit */
  SPI_MST_PORT ^=  (1 << 7);
  _delay_us(20);
  SPI_MST_PORT ^=  (1 << 7); 

And re-tries.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:

Yes, true -- the easiest way would be to have the master control the slave's /SS pin. However, it seemed India_AVR didn't want that, so I used the alternative solution. It's the same system ButtLoad uses to synch with an AVR's ISP interface, where there *is* no /SS pin.

Interesting, I had always thought that /RESET would act as the /SS pin for ISP. Though since I've never tried to write an ISP programmer, I'll have to revert to your experience in that it does not.

As for regular SPI, the problem is that the receive shift registers 'shift enable' signal is controlled by /SS, so if /SS is not pulled low, the slave AVR will never receive any data. So unless india_avr had some mechanism that was externally pulling /SS on the slave low, that I could not see, the only way he would enter receive mode, was by sheer luck when /SS floated low enough to register as a low, thus enabling the SPI slave mode.

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

After implementing guidelines as per Clawson and Glitch to India_AVR and Rukus to Atif and comments by ABCminiuser, I would like to report as below :-

    All wrong testing results removed to avoid confusion

All wrong testing conclusions removed to avoid confusion. The corrections and results are in next post.

Many thanks to Abcminiuser for the initial code and constructive guidance from him, Clawson, Glitch, Rukus and to Atif whose similar trials and support postings lead to a successful learning of data-byte exchange via SPI between two microcontrollers. I also thank Mr John Simperl and /Lars for their support. I would like to post the cleaned up working code in the Tutorial section for all those interested.

Any further comments will be most welcome.

India_AVR

PS : If inadvertantly, I have missed out names who have guided me in this, please, they are included in my thanks.

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

Last Edited: Thu. Feb 14, 2008 - 06:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think I was really very blearry eyed at 2am in the morning when I posted my report as success after multiple test runs. Seems my mind was falsely conditioned to see "success". In none of the 4 cases has the MST-SPDR gone to SLV or SLV-SPDR gone to MST. I still have problem.

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Just to clarify what I had done :-
Hardware Case 1:
/SS---/SS, MISO---MISO, MOSI---MOSI, SCK---SCK
Hardware Case 1:
MST/SS=>open SLV/SS=>GND, MISO---MISO, MOSI---MOSI, SCK---SCK

Master Initialisation :

{
	/* Initially set /SS as output */
	SPI_MST_DDR = (1<<SPI_MST_U_SS_DD);
	/* Initially set /SS as "high", YAVARI, Clawson */
	SPI_MST_PORT = (1<<SPI_MST_U_SS_PORT);
	/* Set MOSI(5), SCK(7) and /SS(4) as output */
	SPI_MST_DDR = (1<<SPI_MST_MOSI_DD)|(1<<SPI_MST_SCK_DD)|
				  (1<<SPI_MST_U_SS_DD);
	/* Pullup MISO(6) and /SS(4) */
	SPI_MST_PORT = (1<<SPI_MST_MISO_PORT)|(1<<SPI_MST_U_SS_PORT);
	/* SPI On, Master, Fcpu/16 */
	SPCR  = ((1<<SPE)|(1<<MSTR)|(1<<SPR0));
	
}

Master Sendbyte :

char SPI_SendByte(char Byte)
{
	/* Before SPI set /SS as "low", YAVARI, Clawson, Glitch, Rukus */
	SPI_MST_PORT &= (1 << SPI_MST_U_SS_PORT);
	
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));
	
	/* Repeat - Rukus @ Atif */
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));	
	
	/* After SPI set /SS as "high", YAVARI, Clawson, Glitch, Rukus */
	SPI_MST_PORT |= (1 << SPI_MST_U_SS_PORT);
	
	return SPDR;
}

Slave Initialisation :

{
	/* Set MOSI(5), SCK(7) and /SS(4) as input; 
	   MISO(6) as output */
	SPI_SLV_DDR = (1<<SPI_SLV_MISO_DD);

	/* pullup MOSI(5) and SCK(7) */
	SPI_SLV_PORT = (1<<SPI_SLV_MOSI_DD)|(1<<SPI_SLV_SCK_DD);

	/* SPI On, Slave, Fcpu/16 */
	SPCR  = ((1<<SPE)|(1<<SPR0));
	< codes >	
}

Slave Sendbyte :

char SPI_SendByte(char Byte)
{
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));

	/* Repeat - Rukus @ Atif */
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));

	return SPDR;
}

The PORTD LED outputs are "Posted: Feb 13, 2008 - 09:37 PM", i.e. data exchange has not taken place as per the two hardware conditions (/SS connections) and two software conditions viz with and without synchronisation codes. Where could I still be making a mistake ?

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

After re-reading datasheets, AVR151, and other C-codes for AVR-SPI, it was noted that in both Master and Slave, during initialisation SPSR and SPDR require clearing by reading into dummy variables. Also in Master while making /SS "low" the "~" was missing. The /SS---/SS were connected. Without the "Synch" now the programs work correctly with Slave-data seen on Master-LEDs and Master-data seen on Slave-LEDs. The program was re-checked with different data values and confirmed working correctly. However *with* "synch" the respective LEDs were incorrect, even when MST-/SS was open and SLV-/SS was connected to GND, this may require some tweaking, to use where an AVR does not have an /SS.

Corrections for code to work :-

int main (void)
{
	char clr_reg;

	/* initialisations */

	/* SPI On, Slave / Master, Fcpu/16 */
	SPCR  = (          );
	
	/* clear the registers by reading them - AVR151, SPIDigitalPot */
	clr_reg = SPSR;
	clr_reg = SPDR;

	/* codes */
}

char SPI_SendByte(char Byte)
{
	/* Before SPI set /SS as "low", YAVARI, Clawson, Glitch, Rukus */
	SPI_MST_PORT &= ~(1 << SPI_MST_U_SS_PORT); // corrected "~"
	
	/* codes */
}

I once again thank all those who helped me in this process, especially to *read* and *read* and *read* thoroughly to implement correctly.

India_AVR

I also thank everyone to wait patiently for me to find my way and correct my mistakes.

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

india_avr wrote:
However *with* "synch" the respective LEDs were incorrect, even when MST-/SS was open and SLV-/SS was connected to GND, this may require some tweaking, to use where an AVR does not have an /SS.

Short of a software SPI implementation, the AVR will ALWAYS have an /SS pin for the SPI interface. SPI is designed to have multiple devices on it, each has its own /SS (sometimes labelled as /CS) pin. Without the /SS pin there would be no way to control which device you're talking to. If you tie the slaves /SS to GND, then you will need to use a bit banged approach on the master (initially) in order to achieve sync. If you use /SS there is no need for synchronization code, as /SS achieves that for you.

With the AVR as a slave, you need a small gap between transfers to allow the slave to read the transferred byte, and load the response into the SPDR register for the next transfer. So to synchronize I suggest using a bit-banged approach (on the master), at a very low rate, as you don't know hwen the slave will see the end of a frame. Once synch is achieved, you can switch over to the hardware SPI on the master, and everything should work fine from that point on.

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

Glitch :

Thanks for the additional information and clarifications to some of the aspects of my understanding. After multiple program runs, I did find on a very rare occasion a wrong value appearing, thus should I incorporate a few micro-seconds / milli-seconds delay between load and transfer ? (Fcpu = 1Mhz, SPI=fcpu/16).

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

I would suggest you to break down your problem and do incremental testing:

1) Are you sure your master is sending data? Measure your MOSI line on the master.

2) Are you sure /SS is being activated as described by other posters? Measure it.

If you don't have measuring equipment (a sound card will do, if you use a low SPI
rate like 5-10KHz), you can try a loopback test. On the Master, connect the MOSI and
MISO lines together; then send a byte and check if you received the byte you sent.
Do the test with different values, like 0, 0xaa, 0x55 and 0xff. Make sure that if you
break the MISO-MOSI connection again you don't get the same byte back.

Now, if your loopback test works, then you know your master works and the problem is
in the slave side (assuming /SS is asserted correctly). Then proceed to
further check your slave code. Check if your Slave at least receives the bytes
from the master. If it does, then your problem is on the transmission side of the
Slave. And so on.

Embedded Dreams
One day, knowledge will replace money.

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

Nuno :
I was keeping track for a few days after my last post of "Feb 14, 2008 - 07:32 PM", I noticed today your post. I have updated the Subject => "SOLVED". I am now looking up literature on how to ensure failsafe data exchange, possibly also an upper software layer in addition to SPI. Possible use of checksum / CRC ?
India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

India_AVR,

I appreciate highly your file of definitions to abstract the pins of the SPI from the code itself. I liked it so much that I used it on my own application, which is using an AT90CAN128 chip. The pins are different on this. I also added a check to make sure it is the correct processor, so that if this is not defined in accordance, it will cause a compile error.

I appreciate your carefulness, and I hope to make the same 1-byte transfer work today, using the same carefulness.

// Special definitions adapted from India_AVR
// to ensure DDRx / PORTx / PINx correctly defined
// and to abstract them from the code.
//
// Note: These definitions differ from one AVR to another, 
// because of different pinouts. So we check for the 
// AVR type and cause an error if it's not AT90CAN128 here.

#ifndef __AVR_AT90CAN128__
#error Check the pinout for the SPI connection!
#endif


/* Master */
#define SPI_MST_DDR      DDRB
#define SPI_MST_PORT      PORTB

#define SPI_MST_DD_MOSI      DDB2
#define SPI_MST_DD_MISO      DDB3
#define SPI_MST_DD_SCK      DDB1
#define SPI_MST_DD_U_SS      DDB0

#define SPI_MST_PORT_MOSI   PB2
#define SPI_MST_PORT_MISO   PB3
#define SPI_MST_PORT_SCK   PB1
#define SPI_MST_PORT_U_SS   PB0

/* Slave */
#define SPI_SLV_DDR      DDRB
#define SPI_SLV_PORT      PORTB

#define SPI_SLV_DD_MOSI      DDB2
#define SPI_SLV_DD_MISO      DDB3
#define SPI_SLV_DD_SCK      DDB1
#define SPI_SLV_DD_U_SS      DDB0

#define SPI_SLV_PORT_MOSI   PB2
#define SPI_SLV_PORT_MISO   PB3
#define SPI_SLV_PORT_SCK   PB1
#define SPI_SLV_PORT_U_SS   PB0

Why choose? Be there *and* be square!

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

India_AVR,

I would like to request you, if you have time, to post some sample code for the successful SPI data transfer of multi-byte messages, interrupt-based transfers, and/or CRC checking, of you have accomplished any of these.

In the interest of having a complete working code posted, I will post my sample code here for non-interrupt, single-byte transfers, which is based on India_AVR's code. They must both be used with the header file that I posted above, which is based on India_AVR's work. My code is for the AT90CAN128, but if you change the header file, it can be adapted to other AVR chips that support SPI.

Sage

//
// SPI_slave.c 
//
//
// Slave component of project to test communications by SPI.
//
// SPI is Serial Peripheral Interface. It is a close-range
// means of serial communications used between master
// controllers and slave peripherals usually on the same
// circuit board.
//
// On the AT90CAN128, the SPI interface uses the low 4 bits of PORTB.
// These should be traced to each other on the PCB, with the 
// MISO to MISO, MOSI to MOSI, SS to SS, and SCK to SCK.
//
// Target chip: AT90CAN128
// STK500 board LEDs on PORTC
// SPI pins on PORTB
//
// Sage Radachowsky, Feb-Mar 2008 ("sager" at "mit" dot "edu")
//



//**********************************************
//               HEADER FILES
//**********************************************

// system libraries...
#include 
#include 
#include 
#include 
#include 
#include 

// SPI data structures
#include "../SPI_header.h"



//**********************************************
//               FUNCTION DEFINITIONS
//**********************************************

void SPI_InitSlaveMode ();
char SPI_SendByte(char Byte);

void BlinkyBlinky ();




//**********************************************
//               GLOBAL VARIABLES
//**********************************************


// SPI data buffers
unsigned char SpiReceiveData = 0;
unsigned char SpiSendData = 0;





//####################             ####################
//####################             ####################
//#################### here we GO! ####################
//####################             ####################
//####################             ####################

int main()
{

	// set up PORTA and PORTC data direction registers
	DDRC = 0xFF;	// set port C (LEDs) to output
	DDRA = 0x00;	// set port A (switches) as input

	BlinkyBlinky();


	// init SPI
	SPI_InitSlaveMode ();



	// ****** Main Loop 

	while (1)
	{		
		PORTC = SPI_SendByte(PINA);
	}



}



//********************************
  void SPI_InitSlaveMode ()

// This function initializes the SPI controller in slave mode.
//

{
	// Set MISO as output, all other pins are input
	SPI_SLV_DDR = (1<<SPI_SLV_DD_MISO);
	// Pullup MOSI and SCK
	SPI_SLV_PORT = (1<<SPI_SLV_PORT_MOSI)|(1<<SPI_SLV_PORT_SCK);
	// SPI On, Slave (Clock has no effect on slave bcs it follows master)
	SPCR  = (1<<SPE); 

}




//********************************
  char SPI_SendByte(char Byte)
//
// Slave-side send byte
//

{
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));
   
	// Repeat - Rukus @ Atif
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));   
   
	return SPDR;

} 


//********************************
  void BlinkyBlinky ()
// Besides being cute, this gives us a visual cue to the clock speed.
// If clock matches programmed speed, it should take 1 sec.
// Also useful to signal a crash and reboot of the microcontroller.

{
	register int i; // general index variable


	for (i=0; i<=7; i++) { PORTC = ~(1<<i); _delay_ms(1000/16); }
	for (i=7; i>=0; i--) { PORTC = ~(1<<i); _delay_ms(1000/16); }
	PORTC = ~0X00; // clear LEDs
}
//
// SPI_master.c 
//
//
// Master component of project to test communications by SPI.
//
// SPI is Serial Peripheral Interface. It is a close-range
// means of serial communications used between master
// controllers and slave peripherals usually on the same
// circuit board.
//
// On the AT90CAN128, the SPI interface uses the low 4 bits of PORTB.
// These should be traced to each other on the PCB, with the 
// MISO to MISO, MOSI to MOSI, SS to SS, and SCK to SCK.
//
// Target chip: AT90CAN128
// STK500 board LEDs on PORTC
//
// Sage Radachowsky, Feb-Mar 2008 ("sager" at "mit" dot "edu")
//


//**********************************************
//               HEADER FILES
//**********************************************

// system libraries...
#include 
#include 
#include 
#include 
#include 
#include 

// SPI data structures
#include "../SPI_header.h"



//**********************************************
//               FUNCTION DEFINITIONS
//**********************************************

void SPI_InitMasterMode ();
char SPI_SendByte(char Byte);

void BlinkyBlinky ();




//**********************************************
//               GLOBAL VARIABLES
//**********************************************


// SPI data buffers
unsigned char SpiReceiveData = 0;
unsigned char SpiSendData = 0;





//####################             ####################
//####################             ####################
//#################### here we GO! ####################
//####################             ####################
//####################             ####################

int main()
{

	// set up PORTA and PORTC data direction registers
	DDRC = 0xFF;	// set port C (LEDs) to output
	DDRA = 0x00;	// set port A (switches) as input

	BlinkyBlinky();


	// init SPI
	SPI_InitMasterMode ();



	// ****** Main Loop 

	while (1)
	{		
		PORTC = SPI_SendByte(PINA);
	}



}



//********************************
  void SPI_InitMasterMode ()

// This function initializes the SPI controller in master mode.
//

{
	// Initially set /SS as output
	SPI_MST_DDR = (1<<SPI_MST_DD_U_SS);
	// Initially set /SS as "high"
	SPI_MST_PORT = (1<<SPI_MST_PORT_U_SS);
	// Set MOSI, SCK and /SS as output
	SPI_MST_DDR = (1<<SPI_MST_DD_MOSI)|(1<<SPI_MST_DD_SCK)|(1<<SPI_MST_DD_U_SS);
	// Pullup MISO and /SS
	SPI_MST_PORT = (1<<SPI_MST_PORT_MISO)|(1<<SPI_MST_PORT_U_SS);
	// SPI On, Master, Fcpu/16
	SPCR  = ((1<<SPE)|(1<<MSTR)|(1<<SPR0)); 
}




//********************************
  char SPI_SendByte(char Byte)
//
// Master-side send byte
//

{
	// Before SPI set /SS as "low"
	SPI_MST_PORT &= ~(1 << SPI_MST_PORT_U_SS);
   
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));
   
	// Repeat - Rukus @ Atif
	SPDR = Byte;
	while (!(SPSR & (1 << SPIF)));   
   
	// After SPI set /SS as "high"
	SPI_MST_PORT |= (1 << SPI_MST_PORT_U_SS);
   
	return SPDR;
} 


//********************************
  void BlinkyBlinky ()
// Besides being cute, this gives us a visual cue to the clock speed.
// If clock matches programmed speed, it should take 1 sec.
// Also useful to signal a crash and reboot of the microcontroller.

{
	register int i; // general index variable


	for (i=0; i<=7; i++) { PORTC = ~(1<<i); _delay_ms(1000/16); }
	for (i=7; i>=0; i--) { PORTC = ~(1<<i); _delay_ms(1000/16); }
	PORTC = ~0X00; // clear LEDs
}

Why choose? Be there *and* be square!

Last Edited: Wed. Mar 12, 2008 - 12:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sagenepal :
I am thrilled that my efforts with help from our senior peers at AVRFreaks has encouraged you to work on this further.

Quote:
I would like to request you, if you have time, to post some sample code for the successful SPI data transfer of multi-byte messages, interrupt-based transfers, and/or CRC checking, of you have accomplished any of these.
Presently I am still on the learning curve, it could take some time.
India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------