SPI inter-byte delay

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
DaHu_285096
Level 5
Level 5
10 likes received 250 replies posted 100 replies posted

I note a gap between each byte in an array being sent over SPI (using 5LP SPIM component).

Is there a way to prevent this? I assume that it is caused by software polling to see when byte has been sent.

My code is sending and receiving an array and can be blocking until all bytes have been sent.

I have seen DMA used but would this be useful mainly when you want to continue processing other commands while SPI is being sent.

Maybe I should be using an interrupt and checking the the flag instead of reading SPI status between each byte?

Thanks

0 Likes
14 Replies
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Have you tried SPIM_PutArray() function?

For sending an array it might be better.

We had some discussion using that function in the topic below.

SPI bus: non SCB components

Although the topic is for PSoC 4, PSoC 5LP's SPM also has

    void SPIM_PutAttry(const uint8 buffer[], uint8 byteCount) ;

moto

0 Likes

I am still a bit lost. Is the requirement to load the first byte only used for Slave but master does not need to do it and do I need to check SPI is ready before sending or do I do as below

int readfromspi(uint16 headerLength, const uint8 *headerBuffer, uint32 readlength, uint8 *readBuffer)

{

    int i=0;

    CS_Write(0);

    SPIM_PutArray( headerBuffer, headerLength );  //Send Read command to device

    while(!(SPIM_ReadTxStatus() & SPIM_STS_SPI_DONE)){};  //wait till sent  //wait till all bytes sent

    SPIM_PutArray( readBuffer, readlength );  //read all bytes by sending dummy bytes

    while(!(SPIM_ReadTxStatus() & SPIM_STS_SPI_DONE)){};  //wait till sent

    for(i=0; i<(int)readlength; i++)  //get received data from Spi received buffer

    {

        readBuffer = SPIM_ReadRxData();

    }

   SPIM_ClearRxBuffer();

    CS_Write(1);

    return 0;

}

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi

> I am still a bit lost. Is the requirement to load the first byte only used for Slave

> but master does not need to do it and do I need to check SPI is ready

> before sending or do I do as below

I'm sorry your English is too difficult for me.

Would you make your sentence shorter and separate topic in each sentence?

I think that you are talking about the SPIS specification.

Yes, SPIS put 0 for the first byte, so if we want to have our data from the first byte

we needed to re-wind the pointer then fill the data from the first byte.

Although this is stated in the Datasheet, I'd rather call it a bug.

Are you planning to use SPIS, too?

Then you need to care this "feature", but otherwise you don't have to care about it.

Now my question is by using SPIM_PutArray(), was the gap you were hoping to get rid of taken care of?

Or is/are there still gap between each byte?

> ... or do I do as below

As I don't know about the slave connected to your SPIM, I can not predict the behavior of the slave

but I would add

     SPIM_ClearRxBuffer() ;

before receiving the result.

And depending on the slave, may be we need to add some delay between

sending header and receiving the result.

So I would modify readfromspi() referring the previous topic something like

=====================

/* note this is an "IDEA SKETCH" and has not been tested */

int readfromspi(uint16 headerLength, const uint8 *headerBuffer, uint32 readlength, uint8 *readBuffer)

{  

    int i = 0 ;

    uint8_t rxBufSz  ;

    SPIM_ClearRxBuffer() ;

    SPIM_ClearTxBuffer() ;

  

    /* Start transfer */

    SPIM_PutArray( headerBuffer, headerLength );

    /* Wait for the end of the transfer. In SPI, the number of transmitted data

       elements has to be equal to the number of received data elements. */

    rxBufSz = SPIM_GetRxBufferSize();

    while( headerLength != rxBufSz ) {

        rxBufSz = SPIM_GetRxBufferSize();

    }

/* may be we need to allow some time for the slave to prepare data */

      SPIM_ClearRxBuffer() ;

    SPIM_PutArray(readBuffer, readlength);

    while(readlength != rxBufSz ) {

        rxBufSz = SPIM_GetRxBufferSize();

    }

  

    for (i = 0 ; i < readlength ; i++ ) {

        mRxBuffer = SPIM_ReadRxData() ;

    }

   return 0 ;

}

=====================

moto

0 Likes

This code cause the micro to lock up at first getRxBufferSize.

I am surprised how few examples or tutorials I can find for SPI on the PSOC. Some are simple send 1 byte and receive 1 byte and some using DMA. Some of the examples clear the buffers during the routine and others don't and the datasheet does not appear to stipulate what MUST be done for proper transfer.

