SPI Reading problem

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

SPI Reading problem

3,032 Views
john71
Senior Contributor I

I read from SPI slave

volatile struct    SPI_MemMap *SPIx[NUM_DSPI] = {SPI0_BASE_PTR, SPI1_BASE_PTR, SPI2_BASE_PTR};

uint32_t SPI_TransferByte(uint32_t spi_num, uint8_t tx_data, uint8_t *rx_data)
{
     uint32_t timeout;
     uint32_t value;


   SPIx[spi_num]->PUSHR = /*SPI_PUSHR_PCS(0) |*/ SPI_PUSHR_CTAS(0) |       SPI_PUSHR_TXDATA((uint32_t)tx_data);

    timeout = 0;
    // while transfer complete
    while (!(SPIx[spi_num]->SR & SPI_SR_TCF_MASK))
    {
       timeout++;
       if (timeout > SPI_TIMEOUT)
       {
         // clear flag
         SPIx[spi_num]->SR = SPI_SR_TCF_MASK;
         return SPI_TIMEOUT_ERROR;
       }
    };
    // clear flag
    SPIx[spi_num]->SR = SPI_SR_TCF_MASK;

    *rx_data = (uint8_t)SPIx[spi_num]->POPR;
    *rx_data = (uint8_t)SPIx[spi_num]->POPR;

    return 0;
}

Then I use it

uint8_t SPI_rw_flash(uint8_t tx, uint8_t* rx)
{
    uint8_t err = 0 ;
    uint8_t dummy;

    err = SPI_TransferByte(SPI2, tx, rx);

     return err;
}

First of all - I have to read it twice.

*rx_data = (uint8_t)SPIx[spi_num]->POPR;
 *rx_data = (uint8_t)SPIx[spi_num]->POPR;

Otherwise I don't get a value.

The second problem - I read a value only if I set a break point on the second *rx_data = (uint8_t)SPIx[spi_num]->POPR;

Otherwise if it a free run - I read zeros.

What do I miss?

20 Replies

1,589 Views
john71
Senior Contributor I

Sorry my friends, It works! Thank you very much for your help!

0 Kudos

1,592 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, all

I am sorry, it is my fault, you can not use the SPIx[spi_num]->SR |= SPI_SR_TCF_MASK; separately, it will clear all the flags.

You can poll both the transmitter and reciever with the lines:

    while (!(SPIx[spi_num]->SR & SPI_SR_TCF_MASK)) {}

    while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) { }

In the end, clear all the flags with only ONE line

SPIx[spi_num]->SR |= (SPI_SR_TCF_MASK|SPI_SR_TCF_MASK|SPI_SR_RFDF_MASK);

BR

Xiangjun rong

1,588 Views
john71
Senior Contributor I

Did it this way. Stay in while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) { } loop.

Then I enabled RX FIFO, I was told the flag is not active without enabling RX FIFO - still no luck.

0 Kudos

1,588 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Evgeny,

regarding the pull-up resistor, I supposed the SPI_SIN is float, maybe the reading data is random, if you connect a pull-up resistor, the received data is a fixed 0xFF.

First of all, PLS disable FIFO mode and clear FIFO for both transmitter and receiver

SPIx_MCR|=0x0F<<10;

change the code as i write here with RONG WROTE

uint32_t SPI_TransferByte(uint32_t spi_num, uint8_t tx_data, uint8_t *rx_data)
{
     uint32_t timeout;
     uint32_t value;


    // Assert CS0, Use config 0
   SPIx[spi_num]->PUSHR = SPI_PUSHR_CTAS(0) | SPI_PUSHR_TXDATA((uint32_t)tx_data);

 

    timeout = 0;
    // while transfer complete
    while (!(SPIx[spi_num]->SR & SPI_SR_TCF_MASK))
    {
       timeout++;
       if (timeout > SPI_TIMEOUT)
       {
         // clear flag
         SPIx[spi_num]->SR| = SPI_SR_TCF_MASK; 
         return SPI_TIMEOUT_ERROR;
       }
    };
    // clear flag
    SPIx[spi_num]->SR |= SPI_SR_TCF_MASK; ////Rong wrote that you have to add the OR 

 

   // for (timeout = 0; timeout < 100; timeout++) ;

    while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK))  {}   //RONG Wrote you have to add the {} i suppose


    *rx_data = (uint8_t)SPIx[spi_num]->POPR;

      SPIx[spi_num]->SR |= SPI_SR_RFDF_MASK;  //Rong wrote that you have to add the OR 

    return 0;
}

Pls have a try

BR

Xiangjun Rong

0 Kudos

1,588 Views
john71
Senior Contributor I

Still have no luck.

Added in the setup function

//  Clear TX FIFO    Clear RX FIFO    Disable Receive FIFO   Disable Transmit FIFO
 SPI2_MCR |= SPI_MCR_CLR_TXF_MASK | SPI_MCR_CLR_RXF_MASK | SPI_MCR_DIS_RXF_MASK | SPI_MCR_DIS_TXF_MASK;

And the final code

