Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 2 of 2

Thread: SMBus with i2c_t3?

  1. #1
    Junior Member
    Join Date
    Dec 2019
    Posts
    5

    SMBus with i2c_t3?

    Hello,

    I am trying to use the Teensy 3.6 to communicate with a bq40z80 chip from Texas Instruments. The chips is a fuel gauge / 'smart' battery management chip. A standard was developed called, SMBus that operates from the well known I2C protocol. However, it has a special start bit that is not typically used in I2C devices. Therefore, the <Wire.h> library does not work to communicate the the battery chip..

    A thread here shows how they got it to work, but it uses a library that was built for AVR devices. Unfortunately I just don't think I know enough to be able to convert that to work with the Teensy/ARM platform.

    I am hoping that the i2c_t3.h library can suffice. So this is the description of the SMBus protocol..

    Click image for larger version. 

Name:	PAC1720-SMBus-Protocol.png 
Views:	5 
Size:	51.6 KB 
ID:	18750

    Below is my code to try and retrieve the voltage from the chip, but the result from Wire.available() is 0.

    Code:
    //#include <Wire.h>
    #include <i2c_t3.h>
    
    
    int led = 13;
    
    // the setup routine runs once when you press reset:
    void setup() {
      Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 100000);
      Serial.begin(9600);
      // initialize the digital pin as an output.
      pinMode(led, OUTPUT);
      while(!Serial){ 
      }
      
      Serial.println("Sending..");
      Wire.beginTransmission(0x16);
      Wire.write(0x9);    //get voltage
      Wire.endTransmission();
    }
    
    // the loop routine runs over and over again forever:
    void loop() {
      byte num=0;
    
    
      //receive
      Serial.println("Receiving...");
      Wire.requestFrom(0x16, 4);
      Serial.println(Wire.available());
      while(Wire.available())
      {
        char c = Wire.read();
        Serial.print(c);
      }
      Serial.print("\ndone\n");
    
      delay(1000);
      
    }

    Supposedly, someone was able to use this "i2cmaster" library for AVR devices, to get the data they wanted. Below is that code.. (if I could somehow adapt this to use the "i2c_t3" instead?)
    If anything, check out the last 3 functions. Particularly the lines containing "i2c_rep_start()". That seems like something I want to be able to use if i2c_t3 supports something like that


    Code:
    #include <i2cmaster.h>
    
    #define readBufferLen 17
    char readBuffer[readBufferLen];
    uint8_t i2cBuffer[readBufferLen];
    #define deviceaddress B00010110
    uint8_t serialCommand, loopCount;
    unsigned int serialData;
    
    void setup() {
      i2c_init();
      PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups
      Serial.begin(9600);
      Serial.println("Ready!");
      Serial.println("------");
      Serial.print("Mfg: ");
      i2c_smbus_read_block(0x20,i2cBuffer,readBufferLen);
      Serial.print((char*)i2cBuffer);
      Serial.print(" | Dev: ");
      i2c_smbus_read_block(0x21,i2cBuffer,readBufferLen);
      Serial.print((char*)i2cBuffer);
      Serial.print(" | Chem: ");
      i2c_smbus_read_block(0x22,i2cBuffer,readBufferLen);
      Serial.println((char*)i2cBuffer);
      serialData = i2c_smbus_read_word(0x1b);
      Serial.print("Date: ");
      Serial.print((uint8_t)(serialData >> 5) & B00001111,DEC);
      Serial.print('/');
      Serial.print((uint8_t)serialData & B00011111,DEC);
      Serial.print('/');
      Serial.print((serialData >> 9) + 1980,DEC);
      Serial.print(" | Cycles: ");
      Serial.println(i2c_smbus_read_word(0x17));
    }
    
    void loop() {
      if (loopCount > 10) {
        // gather things
        int currVoltage, currAmps, estPercent, currTemp, timeLeft;
        currVoltage = i2c_smbus_read_word(0x9);
        currAmps = i2c_smbus_read_word(0xA);
        estPercent = i2c_smbus_read_word(0xD);
        currTemp = i2c_smbus_read_word(0x8);
        timeLeft = i2c_smbus_read_word(0x13);
     
        Serial.print((float)currVoltage / 1000, 2);
        Serial.print("v, ");
        Serial.print((float)currAmps / 1000, 2);
        Serial.print(" A, ");
        Serial.print(((float)currTemp/10 - 273.15) * 1.8 + 32, 2);
        Serial.print(" F, ");
        Serial.print(estPercent,DEC);
        Serial.print("%, ");
        Serial.print(timeLeft,DEC);
        Serial.println(" min remaining.");
        loopCount = 0;
      }
      loopCount++;
    
      if (Serial.available()) {
        delay(500);
        uint8_t x = 0;
        if (Serial.peek() == 'w') {
          // write
          Serial.read(); // get the state out of the buffer
          while (Serial.peek() != ';') {
            readBuffer[x] = Serial.read();
            x++;
            if (x > readBufferLen) return;
          }
          readBuffer[x] = 0; // null the buffer
          Serial.read(); // get the semicolon out of there
          serialCommand = strtoul(readBuffer, NULL, 0);
          Serial.print("Writing word using command: ");
          Serial.print(serialCommand,HEX);
          x = 0;
          while (Serial.peek() != ';') {
            readBuffer[x] = Serial.read();
            x++;
            if (x > readBufferLen) return;
          }
          readBuffer[x] = 0; // null the buffer
          Serial.read(); // clear the semicolon
          serialData = strtoul(readBuffer, NULL, 0);
          Serial.print(", data: ");
          Serial.println(serialData,HEX);
          i2c_smbus_write_word(serialCommand,serialData);
          Serial.println("Completed.");
         
        } else if (Serial.peek() == 'r') {
          // read word
          Serial.read(); // get the state out of the buffer
          while (Serial.peek() != ';') {
            readBuffer[x] = Serial.read();
            x++;
            if (x > readBufferLen) return;
          }
          readBuffer[x] = 0; // null the buffer
          Serial.read(); // clear the semicolon
          serialCommand = strtoul(readBuffer, NULL, 0);
          Serial.print("Reading word using command: ");
          Serial.println(serialCommand,HEX);
          serialData = i2c_smbus_read_word(serialCommand);
          Serial.print("Completed, received: ");
          Serial.print(serialData,HEX);
          Serial.print(" (hex), ");
          Serial.print(serialData,DEC);
          Serial.println(" (dec)");
    
        } else if (Serial.peek() == 'b') {
          // read block
          Serial.read(); // get the state out of the buffer
          while (Serial.peek() != ';') {
            readBuffer[x] = Serial.read();
            x++;
            if (x > readBufferLen) return;
          }
          Serial.read(); // clear the semicolon
          readBuffer[x] = 0;
          serialCommand = strtoul(readBuffer, NULL, 0);
          Serial.print("Reading block using command: ");
          Serial.println(serialCommand,HEX);
          x = i2c_smbus_read_block(serialCommand,i2cBuffer,readBufferLen);
          Serial.print("Completed, received ");
          Serial.print(x,DEC);
          Serial.println(" bytes:");
          x = 0;
          for (x=0; x<readBufferLen; x++) {
            Serial.print(i2cBuffer[x]);
          }
          Serial.println();
        } else {
          Serial.print("Junk: ");
          Serial.println(Serial.read(),HEX);
        }
      }
      delay(500);
    }
    
    void i2c_smbus_write_word ( uint8_t command, unsigned int data ) {
      i2c_start_wait(deviceaddress + I2C_WRITE);
      i2c_write(command);
      i2c_write((uint8_t)data);
      i2c_write((uint8_t)(data>>8));
      i2c_stop();
      return;
    }
    
    unsigned int i2c_smbus_read_word ( uint8_t command ) {
      unsigned int buffer = 0;
      i2c_start_wait(deviceaddress + I2C_WRITE);
      i2c_write(command);
      i2c_rep_start(deviceaddress + I2C_READ);
      buffer = i2c_readAck();
      buffer += i2c_readNak() << 8;
      return buffer;
    }
    
    uint8_t i2c_smbus_read_block ( uint8_t command, uint8_t* blockBuffer, uint8_t blockBufferLen ) {
      uint8_t x = 0;
      uint8_t y = 0;
      i2c_start_wait(deviceaddress + I2C_WRITE);
      i2c_write(command);
      i2c_rep_start(deviceaddress + I2C_READ);
      y = i2c_readAck();
      for (x=0; x<y-1; x++) {
        blockBuffer[x] = i2c_readAck();
      }
      blockBuffer[x] = i2c_readNak();
      blockBuffer[x+1] = 0;
      return y;
    }


    Any ideas on how to go about properly requesting data from the chip?
    Thank you for any and all help! It is vastly appreciated and would make me a very happy person if I can get this working.
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	jEpgk.png 
Views:	1 
Size:	5.3 KB 
ID:	18749  

  2. #2
    Junior Member
    Join Date
    Dec 2019
    Posts
    5
    If someone finds this thread in the future, I got it to work just fine! There may be a few different ways to do this, but the following works for me:

    This was for a bq40z80 chip

    Code:
    #include <i2c_t3.h>
    void setup(){
      Wire.begin(I2C_MASTER, 0x00, I2C_PINS18_19, I2C_PULLUP_EXT, 100000);
      Wire.setDefaultTimeout(250000);
      Serial.begin(9600);
      while(!Serial){
      }
    
    void loop() {
      target_address = 0x0B;
      //To read from the device, you must write to it first
      Wire.beginTransmission(target_address);
      Wire.write(0x00);  //this goes to ManufacturerAccess().   Some physical toggling (like LEDs) require you to go here.  Otherwise, you can go to ManufacturerBlockAccess() which is 0x44.  You can ignore this one if you are not trying to use a command from ManufacturerAccess()
      Wire.write(0x71);   //This data will tell the chip where to go.  if you go to 0x2B, you'll toggle the LED's.  0x71 will go to DAStatus1, which can then be read from to find out certain data parameters (such as cell voltage).  Depending on your battery chip of course!
      Wire.write(0x00);  //data is in little endian, so the above means 0x0071
      Wire.endTransmission(I2C_STOP);
      //If you are just trying to toggle the LEDs, that's it!  if you want to read data, such as sending the command 0x0071 to find out the DAStatus1 section, now we must read.
    
      Wire.beginTransmission(I2C_STOP);
      Wire.write(0x44);  //data is read from 0x44, but see the data sheet for your chip!
      Wire.endTransmission(I2C_STOP);
      Wire.requestFrom(target_address, 32, I2C_STOP);
      while(Wire.available()) {
        Serial.printf("%02X ", Wire.readByte());
      }
      Serial.print("\n");
      print_i2c_status();
      delay(4000);
    }
    
    void print_i2c_status(void)
    {
        switch(Wire.status())
        {
        case I2C_WAITING:  Serial.print("I2C waiting, no errors\n"); break;
        case I2C_ADDR_NAK: Serial.print("Slave addr not acknowledged\n"); break;
        case I2C_DATA_NAK: Serial.print("Slave data not acknowledged\n"); break;
        case I2C_ARB_LOST: Serial.print("Bus Error: Arbitration Lost\n"); break;
        case I2C_TIMEOUT:  Serial.print("I2C timeout\n"); break;
        case I2C_BUF_OVF:  Serial.print("I2C buffer overflow\n"); break;
        default:           Serial.print("I2C busy\n"); break;
        }
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •