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.

CCS/CC2640R2F: Issue using CC2640R2F as SPI slave without CS

Part Number: CC2640R2F
Other Parts Discussed in Thread: CC2640,

Tool/software: Code Composer Studio

Hi Forum;

I'm using CC2640R2F running TI-RTOS (with Simplelink CC2640R2F SDK 1.50) as an SPI slave connected to an MSP432P4011 (with TI-RTOS with Simplelink SDK 3.30) as the master. I have omitted the chip select line and am using a GPIO instead to signal slave data ready. Whenever the CC2640 wants to initiate data transfer, it pulls the line high which triggers a rising edge interrupt on the MSP432, which then initiates an SPI transaction. The MSP432 is the source of data and CC2640 acts as a communication interface, hence configured them as master and slave respectively. However, in 2 cases, the CC2640 needs to initiate the SPI, so I'm using the GPIO as a signalling line.

My SPI parameters on the CC2640 are as follows - 

    SPI_Params_init(&spiParams_slave);
    spiParams_slave.bitRate = 50000;           //50 kHz SPI CLK
    spiParams_slave.frameFormat = SPI_POL0_PHA1;
    spiParams_slave.mode = SPI_SLAVE;
    spiParams_slave.dataSize = 8;
    spiParams_slave.transferCallbackFxn = SPITransferCompleteFxn;
    spiParams_slave.transferMode = SPI_MODE_CALLBACK;
    spiHandle_slave = SPI_open(Board_SPI1, &spiParams_slave);

The clock is low because transfer speed is not a concern, but I was worried about data integrity without CS line, so I brought down the speed from 1 MHz to 50 kHz.

On the CC2640 slave, the SPI is set up as a callback mode, which enqueues an RTOS event whenever a transaction is complete. The event then copies over the slaveRxbuffer and parses the data and treats it accordingly.

On the master (MSP432) the SPI is configured in Blocking mode as follows - 

    SPI_Params_init(&spiParams_Kernel);
    spiParams_Kernel.bitRate = 50000;             //50 kHz clock
    spiParams_Kernel.frameFormat = SPI_POL0_PHA1;
    spiParams_Kernel.mode = SPI_MASTER;
    spiParams_Kernel.transferMode = SPI_MODE_BLOCKING;
    spiParams_Kernel.dataSize = 8;
    spiParams_Kernel.transferTimeout = 500;
    spi_Kernel = SPI_open(KERNEL_SPI, &spiParams_Kernel);

Now, this interface works very well for the most part - I'm able to transfer complete frames and transactions up to 30-35 bytes in length.

However, if I reset the MSP432 MCU, everything goes haywire. When the MSP432 is reset, the all the GPIOs get toggled and I scoped the MOSI and CLK lines, and they also get toggled as shown in the attached image - 

After this happens, all my subsequent SPI transactions are bit or byte shifted and everything gets messed up. Interestingly, this does NOT happen if I reset the CC2640. Infact, resetting CC2640 after MSP has been reset solves this issue.

My hunch is that since the CC2640 SPI slave is configured without a transfertimeout, it keeps waiting for data from the master and the synchronization gets disturbed. The one clock pulse and MOSI toggle shifts the internal register by 1 bit and loses synchronization. Is this analysis correct?

I was wondering if adding a transferTimeout to the slave SPI configuration will overcome this issue but I had questions regarding this parameter, which I could not get answered from the documentation. How does this work in callback mode? For instance, if I add a timeout of 1 sec in callback mode, does the SPI transaction have to be completed within 1 sec of calling SPI_transfer() function?

Noticed the following line in the SPI.h driver - 

 *  From calling %SPI_transfer() until transfer completion, the SPI_Transaction
 *  structure must stay persistent and must not be altered by application code.
 *  It is also forbidden to modify the content of the SPI_Transaction.txBuffer
 *  during a transaction, even though the physical transfer might not have
 *  started yet. Doing this can result in data corruption. This is especially
 *  important for slave operations where %SPI_transfer() might be called a long
 *  time before the actual data transfer begins.

So my question is - If I implement a transfertimeout = 1 sec, will it time out if the SPI transaction has been initiated by the master but did not complete within 1 sec? If this is the case, all spurious GPIO toggles like the one I captured above will get ignored and the bit shifts, possibly avoided.

