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.

Can't get I2C working in SimpleBLEPeripheral based project on CC2640 custom board

Other Parts Discussed in Thread: TMP112, CC2640

Hello again everyone,

After solving my previous issue with input capture/timing, I'm back with a question about I2C.

I'm attempting to talk to a TMP112 (temperature sensor) connected via I2C on my custom CC2640 based board. I've exhaustively searched these forums for help or answers, and haven't come up with one.

My project is based on the SimpleBLEPeripheral code base, using TI-RTOS for SimpleLink Wireless MCU's 2.13.06. For reasons beyond my control, I can't switch versions to a newer build of the toolchain, so that option isn't possible. I've successfully built/programmed the SensorTag project included with this version of code, and its I2C devices work fine, and I can see them talking happily on my oscilloscope.

Again, I've carefully read these forums, and dissected the SensorTag code, and have modelled mine after it. I correctly have my Pins identified, I'm calling PIN_Init() before I do anything else, and am initializing the I2C stuff in the same way the SensorTag does (just in SimpleBLEperipheral_init().) I can successfully call bspI2cInit() and return, however, my code hangs at the call to Semaphore_pend() in I2CCC26XX_transfer() in the I2CCC26XX.c file around line 862. This is a line that many other people have had their code hang at.

The problem is that no clock pulses are ever generated on the I2C bus, and so the hardware interrupt that is supposed to return after Semaphore_pend() never happens.

Here are things I've tried unsuccessfully:

 - adding .intPriority = ~0; to my I2CCC26XX_HWAttrs - this doesn't work because it relies on TI-RTOS 2.14 or greater.

 - followed the TI-RTOS 2.13 user guide, and added TIRTOS.useI2C = 1 to my appBLE.cfg - this fails compilation, and seems incorrect because the SensorTag project doesn't use it

 - tried toggling the SCL pins "manually" by using them as GPIO and toggling them - this works and there is no problem with the hardware, as I had suspected.

Does anyone have ANY pointers as to what steps I should try next? This I2C stuff seems extremely complicated for something that I've had to do many times in the past. I understand TI has to meet a wide variety of needs over a wide number of targets, but I'm really struggling.

Please let me know if anyone needs any clarifications or has suggestions or questions.

Thanks,

