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 with restart - bq27510 fuel gauge IC - RM46

Other Parts Discussed in Thread: BQ27510, HALCOGEN

I'm trying to use the I2C module on a LAUNCHXL2 RM46 LaunchPad to talk to the Texas Instruments bq27510 fuel gauge IC.

I should send commands in this order:

Start
Send Write Address (0xAA)
- ACK
Send 1 byte: 0x2e
- ACK
Re-Start
Send Read Address (0xAB)
- ACK
Read byte
- ACK
Read byte
- NACK
Stop

I have issues working with the restart mode.
I've checked TRM, section 30.2.5.3 Using the Repeated START Condition


In HALCoGen, I set 

  • enable Master
  • 7BIT_AMODE
  • enable repeat mode
  • TRANSMITTER
  • 8_BIT
  • Data Count 8 (modified in code to be 1 at send, 2 at receive)

 

#define bq27510_address 0x55
#define Slave_Address  bq27510_address

uint8_t TX_Data_Master[1] = { 0x2E};
uint8_t RX_Data_Master[2] = { 0 };


// ...
	i2cInit();

	i2cSetSlaveAdd(i2cREG1, Slave_Address);
	i2cSetDirection(i2cREG1, I2C_TRANSMITTER);

	i2cSetCount(i2cREG1, 1);
	i2cSetMode(i2cREG1, I2C_MASTER);

	i2cSetStart(i2cREG1);

	i2cSend(i2cREG1, 1, TX_Data_Master);

	while(i2cIsBusBusy(i2cREG1) == true);

	/* Simple delay before starting Next Block */
	/* Depends on how quick the Slave gets ready */
	for(delay=0;delay<1000000;delay++);

	i2cSetSlaveAdd(i2cREG1, Slave_Address);
	i2cSetDirection(i2cREG1, I2C_RECEIVER);

	i2cSetCount(i2cREG1, 2);
	i2cSetMode(i2cREG1, I2C_MASTER);
	i2cSetStop(i2cREG1);

	i2cSetStart(i2cREG1);

	i2cReceive(i2cREG1, 2, RX_Data_Master);

	/* Wait until Bus Busy is cleared */
	while(i2cIsBusBusy(i2cREG1) == true);

	/* Wait until Stop is detected */
	while(i2cIsStopDetected(i2cREG1) == 0);

	/* Clear the Stop condition */
	i2cClearSCD(i2cREG1);

The program doesn't generate the repeated start after sending the 0x2e, and I'm uncertain on how to proceed.

Edit and clarification:

The STOP line on position 5 in this image is what I should get rid off (repeated start scenario):

