I2C addressing, registers, and bits

Hi, thanks for looking.

I'm working with the TAS6422 from TI. A class D audio amp with I2C diagnostics + control

The I2C address pins pulled low give me a write address of 0xD4 and read address of 0xD5. My first task is to clear the latched WARN pin by writing bit 7 high on register 0x21.

Page 43 table 70 + figure 33 says I would want to write bit 7 high and all the others low.

This is the part of the code I'm having trouble with:

 Wire.beginTransmission(byte(0xD4));  //   device address for writing

    Wire.write(byte(0x21));                                          // register address

    wire.write(byte(1000000));                                  // set bit 7 (clear WARN) high; set bits [6:0] low

    wire.endTransmission();                                       // end transmission

I don't know how to specify a 1 for the 7th bit. Page 43, figure 70 + table 33 says I should write bit 7 high. I'm a newb. Why does it say address = 0x21 but then says default is 0x00? Is that the bit settings 0x00? The other registers have other defaults, but I dont see how they correlate to 1's and 0's in binary.

This is my whole code as of now. It's not done yet. : (I was using LED for debuging while a coworker was using my ftdi cable i was gonna use for serial monitor)

#include <Wire.h>



#define tas

#define clrWARN 3   // button to clear WARN pin

#define readTAS 2   // button to read TAS6422 registers over i2c

#define led1  8     // LED1

#define led2  9     // LED2



// variables will change:

int clrWARNs = 0;         // State variable for reading the button used to clear latched WARN pin

int readTASs = 0;         // State variable for reading the button used to command i2c read





void setup() {

  pinMode(led1, OUTPUT);

  pinMode(led2, OUTPUT);

  pinMode(clrWARN, INPUT);

  pinMode(readTAS, INPUT);



  Wire.begin();

  Serial.begin(9600);

  Serial.println("TAS6422 Diagnostics");



}





void loop() {

  clrWARNs = digitalRead(clrWARN);



  readTASs = digitalRead(readTAS);



  if (clrWARNs == HIGH) {

    digitalWrite(led1, HIGH);

    Wire.beginTransmission(byte(0xD4));

    Wire.write(byte(0x21));

    Wire.write(byte(1000000));



 

    delay(500);



  }

  else {

digitalWrite(led1, LOW);

  }



  if (readTASs == HIGH) {

    digitalWrite(led2, HIGH);

    Wire.requestFrom(1, 8);   // Ask slave#1 for 8 bytes



    while (Wire.available()) {

        char c = Wire.read();

        Serial.println(c);

        delay(500);

    }

  }

  else {

    digitalWrite(led2, LOW);

  }







}

The device has an address for reading and an address for writing. The Wire class adjusts THE device's address (that you supply) to handle reading and writing.

Use the I2C scanner sketch to get THE device address.

    Wire.beginTransmission(byte(0xD4));

    Wire.write(byte(0x21));

    Wire.write(byte(1000000));

byte(It) byte(is) byte(NOT) byte(necessary) byte(to) byte(cast) byte(a) byte(byte) byte(to) byte(a) byte(byte).

The value 1000000 can NOT be meaningfully cast to a byte. The value is in decimal. You want a value in binary, like 0b1000000.

Hi Paul,

Thank you for responding the thread. I used the scanner and found the address 0x6A which seems bit shifted relative to 0xD4.

This is what I have for the code and the WARN pin is remains asserted.

Wire.beginTransmission(byte(0x6A));
    //Wire.write(byte(0xD4));
    Wire.write(byte(0x21));
    Wire.write(byte(0x80));
    Wire.endTransmission();

Maybe I need to reverse the order of the write instruction and data?

Thank you for your time

You are still uselessly casting byte values to byte. Why?

PaulS:
You are still uselessly casting byte values to byte. Why?

Because I just don't know better. I'm still picking up and piecing together the fundamentals as I go. I'm writing in hex with (0x80). I could have used binary 0b1000000. Either way when I type wire.write(xyz), the library wire.h is making it a byte? That's my current understanding of that.

The code actually does work! I had made a typo (0x2) while pasting this and found it, changed back to 0x21. Warn pin de-asserts when I press my button.