I cannot find any decent examples showing how to read a block of data from an external device. The PSOC 4 did have an example for FRAM but it was clearing the buffers every read or write and waiting for each write to end before continuing and resulting in gaps between digits.

For your example, are you assuming the buffer size is set to 4 in the component and not using the internal circular buffer?

I also looked around for any decent ebooks for PSOC but could not find much at all.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

> This code cause the micro to lock up at first getRxBufferSize.

That is not a good sign, but probably it's    

  SPIM_PutArray( headerBuffer, headerLength );

who triggered the problem.

> I am surprised how few examples or tutorials I can find for SPI on the PSOC.

I totally agree with you. Meantime, the behavior of SPI(M) needs to be adjusted depending on the behavior of the Slave and there are many different behavior(s) from different Slaves, so unless you specify the Slave, it it impossible to specify the exact  behavior of a Master.

> For your example, are you assuming the buffer size is set to 4 in the component and not using the internal circular buffer?

In the previous discussion we were assuming 16.

000-SPI-CONFIG-SPIM.JPG

I suppose that the SPIM_PutArray() assumes that the Buffer size is bigger or equal to the length of the buffer to be sent.

So I wonder how big is your headerLength and readlength?

moto

0 Likes

I think I need to use DMA to get rid of the delays. Are there any "simple" tutorials that show how to get data from SPI to array in memory and visa versa?

The packet sizes for the data are specified in the calling routines and are different for various parts of the application so I need to be able to start the DMA each time with different number of bytes to send and receive.

Also, the function using the DMA needs to stay in the routine until received bytes are loaded in the receive buffer before returning.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

I'm sorry for could not be a help.

> I think I need to use DMA to get rid of the delays.

> Are there any "simple" tutorials that show how to get data from SPI to array in memory and visa versa?

I, myself do not know such tutorial nor application notes,

there may be some though.

moto

0 Likes

Do you know how I can get help from Cypress Technical Support? The support has gone and now referred just to the forum but I cannot get the answers I need or appropriate documentation.

The most hopeful info I found was a post by Bob. I am attempting to rearrange it to suit as per the code below but not sure if this will work.

