SPI rate change in code

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 have an application that needs to set up a device at low SPI rate (< 3MHz) and then switch to SPI rate up to 20MHz for normal speed operation.

Could someone please explain or give example how to change the SPI rate for these in code.

Thank you

0 Likes
1 Solution

You can specify the PLL and MASTER clock by the Configure System Clocks dialogue which is invoked by the "Edit Clock..." button at the top of the "Clocks" tab of the CYpress Design Wide Resource (CYDWR) file in the workspace explorer.

GS003211.png

You can specify the clock frequency by this dialogue.  PSoC Creator calculate a suitable frequency realized by the PSoC 5LP and show as the "Nominal" frequency.

GS003212.png

The datasheet of the SPIM component can be opened from the context menu of the component instance.  There is an AC characteristics table in the datasheet as follows.

GS003213.png

The maximum bit rate is depend on the operation mode.  In 8-bit High-speed mode case, the maximum frequency is 18MHz.  Sorry, this SPIM does not support 20MHz bit rate.

Regards,

Noriaki

View solution in original post

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

Use an external clock for your SPI component. Then you can change the clock frequency using different approaches. On PSoC5 you can use a PWM as input to SPI. Generally you can modify the clock divider using the clock APIs described in "System Reference Guide" from Creator help.

Bob

NoriTan
Employee
Employee
25 sign-ins 5 questions asked 10 sign-ins

THE SPIM component can have a Clock input to specify the bit rate.

GS003207.png

There is a radio button on the Advanced tab.  The clock input appears when the "External Clock" is selected as the "Clock Selection"

GS003208.png

It was shown in the "Configure" tab saying the bit rate is 1/2 of the Input Clock Frequency.

GS003209.png

The output frequency of the Clock component (Clock_SPIM) is set to MASTER_CLK/1 where the MASTER_CLK=40MHz in this case.

GS003210.png

The output frequency can be set by an API functions Clock_SPIM_SetDividerValue()  For example, the divider value is set to 7, the Clock output becomes 5.71MHz and the bit rate is set to 2.86MHz

The important thing is to set a highest clock frequency to the Clock component when the project is built.  If lower frequency is set, a slow logic may be synthesized not supporting higher frequency.

Please note that I did not confirm the behavior of my project on the PSoC 5LP because I don't have enough measurement equipment right now.

Regards,

Noriaki

Thanks for the responses,

I can get it running up to 12MHz with a bit of slope on the output signal and some ringing on my scope.

I have not played around much with system master clock. The IMO is set to 3MHz and PLL as well as master clock set to 24MHz.

Can I simply enter any desired PLL and Master clock value that is multiple of 3 up to 5LP max. frequency?

I read somewhere the max.SPI rate is 18MHz, would this mean I am best to set the Master clock to say 32MHz and run SPI at 16MHz?

The waveform is deteriorating at the higher speeds. The High Speed Mode is selected. Is there something I can do with the drive pins to ensure better waveform at higher rates?

0 Likes

You can specify the PLL and MASTER clock by the Configure System Clocks dialogue which is invoked by the "Edit Clock..." button at the top of the "Clocks" tab of the CYpress Design Wide Resource (CYDWR) file in the workspace explorer.

GS003211.png

You can specify the clock frequency by this dialogue.  PSoC Creator calculate a suitable frequency realized by the PSoC 5LP and show as the "Nominal" frequency.

GS003212.png

The datasheet of the SPIM component can be opened from the context menu of the component instance.  There is an AC characteristics table in the datasheet as follows.

GS003213.png

The maximum bit rate is depend on the operation mode.  In 8-bit High-speed mode case, the maximum frequency is 18MHz.  Sorry, this SPIM does not support 20MHz bit rate.

Regards,

Noriaki

0 Likes

OK, 18MHz max.

I note on the scope there is delay between each byte being sent out the SPI port.

My code writes the packet out in a loop like this:

SPI_WriteTxData(ch);

  while(!(SPI_ReadTxStatus() & SPI_STS_SPI_DONE));

Is there a  way to prevent gaps between the bytes?

0 Likes

The status flag SPI_DONE means the end of the current transfer.  So, there should be some gap between data bytes.

GS003215.png

Please try to use the TX_FIFO_NOT_FULL flag.  You can push multiple data unless the FIFO full and the SPIM component automatically sent data bytes as fast as possible.

Regards,

Noriaki

0 Likes

Thanks Noriaki,

So I could just wait until buffer not full like this...

uint8 writeSPI(uint8 ch)

{

  while(!(SPI_ReadTxStatus() & SPI_STS_TX_FIFO_NOT_FULL)){};

  SPI_WriteTxData(ch);

  return SPI_ReadRxData();

}

What about the BYTE_COMPLETE Flag? would that also work.

0 Likes

Please refer the component datasheet.  There is a description about the Status Register.

GS003220.png

The datasheet is contained in the PSoC Creator or can be downloaded from following page.

Serial Peripheral Interface (SPI) Master | Cypress Semiconductor

Regards,

Noriaki

0 Likes

I am still having trouble getting rid of the gaps between bytes. Below is my code and image of the data output.

The Master clock is set to 64MHz and the SPI clock to 32MHz.

char buf[11] = {0xa1,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x99};

void writeSPI(char c)

{

   while(!(SPI_ReadTxStatus() & SPI_STS_TX_FIFO_NOT_FULL)){};

   SPI_WriteTxData(c); 

  

}

int main(void)

{

    SPI_Start();

    CyGlobalIntEnable; /* Enable global interrupts. */

    SPI_Clock_SetDividerValue(3);  //3 = 12MHz

    for(;;)

    {

       CS_Write(0);

       for(int i = 0 ; i < 11; i++)

       {

          writeSPI(buf);

          while(!(SPI_ReadTxStatus() & SPI_STS_SPI_DONE));

       }

       CS_Write(1);

       CyDelay(1);

      

    }

}