-Ben

  • As an update, tonight I was able to trace the code execution a bit further.

    When I had previously thought my code was stopping at the call to Semaphore_pend() around line 862 of I2CCC26XX.c, it in fact was not. Line 862 is exactly this:

    Semaphore_pend(Semaphore_handle(&(object->transferComplete)), BIOS_WAIT_FOREVER);

    I set a breakpoint inside the of I2CCC26XX_hwiFxn() on line 296, and indeed, my code makes it there. However, the call to I2CMasterErr() on line 307 returns an errStatus of 8 instead of the expected value of 0. Unfortunately, I can't debug any further than this because the I2CMasterErr() function leads to addresses inside the ROM that have no source available.

    Can anyone help me guess why this is occurring?

    I'll continue digging through documentation and source, but man, I'm losing hope.

    I really don't want to have to hand roll a bit-banged I2C driver to make my project work. Shouldn't this be easier than I'm making it?

    Thanks again guys,

    -Ben

  • Ben,

    Are you disabling interrupts before you call I2C_transfer()? That semaphore should be posted by the I2C interrupts. Can you place a breakpoint in I2CCC26XX_hwiFxn and see if you are getting any interrupts after calling I2C_transfer()?
  • Good night Benjamin Foote (4319689)

    What I do when I want to successfully add I2C to my project is first start with SimpleBLEPeripheral, not SensorTag, but this is your choice.

    Just add

    #include <ti/drivers/I2C.h>

    at the top of your code. Then, even if ugly, you can also add these two global variables:

    static I2C_Handle SbpI2cHandle;
    static I2C_Params SbpI2cParams;

    Then, I init i2c like this in SimpleBLEPeripheral_init():

    //  INIT i2c
    // --------------------
    
    I2C_Params_init(&SbpI2cParams);
    SbpI2cParams.bitRate = I2C_100kHz;
    SbpI2cParams.transferMode = I2C_MODE_BLOCKING;
    SbpI2cHandle = I2C_open(CC2650_LAUNCHXL_I2C0, &SbpI2cParams);

    Afterwards, I successfully can do i2c by calling my function:

    void do_i2c_things(uint8_t par1, uint8_t par2, uint8_t wc, uint8_t rc)
    {
         I2C_Transaction I2C_Transaction;
         I2C_Transaction.slaveAddress = TARGETDEVICE_I2C_BASEADDRESS;
         I2C_Transaction.writeBuf = txBuf;
         I2C_Transaction.readBuf = rxBuf;
    
         // TASK SLEEP
         Task_sleep(100); // 1 ms = 100
    
         // SEND the command
         txBuf[0] = par1;
         txBuf[1] = par2;
    
         I2C_Transaction.writeCount = wc;
         I2C_Transaction.readCount = rc;
         I2C_transfer(SbpI2cHandle, &I2C_Transaction);
    }

    Mmmmm, there is no mistery here. I have so many posts and I cannot check if this will work for you but I hope it does! :) It works for me.

    Oh, BTW, check if you have proper pull-up R in your I2C pins. I had no need to do anything with semaphores. Please note this is blocking-type i2c :)

    Have a nice day.

  • The interrupt enabling/disabling is handled automatically in I2CCC26XX I thought, no?

    In I2CCC26XXX, the function I2CCC26XX_transfer() seems to disable/enable them as needed.

    See here:

    bool I2CCC26XX_transfer(I2C_Handle handle,
                            I2C_Transaction *transaction)
    {
        bool                     ret = false;
        UInt                     key;
        I2CCC26XX_Object        *object;
        I2CCC26XX_HWAttrs const *hwAttrs;
    
        /* Get the pointer to the object and hwAttrs */
        object = handle->object;
        hwAttrs = handle->hwAttrs;
    
        /* Check if anything needs to be written or read */
        if ((!transaction->writeCount) && (!transaction->readCount)) {
            /* Nothing to write or read */
            return (ret);
        }
    
        if (object->transferMode == I2C_MODE_CALLBACK) {
            /* Check if a transfer is in progress */
            key = Hwi_disable();
            if (object->headPtr) {
                /* Transfer in progress */
    
                /*
                 * Update the message pointed by the tailPtr to point to the next
                 * message in the queue
                 */
                object->tailPtr->nextPtr = transaction;
    
                /* Update the tailPtr to point to the last message */
                object->tailPtr = transaction;
    
                /* I2C is still being used */
                Hwi_restore(key);
                return (true);
            }
            else {
                /* Store the headPtr indicating I2C is in use */
                object->headPtr = transaction;
                object->tailPtr = transaction;
            }
            Hwi_restore(key);
        }
    
        /* Set standby disallow constraint. */
        threadSafeStdbyDisSet();
    
        /* Acquire the lock for this particular I2C handle */
        Semaphore_pend(Semaphore_handle(&(object->mutex)), BIOS_WAIT_FOREVER);
    
        /*
         * I2CCC26XX_primeTransfer is a longer process and
         * protection is needed from the I2C interrupt
         */
    
        Hwi_disableInterrupt(hwAttrs->intNum);
        I2CCC26XX_primeTransfer(handle, transaction);
        Hwi_enableInterrupt(hwAttrs->intNum);
    
            if (object->transferMode == I2C_MODE_BLOCKING) {
                Log_print1(Diags_USER1,
                        "I2C:(%p) Pending on transferComplete semaphore",
                        hwAttrs->baseAddr);
                /*
                 * Wait for the transfer to complete here.
                 * It's OK to block from here because the I2C's Hwi will unblock
                 * upon errors
                 */
                Semaphore_pend(Semaphore_handle(&(object->transferComplete)), BIOS_WAIT_FOREVER);
    
                /* Release standby disallow constraint. */
                threadSafeStdbyDisRelease();
    
                Log_print1(Diags_USER1,
                        "I2C:(%p) Transaction completed",
                        hwAttrs->baseAddr);
    
                /* Hwi handle has posted a 'transferComplete' check for Errors */
                if (object->mode == I2CCC26XX_IDLE_MODE) {
                    Log_print1(Diags_USER1,
                            "I2C:(%p) Transfer OK",
                            hwAttrs->baseAddr);
                    ret = true;
                }
            }
            else {
                /* Always return true if in Asynchronous mode */
                ret = true;
            }
    
        /* Release the lock for this particular I2C handle */
        Semaphore_post(Semaphore_handle(&(object->mutex)));
    
        /* Return status */
        return (ret);
    }

    I've distilled my code in simpleBLEPeripheral.c down to the most minimal example I could come up with, and it still gets stuck in the same place. In SimpleBLEPeripheral_init() in simpleBLEPeripheral.c, right after the call to ICall_registerApp(&selfEntity, &sem), I'm trying to do the following:

    I2C_init();
    
    	// PIN Open
    	I2C_Handle i2c;
    	UInt peripheralNum = 0; /* Such as I2C0 */
    
    	I2C_Params i2cParams;
    	I2C_Params_init(&i2cParams);
    
    	i2cParams.transferMode = I2C_MODE_BLOCKING;
    	i2cParams.transferCallbackFxn = NULL;
    
    	i2c = I2C_open(peripheralNum, &i2cParams);
    
    	if (i2c == NULL)
    	{
    	    /* Error opening I2C */
    	}
    
    	// Write to I2C
    	I2C_Transaction i2cTransaction;
    	UChar writeBuffer[3];
    	UChar readBuffer[2];
    	Bool transferOK;
    
    	i2cTransaction.slaveAddress = 0x48; /* 7-bit peripheral slave address */
    	i2cTransaction.writeBuf = writeBuffer; /* Buffer to be written */
    	i2cTransaction.writeCount = 3; /* Number of bytes to be written */
    	i2cTransaction.readBuf = NULL; /* Buffer to be read */
    	i2cTransaction.readCount = 0; /* Number of bytes to be read */
    
    	transferOK = I2C_transfer(i2c, &i2cTransaction); /* Perform I2C transfer */
    
    	if (!transferOK)
    	{
    	    /* I2C bus fault */
    	}

    My code, again, gets stuck at transferOK = I2C_transfer(i2c, &i2cTransaction), just as it did in my more complicated setup. Inside the transfer function, the call to I2CMasterErr returns 8 instead of 0. I couldn't find any documentation after looking further at this function. It doesn't seem to be mentioned in the TI-RTOS user guide, or the SimpleLink user guid.

    To my eyes, the structure of the I2C stack is this: SensorTmp007.h/c --> sensor.h/c --> bspI2C.h/c --> I2CCC26XX.h/c --> I2C.h/c. Seems a tad complicated.

    And yes, I've got the correct pullups on my SDA/SCL lines.

    If anyone has experience this before, or can provide me with a minimal example or code, I'd love to try it out and report back.

    Thanks again for the help guys,

    -Ben

  • Ok,

    So I've got the code down to the point that it passes the call to I2CMasterErr without returning an error. It returns 0 now, but after the function I2C_transfer finishes, the code proceeds back into "binary blob" territory and eventually gets stuck. I've single stepped (F6) the code, and it just jumps back and forth between two addresses eventually after exiting I2C_transfer().

    The addresses are it gets stuck at are:

    0x1001BBD6
    0x1001BBD8

    Thanks again,

    -Ben

  • Ok guys,

    I can't believe this, but I had my oscilloscope connected to the wrong side of of the pullup on the SCLK line.

    My data is getting out of the CC2640 to my I2C device, so I'll continue debugging from here.

    Thanks for the suggestions...and sorry for wasting anyone's time.

    -Ben