SPI Master is not working properly

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

cross mob
kemic_264446
Level 4
Level 4
First like received

The SPI Master control component does not work properly.

   

If I have an SPI Master component in my design, properly connected to some I/O Pins and try to send an Array of data to it, even if the size of the array easily fits into the defined buffer, then the SPI Component drops the SS line between each byte being output.

   

I believe that this is a bug in the implementation of the component.

   

The SS line should not be dropped bewteen bytes, as this signals the end of the transmission to the slave. 

   

It makes it impossible to do sequential writes or reads to slave devices.

   

There are two possible work arounds that I can see:

   

1. Manually control the SS I/O Pin, but this is always going to be sub-optimal and some slave devices may not like the timing.

   

2. Manually disable / re-enable the TXInterrupt - but I do not think this will work, if using a buffer bigger than 4 bytes, then the SPIM_WriteTxData() method forcibly re-enables the interrupt anyway. And SPIM_PutArray() calls SPIM_WriteTxData().

   

So the SPI Master control does not work as intended unless you manually control the SS line. To do that you'd need to add an OR gate and an extra Output Pin (without hardware connection) or Control register to your design.

   

Having looked at the generated source I believe I can see what is happening...

   

When you use SPIM_PutArray() to write a numebr of bytes over SPI, it calls SPIM_WriteTxData() which in turn enables the TX Interrupt. The TX Interrupt fires and transfers the byte out the SPI Master. It then looks to see if there is any more data in the buffer - if not, then it releases the SS line. This happens while SPIM_PutArray() is still trying to write data into the buffer, but happens so quickly that it outruns the PutArray loop and the SS line gets released while data is still being added to the output buffer.

   

The fix would be to not call SPIM_TxWriteData until the buffer had been filled. If the array is to big to fit into the buffer, then as much as possible should be copied in, then an OVERFLOW error bit should be set and an error result returned (or, like POSIX writes) the actual number of bytes added to the buffer should be returned. Only when SPIM_PutArray() is done adding to the buffer should the TX Interrupt be enabled.

   

Regards,

   

Kenny.

10 Replies
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

Kenny,

   

I would strongly suggest to have Cypress informed about that issue. Can you please create a MyCase and support them with your tested projects?

   

 

   

Bob

0 Likes

Hi Bob

   

Sorry for the delay in replying - the email notification only arrived this morning.

   

Yes I will create a MyCase with some demo code. 

0 Likes
HeLi_263931
Level 8
Level 8
100 solutions authored 50 solutions authored 25 solutions authored

Another workaround is tor educe the SPI clock frequency. That increases the time it takes to write out data over SPI, giving the ISR more time to re-fill the TX buffer.

   

I think the next version of the SPI master data sheet will state that this is a known problem (and AFAIK there are no real workarounds).

0 Likes

Thank you @hli

   

I think a good solution would be to have a SPIM_WriteArray(...) method that is non-blocking, returns the number of bytes actually added to the buffer, and doesn't start the TX Interrupt until it returns. The user can then poll the TX Status registers to see when the last byte has been trasnferred.

0 Likes
Anonymous
Not applicable

Tep. This worked for me. Too bad this "feature" is not clearly stated in the manual...

0 Likes
Anonymous
Not applicable

Does somebody have simple SPI Master main.c code where Cypress kit CY8CKIT-042-BLE sends one byte to a accelerometer and receives one byte back? There are many functions in the SPI.c code and I don't know which ones to use. So far I have:

   

#include <project.h>

   

int main()
{
    CyGlobalIntEnable; /* Enable global interrupts. */

   

    /* Place your initialization/startup code here (e.g. MyInst_Start()) */
    SPIM_Start();
   
    for(;;)
    {
        /* Place your application code here. */
        SPIM_WriteTxData(0xF1u);
        SPIM_WriteTxData(0xF1u);

   

SPIM_WriteTxData(0xF1u);

    }
}

   

In the debugger I'd place a breakpoint on the third write thus there would be two writes, first one to write the address/read request into the slave and the second one to actually receive requested register content from the slave. The first write will also force the slave to empty its transmision register with whatever was there from before, correct?

   

Don't I need a loop to know when Master Rx register is full?

   

Any other function missing, enable etc?

   

In the part, do you use internal or external clock? I connect a clock to the SPI Master in the schematic.

   

Just trying to read/write to accelerometer for now with the kit connected via USB cable to the PC.

   

Thank you!

0 Likes
Bob_Marlowe
Level 10
Level 10
First like given 50 questions asked 10 questions asked

For every bit (byte) you send via SPI you receive one bit (bate) back.

   

SPIM_WriteTxData(0xF1u); returns before the byte is sent, there is a transmit buffer (and a receive buffer). Only when the Tx buffer is full the write function will wait.

   

As a consequence you will have to wait for a complete transmission before you will have received your data.

   

Depending on the type of SPI you used (SCB or UDB based) there are functions to check how many bytes are in the receive buffer

   

"The first write will also force the slave to empty its transmision register with whatever was there from before, correct?" Not quite.

   

You have to clear the input buffer yourself using the appropriate API. The output buffer will be emptied when the last byte is transmitted.

   

 

   

Bob

0 Likes

Hi Bob,

My SPI interface is also not working.  I am trying to connect a CYBLE-022001 to an MCP3551 ADC using an SCB configured as a SPI master.

I am getting no proper communication, when I measure the SS pin it is never getting asserted low,

I've attached a workspace archive...could you please take a look and offer some advice about where I am going wrong

for(;;)

    {

     

    SPI_ss0_m_Write(0); 

 

    while( SPI_miso_m_Read()){

        SPI_ss0_m_Write(0);

        }

   

    SPI_SpiUartWriteTxData(0x00);

      CyDelayUs(1);

 

    SPI_SpiUartWriteTxData(0x00);

      CyDelayUs(1);

 

    SPI_SpiUartWriteTxData(0x00);

      CyDelayUs(01);

 

    c.ab[2]= SPI_SpiUartReadRxData();

    c.ab[1]= SPI_SpiUartReadRxData();

    c.ab[0]= SPI_SpiUartReadRxData();

 

    c.ab[3] =0x00;

 

    SPI_ss0_m_Write(1);

    SPI_SpiUartClearRxBuffer();

    sprintf(Buffer,"Reaading :- %d", c.value);

    UART_UartPutString(Buffer);

    UART_UartPutString("\r\n");

    while(SPI_SpiIsBusBusy());

    CyDelay(1000);

 

    }

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

Attached

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

Hi Bob,

I updated the attachement, I am still not reading the SPI with this...Please look at it...

Mike

0 Likes