This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430 SPI - Intermittent garbage read

Other Parts Discussed in Thread: MSP430F2419

I am using an MSP430F2419.  I am utilizing UCB1 in SPI mode to communicate with an external DataFlash.  I have noticed that very intermittently the MSP430 will read garbage data from the DataFlash.  Below is my code for the SPI read/write operations.  I am using interrupts to perform the read/write, but am waiting for the SPI busy bit to clear before returning from the SPIReadBytes and SPIWriteBytes functions.  The global gSPI1 structure gets setup before these functions are called.  The chip select line is handled outside of the read/write functions.

uint16_t SPIReadBytes( void )
{
    uint16_t timeout = SPI_TIMEOUT;

	gSPI1.nBytesReceived = 0;

	UCB1TXBUF = SPI_DUMMY_BYTE;
	UC1IE |= UCB1RXIE;

	while( SPICheckBusy() && timeout-- );

	return timeout;
}

uint16_t SPIWriteBytes( void )
{
	uint16_t timeout = SPI_TIMEOUT;

	gSPI1.nBytesTransmitted = 0;
	
	UCB1TXBUF = gSPI1.txBytes[gSPI1.nBytesTransmitted++];
	UC1IE |= UCB1TXIE;

	while( SPICheckBusy() && timeout-- );

	return timeout;
}

void serialOut( void ) __interrupt[USCIAB1TX_VECTOR]
{
	// SPI interrupt
	if( UC1IFG & UCB1TXIFG )
	{
		if( gSPI1.nBytesTransmitted >= gSPI1.nBytesToTransmit )
		{
			UC1IE &= ~UCB1TXIE;
		}
		else
		{
			UCB1TXBUF = gSPI1.txBytes[gSPI1.nBytesTransmitted++];
		}
	}

        // other serial interrupts
        ....
}
void serialIn(void) __interrupt[USCIAB1RX_VECTOR]
{
	// Check if SPI interrupt
	if( UC1IFG & UCB1RXIFG )
	{
		gSPI1.rxBytes[gSPI1.nBytesReceived++] = UCB1RXBUF;

		if( gSPI1.nBytesReceived >= gSPI1.nBytesToReceive )
		{
			UC1IE &= ~UCB1RXIE;
		}
		else
		{
			UCB1TXBUF = SPI_DUMMY_BYTE;
		}
	}

        // other serial interrupts
        ....
}

This method works great 99% of the time.  However, it occasionally reads a garbage value, usually some number of binary 1s.  I have tried a bit-banged SPI controller and it does not have the same issue, so the issue doesn't reside in the DataFlash unless I am violating its timing requirements.  The DataFlash is capable of 66MHz SPI communications, and the MSP430 I am using is only running at 1MHz.  The timeout check I have in the code above has never failed.

Is there any insight as to what may be causing my issue?  I have added redundant reads to make sure the same value gets read twice from the DataFlash, and sometimes even that approach results in reading garbage data.  I am getting fly-wires soldered onto the SPI signals to check the communications (it isn't done yet), but I thought I'd make sure that my code is at least proper before diving too much deeper.

  • Your code looks a bit weird to me. On one hand, you use interrupts for efficient handling, but then you just wait in a loop until all interrupts are done.

    With SPI, you could take advantage of the fact that it works like shift register. As soon as the outgoing byte is transmitted, the incoming one has already been received. You could set up your code like this (dummy code):

    loop:

      transmit byte
      wait while SPI is busy
      read incoming byte
      [no extra waiting here]

    end loop

    and do away with the interrupts.

    Max

  • I have a solution very similar to that that does away with interrupts.  It also occasionally reads garbage.  The code for it is shown below:

    uint16_t SPIReadBytes__( void )
    {
    	uint8_t i;
    	uint16_t timeout;
    
    	gSPI1.nBytesReceived = 0;
    
    	for( i=0; i<gSPI1.nBytesToReceive; i++)
    	{
    		timeout = SPI_TIMEOUT;
    
    		// Write dummy byte - required so the SPI hardware transmits a clock signal
    		UCB1TXBUF = SPI_DUMMY_BYTE;
    		while( SPICheckBusy() && --timeout );
    
    		// Read the receive buffer
            gSPI1.rxBytes[gSPI1.nBytesReceived++] = UCB1RXBUF;		
    	}
    
    	return timeout;
    }
    uint16_t SPIWriteBytes__( void )
    {
    	uint8_t i;
    	uint16_t timeout;
    
    	gSPI1.nBytesTransmitted = 0;
    
    	for( i=0; i<gSPI1.nBytesToTransmit; i++ )
    	{    
    		timeout = SPI_TIMEOUT;
    		UCB1TXBUF = gSPI1.txBytes[i];
    		while( SPICheckBusy() && --timeout );
            gSPI1.nBytesTransmitted++;
    	}
    
    	return timeout;
    }

    The timeout check isn't too effective here as the next loop iteration will just reset it, but I don't think that's the issue. 

  • I am suspecting that I had an error in my configuration.  Below is my configuration code.

    void SPI1Init( unsigned int baud )
    {
    	// Hold module in reset state during configuration
    	UCB1CTL1 = UCSWRST;
    
    	// Data is captured on the first UCLK edge and changed on the following edge.
        UCB1CTL0 |= UCCKPH;
    
    	// Master mode
    	UCB1CTL0 |= UCMST;
    
    	// In-active clock high
    //	UCB1CTL0 |= UCCKPL;
    
    	// MSB first
    	UCB1CTL0 |= UCMSB;
    
    	// Configure the module as 3-pin SPI mode - CS controlled by GPIO
    	UCB1CTL0 |= UCMODE_0;
    
    	// Set SPI clock source as DCO
    	UCB1CTL1 |= UCSSEL_2;
    
    	// Configure SPI clock frequency
    	UCB1BR0 = (unsigned char)(SPI_BAUD[baud]);
    	UCB1BR1 = (unsigned char)(SPI_BAUD[baud]>>8);
    
    	// Configure P5.1 (SIMO), P5.2 (SOMI), and P5.3 (CLK) as peripheral module pins
    	P5SEL |= BIT1 + BIT2 + BIT3; 
    
    	// Bring module out of reset state
    	UCB1CTL1 &= ~UCSWRST;
    }

    I believe UCCKPH and UCCKPL were set to the opposite of what they should be, but the timing worked out in such a way that the DataFlash would still communicate appropriately most of the time.  Changing what UCCKPH and UCCKPL are set to more closely resembles what the bit-banged version looks like that has always worked without issue (other than being slow).  I will test this further and report on my findings.

  • Yep, it was a configuration problem.

**Attention** This is a public forum