5 Replies Latest reply on Nov 21, 2014 3:08 AM by user_1377889

    SPI master circular software buffer problem

    mads.petersen

       Hello. I have run into a wall with the SPI master module in the PSoC...

         

      I'm using the CY8CKIT-042 development kit. I'm using the PSoC 5LP chip in bootload mode. 

         

      I'm trying to communicate with a nRF24L01+ from nordic semiconductors. Everything is working fine when i'm only using the 4 byte hardware rx/tx buffers. I can send and receive the data i'm expecting to/from the single 1 byte registers. But the problem is I have to read a packet of 32 bytes from the nRF24L01+ and write 5 bytes to it. 

         

      I'm using the SPI master module 2.4 in standard cpha/cpol 0/0 8 bit transfer. Pretty much just default. 

         

      Whenever i try to read i get the weirdest things. Sometimes the bytes are shifted around, and other times I get repeats of the same bytes, or reading something different from what I was expecting. So naturally I expect the software buffer is causing problems. But after googling for hours and testing different things I just got more confused. 

         

      From what I understand an internal interrupt is being issued and the data is being transfered from the software buffer to the hardware buffer, albeit slower, and then transmitted. - 1. Is this something that I should take into account? Use a DMA or something? 

         

      The next thing I don't understand is how to the RxGetBufferSize function works with softwarebuffers. I have made 2 functions for read/write from the single 1 byte register. I utilize the function to check if data is actually received (in accordance to the datasheet). - 2. How can I achieve the same functionality when it always returns 0 with software buffers? 3. And how do I know how much is ready to be read?

         

      Right now all I have is for my read function. 

         

      uint8 nRF24L01_Read_Buffer(uint8 registerAdress, uint8 pdata[], uint8 datalength) {

         

      uint8 i = 0;

         

       SPIM_1_ClearTxBuffer();
       SPIM_1_ClearRxBuffer();    
       SPIM_1_WriteTxData(R_REGISTER + registerAdress); //command word to the nrf24l01

         

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

         

              SPIM_1_WriteTxData(5); //dummy bytes

         

          }

         

        while(!(SPIM_1_ReadTxStatus() & SPIM_1_STS_SPI_DONE));

         

          if(SPIM_1_GetRxBufferSize() > 0) { //I understand I can't do this? But what do I do!?

         

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

         

                         pdata[i] = SPIM_1_ReadRxData();

         

                        } 

         

               }

         

        }

        • 1. Re: SPI master circular software buffer problem
          user_1377889

          Welcome in the fascinating world of PSoCs!

             

          You are checking if you have got at least one (1) byte in your buffer, but then you read datalength bytes off without checking if anything is availlable.

             

          Processing is done internally as follows:

             

          Your data is put into the buffer and the first byte is started to be sent out. Because the sending takes some time, your buffer gets filled with the remaining data. On the first successful transmit an interrupt is raised and the next byte will be sent.

             

          At the same time, the first byte is received and put into your buffer. Now your program may read off exactly one byte since there are not any other bytes ready for now.

             

           

             

          Hope that heelps

             

          Bob

          • 2. Re: SPI master circular software buffer problem
            mads.petersen

             But if the interrupts are handled internally, why can't I just read the data from the buffers when I know the data has been sent using:

               

             while(!(SPIM_1_ReadTxStatus() & SPIM_1_STS_SPI_DONE));

               

            If i'm sending datalength of bytes then shouldn't I receive the same amount in the rx buffer when transmit is completed? 
            (a little note, in the code I pasted i also send a command word, can this just be discarded with calling SPIM_1_ReadRxData(); prior to my for loop?

               

            I had a look at some of the example projects included in PSoC Creator, but I still can't see what the difference is. 

               

            Or is it possible to use the hardware buffer only 4/4 rx/tx and then read it continually? What would be the easiest solution? 

            • 3. Re: SPI master circular software buffer problem
              user_1377889

              Can you post your complete project, so that we all can have a look at all of your settings? To do so, use
              Creator->File->Create Workspace Bundle (minimal)
              and attach the resulting file.



              Bob
               

              • 4. Re: SPI master circular software buffer problem
                user_193124556

                 I found PSoC SPI to be a royal pain in the ass.

                   

                 

                   

                You have that lovely 4 byte hardware FIFO, but no way to keep it full. You have automatic slave select, but no way to specify "the SPI command is done, negate the slave select." I spent a considerable amount of time working around this.

                   

                 

                   

                Just as an example, here's how I had to do the nRF "query status/clear interrupts" command. It shouldn't have to be this hard:

                   

                 

                   
                /* returns the status byte (and clears any pending nRF interrupts) */ static uint8_t get_status(void) {  uint8_t b;   while (! (SPIM_ReadTxStatus() & SPIM_STS_SPI_IDLE)) ;   SPIM_ClearTxBuffer();  SPIM_ClearRxBuffer();   SPIM_WriteTxData(0x27);  SPIM_WriteTxData(0x70);   while (! (SPIM_ReadRxStatus() & SPIM_STS_RX_FIFO_NOT_EMPTY)) ;  b = SPIM_ReadRxData();  /* read status byte */   while (! (SPIM_ReadRxStatus() & SPIM_STS_RX_FIFO_NOT_EMPTY)) ;  (void)SPIM_ReadRxData(); /* read dummy byte */   while (! (SPIM_ReadTxStatus() & SPIM_STS_SPI_IDLE)) ;   return b; } 
                   

                Simiilarly, receiving the payload:

                   
                 static void spi_tx_seq(uint8_t *seq) {  while (*seq != 0x00) {   uint8_t len;    while (! (SPIM_ReadTxStatus() & SPIM_STS_SPI_IDLE)) ;    CE_Write(0);    len = *seq++;    SPIM_PutArray(seq, len);   while (! (SPIM_ReadTxStatus() & SPIM_STS_SPI_IDLE)) ;   CyDelayUs(100);   if (*seq == 0x25 || *seq == 0xa0) {    CE_Write(1);    CyDelayUs(10);   }    seq += len;  }; }   bool rx_packet(uint8_t *buf) {  uint8_t which, s;  bool rxdr;   uint8_t postrx_seq[] = {   0x02, 0x25, 0x2d,    /* RF_CH: channel 45 */   0x02, 0xe2, 0x00,    /* clear rx fifo */   0x02, 0x27, 0x70,    /* clear interrupts */   0x00  };   s = get_status();  rxdr = s & (1 << 6);    /* RX_DR means there is data in the RX FIFO for us */  which = (s >> 1) & 0x7;    /* extract PIPE # from the status byte */   /* if we have somewhere to put a packet and there's FIFO data waiting for us... */  if (buf && rxdr) {   int i;    /*    * For some reason we seem to get spurious RX_DR flags.    * It seems that we get RX_DR set with PIPE # 0x7 which means "RX FIFO empty"    * Oh well. Look at the PIPE number and if it's not zero, we ignore it.    */   if (which == 0) {   /* only interested in pipe #0 */      /* CE must go LOW to read the FIFO data correctly */    CE_Write(0);     SPIM_ClearTxBuffer();    SPIM_ClearRxBuffer();     SPIM_WriteTxData(0x61);   /* R_RX_PAYLOAD */     for (i=0; i<9; i++) {     uint8_t b;      SPIM_WriteTxData(0x00);     while (! (SPIM_ReadRxStatus() & SPIM_STS_RX_FIFO_NOT_EMPTY)) ;     b = SPIM_ReadRxData();     if (i != 0) {      *buf++ = b;     }    }     /* we've transmitted all the bytes, now we wait for the last response byte to come in */    while (! (SPIM_ReadRxStatus() & SPIM_STS_RX_FIFO_NOT_EMPTY)) ;    *buf++ = SPIM_ReadRxData();   }    /* wait for the SPI transaction to finish so we can send our post-packet sequence */   while (! (SPIM_ReadTxStatus() & SPIM_STS_SPI_IDLE)) ;   spi_tx_seq(postrx_seq);  }   /* CE must be 1 if we want to receive data */  CE_Write(1);   return rxdr; }  

                Honestly, it shouldn't have to be this hard. And seriously, this forum software is crap. Trying to paste code and switch between formatted and normal text is a chore.

                 


                • 5. Re: SPI master circular software buffer problem
                  user_1377889

                  @Alkohlsmith

                     

                  Instead of copy & paste to/from forum software, it is always advisable to post a complete working (or not working when that's the issue) project here. Yes, the forum software did not yet arrive completely in the 21st century, but there have been made large improvements recently. To force a text-based editor to accept programming-oriented indented lines is a bit of over-estimation. As I said there are better solutions.

                     

                   

                     

                  Bob