SPI in frame mode?

13 Dec 2011

Hi, I've noticed a pause of 1µs between SPI blocks (frames?). Is this because the SPI is in frame mode or does the send command do something there? If it is in frame mode, how can I switch to normal mode?

Thanks in adance, René

14 Dec 2011

Ok found out that the phase is responsible for the delay between the frames. If phase is 0, the delay exists.

14 Dec 2011

I would assume that the small delay between frames is mainly caused by the mbed SPI library implementation. There is no write-only function but just the write-and-read one. So with "var = spi.write(var2)" the mbed waits until the frame is completely clocked out, so it can read a valid clocked in value and store it in var, before the next statement can be executed (ev. send next frame). This will at least take some 10 clockcycles between the frames, so it depends on SPI clockrate and phase, how noticeable this delay will be. If you really need a fast and seamless SPI frame transfer in only the out-direction (don't care about return values), then you should write directly to the SPI FIFO register and just check that the FIFO is not full before writing the next frame etc.

Best regards Neni

14 Dec 2011

The 1µs delay I recognized was with a frequency of 4MHz. And it depends on it. It looks like it's always 4 clock cycles delay, but only in the case where the phase is 0. I don't get any delay when phase is 1. I'm a beginner in µC programming and currently I don't know how to write to FIFO and check it.

15 Dec 2011

The SPI or in our case on the LPC1768 actually the SSP interface has an 8 frame FIFO for IN and OUT each. The good thing is, that you can still use the mbed library functions for SSP setup and then just use direct register access for writing the frames. For that it's important to know, which SSP is on which pins (datasheet). On mbed p5, p6, p7 is SSP1 and p11, p12, p13 is SSP0.
The following code demonstrates the direct register access for SSP1. At phase 0 there is still a small delay between frames, but it's only roughly 2.5 SSP clocks. So with 8 MHz i get a 128 ns clock period and an interframe delay of 310 ns. These are probably the clocks needed for fetching the next frame from FIFO and setting up the output to a valid bit before the first clock pulse (phase 0) for the frame. I didn't test other freq nor other phase yet.

#include "mbed.h"

int spi_data;

SPI my_spi(p5, p6, p7);

int main() {
    spi_data = 0;
    my_spi.format(16, 0);
    my_spi.frequency(8000000);
    while(1) {
        while (LPC_SSP1->SR & 2) {   //if TNF-Bit = 1 (FIFO not full); TNF-Bit is Bit 1 in SSPxSR
            LPC_SSP1->DR = spi_data;  // write to FIFO data register
            spi_data++;
            if (spi_data > 65535) spi_data = 0;
        }
    }
}

Best regards
Neni

15 Dec 2011

Inspired by your code piece I've changed the following code

for( int i=0; i<10000; ++)
  spi.write( 0);

to:

for( int i=0; i<10000; ++i) {
  while (!(LPC_SSP1->SR & 2)); // if FIFO full ... wait
  LPC_SSP1->DR = 0;
}

But it doesn't work. Were's my error in reasoning? Thanks for your help René

15 Dec 2011

I think you have to cast it to bool before the negation, because & is a bitwise operation:

while (!((bool)(LPC_SSP1->SR & 2))) ...

Best regards
Neni

15 Dec 2011

I don't think so, because !(2) should give the same result as !(1) as it is a logical operation

15 Dec 2011

hmmm, i don't know why it's not working for you, as the following code works for me without problems:

#include "mbed.h"

int spi_data;

SPI my_spi(p5, p6, p7);

int main() {
    spi_data = 0;
    my_spi.format(16, 0);
    my_spi.frequency(8000000);
    while(1) {
        while (!(LPC_SSP1->SR & 2));   //if TNF-Bit = 0 (FIFO full) then wait; TNF-Bit is Bit 1 in SSPxSR
        LPC_SSP1->DR = spi_data;  // write to FIFO data register
        spi_data++;
        if (spi_data > 65535) spi_data = 0;
    }
}

I even tested it with "LPC_SSP1->DR = 0;" instead of spi_data, and it works too. I get the same 2.5 clock delay as with my former code, when phase = 0. When phase = 1, there is absolutely seamless clocking, because there is no SSEL-framing then.

Are you sure that you configured and measured the correct pins? SSP1 is p5 (MOSI), p6 (MISO) and p7 (SCLK).

Best regards
Neni

16 Dec 2011

That's my code

class SPIOut : public SPI {
public:
    SPIOut( PinName mosi, PinName sclk) : SPI( mosi, NC, sclk) {}
    void write0( int count) {
        for ( ; count; --count) {
            while (!(LPC_SSP1->SR & 2)); //if TNF-Bit = 0 (FIFO full); TNF-Bit is Bit 1 in SSPxSR
            LPC_SSP1->DR = 0;  // write to FIFO data register
        }
    }
};

SPIOut spi(p5,p7);
void main()
{
  spi.format( 8, 3);       //set the SPI interface perameters for the Display
  spi.frequency( 4000000);
// init display code ...

  for (short x=0; x<504; x++)   //Clear the display memory
    spi.write( 0);
//  spi.write0( 504);  // won't work

// do some output to the display ...
}

Some more information. I use the output to send data to Nokia 5110 LCD. The write0() function of course sends 0 to the LCD clearing it. But everything what comes after that write0 doesn't work anymore. The display is in an unusable state. I also get very strange outputs of "blue" pixels (lines or rows), which even don't disappear if I disconnect from power!

20 Dec 2011

Because I don't know the internals of SPI::write() ... can someone of the experts tell me, why SPI::write does the job perfectly for Nokia LCD but the code from Nenad doesn't?

If this doesn't work, I wonder whether DMA could do it better?!

23 Dec 2011

Here's what the mbed implementation does:

uint32_t spi_write(LPC_SSP_TypeDef *ssp, uint32_t data)
{
  while ( !(ssp->SR & 2) )
    ;
  ssp->DR = data;
  while ( !(ssp->SR & 4) )
    ;
  return ssp->DR;
}
23 Dec 2011

Thank you Igor, that's it. I think I understood now what's going on. Because SPI always is a full duplex protocol, the Miso FIFO buffer is filled even if I set this pin to NC. And after each output also the input FIFO must be cleared by reading the DR register.

Now my corrected write0 function looks like this:

void write0( int count) {
        for ( ; count; --count) {
            while (!(LPC_SSP1->SR & 2)); //if TNF-Bit = 0 (FIFO full); TNF-Bit is Bit 1 in SSPxSR
            LPC_SSP1->DR = 0;  // write to FIFO data register
            while( !(LPC_SSP1->SR & 4));
            uint8_t v = LPC_SSP1->DR;  // hopefully the compiler won't optimize this
        }

Result: Reduced timing by about 20%.

05 Apr 2016

Just found this now and it looks like something I might need.

I want to write to a serial DAC repeatedly and I don't want my program to wait for the transfer to complete, I want to just start the send and have the program carry on while the data is sent.

As I understand it the default SPI Write function normally waits for the port to be writable, sends a word then loops waiting for a received word. I looked in spi_api to find this.

Now If I write one dummy word to the DR at program start then that will leave one extra word of data in the "pipe" so I think the standard SPI Write function will return immediately instead of waiting as it will find and return the result of the previous transfer.

Looks like I may need to do some benchmarking though.

Just tried it with deliberately slow SPI, slowed it down so it took 100us then followed it with a 100us delay so the total time is 200us. If I stuff a word beforehand it returns immediately then the transfer and delay run concurrently and program execution time is halved.

06 Apr 2016

Hi Oliver,

You're bumping a 5 year old thread, if you got an observation (or question) might be better to start a new one.