spi captured.JPG

0 Likes

You don't need to check the SPI_DONE flag in the LOOP.  Move the line after the for loop.  Data are pushed to FIFO as possible.

In addition, it is not required to wait for the FIFO_NOT_FULL flag before calling the WriteTxData() method because the method blocks until the FIFO not full.  There is following sentence in the datasheet as follows.

Side Effects:

Data may be placed in the memory buffer and will not be transmitted until all other previous data has been transmitted. This function blocks until there is space in the output memory buffer.

GS003221.png

Regards,

Noriaki

0 Likes

Does the PutArray function send bytes as fast as possible and block until sent? I don't mind clocking while data being sent but need to send data as fast as possible.

Also, if I used PutArray, does this automatically accumulate received bytes into the rx register and after the put I read the rx buffer?

I set up the 64MHz Master clock and tried divide by 2 as SPI clock source - this should set rate to 16MHz but I get invalid data output.

Seems I can only get around 12MHz reliably (By setting divider to 3 and use 21.3 MHz as SPI source clock.

Thanks

0 Likes

When I use PutArray at around 12MHz I get reliable data but wondering why after every 3 bits the Clock duration is doubled (changes to 80ns instead of 40ns).

SPI waveform.JPG

0 Likes

It seems that the Clock component connected to the SPIM's clock input has non-50% duty clock waveform.  Please check the Clock signal at the clock input of SPIM.

Regards,

Noriaki

0 Likes

Do you mean to connect a pin to the Clock source and look on scope?

Below is image of the component on screen and values.

The closest I can get to what I want is using PutArray but I only get 1 byte back when reading rx data?

When I look on my analyzer I see the correct data coming back from the Slave device. But not receiving it in my code.

uint8 spiTxData[8] = {0xA1,0x11,0x22,0x33,0x44,0x55,0x66,0x77};  //write to reg 0x00

uint8 spiReadData[8] = {0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00};

int main(void)

{

    CyGlobalIntEnable; /* Enable global interrupts. */

    SPI_Start();

    SPI_CLOCK_SetDivider(8); //2.66 MHz

    for(;;)

    {

      SPI_PutArray(spiTxData,8);  //Write an array of data to the Device (0xa1 = write command for location 0x21)

      while(!(SPI_ReadTxStatus() & SPI_STS_SPI_DONE));

      SPI_PutArray(spiReadData,8); //Read from location 0x21

      while(!(SPI_ReadTxStatus() & SPI_STS_SPI_DONE));

      numRxBytes = SPI_GetRxBufferSize();  //ONLY get 1 bytes here

      for(uint8 i = 0; i < 8;i++){

        spiRxData = SPI_ReadRxData();

      }

      CyDelay(1);

    

    }

}

clock onfig.JPG

0 Likes

There is a checkbox "Sync with BUS_CLK" in the Clock component configuration dialogue.  This checkbox is checked as default.

GS003231.png

If this checkbox is checked, the clock output is synchronized to the BUS_CLK.  From your waveform, it seems that the SPIM's clock input is synchronized to a 25MHz clock.  Please let me check the System Clock configuration.

The reason why the GetRxBufferSize() API returns 1 is because the RX software buffer is disabled.

GS003232.png

If you want to know the actual size of data in the FIFO, specify the RX buffer size higher than 8.  So. all data is stored in the software FIFO.

Regards,

Noriaki

0 Likes

I did not use the larger buffer because this apparently causes delays according to the documentation? I suppose reading each MISO byte as the data is being clocked out byte-by-byte is fastest but I cannot get a routine running to do this without larger inter-byte gaps.

The Bus Clock is set to 64MHz.

Capture.JPG

0 Likes

Try as I might but I cannot quite get what I need.

I played with using the SPI buffers > 4 and ended up with whopping 2.7us gap between each byte. I also found the received data in the code was not correct.

Setting the buffer size back to 4 I can reduce the inter-byte gap to 1.2us. Basically I want to send out byte at a time and read each byte from MISO as the bytes are going out, with minimal gap between bytes. And now the bytes are received correctly.

The Bit timing is sufficient, I see 40ns/60ns (100ns period) clock pulses.

#define DATAREG 0x21

uint8 spiTxData[7] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07};  //write to reg 0x00

uint8 spiRxData[7];

uint8 readbyte;

uint8 numBytes;

uint8 writeSpi(uint8 ch)

{

  SPI_WriteTxData(ch);

  while(!(SPI_ReadTxStatus() & SPI_STS_SPI_DONE));

  readbyte = SPI_ReadByte();

  return readbyte;

int main(void)

{

    CyGlobalIntEnable; /* Enable global interrupts. */

    SPI_Start();

    for(;;)

    {

      csPin_Write(0);

      writeSpi(DATAREG | 0x80);

      for(int i = 0; i < 7 ; i++)

      {

        writeSpi(spiTxData);

      }

      csPin_Write(1);

   

      CyDelayUs(1);

      csPin_Write(0);

      writeSpi(DATAREG);

      for(int i = 0; i < 7 ; i++)

      {

        spiRxData = writeSpi(0x00);

      }

      csPin_Write(1);

    

      CyDelayUs(10);

    }

}

0 Likes

If you do not satisfy with the transfer speed including the inter-byte gap, DMA would be the solution.  You can find an example project "SPIM_Example" from the PSoC Creator's "Find Code Example" dialogue.

Regarding the non-50% duty pulse, please check the sampling frequency of you measurement equipment.  I suppose that you are using a 50Msps logic analyzer.  Can you use more fast equipment?

Regards,

Noriaki

0 Likes