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.

CC2640: How to send 10 clock pulses on SCL line using TI's I2C.h I2C implementation

Part Number: CC2640
Other Parts Discussed in Thread: SYSBIOS

I have a ProjectZero based project that I have implemented an I2C bus using TI's I2C.h functions.

I have had it working well for years now but it has come time to create a function that tries to deal with a slave device which is holding the SDA line low without doing a full reset of all devices on the bus.

I believe that by sending 10 clock pulses on the I2C clock line at the same frequency that the I2C bus is set to should get 'most' slaves that might hold the SDA line low to release the SDA line.

I do not see a function in the I2C.h implementation that could be used to do these 10 clock cycles.

It looks like I might have to redefine the clock line as a GPIO and do these cycles myself.

I use IOID_3 as the SCL line using: #define Board_I2C0_SCL0             IOID_3

In my boards .c file I find this code that seems to further setup the SDA and SCL lines:

/* I2C configuration structure, describing which pins are to be used */
const I2CCC26XX_HWAttrsV1 i2cCC26xxHWAttrs[CC2650_LAUNCHXL_I2CCOUNT] = {
    {
        .baseAddr = I2C0_BASE,
        .powerMngrId = PowerCC26XX_PERIPH_I2C0,
        .intNum = INT_I2C_IRQ,
        .intPriority = ~0,
        .swiPriority = 0,
        .sdaPin = Board_I2C0_SDA0,
        .sclPin = Board_I2C0_SCL0,
    }
};

How do I redefine my IOID_3 on the fly as a GPIO so I can do my cycles?

After I do my cycles, how do I redefine my IOID_3 on the fly so that the I2C.h functions can use them immediately again?

Thanks,

Dale

  • Hi Dale,

    Here are some suggestions from a prior E2E thread concerning recovering the I2C lines.  You can initialize the GPIO pin configuration as an output (PIN_GPIO_OUTPUT_EN) so that you may be able to directly control it after using I2C_close to close the I2C instance.  After toggling, you can call I2C_open to re-open the I2C bus for use.

    Regards,
    Ryan

  • Hi Ryan,

    Are you saying that I can use I2C_close and then I2C_open after I send my 10 clock pulses and all the I2C devices would still be initialized and calibrated etc the way they were before my I2C_close (I suppose there is no reason why they wouldn't still be since there would be no power cycling on the I2C power line)?

    If that is the case then I think I can take it from here Slight smile

    Dale

  • I2C_init handles all of the I2C initialization and is called once, therefore you can freely use I2C_open and I2C_close to constantly open/close the I2C instance as you see fit.

    Regards,
    Ryan

  • I have created code that seems to achieve making my 10 SCL cycles at 400khz. I have tested the code by repetitively sending out the 10 cycle burst every two seconds with no other traffic on the I2C bus.

    Most of the time the timing of the 10 cycles is excellent as you see here:

    But every few set of bursts, there is a timing delay which I think is caused by the BLE stack but I am not sure, here is what happens (not always stretched at the same point in the burst):

    Here is the ugly code I have written:

    void letgoofthefingI2Cdataline(void)
    {
       if (I2Chandle != 0) {
           I2C_close(I2Chandle);
           I2Chandle = 0;
       }
       PIN_State  scl;
       PIN_Status status = PIN_add(&scl, Board_I2C0_SCL0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX);
       if (status != PIN_SUCCESS) {
           // Handling allocation error is especially important with PIN_add()
       }
       for (int z=0;z<10;z++){ //400khz I2C bus is 25 microsec per cycle
           PIN_setOutputValue(&scl, Board_I2C0_SCL0, 0);
           _delay_cycles (555); //
           PIN_setOutputValue(&scl, Board_I2C0_SCL0, 1);
           _delay_cycles (555); //
       }
       PIN_Status removestatus = PIN_remove(&scl, Board_I2C0_SCL0);
       if (removestatus != PIN_SUCCESS) {
           // Handling error
       }
       I2Chandle = I2C_open(Board_I2C0, &I2Cparams);
    }

    A few questions:

    1. Have I used PIN_add and PIN_remove correctly?
    2. In the documentation here, it says the error handling needs to be addressed but does not give a clue as to how the errors need to be handled, can you suggest a way for my situation?
    3. How do I fix the timing error I noted above on some groups of bursts?

    Thanks,

    Dale

    1. This appears to be the correct runtime handling of GPIOs handled with the PIN TI Driver.
    2. Pin errors include PIN_ALREADY_ALLOCATED, PIN_NO_ACCESS, and PIN_UNSUPPORTED, you can choose how to handle each.  So long as you are making sure to adequately switch between I2C and pin functionality, I would not expect an error to occur.
    3. You are most likely observing a hardware interrupt.  Disabling hardware interrupts is useful during a critical section of processing, but practice caution as Hwi threads are the highest priority since they are used to perform time critical tasks that are subject to hard deadlines.  You can review the TI-RTOS Kernel User's Guide and consider using Hwi_disable/Hwi_restore (see the ti.sysbios.family.arm.m3.Hwi module from the TI-RTOS Kernel Runtime APIs).

    Regards,
    Ryan