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.

TM4C1294NCZAD: I2C I2CMasterTimeoutSet not working

Part Number: TM4C1294NCZAD

Hello TI,

I'm trying to get the MasterTimeoutSet function working on the I2C Modules, but I'm having no luck.

No matter how I try to set it, the I2C_MCLKOCNT register is remaining at 0x00000000

I've tried using driverlib - I2CMasterTimeoutSet(I2C0_BASE, 0x7D);

and I've tried doing direct register manipulation - HWREG(I2C0_BASE + I2C_O_MCLKOCNT) = 0x7D;

And in both cases, the I2C_MCLKOCNT register doesn't change.  Am I doing something horribly wrong here?

I've wrapped the write in a while loop to confirm, and the while loop never exits.

SysCtlPeripheralDisable(SYSCTL_PERIPH_I2C0);
SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0));
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
GPIOPinConfigure(GPIO_PB2_I2C0SCL);
GPIOPinConfigure(GPIO_PB3_I2C0SDA);
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
I2CMasterInitExpClk(I2C0_BASE, SysFreq, false); //False = 100kbs, True = 400kbs
while ((HWREG(I2C0_BASE + I2C_O_MCLKOCNT) & 0xFF) != 0x7D) {
    HWREG(I2C0_BASE + I2C_O_MCLKOCNT) = 0x7D;
}
I2CMasterGlitchFilterConfigSet(I2C0_BASE, I2C_MASTER_GLITCH_FILTER_16);

Can you tell me how to get this register to actually set?

