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.

SPI burst data xfer



I am trying to write burst SPI transfer. This is my first spi code. It looks simple compared to other SPI burst transfers. Before loading to ez430 and hooking up the logic analyzer, I'd like to know if I have overlooked something or if something is not correct. The code below is just the SPI part, the initialization etc is not included. SPI clock is 100KHz. MSP430(spi master) runs at 1MHz. The code starts burst write, whenever there is USI interrupt, the data in shift reg is loaded to rxdata array.


nCS_LOW(); //Make Chip sel low
for (n=0; n<10; n++) //10 byte burst
{
USISRL=mydata[n]; // Load data to Shift Reg
USICNT=8; // Start SPI Tx
}
nCS_HIGH(); //end burst

}


// Reading part. 
#pragma vector=USI_VECTOR
__interrupt void USI_interrupt(void)
{
USICTL&=~USIIFG; //Clear interrupt flag
rxdata[n]=USISRL; n=n+1;

}

  • You're missing one important thing: there is no implicit wait when you start a transfer. Your code starts the transfer and then continues while the transfer is done by the hardware in the background. However, your code immediately loops and starts another transfer, and another.

    You either need to wait until one transfer is complete before you start the next (wait for USICNT==0 before writing the next byte), or you only write the first byte and then write all subsequent bytes inside the ISR (which is called after a transfer has completed). (See below).

    Also, your loop variable 'n' must be declared volatile, or lelse the compiler might generate code that operates it asynchronously, so the 'current' valeu of n might be in a processor register when the ISR is called and tries o read the current value from memory etc.

    Try this (not tested!):

    volatile unsigned int n;
    volatile unsigned int size;

    nCS_LOW();
    // depending on slave, maybe a small delay is necessary here, using __delay_cycles(x);
    size=10;
    n=1; // first byte to be written by ISR is index 1
    USISRL=mydata[0];
    USICNT=8;
    while(n<=size); // wait until last byte is transmitted ( "n<size" would be when last byte has stated transmitting, which would be wrong )
    // depending on slave, maybe a small delay is necessary here, using __delay_cycles(x);
    nCS_HIGH();

    #pragma vector=USI_VECTOR
    __interrupt void USI_interrupt(void)
    {
      USICTL&=~USIIFG; // clear pending interrupt flag
      rxdata[n]=USISRL;  // store received byte
      n++; // increment index
      if(n<size) // more bytes to transmit?
      {
        USISRL=mydata[n]; // write next byte to USI
        USICNT=8; // and start next transfer
      }
    }

    Keep in mind that the first byte received is NOT the answer of the first byte sent. If you expect an answer to the last byte you sent, you'll have to send another dummy bate in order to receive the answer.

  • Great. So I cannot use "for loop" for SPI data transfer. 

    I didn't understand your last statement  - "Keep in mind that the first byte received is NOT the answer of the first byte sent. If you expect an answer to the last byte you sent, you'll have to send anotherdummy bate in order to receive the answer."

    Are you saying the received byte is delayed?

    How about the code below - 

    nCS_LOW(); //start burst xfer
    //First data xfer. Subsequent data xfer will be done in ISR
    USISRL=mydata[0];
    USICNT=8; //begin
    NOP();

    #pragma vector=USI_VECTOR
    __interrupt void USI_interrupt(void)
    {
    USICTL&=~USIIFG; //Clear interrupt flag
    rxdata[n]=USISRL;
    n++;
    if (n<11)
    {
    USISRL=mydata[n];
    USICNT=8;
    }
    else
    {
    nCS_HIGH();
    }
    }


  • Hithesh said:
    I didn't understand your last statement


    It is often missed that when you send a command to an SPI device, you'll at the same time receive something. So when the command has been sent, you also have received a byte. This byte. however, is not the answer to the command. (usually a dummy byte or a status report byte). To get the answer to the command you sent, you'll have to send another (dummy) byte. While this dummy byte is sent (usually 0xff is used), the answer is received.
    This is because SPI always sends and receives one bit per clock pulse. You may have noticed that there is no control bit to set the transfer direction.

    Hithesh said:
    Are you saying the received byte is delayed?

    Not exactly 'delayed'. You can of course only receive an answer after the slave has received the question. But the first byte you receive is received while the question is being sent, so the first byte you received (at the same moment when the question send was complete) is NOT the answer to the question.

    With the USI you use, which only has a single 'transfer complete' interrupt, the typical program flow will implicitely ignore the byte that is received during command sending, as sending and receiving on the USCI are a matter of interpretation: First, you 'send' then you 'receive', ignorign that while sending you receive and while receiving you send. Just that when 'receiving' without loading USISRL with a dummy byte, you'll implicitely send what you received during the previous transfer. Which can in some situations cause some 'unexplainable' behaviour. The available USI demo code doesn't explain this implicit behaviour.
    And when you later deal with the double-buffered USCI on other MSPs, which has separate read and write buffers and interrupts, it is very important to know that the first receive interrupt comes from the byte received while the command was send. A number of people has fallen into this trap.

    About your code: Don't forget to set n to 1 before starting the transfer.
    And I'd not use an expicit number as the limit but rather a global variable or a golbal define. It's easy to miss one of all the places that have to be changed later when the transfer length should be changed. '11' is just a number without any implicit meaning. '(TRANSFERSIZE+1)' however, expresses a meaning and changes with change of TRANSFERSIZE throughout the whole application.

    I also wouldn't put the nCS_HIGH into the ISR. After a transfer is compete, the slave might need a certain delay during which CS has to stay low. Raising CS inside the ISR means that the delay has to happen inside the ISR too, which blocks the CPU for any other interrupt. Waiting in main (or setting up a timer delay inside the USI ISR and raising CS in the timer ISR, so main can 'fire and forget' the transfer) is usually the better choice. Anyway, main has to check whether the transfer is complete so it won't start another transfer while the previous one is still running.

    However, I don't see a reason why the code shouldn't work for this specific application.

**Attention** This is a public forum