Configured as a SPI Master to talk to a device

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

cross mob
Anonymous
Not applicable

I have the PSoC 4 BLE system connected via SPI to a device. That device has a "Who Am I" register at 0x0f that will reply with the value 0x6a if read correctly.

   

I have used a SCB dialog to create a SPI master, Sub mode: Motorola  CLK CPHA=1, CPOL=1

   

Data Rate: 1Mbps

   

Rx & Tx data bits 16

   

Transfer Separation: Separated

   

Rx & Tx Buffer Size: 8

   

No Interrupts

   

I have written the code to just query that register to prove that the SPI bus works. When the register is read correctly a LED connected to Pin 2 is turned on.

   

What I found was that I had to put a while loop to continuously read the SPI bus to get the correct value. In fact I had to read the bus 9 times first and then on the 10th time the value returned was correct.

   

Here is the code:

   

static uint8_t readTheRegByUart(uint8_t theRegAddr) {
  uint8_t theRegValue = 0;
  uint16_t theReadCmd = 0x8000;
  
  /* read the register */
  theReadCmd |= (uint16_t)((uint16_t)theRegAddr << 8);
  SPI_SpiUartWriteTxData(theReadCmd);
  
  while(SPI_SpiUartGetRxBufferSize() > 0) {
    theRegValue = (uint8_t) (SPI_SpiUartReadRxData() & 0x000000ff);
  }
  
  return theRegValue;
}

   

int main(void) {
  uint8_t theValue;
  
    CyGlobalIntEnable; /* Enable global interrupts. */
    SPI_Start();
    while((theValue = readTheRegByUart(0x0f)) != 0x6a);
    Pin_2_Write(0);

   

    for (;;)
    {
        ;
    }
}

   

Am I running into a buffering issue from the Rx FIFO in the SPI implementation? Is using the "Uart" version of the SPI code wrong?

   

Any insights would be helpful.

   

Cheers!!

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

  SPI_SpiUartWriteTxData(theReadCmd);

   

  while(SPI_SpiUartGetTxBufferSize() > 0); // Not needed

   

  while(SPI_SpiUartGetRxBufferSize() == 0); // needed to blocking wait for char ready
  while(SPI_SpiUartGetRxBufferSize() > 0) // Not needed{
    theRegValue = (uint8_t) (SPI_SpiUartReadRxData() & 0x000000ff); // This is what you want...

   

 

   

Bob

View solution in original post

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

Between these lines

   

  SPI_SpiUartWriteTxData(theReadCmd);
  
  while(SPI_SpiUartGetRxBufferSize() > 0) {
you should wait for SPI write is completed.

   

For every bit (byte) the SPI interface gets, one bit (byte) is returned immediately. When the very first byte is sent, the interface does not "know" yet what to answer, so a dummy byte is returned which should be skipped.
SPI has no read command, so you must send dummy bytes to retrieve the information wanted.
A pitfall is the select line, which is automatically taken low when a byte is sent. When the buffer is empty it is taken high again. This can lead to interface errors when the byte sequence is not provided fast enough resulting in ss-line glitches.

   

 

   

Bob

0 Likes
Anonymous
Not applicable

Bob,

   

Thanks for replying. I stuck this line in as you suggested:

   

while(SPI_SpiUartGetTxBufferSize() > 0);

   

However no joy. 

   

If I'm sending "0x8f00" I'm assuming I should get something like 0x??6a" back. Where the 0x8f is the slave devices read command of it's ID register. What would be read back is "0x6a" (Slaves ID register contents) not so? In other words I send 2 bytes I immediately receive 2 bytes of data that are the effect of the sent 2 bytes which are commands to the slave device.

   

Cheers!!

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

Instead of

   

while(SPI_SpiUartGetTxBufferSize() == 0);

   

look at the SPI status / status register / bits. The SPI component thells you when a transfer is in progress. Even when the TX FIFO is empty it might take a (little) while until the data is available in the RX FIFO.

0 Likes
Anonymous
Not applicable

Hli,

   

In fact "SPI_SpiUartGetTxBufferSize()" is a call to the following code:

   

#define SPI_GET_TX_FIFO_ENTRIES    (SPI_TX_FIFO_STATUS_REG & \
                                                 SPI_TX_FIFO_STATUS_USED_MASK)

   

So it does what you are describing.

   

Cheers!!

0 Likes
Anonymous
Not applicable

Me again...

   

Perhaps I've solved this myself but I hate the solution...

   

 

   

Essentially I just added a wait until I saw that data was received...

   

  uint8_t theRegValue = 0;
  uint16_t theReadCmd = 0x8000;

   

  SPI_SpiUartClearRxBuffer();

   

  /* read the register */
  theReadCmd |= (uint16_t)((uint16_t)theRegister << 8);
  SPI_SpiUartWriteTxData(theReadCmd);

   

  while(SPI_SpiUartGetTxBufferSize() > 0);

   

  while(SPI_SpiUartGetRxBufferSize() == 0);
  while(SPI_SpiUartGetRxBufferSize() > 0) {
    theRegValue = (uint8_t) (SPI_SpiUartReadRxData() & 0x000000ff);
  }
  return theRegValue;

   

I don't like having a blocking read in the code but I'm wondering if this is a typical solution to using SPI in this environment?

   

Cheers!!

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

  SPI_SpiUartWriteTxData(theReadCmd);

   

  while(SPI_SpiUartGetTxBufferSize() > 0); // Not needed

   

  while(SPI_SpiUartGetRxBufferSize() == 0); // needed to blocking wait for char ready
  while(SPI_SpiUartGetRxBufferSize() > 0) // Not needed{
    theRegValue = (uint8_t) (SPI_SpiUartReadRxData() & 0x000000ff); // This is what you want...

   

 

   

Bob

0 Likes
Anonymous
Not applicable

Thanks Bob.

   

that works.

   

Have a great New Year!!

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

Thanks, I'll do my very best 😉

   

 

   

Bob

0 Likes