Thank you,
Ben

  • I should add that I have also confirmed that SCL timeout is not working.  Holding SCL low never causes I2C_MCS_CLKTO to go high.

    I have also attempted to set the Timeout and GlitchFilter before and after the MasterInitExpClk.

  • Hi Ben,

      I'm currently traveling. I will take a closer look next week when I come back. How did you force the external slave to pull SCL low?

    18.3.1.6 Clock Low Timeout (CLTO)
    The I2C slave can extend the transaction by pulling the clock low periodically to create a slow bit
    transfer rate. The I2C module has a 12-bit programmable counter that is used to track how long the
    clock has been held low. The upper 8 bits of the count value are software programmable through
    the I2C Master Clock Low Timeout Count (I2CMCLKOCNT) register. The lower four bits are not
    user visible and are 0x0. The CNTL value programmed in the I2CMCLKOCNT register has to be
    greater than 0x01. The application can program the eight most significant bits of the counter to
    reflect the acceptable cumulative low period in transaction. The count is loaded at the START
    condition and counts down on each falling edge of the internal bus clock of the Master. Note that
    the internal bus clock generated for this counter keeps running at the programmed I2C speed even
    if SCL is held low on the bus. Upon reaching terminal count, the master state machine forces ABORT
    on the bus by issuing a STOP condition at the instance of SCL and SDA release.
    As an example, if an I2C module was operating at 100 kHz speed, programming the I2CMCLKOCNT
    register to 0xDA would translate to the value 0xDA0 since the lower four bits are set to 0x0. This
    would translate to a decimal value of 3488 clocks or a cumulative clock low period of 34.88 ms at
    100 kHz

  • Charles,

    Appreciate it.  We have a board with a simple PIC microcontroller on it acting as an I2C Slave.  It can hold SCL low for clock stretching, and we use this to test with.  No matter how long we stretch, the TM4C1294 never times out, and in fact will return good data.  Even seconds of clock stretching don't trigger a timeout.

  • Hi Benjamin,

      It does seem that the register will return a zero after the register is written. I find this post with the same reporting. 

    https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/682698/tm4c129encpdt-i2cmclkocnt---default-value-does-not-meet-documented-requirements

      Can you enable Clock Timeout Interrupt?

      Can you observe the CLKRIS bit in the I2CMRIS register if ever set?

      When you stretch the SCL, is it during master writing or master reading?  The reason I ask is because it makes sense for the slave to stretch clock to wait the master if the master is writing to the slave. 

  • Charles,

    We have tried stretching the clock on both reads and writes, and the timeout does not occur in either case 

    While we're not using interrupts, I have confirmed that neither CLKTO or CLKRIS ever sets.

    Now, I have found a new piece of information.  It seems that if we write the timeout register (MCLKOCNT) AFTER the first I2C transaction of each I2C peripheral, then it works correctly for subsequent transactions.  Just found this in a very accidental way.

    That mostly solves our problem, unless of course there's a timeout on the very first transaction - which isn't impossible..  so it's not a perfect solution.

    I'm going to dig deeper later today and see if there's a particular instruction that executes where the clock timeout can then be written - right now, all I know for sure is that if one full transaction (a single byte read in our case) has occurred, we can then set the clock timeout and it works as expected from that point forward.

  • Charles,

    After a little more investigation, I've found the earliest point at which the I2CMasterTimeoutSet will actually set.  Our first transaction on each I2C Bus is a read, and here's what I've come up with.

        //The Slave Address for Write Mode
        I2CMasterSlaveAddrSet(I2CBase, slave_addr, false);
    
        //Specify the register to read
        I2CMasterDataPut(I2CBase, reg);
    
        //Start Transaction
        I2CMasterControl(I2CBase, I2C_MASTER_CMD_BURST_SEND_START);
    
        //Delay because TM4C I2C is broken
        SysCtlDelay(1000);
    
        //Wait for Master to finish
        while (I2CMasterBusy(I2CBase)) { }
    
        //Check for errors
        if (I2CMasterErr(I2CBase)) {
            I2CMasterControl(I2CBase, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP);
            I2C_Log_Error(I2CBase, false);
            return 0x00;
        }
    
        //Now switch to a read
        I2CMasterSlaveAddrSet(I2CBase, slave_addr, true);
    
        //Set a reasonable timeout
        HWREG(I2CBase + I2C_O_MCLKOCNT) = I2C_TIMEOUT;
    
        //Begin first read
        I2CMasterControl(I2CBase, I2C_MASTER_CMD_SINGLE_RECEIVE);
        
        //Delay because TM4C I2C is broken
        SysCtlDelay(1000);
    
        //Wait for Master to finish
        //Return Error if Timeout
        while (I2CMasterBusy(I2CBase)) {
            if (I2CMasterTimeout(I2CBase) == true) {
                I2CMasterControl(I2CBase, I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP);
                *I2CErr = 6;
                I2C_Log_Error(I2CBase, false);
                return 0x00;
            }
        }
    
        //Check for errors
        if (I2CMasterErr(I2CBase)) {
            I2CMasterControl(I2CBase, I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP);
            *I2CErr = 7;
            I2C_Log_Error(I2CBase, false);
            return 0x00;
        }
    
        //Get Data from first byte
        return I2CMasterDataGet(I2CBase);

    If we set it right there during the first transaction, then it sticks and functions properly forever more.

    Thoughts?  I'm tempted to wrap the first transaction in it's own software timeout, and then let the hardware timeout do it's thing from there.

  • Hi Ben,

      According to the datasheet, the timeout counter is supposed to start at the beginning of the START condition. This means that the counter should start to down count when you start the first transaction at line 8. 

    The application can program the eight most significant bits of the counter to
    reflect the acceptable cumulative low period in transaction. The count is loaded at the START
    condition and counts down on each falling edge of the internal bus clock of the Master. Note that
    the internal bus clock generated for this counter keeps running at the programmed I2C speed even
    if SCL is held low on the bus. Upon reaching terminal count, the master state machine forces ABORT
    on the bus by issuing a STOP condition at the instance of SCL and SDA release.
    As an example, if an I2C module was operating at 100 kHz speed, programming the I2CMCLKOCNT
    register to 0xDA would translate to the value 0xDA0 since the lower four bits are set to 0x0. This
    would translate to a decimal value of 3488 clocks or a cumulative clock low period of 34.88 ms at
    100 kHz.
    The CLKRIS bit in the I2C Master Raw Interrupt Status (I2CMRIS) register is set when the clock
    timeout period is reached, allowing the master to start corrective action to resolve the remote slave
    state. In addition, the CLKTO bit in the I2C Master Control/Status (I2CMCS) register is set; this bit
    is cleared when a STOP condition is sent or during the I2C master reset. The status of the raw SDA
    and SCL signals are readable by software through the SDA and SCL bits in the I2C Master Bus
    Monitor (I2CMBMON) register to help determine the state of the remote slave.

    On a side note, can you at everywhere you have the below line, add a additional while (!I2CMasterBusy(I2CBase)) { }

    Replace:

    while (I2CMasterBusy(I2CBase)) { }

    With:

    while (!I2CMasterBusy(I2CBase)) { } // Add this line

    while (I2CMasterBusy(I2CBase)) { } // Still keep this line

    Perhaps you have not run into a issue raised in this post but adding the recommended workaround will avoid a racing issue. Please refer to this post answered by Ralph. This is independent of your SCL timeout question. 

    https://e2e.ti.com/support/power-management-group/power-management/f/power-management-forum/844446/tpsm846c23evm-806-why-ic2-needs-certain-delay-in-tpsm846c23evm-806-for-every-data-byte-transmission

  • Charles,

    We originally had the while (!I2CMasterBusy(I2CBase)) { } in there, but because we're running an RTOS, it caused problems.

    If the tick happened to occur between the I2CMasterControl(...) and the while (!I2CMasterBusy(...) { }, it would end up stuck there.

    Replacing the while (!MasterBusy) with a static delay solved that issue..

    I2CMasterControl(I2CBase, I2C_MASTER_CMD_SINGLE_RECEIVE);
    
    //Delay because TM4C I2C is broken
    SysCtlDelay(1000);
    
    //Wait for Master to finish
    while (I2CMasterBusy(I2CBase)) { }

    And as for the timeout, yes, I understand how it's supposed to work.  It just doesn't if it's set during I2C Initialization.

    I've solved the issue well enough and I'm moving on.  I've wrapped the very first I2C Transaction on each I2C peripheral in a software timeout, and from that point forward, the hardware timeout works as expected.