The image below is the communication scenario that I should achief:

  • Hi Jan,

    I will ask our I2C expert to look into this. In the mean while you can take a look at this thread and see if it helps.

    e2e.ti.com/.../270730

    Regards,
    Sunil
  • Hi Sunil.
    I had read the thread you are referencing. I tried and failed to see how it relates to (or could be embedded into) the 2 RM46 HALCoGen examples for I2C Master.
  • Would you please check if the pull up resistors (~5K) are installed on SDA and SCL pins in your setup? They are required for I2C communication to work correctly. If something does not work, you need to use scope to monitor the SDA and SCL pins to see if the start, stop, ACK, and data are generated correctly.

    Thanks and regards,

    Zhaohong
  • Hi Zhaong.

    Yes, there are pull-ups. The chip is mounted on a TI Fuel Tank boosterpack, and that has pull-ups. The jumper that can be used to enable or disable the pull-ups is in place.

    The signal looks good on the logic analyzer and on the oscilloscope.

    The only issue I have is that an unwanted STOP is generated at step 5. According to TI's instructions for the bq27510, I should do a new start without a stop (repeated start scenario). But I have difficulties configuring/writing code that doesn't generate a STOP before I do the RESTART.
  • More info of my exercise is available at

    I'm writing a blog on integrating the ti fuel gauge chips with Hercules microcontrollers.

  • There may be some misunderstanding between repeated mode and repeated start. After master completes a transmission or reception, a stop condition can be generated by setting the "stp" bit to one. Instead of generating the stop condition, the user can also generate a start condition by setting the "stt" bit to 1.

    First, you need to disable "rm" mode from I2C configuration. Then try the following sequence for the increment read (send one cmd and read two data).

    i2cInit();
    i2cSetMode(i2cREG1, I2C_MASTER);
    i2cSetSlaveAdd(i2cREG1, Slave_Address);
    i2cSetDirection(i2cREG1, I2C_TRANSMITTER);
    i2cSetStart(i2cREG1); // generate the start phase, slave address, and R/W bit
    while(i2cIsBusBusy(i2cREG1) == true); // wait for the transmission to complete
    i2cSend(i2cREG1, 1, TX_Data_Master); //send the cmd
    while(i2cIsBusBusy(i2cREG1) == true); // wait for the transmission to complete
    i2cSetDirection(i2cREG1, I2C_RECEIVER);
    i2cSetStart(i2cREG1); // generate the start phase, slave address, and R/W bit
    while(i2cIsBusBusy(i2cREG1) == true); // wait for the transmission to complete
    i2cReceive(i2cREG1, 2, RX_Data_Master); //read two bytes
    while(i2cIsBusBusy(i2cREG1) == true); // wait for the reception to complete
    i2cSetStop(i2cREG1); // generate the stop phase. both sda and SCL pins will stay high after the transition

    You do not need to add wait for the slave. I2C slave will hold the SCL line low when it is not ready. If the above sequence does not work, you need to put the breakpoint in the sequence and monitor scope to see if the waveform is expected.

    Thanks and regards,

    Zhaohong
  • Thank you. I am testing this.
  • The code didn't have the expected result. I'll have to do some more digging into the TRM.

    One thing I was uncertain of when reading the example above, is that you've put the i2cSetStop after the i2cSetStart. In most examples I've seen, it is coming before i2cSetStart, just after i2cSetCount.

    Note to self: It can't be too hard. I just want to replicate this exact scenario from the TRM with HALCoGen API:

  • I reviewed the TRM and Halcogen source code for one more time. Can you try the following?

    i2cInit();
    i2cSetMode(i2cREG1, I2C_MASTER);
    i2cSetSlaveAdd(i2cREG1, Slave_Address);
    i2cSetDirection(i2cREG1, I2C_TRANSMITTER);
    i2cSetCount(i2cREG1 ,1);
    i2cSetStart(i2cREG1); // generate the start phase, slave address, and R/W bit
    i2cSend(i2cREG1, 1, TX_Data_Master); //send the cmd
    i2cSetDirection(i2cREG1, I2C_RECEIVER);
    i2cSetCount(i2cREG1, 2);
    i2cSetStop(i2cREG1);   //set count and stop bit so that stop can be generated after receiving two bytes.
    i2cSetStart(i2cREG1); // generate the start phase, slave address, and R/W bit
    i2cReceive(i2cREG1, 2, RX_Data_Master); //read two bytes
    while(i2cIsBusBusy(i2cREG1) == true); // wait for the reception to complete

    RM needs to be 0. You can use the scope on the bus to see which portion of the waveform is not generated.

    Thansk and regards,

    Zhaohong

  • Zhaohong, thank you.

    I am going to try out the above code. I will report on the results.

    Regards, Jan
  • I have tried this, with RM=0.

    What happens when I run that code,  is that indeed there is no stop condition, but the send part sends different data than what's expected.
    We should send write address, and then 1 single value: 0x2e. Then we should get an ACK and a new start.
    What happens is that we send a wrong value 0xff, then get an ACK, we send a wrong value 0xff again,  don't get an ACK, and get a new Start.

    I was then expecting it would send the read address, but it sends the write address again after the restart.

  • The first message should be a write but the waveform shows a read. Would you please put a breakpoint at the instruction for the first start and check if the trx bit is set correctly in the MDR register? If the slave read address is different from write, you need to reconfigure the slave address before generating the second start condition.

    Thanks and regards,

    Zhaohong
  • Zhaohong, my scope isn't capable of capturing this info.

    The capture with my logic analyze that I posted in the previous answer is te best I can give.

    My end goal is to get the 100% completely same communication as in TRM for RM46, section 30.2.5.3:

    I have been able to achieve this with a non-ti controller with I2C module, and got this capture on my analyzer:

    This is exactly as what 30.2.5.3 documents.

    As how to do this with Hercules and its I2C module, I'm currently at a loss.

  • The analyzer waveform is fine for debugging. Can you capture the whole sequence (start, slave address, write data, second start, read data, stop) again? What is the difference in your code between your last two posts? There is no data write phase in the first one. Can you post the exact code use for the waveform?

    Thanks and regards,

    Zhaohong
  • Hi,

    The first part of this post is a capture my almost working example - just that one stop condition too much. The second part is the capture of the latest code you have posted in the thread.

    1: working, but with a STOP/START in stead of a restart:

    Code:

    
    

    #define Slave_Address  0x55
    
    uint8_t TX_Data_Master[1] = { 0x2E};
    uint8_t RX_Data_Master[2] = { 0 };
    
    
    
    void bq27510Init() {
    
      int delay =0;
    
      ///////////////////////////////////////////////////////////////
      //        Master Transfer Functionality                      //
      ///////////////////////////////////////////////////////////////
      i2cInit();
    
      /* Configure address of Slave to talk to */
      i2cSetSlaveAdd(i2cREG1, Slave_Address);
    
      /* Set direction to Transmitter */
      /* Note: Optional - It is done in Init */
      i2cSetDirection(i2cREG1, I2C_TRANSMITTER);
    
      /* Configure Data count */
      i2cSetCount(i2cREG1, 1);
    
      /* Set mode as Master */
      i2cSetMode(i2cREG1, I2C_MASTER);
    
      /* Set Stop after programmed Count */
      i2cSetStop(i2cREG1);
    
      /* Transmit Start Condition */
      i2cSetStart(i2cREG1);
    
      /* Tranmit DATA_COUNT number of data in Polling mode */
      i2cSend(i2cREG1, 1, TX_Data_Master);
    
      /* Wait until Bus Busy is cleared */
      while(i2cIsBusBusy(i2cREG1) == true);
    
      /* Wait until Stop is detected */
      while(i2cIsStopDetected(i2cREG1) == 0);
    
      /* Clear the Stop condition */
      i2cClearSCD(i2cREG1);
    
      /* Simple Dealya before starting Next Block */
      /* Depends on how quick the Slave gets ready */
    //  for(delay=0;delay<1000;delay++);
    
    
      ///////////////////////////////////////////////////////////////
      //        Master Receive Functionality                      //
      ///////////////////////////////////////////////////////////////
    
    
      /*****************************************/
      //// Start receving the data From Slave
      /*****************************************/
    
      /* wait until MST bit gets cleared, this takes
       * few cycles after Bus Busy is cleared */
      while(i2cIsMasterReady(i2cREG1) != true);
    
      /* Configure address of Slave to talk to */
      i2cSetSlaveAdd(i2cREG1, Slave_Address);
    
      /* Set direction to receiver */
      i2cSetDirection(i2cREG1, I2C_RECEIVER);
    
      /* Configure Data count */
      /* Note: Optional - It is done in Init, unless user want to change */
      i2cSetCount(i2cREG1, 2);
    
      /* Set mode as Master */
      i2cSetMode(i2cREG1, I2C_MASTER);
    
      /* Set Stop after programmed Count */
      i2cSetStop(i2cREG1);
    
      /* Transmit Start Condition */
      i2cSetStart(i2cREG1);
    
      /* Tranmit DATA_COUNT number of data in Polling mode */
      i2cReceive(i2cREG1, 2, RX_Data_Master);
    
      /* Wait until Bus Busy is cleared */
      while(i2cIsBusBusy(i2cREG1) == true);
    
      /* Wait until Stop is detected */
      while(i2cIsStopDetected(i2cREG1) == 0);
    
      /* Clear the Stop condition */
      i2cClearSCD(i2cREG1);
    
    
      asm(" nop");
      asm(" nop");
      asm(" nop");
    
    
    }
    

    Capture

    Summary: there is a stop at step 5 that I want to not generate. The TI bq27510 IC expects a restart without a stop.

    2: capture of the proposed test:

    Code:

    #define Slave_Address  0x55
    
    uint8_t TX_Data_Master[1] = { 0x2E};
    uint8_t RX_Data_Master[2] = { 0 };
    
    
    
    void bq27510Init() {
    
    	i2cInit();
    	i2cSetMode(i2cREG1, I2C_MASTER);
    	i2cSetSlaveAdd(i2cREG1, Slave_Address);
    	i2cSetDirection(i2cREG1, I2C_TRANSMITTER);
    	i2cSetCount(i2cREG1 ,1);
    	i2cSetStart(i2cREG1); // generate the start phase, slave address, and R/W bit
    	i2cSend(i2cREG1, 1, TX_Data_Master); //send the cmd 
    	i2cSetDirection(i2cREG1, I2C_RECEIVER);
    	i2cSetCount(i2cREG1, 2);
    	i2cSetStop(i2cREG1);   //set count and stop bit so that stop can be generated after receiving two bytes.
    	i2cSetStart(i2cREG1); // generate the start phase, slave address, and R/W bit
    	i2cReceive(i2cREG1, 2, RX_Data_Master); //read two bytes
    	while(i2cIsBusBusy(i2cREG1) == true); // wait for the reception to complete
    
    }

    Capture

    Summary: this example generates a wrong communication. The first address is wrong (line 2: should be 0xaa), It's transmitting two datas (3 - 4 - 5 and 6) before the restart - these two data fields (3 and 5) are not what's in the transfer buffer, and a NACK is detected (6) after the second (unwanted) data. No final end condition generated (after 13).

    I've also attached a CSV export of both captures.

    2388.i2c.zip

  • Would you please confirm the value of the RM bit in both tests? To change the RM value, you need to rerun Halcogen.

    Thanks and regards,

    Zhaohong
  • Hi, the value RM is 'not set', in both cases.

    (extract from i2c.h: (uint32)((uint32)0U << 7U) )

    #define I2C_MDR_CONFIGVALUE       ((uint32)0x00000000U \
                                     | (uint32)((uint32)1U <<11U) \
                                     | (uint32)((uint32)1U <<10U) \
                                     | (uint32)((uint32)I2C_TRANSMITTER) \
                                     | (uint32)((uint32)I2C_7BIT_AMODE) \
                                     | (uint32)((uint32)0U << 7U) \
                                     | (uint32)((uint32)0U) \
                                     | (uint32)((uint32)I2C_8_BIT) \
    								 | (uint32)I2C_RESET_OUT)
    

  • 5684.I2C37_RAM_Poll.cI found a I2C test for accessing an I2C RAM. It is a validation test. Although it does not use Halcogen, I attach it here so that you can see what is required because this test code directly accesses the registers.  The I2C_Read function provides the functionality you are looking for.  In the mean time, I still need to figure out what is missing in the code sequence using Halcogen functions.

  • Thank you for the pointer to the memory example, . I will try to translate that to the register definitions of the RM46, and for the bq27510 - and run it to see if it works...

  • Hi Jan,

    Have you progressed further with this issue?

    In my case I am using TMS570LC43x Launchpad

    In my case, I am interfacing with IRThermo sensor MLX90614; However, I need to find a way to stop the stop bit from being sent!

    I have checked the TRM which seems to support that but when I try it, it doesn't really work.

    Regards

    Fouad

  • , I haven't been able to get this working.
    It's not the end of the world in my case, because the sensor - even though the spec tells that it needs a RESTART - works with the STOP/START sequence.
    I'd feel more comfortable though if I would be able to create that RESTART sequence.

  • Hi Jan,

    Thank you for your reply,

    I have dug further but in my case for the TMS570LC43, where I found few issues in the code generated by the HALCoGen, here is a link to a question with feedback to TI, which I have started: e2e.ti.com/.../1892569

    I have posted some code as well, but as you are working on different MC, check the HL_i2c. and HL_i2c.h generated by the HALCoGen

    Tips:

    When you call i2cInit(), you are actually setting the stop bit! so you can either skip calling it, or you need to write it through registers.

    The main thing I have noticed to make it to work, you need to set nIRS to "1", while the rest "0" then you start setting the rest of the bits, like master, start bit condition and so on.

    The "count" is an important factor as well in the process, so you need to set it up with how many bytes to send for transmitting before you change the i2c to receiving.

    When you start receiving, follow the same steps, but this time with "stop bit conditions set"

    I hope this help

    Regards

    Fouad