uint32_t SPI_TransferByte(uint32_t spi_num, uint8_t tx_data, uint8_t *rx_data)
{
    SPIx[spi_num]->PUSHR = SPI_PUSHR_CTAS(0) | SPI_PUSHR_TXDATA((uint32_t)tx_data);


    // while transfer complete
    while (!(SPIx[spi_num]->SR & SPI_SR_TCF_MASK))   { }
    SPIx[spi_num]->SR |= SPI_SR_TCF_MASK;


    while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) { }
    SPIx[spi_num]->SR |= SPI_SR_RFDF_MASK;


    *rx_data = (uint8_t)SPIx[spi_num]->POPR;

    return 0;
}

And I stay in the second while - while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) { }

0 Kudos

1,588 Views
guoli
NXP Employee
NXP Employee

SPIx[spi_num]->SR |= SPI_SR_TCF_MASK; This line will clear all bit set in SR register, Please remove "|", but i am not sure if this is root cause, you can try it.

1,588 Views
john71
Senior Contributor I

Still no luck. I'm quite frustrated. Do you have an example? Setting and transferring on SPI.

0 Kudos

1,589 Views
guoli
NXP Employee
NXP Employee

Hello john7‌,

Actually, there are a lot of examples provided by NXP, you can download the SDK example on website: 

Welcome | MCUXpresso SDK Builder 
I think it will be helpful for your development.

0 Kudos

1,589 Views
Ray_V
Contributor V

using |= will definitely cause a problem. It will write back a 1 to any bit that is set clearing all clearable bits.

You don't even have to wait for TCF all you need is to check if RX FIFO has any data.

try this:

uint32_t SPI_TransferByte(uint32_t spi_num, uint8_t tx_data, uint8_t *rx_data)
{
    while (!(SPIx[spi_num]->SR & SPI_SR_TFFF_MASK)) { }

    SPIx[spi_num]->PUSHR = SPI_PUSHR_CTAS(0) | SPI_PUSHR_TXDATA((uint32_t)tx_data);

    while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) { }

    *rx_data = (uint8_t)SPIx[spi_num]->POPR;

     SPIx[spi_num]->SR = SPI_SR_TCF_MASK|SPI_SR_RFDF_MASK; // I believe this is not needed

    return 0;
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

If it still doesn't work you could try checking RXCTR (bits 7-4 RX FIFO Counter) instead of RFDF.

By the way, I don't think you need to clear RFDF, it will clear when the fifo is empty. Writing a 1 to this bit will force it to discard all bytes in the fifo, so you should not write a 1 to this bit before reading POPR.

1,589 Views
john71
Senior Contributor I

I stay in  while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) { } loop.

I also try (!(SPIx[spi_num]->SR & 0xF0)) to check RX FIFO counter - zero value in the counter bits - and I stay in the loop.



0 Kudos

1,589 Views
guoli
NXP Employee
NXP Employee

Hello john7‌,


According to your code and your debug information below, I think the root cause is that, the receive data register is not ready before reading it. because you are checking the TCF bit, as the reference manual said, this bit indicates all bits in a frame have been shifted out, but it not stands for the transfer is end, because the SOUT pin is transferring the last bit at this monment, do you understand my explain? So, I do not suggest you to use this bit checking if the transmit is end.

Here is my workflow for SPI transfer one byte,

1. Checking TFFF bit to make sure the transmit data register is ready to be written in.

2. Write data to transmit data register.

3. Wait for the receive data is ready in register.(RFDF bit should be checked)

4. Reading the data from receive data register.

Hope it be helpful to you。Good luck!

0 Kudos

1,589 Views
john71
Senior Contributor I

Is TFFF bit working when FIFO disabled?

0 Kudos

1,589 Views
guoli
NXP Employee
NXP Employee

Yes, it will still working even there is no FIFO.

1,589 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Evgeny,

I suggest you disable FIFO mode. If you want to test the SPI transfer, i suggest you transfer multiple data for example, you can check the spi waveform and the received data array.

Anyway, duble reading the same register is incorrect. Pls use the following code which I modify based on your orginal code.

BTW, you can connect the SPI_SOUT with SPI_SIN directly so that you can check if the received data is correct or not..

Hope it can help you

BR

xiangjun Rong

uint8_t SPI_rw_flash(uint8_t tx, uint8_t* rx)
{
    uint8_t err = 0 ;
    uint8_t dummy;

 //transfer 50 bytes

   for(unsigned int i=0; i<50; i++)

{

   err = SPI_TransferByte(SPI2, tx, rx++);

}

 asm("nop"); //set a break point here

     return err;
}

uint32_t SPI_TransferByte(uint32_t spi_num, uint8_t tx_data, uint8_t *rx_data)
{
     uint32_t timeout;
     uint32_t value;


   SPIx[spi_num]->PUSHR = /*SPI_PUSHR_PCS(0) |*/ SPI_PUSHR_CTAS(0) |       SPI_PUSHR_TXDATA((uint32_t)tx_data);

 

    timeout = 0;
    // while transfer complete
    while (!(SPIx[spi_num]->SR & SPI_SR_TCF_MASK))     { }

    // clear flag
    SPIx[spi_num]->SR = SPI_SR_TCF_MASK;

 

    *rx_data = (uint8_t)SPIx[spi_num]->POPR;

 

    return 0;
}

