[Teensy3.1]: [I2C] Communication Issue.

Status
Not open for further replies.

Prasad

Member
Hi,

I am integrating Teensy3.1 with ADE7880(energy meter IC) using I2C interface. I am using i2c_t3 library.
I observe the following.
1. For writing or reading into a slave device I need to right shift the slave address once.(e.g: slave_addr >> 1)
2. I am able to write and read 1 byte register.(e.g: LCYCMODE register, able to retrieve the same data which is written)(This is OK)
3. I am unable to read the correct data if its greater than 1 byte data.(e.g: AIRMS, 3 byte read only register)
4. I am able to read correct values of AIRMS register using other controller with I2C interface.

Thanks for the support and for i2c_t3 library.

Below is the code for reference:

Code:
void setup() {                
	Serial.begin(115200);
	Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_400);
}

void loop() {
	uint8_t raw_data[2];
	int int_data;
	
	//Read Write register
	write_meter(LCYCMODE, 10);
	read_meter(LCYCMODE, 1, raw_data);
	int_data = raw_data[0]);
	Serial.println("LCYCMODE: ");
	Serial.println(int_data, HEX);
	Serial.print("---------------------------------------------------\n");
	Serial.println("I2C READ 1 byte from MemAddr 0\n");
	
	//Read only register
	read_meter(AIRMS, 3, raw_data);
	int_data = (raw_data[2] << 16 | raw_data[1] << 8 | raw_data[0]);
	Serial.println("AIRMS: ");
	Serial.println(int_data, HEX);
	Serial.print("---------------------------------------------------\n");
	Serial.println("I2C READ 1 byte from MemAddr 0\n");
}

void write_meter(int add, int data)
{
	uint8_t target = 0x70; 				   //slave addr
	Serial.print("---------------------------------------------------\n");
	Serial.print("I2C WRITE 1 byte at MemAddr 0\n");
	
	Wire.beginTransmission(target>>1);     // slave addr needs to be shifted for ack from slave
	Wire.write((add>>8));                  // memory address
	Wire.write(add);                       // memory address
	Wire.write(data);                      // data
	Wire.endTransmission(I2C_STOP);   	   // blocking I2C Tx (NOSTOP)
	print_i2c_status();                    // print I2C final status
}


void read_meter(int add, char no_bytes, uint8_t meter_data[])
{
	uint8_t target = 0x70; 				// slave addr
	
	Serial.print("---------------------------------------------------\n");
	Serial.print("I2C READ 1 byte from MemAddr 0\n");

	Wire.beginTransmission(target>>1);     // slave addr
	Wire.write((add>>8));                  // memory address
	Wire.write(add);                       // memory address
	Wire.endTransmission(I2C_NOSTOP);      // blocking write   (NOSTOP - next cmd is RepSTART)
	Wire.requestFrom(target>>1,1,I2C_STOP);// blocking read (NOSTOP - next cmd is RepSTART)	
	while(Wire.available() && no_bytes)
    {
		meter_data[no_bytes-1] = Wire.readByte();	//Read byte
		no_bytes--;
	}
}


Thank you,
Prasad
 
