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/CC2642R-Q1: Communicating via SPI from CC2642 to CC1101

Part Number: CC2642R-Q1
Other Parts Discussed in Thread: CC1101, CC2642R, CC1120

Tool/software: Code Composer Studio

Hello

We have a CC1101 SubGig radio connected to a CC2642 via SPI.

However the CC1101 does not use "conventional" SPI; for example:

  • during initialisation, pulse nCS with no other activity (ie MOSI, SCLK)
  • every transaction, after asserting nCS, wait for MISO to go low

To allow the CC2642 to communication with the CC1101, we have had to resort to bitbanging the SPI, but this is slow.

Is there a clean mechanism allow the use of the CC2642 SPI hardware via supplied APIs, and yet still control the chip select and monitor MISO?

The best we have come up with so far, is give a dummy nCS to the SPI APIs, and use my own nCS so allowing greater control; and have the MISO line to go a second pin to allow monitoring via normal PIN APIs.

I am sure someone in TI has been asked this question before but I cant find a reference on the E2E forums.

Kind regards

  • We have a project that communicate from CC2642R to CC1120. Since CC1101 and CC1120 uses the same SPI interface the code should be possible to reuse:

    Set up the SPI: 

        /* Setup SPI */
        SPI_Params_init(&spiParams);
        spiParams.bitRate     = 1000000;
        spiParams.frameFormat = SPI_POL0_PHA0;
        spiParams.mode        = SPI_MASTER;
        spiHandle = SPI_open(Board_SPI0, &spiParams);
    
        if (!spiHandle) {
            /* Error opening the SPI driver */
            while(1) {};
        }

    /* Send a command strobe */
    static CC1120_Status sendStrobe(CC1120_CmdStrobes strobe) {
        CC1120_Status   returnStatus    = STATUS_SUCCESS;
        bool            spiStatus       = false;
        SPI_Transaction transaction;
    
        /* Prepare transaction */
        txBuf[0] = strobe;
        transaction.txBuf = txBuf;
        transaction.rxBuf = NULL;
        transaction.count = 1;
    
        /* Perform the transaction */
        assertChipSelect();
        spiStatus = SPI_transfer(spiHandle, &transaction);
    
        if (!spiStatus) {
            /* Transaction failed */
            deassertChipSelect();
            returnStatus = ERROR_TRANSACTION_FAILED;
        }
        else {
    
            /* If the strobe is SRES, SPWD, SWOR or SXOFF
             * we need to wait for MISO to go low again before de-asserting CS*/
            if ((strobe == SRES) ||
                (strobe == SPWD) ||
                (strobe == SWOR) ||
                (strobe == SXOFF)) {
                /* We can check the status of the SPI MISO line using the
                 * GPIO driverlib function.
                 */
                while(GPIO_readDio(CC26X2R1_LAUNCHXL_SPI0_MISO)) {
                    // Busy spin ... sleep for 200 ticks to allow other tasks to run if needed
                    Task_sleep(200);
                }
            deassertChipSelect();
            }
        }
    
        return returnStatus;
    }
    /* Reads the value of a single register */
    static CC1120_Status registerReadWrite(const uint16_t address, bool isRead, uint8_t* data, uint8_t count) {
        CC1120_Status   returnStatus    = STATUS_SUCCESS;
        bool            spiStatus       = false;
        bool            isExtended      = false;
        SPI_Transaction transaction;
    
        /* Is extended? */
        if ((address >> 8) == 0x2F) {
            isExtended = true;
        }
    
        /* Check range */
        if (((address >> 8) > 0x98) || (!isExtended && ((address >> 8) > 0x2F))) {
            returnStatus = ERROR_ADDRESS_OUT_OF_RANGE;
        }
        else {
            /* Prepare transaction */
            uint8_t i = 0;
            if (isExtended) {
                if (isRead)
                    txBuf[i++] = EXTENDED_READ;
                else
                    txBuf[i++] = EXTENDED_WRITE;
    
                txBuf[i++] = (address & 0xFF);
            }
            else {
                if (isRead)
                    txBuf[i++] = NORMAL_READ | (address & 0xFF);
                else
                    txBuf[i++] = NORMAL_WRITE | (address & 0xFF);
            }
    
            /* If dataCount > 1 use burst */
            if (count > 1) {
                txBuf[0] |= BURST_OPERATION;
            }
    
            /* Copy data to txBuffer */
            if (isRead) {
                memset(&txBuf[i], 0x00, count); /* Dummy bytes to read out data */
                i += count;
            }
            else {
                memcpy(&txBuf[i], data, count);
                i += count;
            }
    
            transaction.rxBuf = rxBuf;
            transaction.txBuf = txBuf;
            transaction.count = i;
    
            /* Perform the transaction */
            assertChipSelect();
    
    
            spiStatus = SPI_transfer(spiHandle, &transaction);
            deassertChipSelect();
    
            if (!spiStatus) {
                /* Transaction failed */
                returnStatus = ERROR_TRANSACTION_FAILED;
            }
            else {
                /* If all OK and is a read, copy data to "value" */
                if (isRead) {
                    /* Offset to the valid data depend on the register location */
                    if (isExtended) {
                        i = 2;
                    }
                    else {
                        i = 1;
                    }
    
                    memcpy(data, &rxBuf[i], count);
                }
            }
        }
    
        return returnStatus;
    }

    This is to show one potential way to do this. Since this is not an official example the code quality has not been checked.

  • Hi TER

    Many thanks for the code fragment. It may well work on the system on which it was designed for, but it is ambiguous in terms of who is truly driving the /CS pin to the CC1101 (SPI subsystems or code; or both; it doesnt show the SPI & PIN setup to give an indication to this), and is passing a null pointer to RX/TX buffer a valid option (or does it just RX/TX from flash at the bottom of memory)?

    Kind regards



  • The code snippets I posted contains function calls to  assertChipSelect and deassertChipSelect. These functions are given below:

    /*  ============== Local helper functions  ============== */
    static void assertChipSelect() {
        PIN_setOutputValue(cc1120PinHandle, CC1120_CS, 0);
        /* Wait until ready */
        while(GPIO_readDio(CC26X2R1_LAUNCHXL_SPI0_MISO)) {
            // Busy spin ...
        };
    }
    
    static void deassertChipSelect() {
        PIN_setOutputValue(cc1120PinHandle, CC1120_CS, 1);
    }

    The pin table is given as follows:

    PIN_Config cc1120PinTable[] = {
           /* DIO11: CC1120 CS */
           CC1120_CS | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX,
           /* DIO4: CC1120 reset */
           CC1120_RESET | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX, /* Keep in reset during startup */
           /* DIO23: CC1120 GPIO2 */
           CC1120_GPIO2 | PIN_INPUT_EN  | PIN_PULLUP | PIN_IRQ_NEGEDGE,
           PIN_TERMINATE
       };

    Hope that makes it a bit clearer

  • Hi again

    Update.

    If I tell the SPI system that there is no /CS pin (.csnPin = PIN_UNASSIGNED), and I control it manually; plus I can also monitor MISO; then I assert /CS, wait for MISO to go low, then pass the rest of the SPI operation to the hardware. At the end I then deassert /CS.
    All good, thus far :-)
    Kind regards