Frozenguy:
Because I just don't know better. I'm still picking up and piecing together the fundamentals as I go. I'm writing in hex with (0x80). I could have used binary 0b1000000. Either way when I type wire.write(xyz), the library wire.h is making it a byte? That's my current understanding of that.

The code actually does work! I had made a typo (0x2) while pasting this and found it, changed back to 0x21. Warn pin de-asserts when I press my button.

You can Wire.write() a value, a string, or a data array. All are sent as a byte or bytes. Any number placed between the parenthesis will be sent as one byte, decimal, hex, binary, octal etc.
Using one notation is a matter of preference. In some cases, writing a constant as a decimal number makes reading your code easier, a particular value in Volts or Amps or a count, etc. If you are using an 8-bit shift register, for example, it might be easier to read when written in binary. AVR registers are a good example of this, so you can match the individual bits to their respective notation in the datasheets. If you are masking bits in an unsigned long, hexadecimal may be more appropriate. In the following code, using hex notation makes it clear that I am wanting to store the lowest byte of the unsigned long variable in x.

byte x = unsignedLongVariable & 0x000000FF;

The following write-up may help you to clear up the confusion with regards to the address of an I2C device.
cbtwi.png

(1) The I2C device address is always 7-bit, and it should be preferably written in bit form.

(2) The TAS6422 chip has two selection bits named ADDR1 and ADDR0; these two selection bits correspond to A1 and A0 bit of Fig-1. With the help of these selection bits, 4xTAS6422 chips could be placed in a system with 4 different I2C addresses.

(3) If we connect LH (1) at ADDR1-pin and LL (0) at ADDR0-pin of the chip, the I2C address becomes as: 1101010 (0b1101010). I am not much interested to write it as 0x6A; because, 0x6A could be written as 0b01101010 and then the confusion comes as to the 7-bit address value of the device -- is it 0110101?

(4) How is 'the data direction bit -- R-W/ bit' is appended with the 7-bit deviceAddress (slaveAddress)?

(a) Let us assume that the data will move from Arduino to TAS6422. This is a 'data write' session from Master (the Arduino) to Slave (the Chip). To indicate this 'write operation' the Master appends (Fig-1) LL (R-W/ = 0) at the end of 7-bit deviceaddress. The following instructions write an arbitrary data byte (say, 0x23) into an arbitrary register with an arbitrary address (say, 0x45).

Wire.beginTransmission(0b1101010);  //7-bit deviceAddress
Wire.write(0x45);       //registerAddress
Wire.write(0x23);       //data byte for the above regsiter
Wire.endTransmission;

The above 4 lines of instructions are not executed line-by-line. In fact, the instructions are queued in a FIFO buffer of the Master. From the presence of Wire.write() command, the compiler understands that the current transmission session is a data write operation. As a result, the compiler appends LL (0) at the end of 7-bit deviceAddress (the 0b1101010) and places the resultant 0b11010100 (0xD4) in the queue. The Wire.endTransmission() transmits the 1st byte (0b11010100 = 0xD4) of the queue on the SDA line in synchronize with the SCL pulses. The following timing diagram clarifies the situation:
txtim1.png

(b) Let us assume that the Master wants to read a data byte from the chip from a register which has already been pointed previously. The Master executes the following instruction:

Wire.requestFrom(0b1101010, 1);  //Wire.requestFrom(deviceAddress, 1)

While the Master makes a Roll Call (checking the presence of the slave), it appends LH (1) at the end of 7-bit deviceaddress (0b1101010) and transmits the resultant 0b11010101 (0xD5) on the SDA line to indicate the 'data direction is from Slave to Master - data read mode (R-W/=1)'.

txtim1.png

cbtwi.png

The simpler way to describe i2c addressing with respect to the Arduino Wire library is that
the full 8bit I2C address reported by some data sheets (like this one) is not what the Wire library uses.
The Wire library uses 7 bit addressing with an additional bit to represent read vs write operations.
The 7 address bits used by the Wire library are the upper 7 bits of an 8 bit i2c address with and additional bit being used under the hood down in the Wire library to select between writes & reads which is bit 0 of an 8 bit i2c address.

So, for example, in this case while the full 8 bit i2c address reported by the datasheet might be 0xD4 (writes) and 0xD5 (reads), the Wire library will always use 0x6A for both reads and writes since it will append an extra bit to represent the read/write operation.

