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.

SSI data & frame questions

I'm using an LM4F with an external SPI flash which appears to work in a reasonably nonstandard way. The way this chip functions is that to read data, I first do a write with the command and the address bits. At that point the chip takes over, and as long as CE#/SSFRAME is held high, data is returned forever for the entire length of the chip.

As far as I know, I have no mechanism to hold CE#/SSFRAME high, and so can't guarantee how long this is held high for. I have two questions:

- Is there a way to clear the processor's receive fifo? This would let me read just the first element or two of data (if they exist) and then clear the fifo and try to read again

- Is all data for this transaction put into the receive fifo? That is, since the data format is <write> <write> <write> <write> <chip returns data> <chip returns data> <chip returns data> <chip returns data>, will TI put data into the receive fifo while CE# was high but we were transmitting, or will it only put data into the receive fifo from when CE was high but we were not transmitting?

Thanks!

  • You do realize that to receive any SPI data you must send data.  And conversely any time you send data you also receive data.  SPI is always simultaneously bi-directional, running off the clock provided when the master sends data.

    The typical model for SPI data transaction (from the SPI master is):

    1) Send command word

    2) Read and discard dummy response

    Loop on 1 and 2 until the entire command sequence (and subsequent data if sending data) is complete.  If you want to receive data in response to the command, then:

    3) Send dummy word

    4) Read response word.

    Loop on 3 and 4 until the entire response is read.

    A chip configured as a slave cannot "take over".  All it can do is respond to the master.  I'm guessing that you have slightly misinterpreted the documentation.

    If you want to control CE/SSFrame, you can configure the pin to be a GPIO pin, and manually control it through software.  BTW - CE high is usually the inactive state, low is usually the active state.  So are you sure you're not supposed to hold CE low throughout the transmission?  

    If the chip uses CE to distinguish commands from when it should be sending data, then you configure CE as GPIO.  You pull it low, send the command and address.  You read back the dummy data returned during the command and address transmission (which will also guarantee that the command and address fully transmitted), then you set CE to high and go into a loop where you send dummy words and read back the data from the chip.

  • Thanks for the quick reply! This is the first time I've dealt with SPI outside of having an FPGA which handles the protocol, so it seems I did have some misconceptions.

    I'm kind of surprised that if the standard model always includes reading and writing at the same time, that TI only provides SSIDataGet and SSIDataPut, it seems like a command that combines the two would be really useful (and fit more in how the protocol is used).

    It also still seems like a way to clear the receive fifo would be useful, since at least in our current testing with a scope, CE is asserted for longer than we would expect it to be given our commands. I think this means we'll get extra garbage data at the end of our read, which we would then pick up during the next read unless we clear the fifo manually.

    Thanks for the help.

  • You don't get garbage when CE is asserted for longer than required because you won't be providing any clock.  If you are the master, you can only read data by sending data.  The act of sending data causes the data clock to be generated.  CE by itself doesn't cause anything to happen.

    SSIDataPut() and SSIDataGet() really are appropriate as separate calls for several reasons.

    Reading and writing don't occur at the same time, the transmission is simultaneously bi-directional, meaning data will be sent and received simultaneously, but the events of sending and receiving won't happen at the same time as far as the CPU is concerned because for writing you are starting the send, but for reading, the transmission (of that word) must already be complete.

    SSIDataPut() causes data to be put into the outgoing hardware FIFO.  If configured as a master, this will also cause data transmission to start (if it's not already in process).  If configured as a slave, the data will not transmit until the master provides clocking for the data (by sending data).

    SSIDataGet() will return a word from the FIFO if there is any data currently available, but will wait for data to become available if there is none currently.

    If you call SSIDataPut(), followed by SSIDataGet(), followed by another SSIDataPut(), SSIDataGet(), you will discover that there will actually be a small gap in the transmission.  This gap can be eliminated by calling SSIDataPut(), SSIDataPut(), SSIDataGet(), SSIDataGet() instead.

    In practice, you can overlap reads and writes to accelerate the process.  For instance if you know that your command sequence to receive data requires 3 words to be sent, you can call SSIDataPut() 3 time, which will load up values into the FIFO so that they can be transmitted back-to-back, then you can call SSIDataGet() 3 times later to read out the dummy response data.  Then say you are going to read 100 words of data back.  The FIFO isn't 100 words long, but you could for example send a dummy word to get the process started, then go into a loop where you send a dummy word, then read a response word.  You go through this loop 99 times, then read one more response word to get all the data you wanted to read.

    You can write a state machine interrupt handler to optimize your SSI transactions, making better use of the FIFO to minimize the frequency of interrupts.

    BTW - I've edited my responses a couple of times to get rid of minor typos and clarify some of what I've said.  I think you responded to my original reply while I was editing it.

  • I appreciate all the suggestions! I've reworked my code a bit, and now I'm seeing reads work correctly by doing the dummy writes as you suggested. Writes weren't working, so we put them under an oscilloscope. Here's the code that I am trying to figure out:

    //First enable writing the status register with EWSR
    SSIDataPut(SSI2_BASE, 0x50); //EWSR
    SysCtlDelay(SysCtlClockGet()/12000000); //TODO FIXME just for debugging, this lets CE go high

    //Then immediately write the status register with WRSR and the status register
    //This will undo the block protect AND write-enable latch.
    SSIDataPut(SSI2_BASE, 0x01); //WRSR
    SSIDataPut(SSI2_BASE, 0x42); //b01000010 for status register
    SysCtlDelay(SysCtlClockGet()/12000000);

    In this code I don't do reads because I clear the read fifo before I read data. The first command works and I see all appropriate data on the bus (just like I do for reads). For the write, I see 0x01 go out correctly, but then I see a 9th clock before the data is clocked out for 0x42. 

    That is, I see

    0001 0000 0 0100 0010

    when I should see

    0001 0000 0100 0010

    We're looking at this on the scope, so it seems to be what's actually happening. I've checked and I'm set to 8 bit data and not 9 bit, so I don't know why I'd be seeing the extra clock. Any thoughts?

  • Spectacularly valuable, insightful - and caring, "slandrum" post.  Bravo.  (and SO deserving of Verified Answer tick)

    Would be immensely helpful if you'd allow this to be included w/in both MCU datasheet and SW-DRL-UG. 

    Most all of the SSI intricacies - glossed over elsewhere - are skillfully and thoughtfully presented here.  We thank you...

  • Benjamin Fitzpatrick said:
    We're looking at this on the scope, so it seems to be what's actually happening. I've checked and I'm set to 8 bit data and not 9 bit, so I don't know why I'd be seeing the extra clock. Any thoughts?

    First, are you certain you are seeing an extra clock, and not just a gap that's 1 clock pulse wide (with no actual change in the clock line)?

    If you are actually seeing 9 clock pulses and you are really set for 8 bit data, I don't know why this would be happening.  It really suggests that for some reason your hardware is set for 9 bit data, or you have the mode settings on the SSI incorrect in some way.

    What is your code for initializing the SSI?  Perhaps there's some sneaky hard to catch error in there, or perhaps some other code has changed the register settings for the SSI configuration.