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.

I2c code TM4c123gh6pm

Genius 3300 points
Other Parts Discussed in Thread: BQ2040

1. I have to write I2c master code, which sequence-> Start->Address->Tx_1st_byte->Tx_2nd_byte->restart->Rx_1st_byte->Rx_2nd_byte_stop.

using Tm4c123gxl lauchpad & driverlib 2.1.1.71.

2. Have written below code for it. Haven't checked the code, since I don't have another slave or TM4C. Is the below sequence correct?

3. Line37: I have SDA OD separtely as mentioned in datsheet & left SCL as in default.

Line 77: have to send restart condition. How to do that here or API automatically do it?

What if I have to send NACK in receive? (never have such condition, but just asking is there any need for it. Suppose incorrect data appears)

  

    uint32_t ready_count;
    
/* enable SSI0 module */    
    SysCtlPeripheralDisable(SYSCTL_PERIPH_I2C0);
    SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    
    ready_count = 2000U;
    while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0)))  &&  (--ready_count));
    if(0U == ready_count)   /* if periph not ready take action */
    {
    }        
    
/* enable scl */
    SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
    ready_count = 2000U;
    while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
    if(0U == ready_count)   /* if periph not ready take action */
    {
    }      
    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinTypeI2C(GPIO_PORTB_AHB_BASE , GPIO_PIN_2);
    
/* enable sda */
    SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);    
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    
    ready_count = 2000U;
    while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
    if(0U == ready_count)   /* if periph not ready take action */
    {
    }      
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    GPIOPinTypeI2C(GPIO_PORTB_AHB_BASE , GPIO_PIN_3);    
    HWREG(GPIO_PORTB_AHB_BASE + GPIO_O_ODR) = HWREG(GPIO_PORTB_AHB_BASE + GPIO_O_ODR) | GPIO_PIN_3;  /* make pin open drain */
    
/* disable i2c first */ 
    I2CMasterDisable(I2C0_BASE);
    I2CMasterIntDisable(I2C0_BASE);
    I2CMasterIntClear(I2C0_BASE);

/* i2c clock set */
    I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);   /* 100Khz */
    I2CMasterTimeoutSet(I2C0_BASE , 0x7dU);                    /* 20ms timeout */
    I2CMasterSlaveAddrSet(I2C0_BASE , 0x80U >> 1U, false);     /* 7 bit address, transmit */
    
/* firt byte to send */ 
    I2CMasterDataPut(I2C0_BASE , 0x01U);                        
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
    while(I2CMasterBusy(I2C0_BASE))
    {}

    uint32_t err;
    uint32_t temp[2];
    err = I2CMasterErr(I2C0_BASE);
    if((I2C_MASTER_ERR_ADDR_ACK == err) || (I2C_MASTER_ERR_DATA_ACK == err) )
    {
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP);
        while(1);
    }

/* second byte send & a retart condition */   
    I2CMasterDataPut(I2C0_BASE , 0x02U);                        
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
    while(I2CMasterBusy(I2C0_BASE))
    {}

    err = I2CMasterErr(I2C0_BASE);
    if((I2C_MASTER_ERR_ADDR_ACK == err) || (I2C_MASTER_ERR_DATA_ACK == err) )
    {
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP);
        while(1);
    }
    
/* first byte rx & send a ack */                          
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
    temp[0] = I2CMasterDataGet(I2C0_BASE);
    while(I2CMasterBusy(I2C0_BASE))
    {}

    err = I2CMasterErr(I2C0_BASE);
    if((I2C_MASTER_ERR_ADDR_ACK == err) || (I2C_MASTER_ERR_DATA_ACK == err) )
    {
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP);
        while(1);
    }
    
    