--- bill

1 Like

bperrybap:
The simpler way to describe i2c addressing with respect to the Arduino Wire library is that
the full 8bit I2C address reported by some data sheets (like this one) is not what the Wire library uses.
The Wire library uses 7 bit addressing with an additional bit to represent read vs write operations.
The 7 address bits used by the Wire library are the upper 7 bits of an 8 bit i2c address with and additional bit being used under the hood down in the Wire library to select between writes & reads which is bit 0 of an 8 bit i2c address.

So, for example, in this case while the full 8 bit i2c address reported by the datasheet might be 0xD4 (writes) and 0xD5 (reads), the Wire library will always use 0x6A for both reads and writes since it will append an extra bit to represent the read/write operation.

cbtwi.png

Is the I2C address of a slave device 8-bit? It seems to be always 7-bit in Arduino Platform. After the addition of the R-W/ bit (Read-Write/) with the 7-bit I2C address, the resultant 8-bit is known as Control Byte -- a name found in this literature (Page-6).

24L64.pdf (588 KB)

For accuracy and true proper terminology the actual i2c spec should be referenced rather than a datasheet for a particular device.
The term "Control Byte" is not used in the actual i2c specification.

Unfortunately, the actual i2c specification can potentially be a little confusing.
See section 3.1.10 page 13 of the specification:

After the START condition (S), a slave address is sent.
This address is seven bits long followed by an eighth bit which is a data direction bit (R/W)
— a ‘zero’ indicates a transmission (WRITE), a ‘one’ indicates a request for data (READ)

That paragraph was worded poorly and is likely the cause of the confusion when combined with figure 10 which shows the the 8 bits that follows the START condition as a byte- especially since they don't have a term for that 8 bit byte value.
As it is written, for those thinking in bytes, the first sentence appears to say that a byte slave address is sent - but it doesn't say that. It clarifies that in the next sentence which says the slave address is 7 bits, and that after that address is sent a direction bit is sent.

While everything stated in that section above is technically accurate and not conflicting, it could have been worded better to avoid confusion.

The address is clearly 7 bits followed by a r/w direction bit which you can see in figure 9 and again in figure 10 which shows both the 7 bit address and the r/w bit as a byte.
It is when a data-sheet specifies the byte value that follows the START condition as the i2c address for reads vs writes that the problem/confusion over i2c addresses occurs.
Some i2c device data-sheets publish the full byte value as the i2c address showing separate "addresses" for read vs write vs specifying the actual 7 bit slave address.

To further complicate things, some s/w implementations require specifying that full byte as the "address" rather than
using 7 bit addresses and inserting the appropriate r/w bit after the slave address.

Just a wild guess but i'd guess that the h/w guys that originally designed it never thought of it as a byte following the START condition but rather as a 7 bit address followed by a direction bit. But since everyone is conditioned to think in bytes, the 8 bits following the START were shown as a byte and that is where the confusion entered.
Just a guess...

In the case of the Arduino Wire library, it uses 7 bit addressing that works as shown in the i2c specification.

Here is some additional reading on the topic:

--- bill

7-bit 8-bit and 10-bit I2C Slave Addressing - Total Phase
[https://www.i2c-bus.org/addressing/[/quote]

The following diagram found in the above link is also confusing to me. In Arduino, we have these two instructions -- (Wire.beginTransmission(slaveAddress); and Wire.requestFrom(slaveAddress, n);) where we enter the slaveAddress as a 7-bit value. So, mentioning of 8-bit write-address and 8-bit read-address do not carry much meanings to the Arduino users as there is no scope to use these 8-bit addresses. The data direction bit (Read=1, Write=0) is automatically appended at the end of 7-bit slaveAddress during Roll Calling phase of the slave.

slaveAddress.png

Thanks @bperrybap for referring to very useful documentations on I2C Bus.

slaveAddress.png](Addressing - I2C Bus)

Thanks for the explanation--helpful!.

Very useful when trying to figure out an issue I was having with an AD quad digital pot chip.

For that (AD5254) the slave address could be found by lopping off the read/write LSB bit and padding the byte with a zero for MSB. Doing that, I could enter the correct slave address--and my sketch worked, but you have to ignore that final read/write bit.

Took me a bit (pun intended) to figure that one out.