Last edited:
While the code is readable, perhaps place it in Code brackets. To do so go into the "Go Advanced" section when editing your post , select the co in the post and click on the hash (#) character.
 
3. I am unable to read the correct data if its greater than 1 byte data.(e.g: AIRMS, 3 byte read only register)

I can see a couple of things:

  1. Your receive problem is here:
    Code:
    Wire.requestFrom(target>>1,[B][COLOR="#FF0000"]1[/COLOR][/B],I2C_STOP);
    You have hardcoded a request for 1 byte. Change it to this:
    Code:
    Wire.requestFrom(target>>1,[B][COLOR="#FF0000"]no_bytes[/COLOR][/B],I2C_STOP);
  2. Also, instead of defining your target = 0x70 and using "target>>1" everywhere, you can just define it as target = 0x38, and use it directly (0x38 is the true device address).
 
Also, I can see another problem here. You are stacking your receive bytes into the buffer backwards (eg. first Rx byte out will go into meter_data[no_bytes-1] and last Rx byte will go into meter_data[0]). Perhaps it is intentional?

Code:
while(Wire.available() && no_bytes)
{
    meter_data[no_bytes-1] = Wire.readByte();	//Read byte
    no_bytes--;
}

If not then you need to reverse the buffer order, something like this:

Code:
size_t index=0;
while(Wire.available())
    meter_data[index++] = Wire.readByte();	//Read byte
 
I can see a couple of things:

  1. Your receive problem is here:
    Code:
    Wire.requestFrom(target>>1,[B][COLOR="#FF0000"]1[/COLOR][/B],I2C_STOP);
    You have hardcoded a request for 1 byte. Change it to this:
    Code:
    Wire.requestFrom(target>>1,[B][COLOR="#FF0000"]no_bytes[/COLOR][/B],I2C_STOP);

    I have modified my code, but still am not able to get the correct values, mean to say the values read from register remains same even after varition in the i/p voltage.(expected to change the values)
  2. Also, instead of defining your target = 0x70 and using "target>>1" everywhere, you can just define it as target = 0x38, and use it directly (0x38 is the true device address).


    0x70 is the actual slave address of ADE7880, and i have successfully communicated with other controller.
    I decided to shift the slave id because, i saw in the waveforms (SDA line) w.r.t to clock(SCL) the slave id was left shifted by 1.
Thank you very much for the quick response.
 
Last edited:
Also, I can see another problem here. You are stacking your receive bytes into the buffer backwards (eg. first Rx byte out will go into meter_data[no_bytes-1] and last Rx byte will go into meter_data[0]). Perhaps it is intentional?



If not then you need to reverse the buffer order, something like this:

Code:
size_t index=0;
while(Wire.available())
    meter_data[index++] = Wire.readByte();	//Read byte

This is intentional as the higher byte values are received first.
 
0x70 is the actual slave address of ADE7880, and i have successfully communicated with other controller.
I decided to shift the slave id because, i saw in the waveforms (SDA line) w.r.t to clock(SCL) the slave id was left shifted by 1.

No, the actual slave address is 0x38. The address byte is 0x70, but the reason for that is because in the I2C protocol the address byte is constructed from the slave address in bit positions 7:1, appended to the Read/Write bit in bit position 0. An excerpt from the I2C spec and the ADE7880 datasheet will illustrate.

The I2C spec:
screenshot.281.jpg

The ADE7880 datasheet (click to enlarge):
screenshot.282.jpg

Notice that the address is 0x38 and the bit0 position in the ADE7880 datasheet changes between write and read. This is generally confounded by poorly written datasheets like this which assume people are too stupid to understand this concept of bit shifting and appending a R/W bit, so they say things like the "Write address is 0x70" and the "Read address is 0x71". No, the slave address is 0x38 in all cases and the R/W bit is changing. Any logic analyzer can confirm that.

So in any case, the I2C library uses actual slave addresses, which is why you need to give it 0x38. Internally it will shift the address and set the R/W bit as needed.

I have modified my code, but still am not able to get the correct values, mean to say the values read from register remains same even after varition in the i/p voltage.(expected to change the values)

I suspect what is going on is that the comm is failing and since you don't error check it appears to work fine, and because you don't clear your Rx buffer you keep reading the same value out. Try adding a function that prints the I2C status like this:

Code:
void read_meter(int add, char no_bytes, uint8_t meter_data[])
{
	uint8_t target = 0x70; 				// slave addr
	
	Serial.print("---------------------------------------------------\n");
	Serial.print("I2C READ 1 byte from MemAddr 0\n");

	Wire.beginTransmission(target>>1);     // slave addr
	Wire.write((add>>8));                  // memory address
	Wire.write(add);                       // memory address
	Wire.endTransmission(I2C_NOSTOP);      // blocking write   (NOSTOP - next cmd is RepSTART)
	Wire.requestFrom(target>>1,no_bytes,I2C_STOP);	// blocking read (NOSTOP - next cmd is RepSTART)
	[COLOR="#FF0000"][B]print_i2c_status();[/B][/COLOR]	
	while(Wire.available() && no_bytes)
	{
		meter_data[no_bytes-1] = Wire.readByte();	//Read byte
		no_bytes--;
	}
}
//
// print I2C status
//
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;
    default:           Serial.print("I2C busy\n"); break;
    }
}

If it says "I2C waiting, no errors" and you are still getting the wrong data then it means the I2C comm worked fine and your problem is with the ADE7880. If it says anything else then it is a comm problem. If you have a scope plot or logic analyzer plot it would help debug any comm problems.
 
I included the print_i2c_status function and i got "I2C waiting, no errors" .
I am always receiving value as "0x182BB",
I observe in the scope that after sending the '0x71'(Wire.requestFrom(0x38,3,I2C_STOP)), the SDA pin is not brought high to indicate the stop bit.
I also tried changing stop condition in 'requestFrom' API as I2C_NOSTOP but no good result.

Thanks,
Prasad
 
Is it possible to see a plot of the SDA/SCL on the three byte receive? If your scope can't plot, try smartphone or camera and take a picture of the screen. I've only ever seen problems with STOP at the highest baud rates, never at 400kHz. Regardless of STOP I'm not sure why the data wouldn't change either. My guess is that ADE7880 might not like something in the timing, perhaps it is getting confused, and that is screwing up it's state. You could try different baud rates and see if that changes anything.
 
Code:
void read_meter(int add, char no_bytes, uint8_t meter_data[])
{
	uint8_t target = 0x38; // slave addr

	meter_data[0] = 0x00;
	meter_data[1] = 0x00;
	meter_data[2] = 0x00;
	meter_data[3] = 0x00;
	
	Serial.print("---------------------------------------------------\n");
	Serial.print("I2C READ from MemAddr 0\n");

	Wire.beginTransmission(target);     // slave addr
	Wire.write((add>>8));                      // memory address  0x43
	Wire.write(add);                      // memory address  0xC1
	Wire.endTransmission(I2C_NOSTOP);   // blocking write   (NOSTOP - next cmd is RepSTART)
	Wire.requestFrom(target,no_bytes,I2C_STOP);// blocking read (NOSTOP - next cmd is RepSTART)	
	print_i2c_status();
	while(Wire.available())
    {
		meter_data[no_bytes-1] = Wire.readByte();
		no_bytes--;
	}
}
void loop() 
{
	read_meter(AVRMS, 3, raw_data);
	int_data = (raw_data[2] << 16) | (raw_data[1] << 8) | raw_data[0];
	Serial.println("raw_data[]: ");
	Serial.println(int_data, HEX);
}

Actual output: 
---------------------------->
	I2C READ from MemAddr 0
	I2C waiting, no errors
	raw_data[]: 
	B3BA
---------------------------->
Hi
I have checked with different baud rates but i see no difference in output.

I have attached the screen shot of SDA w.r.t SCL for receiving 3 bytes data, for slave address 0x38 and read memory address(0x43C1).
 

Attachments

  • 001.png
    001.png
    80 KB · Views: 257
I also attach the screen shot for 1 byte receiving, which is working fine as expected. Slave address 0x38 reading from memory 0xE702(LCYCMODE register)
I am able to write and read the same data.

Actual output:
---------------------------->
I2C READ from MemAddr 0
I2C waiting, no errors
raw_data[]:
B3BA
---------------------------->
 

Attachments

  • 000.png
    000.png
    83.7 KB · Views: 175
Hi
I have checked with different baud rates but i see no difference in output.

I have attached the screen shot of SDA w.r.t SCL for receiving 3 bytes data, for slave address 0x38 and read memory address(0x43C1).

