This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2642R: SPI first byte zero issue

Part Number: CC2642R
Other Parts Discussed in Thread: CC2640

Hell team,

My customer is using CC2642R for mPOS application. They tried using CC2640R2 for this case now switched to CC2642R for larger Flash size, and they are aware of the workaround for "SPI first byte zero" issue on CC2640R2. 

Now they found a similar phenomenon (but not the same) on CC2642R, so they have some questions:

  1. Is this issue known by TI?
  2. If there is big delay such as 10secs between two SPI transfer, it looks like CC2642 is appending `zero byte`
  3. If there is short delay such as 20msecs between two SPI transfer, it looks like CC2642 is working as expected.
  4. Our master is working with CC2640 without problem since we already appended `zero byte` before each packet. But the same firmware gives the above results surprisingly.

I attach the captured waveform (open through Saleae LA) and detailed report (.txt file) provided by the customer for analysis. Please take a look.

20181227_CC2642_SPI.zip

  • Hi Jo Chen,

    I'm not aware of this behavior, I would expect the same behavior from the CC2642R as for CC2640R2. They do however use different drivers so I need to look closer at this.

    Is it possible for your customers to put together a small "test project" based on any of our SDK driver examples (for example, the "Empty" project) which show how they use the SPI driver and where the issue appears? If they could do this, I could test this out when I'm back at the office in January.

    You could also ask them to add the SPIDMACC26XX.c source file to the project to override the library version used and see if the behavior changes.
  • HI M-W,

    Thank you for your response. This is Farabi, I am the one who issued this topic to Jo Chen.

    Upon the your requests, please find my comments below:

    •  Override the library version of CC2642
      • It is tested, but the same phenomenon is observed again.
      • The default SPI driver code block (SPICC26X2DMA.h) is commented out in our custom board files, and SPICC26XXDMA.h is imported with the required modifications of configuration variables.
    • Preparing a small "test project"
      • In order to use internal investigation, I have modified the existing project to be able to isolate the issue from other parts of the system.
      • Since it is not fully stripped yet, please find the SPI driver related parts below. So that, you can check the logic. 

    • For internalinvestigation, 
      • An Arduino based ESP32 is used as master.
      • CC2642 is used as slave.
      • CC2642 is programmed to send 5 bytes (A,B,C,D,E) when master is available. 
      • The code snippet can be found below. 
      • The results of the investigation isas below;
        • When the SPI driver starts, it always sends the very first byte as `0x00`.
        • SPI_Transfer is seemed has a bad buffer management in it. Because it holds the unsent bytes in it and sends again in the next CLK even if new bytes are loaded via SPI_Transaction.
        • It makes the SPI Slave to give unpredicatable patterns since the internal buffer is circulating/shifting somehow.
        • The actual byte arrays received by master is as given below upon sending only `65, 66, 67, 68, 69`.
        • WaitForSlave
          SlaveOK
          0
          65
          66
          67
          68
          WaitForSlave
          SlaveOK
          69
          65
          66
          67
          68
          WaitForSlave
          SlaveOK
          69
          65
          66
          67
          68
          ....
          WaitForSlave
          SlaveOK
          66
          67
          68
          69
          65

    • The workaround I found;
      •  I have made the CC2642 to behave in a predictive manner by calling SPI_Close() after each transfer. 
      • And, calling SPI_Open before each transfer. 
      • In this way, CC2642 is sending 0 before each transmission, and is prevented to have circularly sending unsent bytes. 
      • I wonder how bad/slow such a workaround for an SPI Bus that is supposed to transfer 10Kbpsec?


    The code snippet without workaround can be found below.

    /**
     * @brief       Initializes SPI Module
     * @details     SPI peripheral initialization and configuration of signalling
     *              will be done here.
     *
     * @returns     void
     */
    static void Spi_Init(void)
    {
        /* Call drivers's init functions that are common among parallel tasks*/
        GPIO_init();
        SPI_init();
    
        ...
    
        /* GPIO Init */
        GPIO_setConfig(Board_SPI_MASTER_READY, GPIO_CFG_INPUT);
        GPIO_setConfig(Board_SPI_SLAVE_READY, GPIO_CFG_OUTPUT | GPIO_CFG_OUT_LOW);
    
        /* Set Slave as Busy */
        GPIO_write(Board_SPI_SLAVE_READY, 1);
    
        Display_printf(dispHandle, S_SP_ROW_SPITASK, 0, "SPI Initializing");
    
        /*
         * Open SPI as slave in callback mode; callback mode is used to allow us to
         * configure the transfer & then set Board_SPI_SLAVE_READY high.
         */
        SPI_Params_init(&gSpiParams);
        gSpiParams.frameFormat = SPI_POL1_PHA1;
        gSpiParams.mode = SPI_SLAVE;
        gSpiParams.transferCallbackFxn = SPI_XfrCompleteFunc;
        gSpiParams.transferMode = SPI_MODE_CALLBACK;
        gSlaveSpi = SPI_open(Board_SPI_SLAVE, &gSpiParams);
    
        ...
    }
    
    static teProcessReturn Spi_DemoTransmitter(void)
    {
        if(Utils_GetMasterReady() == 1)
        {
            /* Inform the console about slave has something to send */
            Display_printf(dispHandle, S_SP_ROW_SPITASK, 0, "Slave has packet to send");
    
            uint8_t buffer[] = {0x41, 0x42, 0x43, 0x44, 0x45};
    
            memcpy(gHeaderBuffer,buffer,sizeof(buffer));
    
            gTransaction.count = sizeof(buffer);
            gTransaction.txBuf = (void *) gHeaderBuffer;
            gTransaction.rxBuf = (void *) NULL;
    
            Utils_PrintMemory((uint8_t *)(gTransaction.txBuf), gTransaction.count);
    
            return eProcessReturnSuccess;
        }
    
        return eProcessReturnError;
    }
    
    /**
     * @brief       Spi Worker Task
     * @details     It is the task that communicates with SPI master when signalling
     *              conditions are satisfied.
     *
     * @returns     void
     */
    static void Spi_Worker(UArg pArg0, UArg pArg1)
    {
        ...
    
        Spi_Init();
    
        while(1)
        {
            ...
    
            /*
             * Prepare the SPI Buffers accordingly to TX/RX Header/Payload
             */
            //if(Spi_PrepareForTransfer() == eProcessReturnError)
            if(Spi_DemoTransmitter() == eProcessReturnError)
            {
                Task_sleep(S_SPI_TASK_LOOP_DELAY / Clock_tickPeriod);
                continue;
            }
    
            lTransferOk = SPI_transfer(gSlaveSpi, &gTransaction);
            if (lTransferOk) {
                GPIO_write(Board_SPI_SLAVE_READY, 0);
    
                /* Wait until transfer has completed */
                if(gSpiParams.transferMode==SPI_MODE_CALLBACK)
                    /* Get access to resource */
                    Semaphore_pend(gSemHandleSpiTransfer, BIOS_WAIT_FOREVER);
    
                Utils_WaitForMaster();
    
                /*
                 * Drive Board_SPI_SLAVE_READY high to indicate slave is not ready
                 * for another transfer yet.
                 */
                GPIO_write(Board_SPI_SLAVE_READY, 1);
                
                ...
            }
            ...
        }
    }
    

    Please let me know if you require further information.
    Regards,
    Farabi

  • Hi Farabi,

    Since it looks like you are not needing to queue SPI transactions, you should be able to switch the SPI driver to the older version that ships for 2640 (SPICC26XXDMA vs SPICC26X2DMA ) to see if the issue is the newer driver. The code should work as is (though you may need to drop the driver into your project). You can look at the 2640 board file to see how to go back to the older driver. If you run into issues we can guide you from there.

    Thanks,

    Sean

  • Hi Sean,

    Farabi said:

     Override the library version of CC2642

    • It is tested, but the same phenomenon is observed again.
    • The default SPI driver code block (SPICC26X2DMA.h) is commented out in our custom board files, and SPICC26XXDMA.h is imported with the required modifications of configuration variables.

    As I indicated in my previous message, I have already tested your suggestion. If I applied it right, I couldn`t find any differences in terms of this issue. Please find my way of passing from old to new in the attached code sample.

    By the way, is there any documentation that indicates how `queue` implementation is works for SPICC26X2DMA? Or is there a way to flush it after each transfer other than SPI_Close? or completely disable it?

    I didn`t experience queue effect in CC2640R2 as it also has "SPI first byte zero" issue.

    Thanks,

    Farabi

    /*
     *  =============================== SPI DMA ===============================
     */
    #include <ti/drivers/SPI.h>
    #include <ti/drivers/spi/SPICC26X2DMA.h>
    
    SPICC26X2DMA_Object spiCC26X2DMAObjects[CC26X2R1_SCRP_SPICOUNT];
    
    /*
     * NOTE: The SPI instances below can be used by the SD driver to communicate
     * with a SD card via SPI.  The 'defaultTxBufValue' fields below are set to 0xFF
     * to satisfy the SDSPI driver requirement.
     */
    const SPICC26X2DMA_HWAttrs spiCC26X2DMAHWAttrs[CC26X2R1_SCRP_SPICOUNT] = {
        {
            .baseAddr           = SSI0_BASE,
            .intNum             = INT_SSI0_COMB,
            .intPriority        = ~0,
            .swiPriority        = 0,
            .powerMngrId        = PowerCC26XX_PERIPH_SSI0,
            .defaultTxBufValue  = 0xFF,
            .rxChannelBitMask   = 1<<UDMA_CHAN_SSI0_RX,
            .txChannelBitMask   = 1<<UDMA_CHAN_SSI0_TX,
            .mosiPin            = CC26X2R1_SCRP_SPI0_MOSI,
            .misoPin            = CC26X2R1_SCRP_SPI0_MISO,
            .clkPin             = CC26X2R1_SCRP_SPI0_CLK,
            .csnPin             = CC26X2R1_SCRP_SPI0_CSN,
            .minDmaTransferSize = 10
        },
        {
            .baseAddr           = SSI1_BASE,
            .intNum             = INT_SSI1_COMB,
            .intPriority        = ~0,
            .swiPriority        = 0,
            .powerMngrId        = PowerCC26XX_PERIPH_SSI1,
            .defaultTxBufValue  = 0xFF,
            .rxChannelBitMask   = 1<<UDMA_CHAN_SSI1_RX,
            .txChannelBitMask   = 1<<UDMA_CHAN_SSI1_TX,
            .mosiPin            = CC26X2R1_SCRP_SPI1_MOSI,
            .misoPin            = CC26X2R1_SCRP_SPI1_MISO,
            .clkPin             = CC26X2R1_SCRP_SPI1_CLK,
            .csnPin             = CC26X2R1_SCRP_SPI1_CSN,
            .minDmaTransferSize = 10
        }
    };
    
    const SPI_Config SPI_config[CC26X2R1_SCRP_SPICOUNT] = {
        {
             .fxnTablePtr = &SPICC26X2DMA_fxnTable,
             .object      = &spiCC26X2DMAObjects[CC26X2R1_SCRP_SPI0],
             .hwAttrs     = &spiCC26X2DMAHWAttrs[CC26X2R1_SCRP_SPI0]
        },
        {
             .fxnTablePtr = &SPICC26X2DMA_fxnTable,
             .object      = &spiCC26X2DMAObjects[CC26X2R1_SCRP_SPI1],
             .hwAttrs     = &spiCC26X2DMAHWAttrs[CC26X2R1_SCRP_SPI1]
        },
    };
    
    const uint_least8_t SPI_count = CC26X2R1_SCRP_SPICOUNT;
    
    ///*
    // *  =============================== SPI DMA ===============================
    // */
    //#include <ti/drivers/SPI.h>
    //#include <ti/drivers/spi/SPICC26XXDMA.h>
    //
    //SPICC26XXDMA_Object spiCC26XXDMAObjects[CC26X2R1_SCRP_SPICOUNT];
    //
    ///*
    // * NOTE: The SPI instances below can be used by the SD driver to communicate
    // * with a SD card via SPI.  The 'defaultTxBufValue' fields below are set to 0xFF
    // * to satisfy the SDSPI driver requirement.
    // */
    //const SPICC26XXDMA_HWAttrsV1 spiCC26XXDMAHWAttrs[CC26X2R1_SCRP_SPICOUNT] = {
    //    {
    //        .baseAddr           = SSI0_BASE,
    //        .intNum             = INT_SSI0_COMB,
    //        .intPriority        = ~0,
    //        .swiPriority        = 0,
    //        .powerMngrId        = PowerCC26XX_PERIPH_SSI0,
    //        .defaultTxBufValue  = 0xFF,
    //        .rxChannelBitMask   = 1<<UDMA_CHAN_SSI0_RX,
    //        .txChannelBitMask   = 1<<UDMA_CHAN_SSI0_TX,
    //        .mosiPin            = CC26X2R1_SCRP_SPI0_MOSI,
    //        .misoPin            = CC26X2R1_SCRP_SPI0_MISO,
    //        .clkPin             = CC26X2R1_SCRP_SPI0_CLK,
    //        .csnPin             = CC26X2R1_SCRP_SPI0_CSN,
    //        .minDmaTransferSize = 10
    //    },
    //    {
    //        .baseAddr           = SSI1_BASE,
    //        .intNum             = INT_SSI1_COMB,
    //        .intPriority        = ~0,
    //        .swiPriority        = 0,
    //        .powerMngrId        = PowerCC26XX_PERIPH_SSI1,
    //        .defaultTxBufValue  = 0xFF,
    //        .rxChannelBitMask   = 1<<UDMA_CHAN_SSI1_RX,
    //        .txChannelBitMask   = 1<<UDMA_CHAN_SSI1_TX,
    //        .mosiPin            = CC26X2R1_SCRP_SPI1_MOSI,
    //        .misoPin            = CC26X2R1_SCRP_SPI1_MISO,
    //        .clkPin             = CC26X2R1_SCRP_SPI1_CLK,
    //        .csnPin             = CC26X2R1_SCRP_SPI1_CSN,
    //        .minDmaTransferSize = 10
    //    }
    //};
    //
    //const SPI_Config SPI_config[CC26X2R1_SCRP_SPICOUNT] = {
    //    {
    //         .fxnTablePtr = &SPICC26XXDMA_fxnTable,
    //         .object      = &spiCC26XXDMAObjects[CC26X2R1_SCRP_SPI0],
    //         .hwAttrs     = &spiCC26XXDMAHWAttrs[CC26X2R1_SCRP_SPI0]
    //    },
    //    {
    //         .fxnTablePtr = &SPICC26XXDMA_fxnTable,
    //         .object      = &spiCC26XXDMAObjects[CC26X2R1_SCRP_SPI1],
    //         .hwAttrs     = &spiCC26XXDMAHWAttrs[CC26X2R1_SCRP_SPI1]
    //    },
    //};
    //
    //const uint_least8_t SPI_count = CC26X2R1_SCRP_SPICOUNT;
    

  • Hi Farabi,

    Apologies, I misread the earlier post and didn't realize you had already tried that.

    The existing documentation is in SPI.h and SPICC26X2DMA.h, although it could definitely use some more detail. Improving documentation is planned work in the immediate future so hopefully you will see that improve :) .

    Queuing means you can call SPI_transfer() multiple times to queue several SPI transactions, and they complete one after the other. It is 'disabled' by default because the driver defaults to blocking mode instead of callback mode. SPI_transferCancel() allows you to stop all requested SPI transactions.

    If you meant the SPI's internal hardware FIFO, you can read the SSI section of the TRM to learn about that:
    www.ti.com/.../swcu185a.pdf

    Back to the issue, I have a hunch that this extra zero shows up after waking up from sleep. If power policy is disabled, do you see the zero event happen more than once?

    Thanks,

    Sean
  • Hi All,
    If I may jump into the discussion, I am also facing a similar issue with a cc2652R1.
    If we were to still compensate at the master side by calling for an additional transfer to compensate for the first 0 byte being sent, In such a scenario, if I want to read some data from the master on the next transfer, the first byte read seems to be this stale byte from the last transfer. Is there any way we can flush out the receive buffer/FIFO from the slave side, without having the master to call another additional transfer for the flushing ?

    I performed 2 write cycles to the master, with the master calling for an additional transfer each time to compensate for the 0. It seems I am getting an additional 0 appended at each transfer. Then when I perform a read next, I get the first data in the rcv buffer as 0. Then the data follows.

    Regards,
    Shyam

  • Hi Shyam,

    There is a flush fifo function in both 26XX spi drivers. However the zeros do not seem to be affected. I'll post the function below:

    /* SPI test control register */
    #define SSI_O_TCR               0x00000080
    #define SSI_TCR_TESTFIFO_ENABLE        0x2
    #define SSI_TCR_TESTFIFO_DISABLE       0x0
    /* SPI test data register */
    #define SSI_O_TDR               0x0000008C
    
    /*
     *  ======== SPICC26XXDMA_flushTxFifo ========
     */
    void SPICC26XXDMA_flushFifos(SPI_Handle handle) {
    
        /* Locals */
        SPICC26XXDMA_HWAttrs const  *hwAttrs;
    
        /* Get the pointer to the hwAttrs */
        hwAttrs = handle->hwAttrs;
    
        /* Flush RX FIFO */
        while(HWREG(hwAttrs->baseAddr + SSI_O_SR) & SSI_RX_NOT_EMPTY) {
            /* Read element from RX FIFO and discard */
            HWREG(hwAttrs->baseAddr + SSI_O_DR);
        }
    
        /* Enable TESTFIFO mode */
        HWREG(hwAttrs->baseAddr + SSI_O_TCR) = SSI_TCR_TESTFIFO_ENABLE;
    
        /* Flush TX FIFO */
        while(!(HWREG(hwAttrs->baseAddr + SSI_O_SR) & SSI_TX_EMPTY)) {
            /* Read element from TX FIFO and discard */
            HWREG(hwAttrs->baseAddr + SSI_O_TDR);
        }
    
        /* Disable TESTFIFO mode */
        HWREG(hwAttrs->baseAddr + SSI_O_TCR) = SSI_TCR_TESTFIFO_DISABLE;
    }

    Thanks,

    Sean

  • Hi Sean,

    Thnx for the reply.

    But since the 0 does not get affected, the flush too is not much use in this case.

    Regards,

    Shyam

  • Hi,

    Thank you for the function about flushing the queue. I tested it with CC2642 and the queue is flushed as expected. But what I observed is the `first byte zero issue on tx` is happening again if master sends more clocks than what slave expects. Therefore it might not be the best solution as this can break the communication.

    Do you have concrete knowledge on when slave sends such a zombie-zero-byte on TX?

    The workaround I am utilizing now is invoking both SPI_Close and SPI_Open respectively every time after SPI_Transfer. Do you think it causes other problems?

    Best Regards,

    Farabi

  • Hi,
    The workaround I made was that the master side always calls an extra byte SPItransfer when the slave has to transfer data to it. And I flush the Rx FIFO at the slave side every time before calling spiTransfer when the slave has to read the data from the master.
    This has been working okay for now(not sure if any issues will pop-up later on).
    The other thing to note was that, I am getting the additional 0 byte appended for every transaction, even when I am doing the transfers within a short duration, say <20ms. This is the case when I am using the cc26x2r1 launchpad as a slave and another device (not TI) as the master.

    But when I try out test projects with cc26x2r as the slave and another cc26x2r as the master, then I am observing that the 0 does not get appended when the trransfers are within a small duration. i.e. say, the first transfer is at 't' and the second transfer is within the next say, 20ms or so.

    Regards,
    Shyam
  • I have a hunch on the problem here. In your board file SPI config struct you probably have something like this:

        {
            .baseAddr           = SSI0_BASE,
            .intNum             = INT_SSI0_COMB,
            .intPriority        = ~0,
            .swiPriority        = 0,
            .powerMngrId        = PowerCC26XX_PERIPH_SSI0,
            .defaultTxBufValue  = 0xFF,
            .rxChannelBitMask   = 1<<UDMA_CHAN_SSI0_RX,
            .txChannelBitMask   = 1<<UDMA_CHAN_SSI0_TX,
            .mosiPin            = CC26X2R1_LAUNCHXL_SPI0_MOSI,
            .misoPin            = CC26X2R1_LAUNCHXL_SPI0_MISO,
            .clkPin             = CC26X2R1_LAUNCHXL_SPI0_CLK,
            .csnPin             = CC26X2R1_LAUNCHXL_SPI0_CSN,
            .minDmaTransferSize = 10
        },

    The .csn field is defined as CC26X2R1_LAUNCHXL_SPI0_CSN, but what is this defined as? If it is PIN_UNASSIGNED, try assigning it to your physical pin which for me is IOID_11.

    /* SPI Board */
    #define CC26X2R1_LAUNCHXL_SPI0_MISO             IOID_8          /* RF1.20 */
    #define CC26X2R1_LAUNCHXL_SPI0_MOSI             IOID_9          /* RF1.18 */
    #define CC26X2R1_LAUNCHXL_SPI0_CLK              IOID_10         /* RF1.16 */
    #define CC26X2R1_LAUNCHXL_SPI0_CSN              PIN_UNASSIGNED

    I saw the extra 0 go away when this change was made. Let me know if this fixes it!

    Thanks,

    Sean