OR, will it timeout after 1 sec elapses after SPI_transfer() gets called in the CC2640's execution and no SPI transfer has taken place? In that case, how can one use this effectively when using callback mode when the SPI transaction may be initiated by the master at an unknown time? If this is the case, how can one prevent spurious transactions with GPIO toggles one reset like the one above?

Will appreciate some more insights on this!

-Shreyas Kulkarni

  • Hi Shreyas,

    Assigning an expert to comment.

    Thanks, 
    Elin

  • Hi Shreyas,

    What you are seeing is typical for the "3-wire" mode as the peripheral is left active at all time expecting a clock signal. This means that the CLK glitch will cause one (or more) bits to be "clocked in" from the peripheral point of view. This might not align with what the driver expects, thus the bit-shift you are seeing.

    Adding a transfer timeout might do the trick here, the 1.50 SDK is so old that I can't clearly remember if this is the case. What you could do is to look into the driver and possibly add in a "SSIDisable()" following a complete reception to make sure the SSI module is not powered when not expecting a transaction (it is typically left on after a transaction, you thus would like to force disable it when ever the "Done callback" arrives). This should help guard against this issue.

  • Hello M-W, thanks for the response.

    I actually tried with a transferTimeout = 10,000 and went down to 1000 but it did not work. The data still gets corrupted after receiving the 1st glitch if the master were to somehow reset.

    What happens if a timeout occurs? Does it still call the same callback function? (i.e. How do I tell if a timeout occured in the SPI?)

    About the SSIDisable() approach, my worry is that this system is pretty non-deterministic and it is impossible to tell when the next SPI transfer can take place. So if an SSIDisable() were to be called after the callback is executed, when do I enable the SSI agian? Besides, there is an I2C and another SPI slave connected to the CC2640 which need to be communicated to and I'm guessing that wont happen if SSIDisable() is asserted, right?

    I do have another approach in mind- 

    I'm thinking of dynamically changing the signalling line as an input for both MCUs, and whenever the CC2640 wants to initiate a transaction, it changes the pin config to output, asserts the GPIO to signal to the master, which then initiates the transaction. After the transaction is complete, the CC2640 can change the pin to an intput again.

    If the master (MSP) were to reset somehow, the glitch will trigger and interrupt on the CC2640, which can then call an SPI_cancel() to flush out and re-load the buffer. Not sure if this is an elegant solution, but would love your insights if this can be robust enough.

    Thanks,

    Shreyas

  • Sorry Shreyas, it was Friday, not thinking straight. Transfer timeout would do nothing for you, it only applies to blocking transfers and not for callback transfers. As fir SSIDisable(), this is on a peripheral basis. With that I mean it does not disable both SPI interface, only the one passed as an argument. The SPI_transfer() API always turns SSI on again. As you do not expect data to be received unless you have called this API, there should be no problem disabling it. As for I2C, it has nothing to do with SSI.

    I would recommend just trying to add in a disable on completion as a first approach, it should be sufficient for you guard against clock glitches as you would keep the module disabled in between transfers. Starting to poke around with the pin mux could potentially also be an approach but I think this would be more complicated then it needs to be and you do not want to "override" the driver to much as you could end up getting conflicting settings (as the driver does not know you try to externally override it).

  • Hi M-W; 

    Thanks for the reply. I will try this approach this week and try to post an update soon.

    BTW, where can I find this SSIDisable() command? I could not find it in my project repo, doing a preliminary search. It could be in the installed SDK path though, so I'm not sure.

    Thanks

  • Hi Shreyas,

    The SSIDisable() is part of the SSI DriverLib:

    dev.ti.com/.../group__ssi__api.html

  • Hi M-W,

    I was actually able to implement the GPIO dynamic configuration and that solved my problem (no glitches so far in 3+ hrs with multiple resets)

    I began implementing your suggestion, but realized that since the CC2640 being the SPI slave in Call back mode, if I perform SSI_Disable() after each call back, I do not know when to keep the transmit buffer ready for the next transmission (initiated by the master) which can happen any time.

    Hence thought the runtime config for the GPIO to be used as a signalling line would work and implemented it and it did :-)

    -Shreyas