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: SPI communication between two CC2640 launchpads

Part Number: CC2640R2F
Other Parts Discussed in Thread: CC2640

Tool/software: Code Composer Studio

Dear TI Support team

Hello,

I have an issue with my project and I hope you can help me out. My project is to send and receive data with the help of SPI between one CC2640 launchpad and one launchpad from C2000 family (Let's say F28379d).

Unfortunately, I am a beginner and I am taking baby steps to learn how to work with microcontrollers. To get to the final goal, I have started from spislave_CC2640R2_LAUNCHXL_tirtos_ccs and spimaster_CC2640R2_LAUNCHXL_tirtos_ccs examples to communicate between two CC2640 microcontrollers and the example is working pretty nice. My next step is to replace the string ("Hello from slave, msg#: ") with two simple numbers (this number should be in the range of adc value, for example, I want to send this pair [3000, 3001]). The first question is how I can modify the code with the least variation?  (Would you please show me how and which part of the code I need to change. I have tried to change it myself but the result is not what I expect, after all, I am a beginner ).

The second question is, for the next step, if I want to communicate between CC2640 and F28379d, is it possible I face a problem because of the difference between the RTOS and Baremetal?

This is the original example code. It is greatly appreciated if you show me how to change it.

%%%%%%%%%%%%%%%%%

SPI MASTER: 

%%%%%%%%%%%%%%%%%

/* Example/Board Header files */
#include "Board.h"

#define THREADSTACKSIZE (1024)

#define SPI_MSG_LENGTH  (30)
#define MASTER_MSG      ("Hello from master, msg#: ")

#define MAX_LOOP        (10)

static Display_Handle display;

unsigned char masterRxBuffer[SPI_MSG_LENGTH];
unsigned char masterTxBuffer[SPI_MSG_LENGTH];

/* Semaphore to block master until slave is ready for transfer */
sem_t masterSem;

/*
 *  ======== slaveReadyFxn ========
 *  Callback function for the GPIO interrupt on Board_SPI_SLAVE_READY.
 */
void slaveReadyFxn(uint_least8_t index)
{
    sem_post(&masterSem);
}

/*
 *  ======== masterThread ========
 *  Master SPI sends a message to slave while simultaneously receiving a
 *  message from the slave.
 */
void *masterThread(void *arg0)
{
    SPI_Handle      masterSpi;
    SPI_Params      spiParams;
    SPI_Transaction transaction;
    uint32_t        i;
    bool            transferOK;
    int32_t         status;

    /*
     * Board_SPI_MASTER_READY & Board_SPI_SLAVE_READY are GPIO pins connected
     * between the master & slave.  These pins are used to synchronize
     * the master & slave applications via a small 'handshake'.  The pins
     * are later used to synchronize transfers & ensure the master will not
     * start a transfer until the slave is ready.  These pins behave
     * differently between spimaster & spislave examples:
     *
     * spimaster example:
     *     * Board_SPI_MASTER_READY is configured as an output pin.  During the
     *       'handshake' this pin is changed from low to high output.  This
     *       notifies the slave the master is ready to run the application.
     *       Afterwards, the pin is used by the master to notify the slave it
     *       has opened Board_SPI_MASTER.  When Board_SPI_MASTER is opened, this
     *       pin will be pulled low.
     *
     *     * Board_SPI_SLAVE_READY is configured as an input pin. During the
     *       'handshake' this pin is read & a high value will indicate the slave
     *       ready to run the application.  Afterwards, a falling edge interrupt
     *       will be configured on this pin.  When the slave is ready to perform
     *       a transfer, it will pull this pin low.
     *
     * Below we set Board_SPI_MASTER_READY & Board_SPI_SLAVE_READY initial
     * conditions for the 'handshake'.
     */
    GPIO_setConfig(Board_SPI_MASTER_READY, GPIO_CFG_OUTPUT | GPIO_CFG_OUT_LOW);
    GPIO_setConfig(Board_SPI_SLAVE_READY, GPIO_CFG_INPUT);

    /*
     * Handshake - Set Board_SPI_MASTER_READY high to indicate master is ready
     * to run.  Wait Board_SPI_SLAVE_READY to be high.
     */
    GPIO_write(Board_SPI_MASTER_READY, 1);
    while (GPIO_read(Board_SPI_SLAVE_READY) == 0) {}

    /* Handshake complete; now configure interrupt on Board_SPI_SLAVE_READY */
    GPIO_setConfig(Board_SPI_SLAVE_READY, GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
    GPIO_setCallback(Board_SPI_SLAVE_READY, slaveReadyFxn);
    GPIO_enableInt(Board_SPI_SLAVE_READY);

    /*
     * Create synchronization semaphore; the master will wait on this semaphore
     * until the slave is ready.
     */
    status = sem_init(&masterSem, 0, 0);
    if (status != 0) {
        Display_printf(display, 0, 0, "Error creating masterSem\n");

        while(1);
    }

    /* Open SPI as master (default) */
    SPI_Params_init(&spiParams);
    spiParams.frameFormat = SPI_POL0_PHA1;
    spiParams.bitRate = 4000000;
    masterSpi = SPI_open(Board_SPI_MASTER, &spiParams);
    if (masterSpi == NULL) {
        Display_printf(display, 0, 0, "Error initializing master SPI\n");
        while (1);
    }
    else {
        Display_printf(display, 0, 0, "Master SPI initialized\n");
    }

    /*
     * Master has opened Board_SPI_MASTER; set Board_SPI_MASTER_READY high to
     * inform the slave.
     */
    GPIO_write(Board_SPI_MASTER_READY, 0);

    /* Copy message to transmit buffer */
    strncpy((char *) masterTxBuffer, MASTER_MSG, SPI_MSG_LENGTH);

    for (i = 0; i < MAX_LOOP; i++) {
        /*
         * Wait until slave is ready for transfer; slave will pull
         * Board_SPI_SLAVE_READY low.
         */
        sem_wait(&masterSem);

        /* Initialize master SPI transaction structure */
        masterTxBuffer[sizeof(MASTER_MSG) - 1] = (i % 10) + '0';
        memset((void *) masterRxBuffer, 0, SPI_MSG_LENGTH);
        transaction.count = SPI_MSG_LENGTH;
        transaction.txBuf = (void *) masterTxBuffer;
        transaction.rxBuf = (void *) masterRxBuffer;

        /* Toggle user LED, indicating a SPI transfer is in progress */
        GPIO_toggle(Board_GPIO_LED1);

        /* Perform SPI transfer */
        transferOK = SPI_transfer(masterSpi, &transaction);
        if (transferOK) {
            Display_printf(display, 0, 0, "Master received: %s", masterRxBuffer);
        }
        else {
            Display_printf(display, 0, 0, "Unsuccessful master SPI transfer");
        }

        /* Sleep for a bit before starting the next SPI transfer  */
        sleep(3);
    }

    SPI_close(masterSpi);

    /* Example complete - set pins to a known state */
    GPIO_disableInt(Board_SPI_SLAVE_READY);
    GPIO_setConfig(Board_SPI_SLAVE_READY, GPIO_CFG_OUTPUT | GPIO_CFG_OUT_LOW);
    GPIO_write(Board_SPI_MASTER_READY, 0);

    Display_printf(display, 0, 0, "\nDone");

    return (NULL);
}

/*
 *  ======== mainThread ========
 */
void *mainThread(void *arg0)
{
    pthread_t           thread0;
    pthread_attr_t      attrs;
    struct sched_param  priParam;
    int                 retc;
    int                 detachState;

    /* Call driver init functions. */
    Display_init();
    GPIO_init();
    SPI_init();

    /* Configure the LED pins */
    GPIO_setConfig(Board_GPIO_LED0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    GPIO_setConfig(Board_GPIO_LED1, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);

    /* Open the display for output */
    display = Display_open(Display_Type_UART, NULL);
    if (display == NULL) {
        /* Failed to open display driver */
        while (1);
    }

    /* Turn on user LED */
    GPIO_write(Board_GPIO_LED0, Board_GPIO_LED_ON);

    Display_printf(display, 0, 0, "Starting the SPI master example");
    Display_printf(display, 0, 0, "This example requires external wires to be "
        "connected to the header pins. Please see the Board.html for details.\n");

    /* Create application threads */
    pthread_attr_init(&attrs);

    detachState = PTHREAD_CREATE_DETACHED;
    /* Set priority and stack size attributes */
    retc = pthread_attr_setdetachstate(&attrs, detachState);
    if (retc != 0) {
        /* pthread_attr_setdetachstate() failed */
        while (1);
    }

    retc |= pthread_attr_setstacksize(&attrs, THREADSTACKSIZE);
    if (retc != 0) {
        /* pthread_attr_setstacksize() failed */
        while (1);
    }

    /* Create master thread */
    priParam.sched_priority = 1;
    pthread_attr_setschedparam(&attrs, &priParam);

    retc = pthread_create(&thread0, &attrs, masterThread, NULL);
    if (retc != 0) {
        /* pthread_create() failed */
        while (1);
    }

    return (NULL);
}

%%%%%%%%%%%%%%%%%

SPI SLAVE: 

%%%%%%%%%%%%%%%%%

#define THREADSTACKSIZE (1024)

#define SPI_MSG_LENGTH  (30)
#define SLAVE_MSG       ("Hello from slave, msg#: ")

#define MAX_LOOP        (10)

static Display_Handle display;

unsigned char slaveRxBuffer[SPI_MSG_LENGTH];
unsigned char slaveTxBuffer[SPI_MSG_LENGTH];

/* Semaphore to block slave until transfer is complete */
sem_t slaveSem;

/*
 *  ======== transferCompleteFxn ========
 *  Callback function for SPI_transfer().
 */
void transferCompleteFxn(SPI_Handle handle, SPI_Transaction *transaction)
{
    sem_post(&slaveSem);
}

/*
 * ======== slaveThread ========
 *  Slave SPI sends a message to master while simultaneously receiving a
 *  message from the master.
 */
void *slaveThread(void *arg0)
{
    SPI_Handle      slaveSpi;
    SPI_Params      spiParams;
    SPI_Transaction transaction;
    uint32_t        i;
    bool            transferOK;
    int32_t         status;

    /*
     * Board_SPI_MASTER_READY & Board_SPI_SLAVE_READY are GPIO pins connected
     * between the master & slave.  These pins are used to synchronize
     * the master & slave applications via a small 'handshake'.  The pins
     * are later used to synchronize transfers & ensure the master will not
     * start a transfer until the slave is ready.  These pins behave
     * differently between spimaster & spislave examples:
     *
     * spislave example:
     *     * Board_SPI_MASTER_READY is configured as an input pin.  During the
     *       'handshake' this pin is read & a high value will indicate the
     *       master is ready to run the application.  Afterwards, the pin is
     *       read to determine if the master has already opened its SPI pins.
     *       The master will pull this pin low when it has opened its SPI.
     *
     *     * Board_SPI_SLAVE_READY is configured as an output pin.  During the
     *       'handshake' this pin is changed from low to high output.  This
     *       notifies the master the slave is ready to run the application.
     *       Afterwards, the pin is used by the slave to notify the master it
     *       is ready for a transfer.  When ready for a transfer, this pin will
     *       be pulled low.
     *
     * Below we set Board_SPI_MASTER_READY & Board_SPI_SLAVE_READY initial
     * conditions for the 'handshake'.
     */
    GPIO_setConfig(Board_SPI_SLAVE_READY, GPIO_CFG_OUTPUT | GPIO_CFG_OUT_LOW);
    GPIO_setConfig(Board_SPI_MASTER_READY, GPIO_CFG_INPUT);

    /*
     * Handshake - Set Board_SPI_SLAVE_READY high to indicate slave is ready
     * to run.  Wait for Board_SPI_MASTER_READY to be high.
     */
    GPIO_write(Board_SPI_SLAVE_READY, 1);
    while (GPIO_read(Board_SPI_MASTER_READY) == 0) {}

    /*
     * Create synchronization semaphore; this semaphore will block the slave
     * until a transfer is complete.  The slave is configured in callback mode
     * to allow us to configure the SPI transfer & then notify the master the
     * slave is ready.  However, we must still wait for the current transfer
     * to be complete before setting up the next.  Thus, we wait on slaveSem;
     * once the transfer is complete the callback function will unblock the
     * slave.
     */
    status = sem_init(&slaveSem, 0, 0);
    if (status != 0) {
        Display_printf(display, 0, 0, "Error creating slaveSem\n");

        while(1);
    }

    /*
     * Wait until master SPI is open.  When the master is configuring SPI pins
     * the clock may toggle from low to high (or high to low depending on
     * polarity).  If using 3-pin SPI & the slave has been opened before the
     * master, clock transitions may cause the slave to shift bits out assuming
     * it is an actual transfer.  We can prevent this behavior by opening the
     * master first & then opening the slave.
     */
    while (GPIO_read(Board_SPI_MASTER_READY)) {}

    /*
     * 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(&spiParams);
    spiParams.frameFormat = SPI_POL0_PHA1;
    spiParams.mode = SPI_SLAVE;
    spiParams.transferCallbackFxn = transferCompleteFxn;
    spiParams.transferMode = SPI_MODE_CALLBACK;
    slaveSpi = SPI_open(Board_SPI_SLAVE, &spiParams);
    if (slaveSpi == NULL) {
        Display_printf(display, 0, 0, "Error initializing slave SPI\n");
        while (1);
    }
    else {
        Display_printf(display, 0, 0, "Slave SPI initialized\n");
    }

    /* Copy message to transmit buffer */
    strncpy((char *) slaveTxBuffer, SLAVE_MSG, SPI_MSG_LENGTH);

    for (i = 0; i < MAX_LOOP; i++) {
        /* Initialize slave SPI transaction structure */
        slaveTxBuffer[sizeof(SLAVE_MSG) - 1] = (i % 10) + '0';
        memset((void *) slaveRxBuffer, 0, SPI_MSG_LENGTH);
        transaction.count = SPI_MSG_LENGTH;
        transaction.txBuf = (void *) slaveTxBuffer;
        transaction.rxBuf = (void *) slaveRxBuffer;

        /* Toggle on user LED, indicating a SPI transfer is in progress */
        GPIO_toggle(Board_GPIO_LED1);

        /*
         * Setup SPI transfer; Board_SPI_SLAVE_READY will be set to notify
         * master the slave is ready.
         */
        transferOK = SPI_transfer(slaveSpi, &transaction);
        if (transferOK) {
            GPIO_write(Board_SPI_SLAVE_READY, 0);

            /* Wait until transfer has completed */
            sem_wait(&slaveSem);

            /*
             * Drive Board_SPI_SLAVE_READY high to indicate slave is not ready
             * for another transfer yet.
             */
            GPIO_write(Board_SPI_SLAVE_READY, 1);


            Display_printf(display, 0, 0, "Slave received: %s", slaveRxBuffer);
        }
        else {
            Display_printf(display, 0, 0, "Unsuccessful slave SPI transfer");
        }
    }

    SPI_close(slaveSpi);

    /* Example complete - set pins to a known state */
    GPIO_setConfig(Board_SPI_MASTER_READY, GPIO_CFG_OUTPUT | GPIO_CFG_OUT_LOW);
    GPIO_write(Board_SPI_SLAVE_READY, 0);

    Display_printf(display, 0, 0, "\nDone");

    return (NULL);
}

/*
 *  ======== mainThread ========
 */
void *mainThread(void *arg0)
{
    pthread_t           thread0;
    pthread_attr_t      attrs;
    struct sched_param  priParam;
    int                 retc;
    int                 detachState;

    /* Call driver init functions. */
    Display_init();
    GPIO_init();
    SPI_init();

    /* Configure the LED pins */
    GPIO_setConfig(Board_GPIO_LED0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
    GPIO_setConfig(Board_GPIO_LED1, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);

    /* Open the display for output */
    display = Display_open(Display_Type_UART, NULL);
    if (display == NULL) {
        /* Failed to open display driver */
        while (1);
    }

    /* Turn on user LED */
    GPIO_write(Board_GPIO_LED0, Board_GPIO_LED_ON);

    Display_printf(display, 0, 0, "Starting the SPI slave example");
    Display_printf(display, 0, 0, "This example requires external wires to be "
        "connected to the header pins. Please see the Board.html for details.\n");

    /* Create application thread */
    pthread_attr_init(&attrs);

    detachState = PTHREAD_CREATE_DETACHED;
    /* Set priority and stack size attributes */
    retc = pthread_attr_setdetachstate(&attrs, detachState);
    if (retc != 0) {
        /* pthread_attr_setdetachstate() failed */
        while (1);
    }

    retc |= pthread_attr_setstacksize(&attrs, THREADSTACKSIZE);
    if (retc != 0) {
        /* pthread_attr_setstacksize() failed */
        while (1);
    }

    /* Create slave thread */
    priParam.sched_priority = 1;
    pthread_attr_setschedparam(&attrs, &priParam);

    retc = pthread_create(&thread0, &attrs, slaveThread, NULL);
    if (retc != 0) {
        /* pthread_create() failed */
        while (1);
    }

    return (NULL);
}

  • masterTxBuffer is a array of char meaning that if you wan to place ADC values into the TX buffer you need to convert the ADC values to char and insert them into the buffer. This is c string manipulation.

    As long as you use the same SPI mode on both sides it doesn't matter if the chip is running bare metal or a OS etc.  

  • Thank you for your answer. Just one more question. When I run the examples, while the microcontroller is connected to USB, it does not start to transfer data. I have to flash it first and then disconnect and reconnect it to start transferring. Is this problem related to the handshake? how can I rectify it? I want to observe transferred values in expression window. That is the reason why i need to keep it connected.

  • For the code to start after you have flashed the device the device has to receive a reset.