/* second byte rx & send a stop */                          
    I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
    temp[1] = I2CMasterDataGet(I2C0_BASE);
    while(I2CMasterBusy(I2C0_BASE))
    {}

    err = I2CMasterErr(I2C0_BASE);
    if((I2C_MASTER_ERR_ADDR_ACK == err) || (I2C_MASTER_ERR_DATA_ACK == err) )
    {
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_ERROR_STOP);
        while(1);
    }   

  • Hello VT,

    Though the Application Note was written for TM4C129 but the command structure should remain the same for TM4C123 and help answer your questions.

    www.ti.com/.../spma073.pdf

    Regards
    Amit
  • And poster needs to attend to the (detail) "I2CSCL" - which escapes his code...
  • Hi Amit,

    I have three caes for which to use I2c as below: (-> send from TM4C, <- received by TM4C) 

    1. start->Address(W)<-Ack->Command<-Ack->Repeated Start->Address(R)<-Ack<-Data->Ack<-Data->Nack->Stop

    2. start->Address(W)<-Ack->Command<-Ack->Data<-Ack->Data<-Ack->Stop

    3. start->Address(R)<-Ack<-Data->Ack<-Data->Nack->Stop

    So for case 1 I have written code below. Took reference from example code provided, but I have some queries on this:

    1. How to handle timeout error in interrupt.

    2. In interrupt, switch case: I2C_OP_IDLE ; will it send only address or it will send both address & first byte?

    3. Is the code below is ok.  (Right now I don't have slave device, I have ordered it will receive it in 3-4 days, then I will check on CRO)

    volatile uint8_t  g_CurrState;
    volatile uint8_t  g_PrevState;
    volatile uint8_t  g_I2CDirection;
    volatile uint8_t  g_I2CRepeatedStart;
    volatile uint8_t  g_ui8MasterTxData[1];
    volatile uint8_t  g_ui8MasterRxData[2];
    volatile uint8_t  g_ui8MasterBytes  	 = 0;
    volatile uint8_t  g_ui8MasterBytesLength = 1;
    
    
    enum I2C_MASTER_STATE
    {
    	I2C_OP_IDLE = 0,
    	I2C_OP_TXADDR,
    	I2C_OP_TXDATA,
    	I2C_OP_RXDATA,
    	I2C_OP_STOP,
    	I2C_ERR_STATE
    };
    
    
    void i2c_isr(void)
    {
        uint32_t I2CMasterInterruptStatus; 
    
        I2CMasterInterruptStatus = I2CMasterIntStatusEx(I2C0_BASE, true);
        I2CMasterIntClearEx(I2C0_BASE, I2CMasterInterruptStatus);
        
        switch (g_CurrState) 
        {
            case I2C_OP_IDLE:
                g_PrevState = g_CurrState;
                g_CurrState = I2C_OP_TXADDR;
                
                I2CMasterSlaveAddrSet(I2C0_BASE, 0x7d, false);
                I2CMasterDataPut(I2C0_BASE, (0x18U));
                I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
            
            break;
            
            
            case I2C_OP_TXADDR:
                g_PrevState = g_CurrState;
    		
            /* If Address has been NAK'ed then go to stop state */
                if(I2CMasterInterruptStatus & I2C_MASTER_INT_NACK)
                {
                    g_CurrState = I2C_OP_STOP;
                }            
            /* Based on the direction move to the appropriate state of Transmit or Receive */
                else if(!g_I2CDirection)
                {
                    g_CurrState = I2C_OP_TXDATA;
                }
                else
                {
                    g_CurrState = I2C_OP_RXDATA;
                }            
                
                I2CMasterDataPut(I2C0_BASE, (0x00));
                I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
                
            break;
                
            
            case I2C_OP_TXDATA:
                g_PrevState = g_CurrState;
    
            
            /* If Address or Data has been NAK'ed then go to stop state
               If a Stop condition is seen due to number of bytes getting
               done then move to STOP state    
            */
                if(I2CMasterInterruptStatus & I2C_MASTER_INT_NACK)
                {
                    g_CurrState = I2C_OP_STOP;
                }
                else if(I2CMasterInterruptStatus & I2C_MASTER_INT_STOP)       /* query: what is purpose */
                {
                    g_CurrState = I2C_OP_STOP;
                }
                else
                {
                    I2CMasterDataPut(I2C0_BASE, g_ui8MasterTxData[g_ui8MasterBytes++]);
                    if(g_ui8MasterBytes == g_ui8MasterBytesLength)
                    {
                        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
                    }
                    else
                    {
                        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
                    }
    
                }            
              
            break;
                
                
            case I2C_OP_RXDATA:
                g_PrevState = g_CurrState;
    
                /*
                 If Address has been NAK'ed then go to stop state
                 If a Stop condition is seen due to number of bytes getting
                 done then move to STOP state and read the last data byte
                */
                if(I2CMasterInterruptStatus & I2C_MASTER_INT_NACK)
                {
                    g_CurrState = I2C_OP_STOP;
                }
                else if(I2CMasterInterruptStatus & I2C_MASTER_INT_STOP)
                {
                    g_CurrState = I2C_OP_STOP;
                    g_ui8MasterRxData[g_ui8MasterBytes++] = I2CMasterDataGet(0x7d);
                }
                else
                {
                    /*
                     If end then NAK the byte and put Stop. Else continue
                     with ACK of the current byte and receive the next byte
                    */
                    if(g_I2CRepeatedStart)
                    {
                        /*
                         Send the Slave Address with RnW as Receive. If only byte is
                         to be received then send START and STOP else send START
                        */
                        I2CMasterSlaveAddrSet(I2C0_BASE, 0x7d, true);
                        if(g_ui8MasterBytesLength == 1)
                        {
                            I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
                        }
                        else
                        {
                            I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
                        }
    
                        /*
                         Change the Repeated Start Flag to false as the condition
                         is now to receive data
                        */
                        g_I2CRepeatedStart = 0;
                    }
                    else if(g_ui8MasterBytes == (g_ui8MasterBytesLength - 2))
                    {
                        /*
                         Read the byte from I2C Buffer and decrement the number
                         of bytes counter to see if end has been reached or not
                        */
                        g_ui8MasterRxData[g_ui8MasterBytes++] = I2CMasterDataGet(I2C0_BASE);
    
                        /*
                         Put a STOP Condition on the bus
                        */
                        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
                    }
                    else
                    {
                        /*
                         Read the byte from I2C Buffer and decrement the number
                         of bytes counter to see if end has been reached or not
                        */
                        g_ui8MasterRxData[g_ui8MasterBytes++] = I2CMasterDataGet(I2C0_BASE);
    
                        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
                    }
                }
    
                break;
                
                
                
            case I2C_OP_STOP:
                /*
                 Move the current state to the previous state
                 Else continue with the transmission till last byte
                */
                g_PrevState = g_CurrState;
    
                break;
            
            
            case I2C_ERR_STATE:
                g_CurrState = I2C_ERR_STATE;
                break;      
    
            
            default:
                g_CurrState = I2C_ERR_STATE;
                break;            
            
        }    
        
    }    
    
    
    
    void i2c_init(void) 
    {     
        uint32_t ready_count;
        
    /* enable I2C module */    
        SysCtlPeripheralDisable(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        
    /* enable sda */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);
        GPIOPinTypeI2C(GPIO_PORTB_AHB_BASE , GPIO_PIN_3);       
        
    /* enable scl */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinTypeI2CSCL(GPIO_PORTB_AHB_BASE , GPIO_PIN_2);
                  
    /* disable i2c first */ 
        I2CMasterDisable(I2C0_BASE);
        I2CMasterIntDisable(I2C0_BASE);
        I2CMasterIntClear(I2C0_BASE);
    
    /* i2c clock set */
        I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);   /* 100Khz */
        I2CMasterTimeoutSet(I2C0_BASE , 0x7dU);                    /* 20ms timeout */
        I2CIntRegister(I2C0_BASE , i2c_isr);
        I2CMasterIntEnableEx(I2C0_BASE , I2C_MASTER_INT_ARB_LOST | I2C_MASTER_INT_STOP | I2C_MASTER_INT_START
                                       | I2C_MASTER_INT_NACK | I2C_MASTER_INT_TIMEOUT | I2C_MASTER_INT_DATA);
        
        
    /* init device is in idle state */    
        g_CurrState = I2C_OP_IDLE;
        
    /* Check if the Bus is Busy or not */    
        while(I2CMasterBusBusy(I2C0_BASE));
        
        
        g_I2CDirection = 0U;
        g_I2CRepeatedStart = 1U;
        g_ui8MasterBytes = 0U;
        g_ui8MasterBytesLength = 2U;
        
    /* trigger */
        IntTrigger(I2C0_BASE);
        
        while(g_CurrState != I2C_OP_STOP);
    	g_CurrState = I2C_OP_IDLE;
        
        while(1);
          
        
    } 
  • Hello VT,

    To answer the points.

    #1: In the state machine, when a timeout condition occurs, the state machine must be moved to IDLE state and at the same time, the external peripheral power-cycled as it is the external device that is holding the bus.

    #2: It will send address and data

    #3: Not knowing what the slave device is there, it is tough to state anything with 100% confidence. I think only case 3 will happen with the code and not case 1.

    But some other feedback as well

    <a> The following lines need not be done twice
    SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    <b> The App Note was written for TM4C129, so I assume you are using TM4C123 at 16MHz system clock

    Regards
    Amit
  • Hi Amit,

    I am reading device Bq2040, here: www.ti.com/.../bq2040.pdf
    Page 11 has a protocol, in which to read I have to send batterry address,command address , repeated start & then read two bytes back.
    Right now I want to do that only.

    As you had told in #2 that code will send both address & first data byte. So how will I know tat whether slave has nacked the battery address or first data byte as it may not also be correct command?
  • Hello VT,

    You would need to poll the I2CMCS status register to see if the battery address is NAK'ed or data is NAK'ed or none. The ERR bit if set should show that there has been some error in the command.

    Regards
    Amit
  • Hi Amit,

    I think first I should rewrite the code using polling method. will try it & see if get any problem. Then I will go for interrupt method.
  • Hello VT

    Also to note that TM4C123 interrupt status is not as exhaustive as the TM4C129 interrupt status for I2C. So interrupt may not have all the information you need.

    Regards
    Amit
  • I have written below code using polling:

    start->Address(W)<-Ack->Command<-Ack->Repeated Start->Address(R)<-Ack<-Data->Ack<-Data->Nack->Stop

    I have also edited I2CMasterErr in i2c.c to return clock timeout error in case it happens. Code is also below, plz see if its ok.

    Another thing is , once I get error, how to send Stop conidtion in case of  I2C_MASTER_ERR_ADDR_ACK,I2C_MASTER_ERR_DATA_ACK or I2C_MASTER_ERR_ARB_LOST.

    In case of I2C_MASTER_ERR_CLK_TOUT, slave is holding clock low. Should I reinit the i2c in that case also?

    void i2c_init(void) 
    {     
        uint32_t ready_count;
        
    /* enable I2C module */    
        SysCtlPeripheralDisable(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        
    /* enable sda */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);
        GPIOPinTypeI2C(GPIO_PORTB_AHB_BASE , GPIO_PIN_3);       
        
    /* enable scl */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinTypeI2CSCL(GPIO_PORTB_AHB_BASE , GPIO_PIN_2);
                  
    /* disable i2c first */ 
        I2CMasterDisable(I2C0_BASE);
        I2CMasterIntDisable(I2C0_BASE);
        I2CMasterIntClear(I2C0_BASE);
    
    /* i2c clock set */
        I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);   /* 100Khz */
        I2CMasterTimeoutSet(I2C0_BASE , 0x7dU);                    /* 20ms timeout */
        
    /* wait while I2C is busy */
        while(I2CMasterBusBusy(I2C0_BASE));
        
    }
    
    
    void i2c_read_data(void)    
    {    
    /* send start+ address +first byte */
        I2CMasterSlaveAddrSet(I2C0_BASE, 0x0BU, false);
        I2CMasterDataPut(I2C0_BASE, 0x10U);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(I2CMasterBusy(I2C0_BASE));
        
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }    
        
    /* send repeated start+repeated start+  receive first byte */
        I2CMasterSlaveAddrSet(I2C0_BASE, 0x0BU, true);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
        while(I2CMasterBusy(I2C0_BASE));
    
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }   
    
        data[0] = (uint8_t)I2CMasterDataGet(I2C0_BASE);  /* first byte rx */    
        
    /* rx last byte & send stop */    
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
        while(I2CMasterBusy(I2C0_BASE));
    
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }   
    
        data[1] = (uint8_t)I2CMasterDataGet(I2C0_BASE);  /* first byte rx */        
        
        
        if(data[0] | data[1])
        {
        }
    
        
        while(1);      
        
    }    
    
    
    
    
    static void i2c_err_check(uint32_t err) 
    {    
        if(I2C_MASTER_ERR_ADDR_ACK == err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_DATA_ACK == err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_CLK_TOUT == err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_ARB_LOST == err)
        {
            //send stop
        }    
        else
        {
        }    
        
        while(1);
        
    } 
    

    uint32_t
    I2CMasterErr(uint32_t ui32Base)
    {
        uint32_t ui32Err;
    
        //
        // Check the arguments.
        //
        ASSERT(_I2CBaseValid(ui32Base));
    
        //
        // Get the raw error state
        //
        ui32Err = HWREG(ui32Base + I2C_O_MCS);
    
        //
        // If the I2C master is busy, then all the other bit are invalid, and
        // don't have an error to report.
        //
        if(ui32Err & I2C_MCS_BUSY)
        {
            return(I2C_MASTER_ERR_NONE);
        }
    
        //
        // Check for errors.
        //
        if(ui32Err & (I2C_MCS_ERROR | I2C_MCS_ARBLST))
        {
            return(ui32Err & (I2C_MCS_CLKTO | I2C_MCS_ARBLST | I2C_MCS_DATACK | I2C_MCS_ADRACK));  //edited here added I2C_MCS_CLKTO 
        }
        else
        {
            return(I2C_MASTER_ERR_NONE);
        }
    }

  • Hello VT,

    The code looks fine to me.

    In case of CLKTO, ADDR, DATA or ARBLST, the master will put the STOP condition on the bus.

    However if the CLKTO is because of the Slave driving the clock line low, then resetting the master is not sufficient. The Slave device must be power cycled / reset as well.

    Regards
    Amit
  • Hi,

    With the above code & interfacing with bq2040. (start->Address(W)<-Ack->Command<-Ack->Repeated Start->Address(R)<-Ack<-Data->Ack<-Data->Nack->Stop). I have tried it, it gives incorrect result everytime. Most of time it does not go to error state, but sometimes it goes to error state with nack on both address & data after start. However even if communication hapenned without error, I receive random data, however data should be same. (as it is voltage register in bq2040). I tried with different commands also, all giving random results.

    Attached are snapshot of CRO porbe. (Marked red/voilet boxes on snapshot indicating the continuation point.) CRO probes are at 10x. Yellow one is clock & blue is data. Lat image is combined image. Also attach is code/

    void i2c_manager(void) 
    {     
        uint32_t ready_count;
        uint32_t err;
        uint8_t data[2];
        
    /* enable I2C module */    
        SysCtlPeripheralDisable(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        
    /* enable sda */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);
        GPIOPinTypeI2C(GPIO_PORTB_AHB_BASE , GPIO_PIN_3);       
        
    /* enable scl */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinTypeI2CSCL(GPIO_PORTB_AHB_BASE , GPIO_PIN_2);
                  
    /* disable i2c first */ 
        I2CMasterDisable(I2C0_BASE);
        I2CMasterIntDisable(I2C0_BASE);
        I2CMasterIntClear(I2C0_BASE);
    
    /* i2c clock set */
        I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);   /* 100Khz */
        I2CMasterTimeoutSet(I2C0_BASE , 0xffU);                    /* 0x7d = 20ms timeout */
        
    /* wait while I2C is busy */
        while(I2CMasterBusBusy(I2C0_BASE));
        
    /* send start+ address +first byte */
        I2CMasterSlaveAddrSet(I2C0_BASE, 0x0BU, false);
        I2CMasterDataPut(I2C0_BASE, 0x09U);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(I2CMasterBusy(I2C0_BASE));
        
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }    
        
    /* send repeated start+repeated start+  receive first byte */
        I2CMasterSlaveAddrSet(I2C0_BASE, 0x0BU, true);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
        while(I2CMasterBusy(I2C0_BASE));
    
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }   
    
        data[0] = (uint8_t)I2CMasterDataGet(I2C0_BASE);  /* first byte rx */    
        
    /* rx last byte & send stop */    
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
        while(I2CMasterBusy(I2C0_BASE));
    
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }   
    
        data[1] = (uint8_t)I2CMasterDataGet(I2C0_BASE);  /* first byte rx */        
        
        
        if(data[0] | data[1])
        {
        }
    
        
        while(1);      
        
    }    
    
    
    static void i2c_err_check(uint32_t err) 
    {    
        if(I2C_MASTER_ERR_ADDR_ACK & err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_DATA_ACK & err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_CLK_TOUT & err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_ARB_LOST & err)
        {
            //send stop
        }    
        else
        {
        }    
        
        while(1);
        
    }   

  • Hello VT,

    Thanks for the scope snapshot. In the read operation when the address is presented by the master, the SCL clock pulse for the ACK/NAK of the address is missing, which seems to be the cause of the issue. Looking at the low level change at this point it seems that the Slave is driving the line low. Can you check if there is any known issue during read phase?

    Regards
    Amit
  • No there are no issues in reading. Earlier I have used arduino to read this IC, it worked fine never had issues. My code for arduino is below. I will post CRO scrrenshot of it also. Have to resolder the setup again.

    #include <SoftI2CMaster.h>
    
    uint8_t read_battery_cmd(uint8_t cmd, uint16_t *r_value) 
    {   
        uint8_t err = 0U;
        uint8_t high_byte;
        uint8_t low_byte;
    
    /* send start + battery address + write */    
        if(!i2c_start(BAT_ADD | I2C_WRITE))
        {
            err = 1U;  
        }
    
    /* send command code */
        if(0U == err)
        {
            if(!i2c_write(cmd))
            {
                err = 2U;
            }  
        }    
    
    /* send repeated start + battery address + read */  
        if(0U == err)
        {
            if(!i2c_rep_start(BAT_ADD | I2C_READ))
            {
                err = 3U;
            }  
        }           
    
    /* read low & high data byte */
        if(0U == err)
        {
            low_byte = i2c_read(false);
            high_byte = i2c_read(true);
            *r_value = ((uint16_t)high_byte << 8U)  |   (uint16_t)low_byte;
        }             
        else
        {
            *r_value = 0U;
        }
    
    /* send stop */
        i2c_stop();
    
        return err;
    
    } 

  • Hello VT,

    OK. I will wait for the scope snapshot to see what the issue is. Also try to read a fixed register like the Device ID.

    Regards
    Amit
  • Hi Amit,

    Below is screenshot of CRO using arduino. Only difference here is arduino is powered by +5V.  I read 0x09 register which returns 10000mV. I received correctly everytime

    Red rectangle on image1 & 2 shows the continuation point.

    Image 3 is combined.

  • Hello VT

    With the Arduino board, it seems that the ACK/NAK SCL pulse is getting delayed during the read phase. Also what is quite perplexing is the voltage scale for the Channel-1 and 2. Is it a 5V IC?

    Regards
    Amit
  • Hello Amit,

    Arduino is powered by 5V so do pull-up of I2c.
    Bq2040 is powered by led acid battery. It measure batery parameters directly. Earlier I had read I2c regsiter on bq2040 using 3.3V MSP430 board also(I dont have right now).

    edit: lead acid voltage : 10v-14v

  • Hello VT,

    So the obvious can be ruled out and also that I may have made a reading error on the SCL waveform for TM4C. My sincere apologies.

    I would suggest to debug with the following info.

    1. Capture one waveform of Battery voltage over I2C, and make the conversions manually
    2. Then check what value is in the TM4C12x variable to see it matches the data capture on the line

    If you can provide the data, I would try to make some sense out of it. (Do note that I don't have a setup like yours, so can't really debug)

    Regards
    Amit
  • Hi Amit,

    1. I will do that & will post it also.
    2. Also is code for I2c using TM4c is correct. I think problem is in repeated start & after that
  • Hello VT,

    Looking at the waveform for TM4C the code looks fine and so does the transaction. The only thing could be a timing issue that we cannot comprehend without having the plot for an incorrect waveform.

    Regards
    Amit
  • Hi Amit,

    1. Actually it was my mistake. In receive mode, first low byte is received & then high byte is received. This was correct in my arduino code, but in TM4C I have messed up by reversing it. ( AAhhhhhhhhh!!!!!!!! , do u guys also give recognition to stupid of month, if yes, this time its me I guess :) :) )

    2. One strange thing I have noticed, is even after communication is stopped by while(1); loop at the end, MCU keeps on sending three packets at continuously at regular interval. I say data is coming out from MCU, since on I2c bus, slave cannot initiate bus transfer.

    These three packets then keeps on coming at regular interval as below.

    I had tried to capture each waveform. This is last of three waveforms. (but i think all three are same).

  • Hello VT,

    Looking at the last snapshot it seems that an address of 0x08 is being put which is getting a NAK

    Regards
    Amit
  • Hi Amit,

    But code never does that. Infact it waits in while(1) loop & there is no interrupt also for I2c.
  • Hello VT,

    There must be a mechanism in the interrupt handler which is causing the spurious transaction somehow. Put a GPIO toggle in the interrupt handler so that you can see if it is coming from the interrupt handler. Then move the GPIO toggle through the conditions so that you can see which statement is unexpectedly executed.

    Regards
    Amit
  • Hi Amit,

    There is no interrupt used. I am using polled method only. No i2c interrupt is used.

    void i2c_configure(void) 
    {     
        uint32_t ready_count;
        uint32_t err;
        uint8_t data[2];
        
    /* enable I2C module */    
        SysCtlPeripheralDisable(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_I2C0)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        
    /* enable sda */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);    
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);
        GPIOPinTypeI2C(GPIO_PORTB_AHB_BASE , GPIO_PIN_3);       
        
    /* enable scl */
        SysCtlGPIOAHBEnable(SYSCTL_PERIPH_GPIOB);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
        
        ready_count = 2000U;
        while( (!(SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOB)))  &&  (--ready_count));
        if(0U == ready_count)   /* if periph not ready take action */
        {
        }      
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinTypeI2CSCL(GPIO_PORTB_AHB_BASE , GPIO_PIN_2);
                  
    /* disable i2c first */ 
        I2CMasterDisable(I2C0_BASE);
        I2CMasterIntDisable(I2C0_BASE);
        I2CMasterIntClear(I2C0_BASE);
    
    /* i2c clock set */
        I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);   /* 100Khz */
        I2CMasterTimeoutSet(I2C0_BASE , 0xffU);                    /* 0x7d = 20ms timeout */
        
    /* wait while I2C is busy */
        while(I2CMasterBusBusy(I2C0_BASE));
        
    /* send start+ address +first byte */
        I2CMasterSlaveAddrSet(I2C0_BASE, 0x0BU, false);
        I2CMasterDataPut(I2C0_BASE, 0x09U);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
        while(I2CMasterBusy(I2C0_BASE));
        
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }    
        
    /* send repeated start+repeated start+  receive first byte */
        I2CMasterSlaveAddrSet(I2C0_BASE, 0x0BU, true);
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
        while(I2CMasterBusy(I2C0_BASE));
    
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }   
    
        data[0] = (uint8_t)I2CMasterDataGet(I2C0_BASE);  /* first byte rx */    
        
    /* rx last byte & send stop */    
        I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
        while(I2CMasterBusy(I2C0_BASE));
    
        err = I2CMasterErr(I2C0_BASE);
        if(I2C_MASTER_ERR_NONE != err)
        {
            i2c_err_check(err);
        }   
    
        data[1] = (uint8_t)I2CMasterDataGet(I2C0_BASE);  /* first byte rx */        
        
        
        val = (data[1] * 256U) + data[0];
    
        
        while(1);      
        
    } /* function ends here */    
    
    
    
    
    
    static void i2c_err_check(uint32_t err) 
    {    
        if(I2C_MASTER_ERR_ADDR_ACK & err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_DATA_ACK & err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_CLK_TOUT & err)
        {
            //send stop
        }    
        else if(I2C_MASTER_ERR_ARB_LOST & err)
        {
            //send stop
        }    
        else
        {
        }    
        
        while(1);
        
    }

  • Hello VT,

    I would add a GPIO toggle in a walking 1 manner. Basically adding a toggle to one of the I2C transaction at any given time to see which is the code that generates such a transfer. A basic walk through of the code does not show anything that can cause such a mistimed I2C transaction.

    Regards
    Amit