[Solved] SPI transmition hangs

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

Hi, I am completely lost with this function. I have a function for transmitting bytes with SPI

void SPI_Send(uint8_t data)
{
	SPDR = data;
	while (!(SPSR & (1 << SPIF))); 	
}

I call this function from various locations in my code to send bytes as a master in SPI.

SPI_Send(0xB0);

But from one location only the while loop gets stuck forever. I was able to verify that the data was put into the register but it never gets cleared and the code hangs.

 

What factors can cause this behavior? Could it also be the recipient? Could my test setup on a breadboard have anything to do with it? I don't think so..

 

Thanks for any help...

 

delcon 

This topic has a solution.
Last Edited: Tue. Nov 18, 2014 - 08:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Show the smallest complete test program that demonstrates the behaviour.  Tell the AVR model and clock speed.  Tell how you determined where the app gets "stuck".

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

I determined the exact point where it waits forever with switching ports on and off.

 

I understand that more code is needed to have the bigger picture. I thought that maybe somebody came across a similar behavior once, or knows what can make the SPI stop transmitting.

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

I can't see how your code could hang unless you are allowing your AVR to go haywire.

 

Otherwise the Master will shift out the SCK regardless.     And SPIF will be set when complete.

 

The only other possibility is if you disable SPCR during an ISR() (before SCK has finished)

 

David.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are not a lot of possibilities.

Either SPI has become disabled or the processor has become a slave.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

Either SPI has become disabled

That helped!! smiley

Thanks a lot, I did not think that I could become disabled.

 

To test I did another SPI_init() and it seams to complete the transmission.

At least I know what to look for now.

 

Thanks again!! 

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

Most likely you have not used the /SS pin as an output...wild speculation of course.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Most likely you have not used the /SS pin as an output...

...which is why we asked for code. indecision  And as is often the case, the suggestions are ignored, and asked-for information not given.

 

Interesting that if indeed it is a /SS situation it seems to happen at a particular point.  And no, OP, SPI doesn't need re-initializing periodically in a real app.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

david.prentice wrote:

you are allowing your AVR to go haywire.

 

It seems that people are determined to avoid making /SS an output.

Yes,   a strong external pull-up would work ok.     But it would cost you almost $0.01 for a 10k resistor.

 

David.

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

I did not ignore your suggestion, I was working on it while the helping suggestion came in.

What I am tracing down now is why that the SPI needs to be initialized again. It shouldn't be.. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
if(!(SPCR & (_BV(SPE) | _BV(MSTR)))) oops();

Insert where desired, e.g. in SPI_Send.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

skeeve wrote:

if(!(SPCR & (_BV(SPE) | _BV(MSTR)))) oops();

Insert where desired, e.g. in SPI_Send.

 

Skeeve, thanks for your suggestion.

What does this line of code do? I would like to understand.

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

I am guessing that skeeve wanted to test for your SPI having gone haywire.     It does not do this properly.

 

A better test would be:

if((SPCR & (_BV(SPE) | _BV(MSTR))) != (_BV(SPE) | _BV(MSTR))) oops();

Mind you,    a better solution is to follow the advice from the data sheet.   i.e. make SS an output

 

Note that you can use the SS pin for something completely different (as long as it is an output)

Of course,    most SPI slaves need a CS of some description.     It is convenient for your AVR as SPI Master to use the SS pin for this purpose.

Which is probably why it is called SS i.e. SlaveSelect

 

David.

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

The title says solved but there's no detail as to how? Was it the _SS thing? How about using the "mark as solution" button if something above was the solution?

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

Hi David

Thanks for looking at it.

 

However, I still do not know what your code does and what do expect from it.

 

Thanks for clarification regarding the SS Pin. Even though the SS Pin is not connected at my setup I see now how it could affect SPI. I am going to change that. I can also imagine that the breadboard wiring can cause exactly these kind of errors.

 

Delcon

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

You don't need a breadboard for haywire-ability.    The AVR will do this by itself.

 

All pins are 'high impedance inputs' when the AVR starts.

The slightest electrical noise or disturbance will cause any input pin to read 0 or 1.

 

So if you don't make SS an output,   it will pick up some noise at some stage.    e.g. when you switch on a TV, light,  radio, ...  or even a motor car driving by.

SS 'reads' as 0 and the SPI goes haywire.    (i.e. it clears the MSTR bit,   stops SCK,    changes the direction of the MOSI and MISO lines. ...)

 

The solution is simple.    Make SS pin an output.

 

The haywire test simply checks to see whether the MSTR bit has been cleared.     I can assure you that 'repeated setting of MSTR' will not stop the AVR going haywire.     Only SS as output or SS as input pulled high.

 

This SPI 'feature' applies to many makes of MCU.    Not only AVR.

 

David.

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

However, I still do not know what your code does and what do expect from it.

As always "divide and conquer". Take the "complex" C statement and look at each part of it in turn:

if((SPCR & (_BV(SPE) | _BV(MSTR))) != (_BV(SPE) | _BV(MSTR))) oops();

usually it's best to work form the inner-most parentheses outwards. So at the core of a lot of that is this _BV() thing. Now you may have come across this before or you might not. A lot of people are happy to turn bit numbers into bit position masks using code like:

(1 << SPIF)

(I stole that from your first post!). As you know that just moves a 1 bit into the bit position of an 8 bit mask given by SPIF. Well <avr/sfr_defs.h> has a "helper" macro that can do the same called _BV() ("Bit Value"). It is defined as:

#define _BV(bit) (1 << bit)

So typing _BV(SPIF) is exactly the same as typing (1 << SPIF). As such perhaps Michael/David's code would be more familiar to you if they had written:

if((SPCR & ((1 << SPE) | (1 << MSTR))) != ((1 << SPE) | (1 << MSTR))) oops();

So those are just define bit masks. The '|' operator should be familiar to you - it is OR. So a segment such as:

((1 << SPE) | (1 << MSTR))

Simply creates a two bit mask in which bot the SPE and MSTR bit positions are set. Working out the parentheses brings us then to :

(SPCR & ((1 << SPE) | (1 << MSTR))

Again '&' should be familiar to you. This reads the SPCR register then AND's the result with the combined SPE and MSTR bits. The result will only be equal to _BV(SPE) & _BV(MSTR) if both bits are set. Finally the other side of the != test is tis same byte value with the two bits set.

 

So this is reading SPCR, picking out just two bits you are interested in and making sure they are set the way you expected. If not "oops()" is called. (side note: <assert.h> has assert() which is a bit like oops with the test built in).

 

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

Hi Clawson,

 

Thanks for pointing this out to me. I did not know.

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

Guys, your patience and experience is so valuable to me!

Awesome explanations!!

I am giving you guys yesyesyes

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

david.prentice wrote:
I am guessing that skeeve wanted to test for your SPI having gone haywire.     It does not do this properly.

 

A better test would be:

if((SPCR & (_BV(SPE) | _BV(MSTR))) != (_BV(SPE) | _BV(MSTR))) oops();

Had I been thinking better, I'd have written

if(!(SPCR & _BV(SPE)) || !(SPCR & _BV(MSTR))) oops();

Of course, If you have not been making /SS an output, you do not need either David's code or mine:

Just make /SS an output and set it to one.

Quote:
Mind you,    a better solution is to follow the advice from the data sheet.   i.e. make SS an output

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?