Then I use it

0 Kudos

1,589 Views
john71
Senior Contributor I

Using a scope I see the correct data on MISO pin. The hardware layer is working good.

I try to disable FIFO

SPI2_MCR |= SPI_MCR_DIS_RXF_MASK;

But the result is the same.

0 Kudos

1,589 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Evgeny,

If you connect the SPI_SIN to high logic via a 10K pull-up resistor, while poll the RFDF bit in SPI_SR register before reading SPI_POPR register, what is the result?

BR

XiangJun Rong

0 Kudos

1,589 Views
john71
Senior Contributor I

Sorry, I'm not sure I understand it right.

Enable software pull-up? How do I do it?

Right now the SIN pin configured

PORTD_PCR14 = (uint32_t)((PORTD_PCR14 & (uint32_t)~(uint32_t)(
                 PORT_PCR_ISF_MASK |
                 PORT_PCR_MUX(0x05)
                )) | (uint32_t)(
                 PORT_PCR_MUX(0x02)
                ));

Well..I found the problem - if I put some delay

uint32_t SPI_TransferByte(uint32_t spi_num, uint8_t tx_data, uint8_t *rx_data)
{
     uint32_t timeout;
     uint32_t value;


    // Assert CS0, Use config 0
   SPIx[spi_num]->PUSHR = SPI_PUSHR_CTAS(0) | SPI_PUSHR_TXDATA((uint32_t)tx_data);

    timeout = 0;
    // while transfer complete
    while (!(SPIx[spi_num]->SR & SPI_SR_TCF_MASK))
    {
       timeout++;
       if (timeout > SPI_TIMEOUT)
       {
         // clear flag
         SPIx[spi_num]->SR = SPI_SR_TCF_MASK;
         return SPI_TIMEOUT_ERROR;
       }
    };
    // clear flag
    SPIx[spi_num]->SR = SPI_SR_TCF_MASK;

    for (timeout = 0; timeout < 100; timeout++) ;

    //while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK))  //dosen't work


    *rx_data = (uint8_t)SPIx[spi_num]->POPR;

    return 0;
}

It works good.

That's why it works with the second *rx_data = (uint8_t)SPIx[spi_num]->POPR; - it added some delay.

I have to wait after a sent byte. But if I poll while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) I stay in the loop.

How do I fix the problem?

0 Kudos

1,589 Views
Ray_V
Contributor V

You have to understand how spi works.

When the master sends some data, the same clock clocks in data from the slave.

I am not sure what your slave is doing, but as an example let's say the slave will receive a byte and respond to the master by sending the same byte it just received.

First, when sending Byte1 the master will be clocking in a byte from the slave, at this point the slave will not have received a byte from the master, so the byte clocked in could be random, 0x00 or 0xFF depending on how the slave is programmed. This byte would be stored in the fifo/receive register. When you execute the spi read it just returns the byte in the fifo/receive register, it does not do another transaction. So you read this byte and discard it. You can at this point send Byte2, during this transaction the slave would be responding with Byte1, you can then execute spi read and it would return Byte1 from the fifo. You can keep this going and you would always be receiving the previous byte. To get the last byte you need to send an extra byte to receive it.

Another example, reading 8 bytes from SPI Flash.

Lets say the read command is 4 bytes (1 byte command + 3 byte start address), for this usually you have to assert the CS and keep it asserted during the whole transaction, then de-assert to finish, but let's concentrate in send/receive.

When sending the 4-byte command, you would be receiving and discarding 4 bytes. Then to actually read the 8 bytes from Flash you need to send 8 bytes to clock in the 8 bytes from the slave. So you send 8-bytes (usually 0x00 or 0xFF depending on what is the "nop" command for the flash) and this allows you to read the data from the slave.

I hope this helps.

0 Kudos

1,593 Views
john71
Senior Contributor I

I understood how the SPI works 10 years ago.

Now I want a simple answer - why after putting some delay for (timeout = 0; timeout < 100; timeout++) ; - I do receive the byte *rx_data = (uint8_t)SPIx[spi_num]->POPR; and without delay - I do not receive the byte.

0 Kudos

1,593 Views
Ray_V
Contributor V

My apologies, since I saw you had 2 reads before I thought that the problem was reading one byte off. I have no way of knowing how experienced you are.

I would say that you have to wait for the transaction to be done before reading the register and that is why the delay works.

As you have above, you should check the status for RFDF bit to be set, but you indicate it does not work.

***********************

   //while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK))  //dosen't work

    *rx_data = (uint8_t)SPIx[spi_num]->POPR;

*********************

If I read this correctly the reason it does not work is because you are missing a semicolon.

while (!(SPIx[spi_num]->SR & SPI_SR_RFDF_MASK)) ;

Without the semicolon one of two things may happen.

1. Data becomes available and RFDF bit set. Loop exits before reading data.

2. Data becomes available and RFDF bit set. Reads data causing RFDF to be cleared. Never exits loop since now RFDF will not be set again. 

0 Kudos