I am not sure whether you just write data to the tx buffer and it automatically sends (So, for example: If I was reading from external SPI ram device should I simply set CS low, write the command bytes and dummy bytes for read and then once tx is done I can assume all received data is in the buffer ready to use?

I have not used DMA before. I assume once set up it is the writing to data buffer that triggers the send and then receive happens as incoming data arrives at spi device.

Also not sure what I need to call to update the Buffer size (in my attempt below I have re-called the configuration with buffer size as a parameter. But not sure if all I really need to do is write to this line

CyDmaTdSetConfiguration(txTD, bufLength*sizeof(DataItem), CY_DMA_DISABLE_TD, TD_INC_SRC_ADR);

#include <project.h>

void DmaTxConfiguration(uint8 bufLength);

void DmaRxConfiguration(uint8 bufLength);

/* DMA Configuration for DMA_TX */

#define DMA_TX_BYTES_PER_BURST      (sizeof(DataItem))

#define DMA_TX_REQUEST_PER_BURST    (1u)

#define DMA_TX_SRC_BASE             (CYDEV_SRAM_BASE)

#define DMA_TX_DST_BASE             (CYDEV_PERIPH_BASE)

/* DMA Configuration for DMA_RX */

#define DMA_RX_BYTES_PER_BURST      (sizeof(DataItem))

#define DMA_RX_REQUEST_PER_BURST    (1u)

#define DMA_RX_SRC_BASE             (CYDEV_PERIPH_BASE)

#define DMA_RX_DST_BASE             (CYDEV_SRAM_BASE)

#define BUFFER_SIZE                 (8u)

#define STORE_TD_CFG_ONCMPLT        (1u)

#if(SPIM_USE_SECOND_DATAPATH)

typedef uint16  DataItem; // Set this to uint8 or uint16

#else

typedef uint8 DataItem;

#endif

/* Variable declarations for DMA_TX*/

uint8 txChannel;

uint8 txTD;

/* Variable declarations for DMA_RX */

uint8 rxChannel;

uint8 rxTD;

DataItem txBuffer [BUFFER_SIZE] = {0x0u};

DataItem rxBuffer[BUFFER_SIZE] = {0};

void SendSpi(uint8 numBytes){

    uint8 i;

   

    DmaTxConfiguration(numBytes);

    DmaRxConfiguration(numBytes);

    txBuffer[0] = 0x01;

    txBuffer[1] = 0x02;

    txBuffer[2] = 0x03;

    while (0u == (SPIM_ReadTxStatus() & SPIM_STS_SPI_DONE))

    {

    }

    //At this point rxBuffer holds all receive data??   

}

int main()

{  

uint8 i;

    SPIM_Start();

    CyDmaChEnable(rxChannel, STORE_TD_CFG_ONCMPLT);

    CyDmaChEnable(txChannel, STORE_TD_CFG_ONCMPLT);

    for(;;)

    {

      SendSpi(3);

      CyDelay(100);

     

    }

}

void DmaTxConfiguration(uint8 bufLength)

{

    txChannel = DMA_TX_DmaInitialize(DMA_TX_BYTES_PER_BURST, DMA_TX_REQUEST_PER_BURST,

                                        HI16(DMA_TX_SRC_BASE), HI16(DMA_TX_DST_BASE));

    txTD = CyDmaTdAllocate();

    CyDmaTdSetConfiguration(txTD, bufLength*sizeof(DataItem), CY_DMA_DISABLE_TD, TD_INC_SRC_ADR);

    CyDmaTdSetAddress(txTD, LO16((uint32)txBuffer), LO16((uint32) SPIM_TXDATA_PTR));

    CyDmaChSetInitialTd(txChannel, txTD);

}   

void DmaRxConfiguration(uint8 bufLength)

{

    rxChannel = DMA_RX_DmaInitialize(DMA_RX_BYTES_PER_BURST, DMA_RX_REQUEST_PER_BURST,

                                     HI16(DMA_RX_SRC_BASE), HI16(DMA_RX_DST_BASE));

    rxTD = CyDmaTdAllocate();

    CyDmaTdSetConfiguration(rxTD, bufLength*sizeof(DataItem), CY_DMA_DISABLE_TD, TD_INC_DST_ADR);

    CyDmaTdSetAddress(rxTD, LO16((uint32)SPIM_RXDATA_PTR), LO16((uint32)rxBuffer));

    CyDmaChSetInitialTd(rxChannel, rxTD);

}

/* [] END OF FILE */

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

> Do you know how I can get help from Cypress Technical Support?

> The support has gone and now referred just to the forum but I cannot get the answers I need or appropriate documentation.

I will try to send a message to the Community Manager to contact you about accessing the Technical Support.

(BTW, I am not a Cypress Employee, though)

moto

0 Likes

The DMA method will be effective only if the number of bytes is fixed. Otherwise also you can use it. However if you want to chnag eth enumber of bytes very frequently it migh not be an effective way. For changing the number of bytes you will need to disable the DMA channel and then reconfigure the DMA.

Can you please have a look at the thread: Problem SPI with DMA

Thanks,

Hima

Thanks for your response Hima,

I will try the approach given in the link you provided.

The small delay at start or end of entire function is ok as long as I can run the transfer at max. rate without gaps, it should be fine

0 Likes
lock attach
Attachments are accessible only for community members.

Hima,

I attached the files in the wrong forum. I replied to your post for the branching problem but it should have been here. The only issue right now is the speed to spi transactions.

Attached are my project, information regarding speed of spi transfer and a snippet from the datasheet showing spi timing diagram.

I need max. rate spi with no inter-digit gaps.

0 Likes

Hello David,

As per the expectation there will not be inter byte delay if you have data in the RX buffer all the time. If DMA is able to load the data at the rate at which the data is being sent. The maximum data rate is application specific mostly as the the main factor limiting the maximum data rate between the master and slave is the round trip path delay.

DMA transfer will happen faster if the drq is configured as level triggered and the Tx Rx flags are configured as given in the code example "SPIM_Example" in PSoC Creator. We do not have a characterised value to give since this is more application dependent.

Are you facing inter byte delay even after implementing DMA based transfer?

Thanks,

Hima

0 Likes

Hima,

The DMA has removed the inter-byte delay. The routines appear to be working ok now. Except the application is not reliable. For my prototype, I have long wires dangling between the PSOC and the device and I suspect I am getting poor transfer as a result. However, It the waveforms look good on the Logic Analyzer.

I have designed a PCB with the 5LP right next to the device to keep tracks short and will try it on the new board.

If I still have trouble I would be interested in finding someone (happy to pay them if needed) to send the small test PCB and Code and ask them to help me debug my application. Do you know anyone I could approach or a link where I could request?

Thanks

0 Likes