There must be something else going on. I annotated the screen shot you uploaded. You can double check the annotations, but to me the comm looks fine - the Write, the RepSTART, the Read, all look normal. If the return data 0x00B3BA is not correct I don't think it is because of the I2C, at least I'm not seeing data corruption. There is a little bit of crosstalk, but nothing severe, and the edges look pretty square.

annotated1.png

Is there any other writable register location that you could test perhaps writing 3 bytes and then reading them back to validate the communication?

Another idea - you mention other interfaces can work. I2C can support multi-master. It is possible to put both the T3 and the other interface on the same bus and then issue the 3byte read to the ADE7880 from both of them in sequence? I really don't know why one interface would get a different answer than the other one.
 
void write_meter(int add, int data)
{
uint8_t target = 0x38; // slave addr
Serial.print("---------------------------------------------------\n");
Serial.print("I2C WRITE 1 byte at MemAddr 0\n");

Wire.beginTransmission(target); // slave addr
Wire.write((add>>8)); // memory address
Wire.write(add); // memory address
Wire.write(data); // data
Wire.endTransmission(I2C_STOP); // blocking I2C Tx (NOSTOP)
print_i2c_status(); // print I2C final status
}


void read_meter(int add, char no_bytes, uint8_t meter_data[])
{
uint8_t target = 0x38; // slave addr

meter_data[0] = 0x00;
meter_data[1] = 0x00;
meter_data[2] = 0x00;
meter_data[3] = 0x00;

Serial.print("---------------------------------------------------\n");
Serial.print("I2C READ from MemAddr 0\n");

Wire.beginTransmission(target); // slave addr
Wire.write((add>>8)); // memory address 0x43
Wire.write(add); // memory address 0x81
Wire.endTransmission(I2C_NOSTOP); // blocking write (NOSTOP - next cmd is RepSTART)
Wire.requestFrom(target,no_bytes,I2C_STOP);// blocking read (NOSTOP - next cmd is RepSTART)
print_i2c_status();
while(Wire.available())
{
meter_data[no_bytes-1] = Wire.readByte();
no_bytes--;
}
}

void setup() {
Serial.begin(115200);
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_INT, I2C_RATE_100); // Setup for Master mode, pins 18/19, external pullups, 100kHz

}
void loop()
{
write_meter(AVGAIN,0xAA);
read_meter(AVGAIN, 3, raw_data);
int_data = (raw_data[2] << 16) | (raw_data[1] << 8) | raw_data[0];//raw_data[3];
Serial.println("raw_data[]: ");
Serial.println(int_data, HEX);
delay(1000);
}

Actual output:
---------------------------->
I2C READ from MemAddr 0
I2C waiting, no errors
raw_data[]:
0
---------------------------->
AVGAIN is the 3 byte read/write register. I am writing 0xAA value to this register but am reading back 0 from the register.

I have attached the screen shot of SDA w.r.t SCL for writing 0xAA to AVGAIN(0x4381) register and read 3 bytes of data from the same address.
1st picture shows the entire sequence of reading and writing. 2nd right extreme one for writing only zoomed,
3rd picture the bottom one is the receive only zoomed.

I did not try for the multi master one i shall try this, but is there any setting to be made for multi master mode and how sequencing has to be done (Is that both controller has to signal each other for sequencing?)

Thank You
 

Attachments

  • 084.png
    084.png
    94.1 KB · Views: 200
  • 085.png
    085.png
    90.3 KB · Views: 230
  • 086.png
    086.png
    92.5 KB · Views: 202
Last edited:
AVGAIN is the 3 byte read/write register. I am writing 0xAA value to this register but am reading back 0 from the register.
All of these I2C communication look fine. It might be the case that the device is not accepting your write because you only wrote 1 byte, but the register is 3 bytes. Can you try writing three bytes, something like this:

Code:
void write_meter_3byte(int add, uint32_t data)
{
  uint8_t target = 0x38; // slave addr
  Serial.print("---------------------------------------------------\n");
  Serial.print("I2C WRITE 3 bytes\n");

  Wire.beginTransmission(target); // slave addr
  Wire.write((add>>8)); // memory address
  Wire.write(add); // memory address
  Wire.write(data>>16); // data
  Wire.write(data>>8); // data
  Wire.write(data); // data
  Wire.endTransmission(I2C_STOP); // blocking I2C Tx
  print_i2c_status(); // print I2C final status
}

Then call it with something like:
Code:
write_meter_3byte(AVGAIN, 0x112233);

I did not try for the multi master one i shall try this, but is there any setting to be made for multi master mode and how sequencing has to be done (Is that both controller has to signal each other for sequencing?)
You don't need to do anything special, just connect the two masters and the slave to the same wires. Then only use one master at a time. The other master will ignore the communication.

I do notice that you are using internal pullups, which are very low resistance. Just make sure your resulting voltage levels are switching low enough (make sure the scope shots look similar to as they are now). If the other master device has trouble with pulling the voltage low, then use a higher value external resistor (~2k or so).
 
I did the test with 2 masters at a time.

Initially
1st master is the NXP89v51rd2:
The o/p i read from AVRMS register was
1) For 230V, AVRMS register value was 10095

2nd master is the Teensy3.1:
The o/p i read from AVRMS register was
1) For 230V, AVRMS register value was 308154

I am surprised i did the test again as follows.

1st master is the NXP89v51rd2:
The o/p i read from AVRMS register was
1) For 230V, AVRMS register value was 10095
2) For 0V, AVRMS register value was 9

2nd master is the Teensy3.1:
The o/p i read from AVRMS register was
1) For 230V, AVRMS register value was 10095
2) For 0V, AVRMS register value was 9

Now its working fine, but i do not know the reason why it was not reading correct values earlier. I am just guessing, would it be the h/w loose connection?
Note: I was using 12K external pullup and using it now also(No change in h/w ckt).
 
Normal and fast mode I2C pins can sink a max of 3mA per spec. So at 3.3V the theoretical minimum value for a pull-up resistor is 1.1K. The 2K value nox71 suggested is a save value and should work perfectly at 100kHz and 400kHz I2C bus frequency.
 
Now its working fine, but i do not know the reason why it was not reading correct values earlier. I am just guessing, would it be the h/w loose connection?
Note: I was using 12K external pullup and using it now also(No change in h/w ckt).

Actually you are using about 200 ohm pullups due to this:
Code:
Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, [COLOR="#FF0000"]I2C_PULLUP_INT[/COLOR], I2C_RATE_100);
That is why the waveforms look so square. For external, change it to I2C_PULLUP_EXT instead.

Regarding it now working, where it did not before - Is your original NXP89v51rd2 master sending it any kind of initialization sequence, or other startup code? The ADE7880 looks like a very complex part, does it not require any initialization code?

All the prior screenshots look ok as far as I2C communication goes, it did not look corrupted or mangled, so there must be some other activity which is controlling its behavior.
 
void write_meter(int add, int data)
{
uint8_t target = 0x38; // slave addr
Serial.print("---------------------------------------------------\n");
Serial.print("I2C WRITE 1 byte at MemAddr 0\n");

Wire.beginTransmission(target); // slave addr
Wire.write((add>>8)); // memory address
Wire.write(add); // memory address
Wire.write(data >> 8); // Added this code
Wire.write(data); // data
Wire.endTransmission(I2C_STOP); // blocking I2C Tx (NOSTOP)
print_i2c_status(); // print I2C final status
}
Wire.write(data >> 8); // Added this line

After this modification data are received successfully.

Thanks for the support.
 
ADE7880 interface with arduino...

