SPI freezes

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

I'm using the SPI to shift some data out and the programm freezes after the first byte shift .

Is there something wrong with the code?

void SPIInit(void)
{
	/* Set MOSI and SCK output, all others input */
	DDRB |= ( 1<<PINB5 ) | ( 1<<PINB7 ) | ( 1 << PINB2 );
	
	/* Enable SPI, Master, set clock rate fck/128 */
	SPCR = ( 1<<SPE ) | ( 1<<MSTR ) | ( 1<<SPR1 ) | ( 1<<SPR0 ) | ( 1 << DORD );
}


void SPITx(char cData)    /* Start transmission */
{
	// The programm freezes at this point after executing the transfer once
	SPDR = cData; // place data in the register to start the ISP


	
	while(!(SPSR & (1<<SPIF)));  /* Wait for transmission complete */
	
	
}
void Pwm_update()
{
	
	for (int x = 0; x < 36;x++)
	{
		SPITx(Pwm_to_serial[x]);
	}
	
	
	PORTB |=  ( 1 << PINB2 );
	PORTB &=  ~( 1 << PINB2 );
	
}

I know that the programm freezes after one transfer because I put some LCD output string before the
SPDR = cData; and the string appears twice . So it executes then stops when waiting for the SPIF flag

Any help is appreciated

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

Also I should mention that I'm using an ATMEGA32
with16mghz external Xtal , Atmel studio 6 and MkII programmer

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

void SPITx(char cData)    /* Start transmission */ 
{ 
   

   SPDR = cData; 
   lcd_puts("test");
   while(!(SPSR & (1<<SPIF))); 
       
}

The string appears twice . Which means that the SPI sends a byte , then the data is placed in the SPDR and the programm gets stuck forever in the

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

which means that the SPIF is never set which means that the data transfer has not been successful .
But why , I have no idea

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

Something really wierd is going on with the micro.
When I disconnect the MOSI and connect it to high impedance then the programm somehow magically works ... for a while ...
What is going on ?

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

Something really wierd is going on with the micro.
When I disconnect the MOSI and connect it to high impedance then the programm somehow magically works ... for a while ...
What is going on ?

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

Your SS line is floating - the standard SPI "gotcha".

(if it floats or is pulled low the master switches to slave and the AVR never clocks so SPIF never gets set - hundreds if not thousands of prior posts about this!).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	DDRB |= (1<<PINB5) | (1<<PINB7) | (1<<PINB2);

Why are you setting PB2 as an output? It is not involved in SPI.

You should instead be setting PB4 as an output, as it is the slave select pin:

In the datasheet Atmel wrote:
If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.
JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

clawson wrote:
Your SS line is floating - the standard SPI "gotcha".

(if it floats or is pulled low the master switches to slave and the AVR never clocks so SPIF never gets set - hundreds if not thousands of prior posts about this!).

How did I miss that :/
I read the SPI section like 5 times

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

joeymorin wrote:

	DDRB |= (1<<PINB5) | (1<<PINB7) | (1<<PINB2);

Why are you setting PB2 as an output? It is not involved in SPI.

You should instead be setting PB4 as an output, as it is the slave select pin:

In the datasheet Atmel wrote:
If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.
JJ

PINB2 is for the latch output of the device. Software related , nothing to do with SPI , although I have already changed it to portD

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

Thanks again , Clawson . You saved me many hours of painfull diagnosis :)

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

Shagas wrote:
joeymorin wrote:

	DDRB |= (1<<PINB5) | (1<<PINB7) | (1<<PINB2);

Why are you setting PB2 as an output? It is not involved in SPI.

PINB2 is for the latch output of the device. Software related , nothing to do with SPI , although I have already changed it to portD
Fair enough, but your comment was misleading:
/* Set MOSI and SCK output, all others input */

... and you omitted SS.

... and @clawson was 60 seconds quicker than I was clicking "Submit" ;)

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Quote:
... and you omitted SS.

Well here is the thing : I copied that piece of code from the datasheet and it says nothing about setting the SS as output , under the example code of "how to initialize SPI and perform basic transmission"
I'm currently using a 2.2 K pull up resistor on the SS Pin and there seem to be no problems so far.
I have not declared it as output . Should I?

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

Shagas wrote:
Quote:
... and you omitted SS.
Well here is the thing : I copied that piece of code from the datasheet and it says nothing about setting the SS as output , under the example code of "how to initialize SPI and perform basic transmission"
I quoted the relevant datasheet passage above. Here it is again:
In the datasheet Atmel wrote:
If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.

Shagas wrote:
I'm currently using a 2.2 K pull up resistor on the SS Pin and there seem to be no problems so far.
I have not declared it as output . Should I?
As the datasheet indicates, if you leave it as an input you must ensure that it never goes low. You could just as easily do that by enabling the internal pull-up, instead of connecting an external pull-up.

Why not simply configure it as an output? Are you using PB4 for anything?

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Yeah , you are right. Not thinking clearly .
I'm not using PB4 for anything so yeah I guess it would make sense to configure it as an output to enable the pullup resistor.

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

