SMBus with i2c_t3?

Status
Not open for further replies.

Rocky

Member
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..

PAC1720-SMBus-Protocol.png

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.
 

Attachments

  • jEpgk.png
    jEpgk.png
    5.3 KB · Views: 89
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;
    }
}
 
Status
Not open for further replies.
Back
Top