Dear all,
I am student .i am trying to communicate ADE7880 with Arduino. using i2c bus i found the slave device but i cant communicate with chip can anybody send me the procedure for write the correct code....
#include <Wire.h>
void setup()
{
Serial.begin (9600); //***** make sure serial monitor baud matches *****
while (!Serial)
{
}
Serial.println ("I2C scanner. Scanning ...");
byte count = 0;
delay (5);
Wire.begin();
for (byte i = 1; i < 126; i++)
{
Wire.beginTransmission (i);
if (Wire.endTransmission () == 0)
{
Serial.print ("Found address: ");
Serial.print (i, DEC);
Serial.print (" (0x");
Serial.print (i, HEX);
Serial.println (")");
count++;
delay (1); // maybe unneeded?
} // end of good response
} // end of for loop

Serial.println ("Done.");
Serial.print ("Found ");
Serial.print (count, DEC);
Serial.println (" device(s).");

Serial.println("Starting....");

Wire.beginTransmission(0x38); //i2c lock
Wire.write(0xEC01);
Wire.write(0x00000000);
Wire.endTransmission();
Serial.println("1");

Wire.beginTransmission(0x38); //i2c lock
Wire.write(0xE60E);
Wire.write(0x00000000);
Wire.endTransmission();
Serial.println("1");


Wire.beginTransmission(0x38);//CONFIG2
Wire.write(0xE60f);
Wire.write(0x00000000);
Wire.endTransmission();
Serial.println("2");

Wire.beginTransmission(0x38); //CONFIG3
Wire.write(0xEA00);
Wire.write(0x00000000);
Wire.endTransmission();
Serial.println("3");

Wire.beginTransmission(0x38); //CONFIG3
Wire.write(0xE702);
Wire.write(0x00000000);
Wire.endTransmission();
Serial.println("4");

Wire.beginTransmission(0x38); //CONFIG3
Wire.write(0xE618);
Wire.write(0x00000000);
Wire.endTransmission();
Serial.println("5");

Wire.beginTransmission(0x38); //CONFIG3
Wire.write(0xE822);
Wire.write(0x0001);
Wire.endTransmission();

} // end of setup

void loop()
{
int c;
int j=0;

Wire.requestFrom(0x38,4,1);
Wire.write(0xE401);

while (Wire.available())
{
c = Wire.read(); // receive a byte as character
Serial.print(c); // print the character
}
delay(5000);
// Serial.println("A");



}




/*
#include <SPI.h>



void setup()
{
// set the 10 as an output:
pinMode(10, OUTPUT);
// initialize SPI:
SPI.begin();

digitalWrite(10, LOW);
digitalWrite(10, HIGH);
digitalWrite(10, LOW);
digitalWrite(10, HIGH);
digitalWrite(10, LOW);
digitalWrite(10, HIGH);
}

void loop()
{
digitalWrite(10, LOW); //checp select LOW to start SPI write
SPI.transfer(0XE7);
SPI.transfer(0XFE);
SPI.transfer(0XAD);
SPI.transfer(0XE7);
SPI.transfer(0XE3);
SPI.transfer(0X80);
Serial.println(" Data memory RAM");
digitalWrite(10, HIGH);

delay(10);
digitalWrite(10, LOW);
SPI.transfer(0XE7); // ENEBALE DATA MEMORY RAM by writing 0xAD to 0xE7FE register and 0x80 to 0xE7E3 register
SPI.transfer(0XFE);
SPI.transfer(0XAD);
SPI.transfer(0XE7);
SPI.transfer(0XE3);
SPI.transfer(0X80);
Serial.println(" Data memory RAM is Enabled w/c is we can write to register value");
digitalWrite(10,HIGH);
delay(10);
}

/*
void digitalPotWrite(int address, int value)
{
// take the SS pin low to select the chip:
digitalWrite(10, LOW);
// send in the address and value via SPI:
SPI.transfer(address);
SPI.transfer(value);
// take the SS pin high to de-select the chip:
digitalWrite(10, HIGH);
}*/

Thanks........
 
Status
Not open for further replies.
Back
Top