Shagas wrote:
I'm not using PB4 for anything so yeah I guess it would make sense to configure it as an output to enable the pullup resistor.
But... that's not how you enable the pull-up...

On the ATmega32 a pin's pull-up is enabled by configuring it for input by clearing the appropriate DDRn bit, and setting the appropriate PORTn bit.

But that's not the best way to solve your SPI problem. I'll quote the datsheet (3rd time):

In the datasheet Atmel wrote:
If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.
Simply configure SS/PB4 as an output. It's PORTn bit value is then irrelevant (to SPI).

Mind you, this is contrary to the recommendation that unused pins be configured for input with pull-ups enabled. This recommendation is meant to prevent excessive current consumption due to floating inputs, and to avoid the possibility of shorting an output pin to Vcc or GND and therefore sourcing or sinking excessive current.

Provided your application can safeguard against shorting SS/PB4 to Vcc or GND, setting it as an output is the best solution for SPI Master mode. If you need to protect against a possible short (something a proper physical design shouldn't have to contend with), you can configure for input with pull-up. However, if SS/PB4 is ever brought low, your code will need to contend with the forced transition into slave mode.

Shagas wrote:
Yeah , you are right. Not thinking clearly .
Happens to the best of us.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Thanks joeymorin .
I understand everything you are saying including what you linked from the datasheet.
In my case I'll just leave the pin as an input and enable the pullup resistor using PORT.
The pin is going to be left clear so there is no worry of it being pulled low by hardware .

Thanks for the info

ps.

I've not been thinking clearly because this issue has been annoying me (to put it lightly) for days as I couldn't get it to work but at least now I know that when the micro is exhibiting strange behavior where it's sensitive to physical touch etc then it's probably a pin floating when it's not supposed to!

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

I am witnessing something unexpected in my SPI code, similar to what Shagas saw.

Hardware: Mega128rfr2 -> FXL2T45 (level shifter) -> Slave IC.

If I try to write to the slave IC when it is not on, my transmit loop hangs. If the slave IC is powered up, the code works perfectly. Am I being dense? Why would the MCU care whether the slave is on or not to receive the signals?

Code:

#define DDR_SPI DDRB
#define DD_SCK  DDRB1
#define DD_MOSI DDRB2
#define DD_MISO DDRB3
#define SS_ACTIVE			0
#define SS_PASSIVE			1
#define SPI_SSn				PB0
#define SPI_SSn_REG			PORTB
#define setSS(val)		do { switch(val){case SS_ACTIVE: SPI_SSn_REG &= ~(1<<SPI_SSn);break;default: SPI_SSn_REG |= (1<<SPI_SSn);break;} } while(0)


//Utilities
void spiMasterInit(uint8_t dataOrder, uint8_t cPol, uint8_t cPha, uint8_t freqDiv) {
	//Set MOSI and SCK as output, leave rest of bits alone
	DDR_SPI |= ((1 << DD_MOSI) | (1 << DD_SCK));

	//Enable SPI, Master
	SPCR = (1 << SPE) | (1 << MSTR);

	if(dataOrder)
		SPCR |= (1 << DORD);
	if(cPol)
		SPCR |= (1 << CPOL);
	if(cPha)
		SPCR |= (1 << CPHA);

	switch(freqDiv) {
		case 4 :
			SPSR &= ~(1 << SPI2X);
			break;
		case 16 :
			SPSR &= ~(1 << SPI2X);
			SPCR |= (1 << SPR0);
			break;
		case 64 :
			SPSR &= ~(1 << SPI2X);
			SPCR |= (1 << SPR1);
			break;
		case 128 :
			SPSR &= ~(1 << SPI2X);
			SPCR |= (1 << SPR1) | (1 << SPR0);
			break;
		case 2 :
			SPSR |= (1 << SPI2X);
			break;
		case 8 :
			SPSR |= (1 << SPI2X);
			SPCR |= (1 << SPR0);
			break;
		case 32 :
			SPSR |= (1 << SPI2X);
			SPCR |= (1 << SPR1);
		default :
			SPSR &= ~(1 << SPI2X);
			break;	//Default: Fosc/4
	}
}

uint8_t spiMasterRW(uint8_t byte) {

	SPDR = byte;
	//Block until transmission is done
	while(!(SPSR & (1 << SPIF)))
	{}

	return(SPDR);
}

//Application - Setup
DDRB  = 0x07;	//1b00000111
power_spi_enable();
//Initialize interface (SPI)
spiMasterInit(0, 0, 0, 128);	//MSB first, CPOL = 0, CPHA = 1, FreqDiv = 128

//Send data
setSS(SS_ACTIVE);
_delay_us(500);
rxData[0] = spiMasterRW(foo);
setSS(SS_PASSIVE);
_delay_ms(1);

Edit: I believe that because the mcu is in Master mode, and the SS pin is configured as an output, the level of the pin should not impact the Maseter/slave state of the MCU (see 22.3.2 on p. 367 of the data sheet for the mega128rfr2).

Science is not consensus. Science is numbers.

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

A brief look at your code says it's OK. You are setting SS/PB0 as an output before enabling SPI Master mode:

DDRB  = 0x07;					 //1b00000111

What about:

power_spi_enable();

?

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

I didn't realize that the order mattered. Are you saying that I need to re-set the pin as an output after enabling SPI Master mode?

As to power_spi_enable() -- look at power.h (avrgcc). It's just a Macro to enable the module.

Science is not consensus. Science is numbers.

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

hobbss wrote:
I didn't realize that the order mattered. Are you saying that I need to re-set the pin as an output after enabling SPI Master mode?
It matters in that, were you to enable master mode before making SS an output, this give a small window when SS can be brought low, triggering the SPI hardware to auto-switch into slave mode. If SS is low (or floating low) when you set master mode, that's exactly what will happen. Making SS an output after that is closing the barn door after the horses are out.

In order to ensure that doesn't happen, set SS as an output first, then enable master mode. But you're already doing that.

Quote:
As to power_spi_enable() -- look at power.h (avrgcc). It's just a Macro to enable the module.
Ah, of course. I never use power.h, myself. Struggle with PRR directly. Just stubborn ;)

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Since I am setting it as output first, do you have any other ideas as to why the code is freezing if the slave device is not powered?

I went through a brief phase of using power.h. Then I went back to setting the register bits directly. This is just an old holdover...

Science is not consensus. Science is numbers.

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

hobbss wrote:
Since I am setting it as output first, do you have any other ideas as to why the code is freezing if the slave device is not powered?
Assuming I'm not missing something in your code, and you're not ommitting any other relevant code, the only thing I can think of is something electrically screwy with your slave device.

Have you placed a DMM, DSO, or logic analyser on the SPI pins to see what happens to them when the slave device is powered off?

Do you have any other code that manipulates DDRB? Any ISR code that might do that? If SS/PB0 subsequently becomes an input, the same pitfall will apply.

Try to write a simple, concise test program that exhibits the behaviour you are seeing, and does little or nothing else. Use an LED for debugging purposes. Disable all interrupts. Keep it simple.

If a dedicated test program is not practical/possible (or even if it is), you could trap for an auto-changeover to slave mode:

uint8_t spiMasterRW(uint8_t byte) {

  SPDR = byte;
  while(!(SPSR & (1<<SPIF)) && (SPCR & (1<<MSTR))) {}
  // Trap cause of exit from above wait loop
  if (!(SPCR & (1<<MSTR)) {
    // Restore SS/PB0 as an output
    DDBR |= (1<<PB0);
    // Restore Master mode
    SPCR |= (1<<MSTR);
    // Debug output to indicate we became a slave (Flage?  LED?  LCD?  USART?)
  }

  // Force a clear of SPIF (avoids a race condition resulting from the two
  // separate tests in the 'if' above.  From the datasheet: "Alternatively, the
  //    SPIF bit is cleared by first reading the SPI Status Register with SPIF
  //    set and then accessing the SPI Data Register (SPDR)."
  SPSR;
  
  return(SPDR);

}

This at least may tell you what you are contending with (i.e. coding error or hardware issue).

[edit] OOPS! Fixed a bug in my code above! [/edit]

For clarity, note that:

In the datasheet Atmel wrote:
22.3.2 Master Mode
    When the SPI is configured as a Master (MSTR in SPCR is set), the user can determine the direction of the SS pin. If SS is configured as an output, the pin is a general output pin which does not affect the SPI system. Typically, the pin will be driving the SS pin of the SPI Slave. 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. To avoid bus contention, the SPI system takes the following actions:
      1. The MSTR bit in SPCR is cleared and the SPI system becomes a Slave. As a result of the SPI becoming a Slave, the MOSI and SCK pins become inputs. 2. The SPIF Flag in SPSR is set, and if the SPI interrupt is enabled, and the I-bit in SREG is set, the interrupt routine will be executed.
    Thus, when interrupt-driven SPI transmission is used in Master mode, and there exists a possibility that SS is driven low, the interrupt should always check that the MSTR bit is still set. If the MSTR bit has been cleared by a slave select, it must be set by the user to re-enable SPI Master Mode.
JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

joeymorin wrote:
Try to write a simple, concise test program that exhibits the behaviour you are seeing, and does little or nothing else. Use an LED for debugging purposes. Disable all interrupts. Keep it simple.

Excellent advice. I stripped the program down completely, and proved that the MCU (as master) does not care if the slave is powered or not.

Now to identify the REAL reason it appears to hang when the slave is unpowered....

Science is not consensus. Science is numbers.

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

hobbss wrote:
Now to identify the REAL reason it appears to hang when the slave is unpowered....
Start by using the modified version of spiMasterRW() I suggested above, or something like it. Will help you narrow down if some other piece of code is causing a switch into Slave mode, or if you have to look for a more complex interaction between software and hardware.

You could also post your schematic.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]