Buggy I2C Bus still with v1.5.3 IDE

Hi Guys,

I've downloaded v1.5.3 IDE thinking that all the I2C bugs would have been ironed out...

I have the following code which works fine on the MEGA board but not on my due board. I'm using a ADXL345 acceleration sensor and just reading the z-axis for testing purposes. The error starts to occur when I flip the sensor around to the negative z-axis. As you can see from the data below it reads the data OK in the positive direction but when flipped it gives me a large meaningless number. When using I2C the DUE does not appear to handle negative numbers or is it just the way I have coded it?

158
153
143
133
126
126
118
113
96
69
61
35
32
2
65508
65468
65452
65429
65391
65372
65341
65335
65305
65297
65282
65314
65352
65397

Here is my code.

#include <Wire.h>

const int ADXL345_I2C_ADDRESS = 0x53;

int POWER_CTL = 0x2D;
int DATA_FORMAT = 0x31;
int DATAZ0 = 0x36;
int DATAZ1 = 0x37;

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  ADXL345_setup(DATA_FORMAT, 0x08);
  ADXL345_setup(POWER_CTL, 0x08);
}

void loop()
{
  Serial.println(ADXL345_read());
  delay(250);
}

void ADXL345_setup(int I2C_address, int configVal) 
{
  Wire.beginTransmission(ADXL345_I2C_ADDRESS);
  Wire.write(I2C_address);
  Wire.write(configVal);
  Wire.endTransmission();
}

int ADXL345_read() 
{
  Wire.beginTransmission(ADXL345_I2C_ADDRESS);
  Wire.write(DATAZ0);
  Wire.endTransmission();
  Wire.requestFrom(ADXL345_I2C_ADDRESS, 2);
  byte LSB_z = Wire.read();
  byte MSB_z = Wire.read();
  return ((int)MSB_z << 8) | LSB_z;
}

Any help in this area would be much appreciated. Before anybody asks I have accounted for the DUE's internal pull-ups...

The DUE is a 32-bit processor so this statement:

return ((int)MSB_z << 8) | LSB_z;

returns a positive 32-bit number.

Pete

One way to fix it:

  return ((int)MSB_z << 8) | LSB_z | ((MSB_z & 0x80) ? 0xFFFF0000:0 );

Pete

Thanks a million Pete, it works a treat!

Apart from fixing the issue I'm struggling to understand the syntax used in the last part

? 0xFFFF0000:0

What does "?" do as I have never seen this used as a bitwise operator.

How does "0xFFFF0000:0" differ from "0xFFFF0000" Not understanding the ":0" part. Sorry I can't stop from making an angry face but I think you know what I mean :).

Cheers

Adam

adamatcooranbong:
Thanks a million Pete, it works a treat!

Apart from fixing the issue I'm struggling to understand the syntax used in the last part

? 0xFFFF0000:0

What does "?" do as I have never seen this used as a bitwise operator.

How does "0xFFFF0000:0" differ from "0xFFFF0000" Not understanding the ":0" part. Sorry I can't stop from making an angry face but I think you know what I mean :).

Cheers

Adam

The colon part is part of the ternary operator ?:, and not part of the integer constant:

The ternary operator tests test, and if it is non-zero (true), it returns true_value, else if it is 0 (false), it returns false_value:

test ? true_value : false_value

So in the example:

((MSB_z & 0x80) ? 0xFFFF0000:0 )

If MSB_z has the top bit in the lower 8-bits set, it will or in all 1's in the upper 16-bits, which given MSB_z is put into the bits just before it, will sign extend the value.

Another way to write the code is:

return (((int)(int8_t)MSB_z) << 8) | LSB_z;

The (int8_t) cast first converts MSB_z to be a signed 8 bit type, and then the (int) cast converts that type to the default int size (16-bits on AVRs, 32-bits on ARMs). Or instead of int8_t, you can use signed char, which is the same thing on most modern machines. There were machines in the past that had different sized chars than 8 bits (but most of those machines predated the int_t and uint_t types that are defined in the Arduino environment.

Thanks Michael. With your comments and Google I now know how to use "?". Kind of like an if statement!

Your way of coding makes more sense to me initially i.e. (int)(int8_t).

Cheers

Adam

adamatcooranbong:
Thanks Michael. With your comments and Google I now know how to use "?". Kind of like an if statement!

Your way of coding makes more sense to me initially i.e. (int)(int8_t).

And depending on the instruction set of the machine, it is likely faster to do it with explicit casts, than with the ? : operator.

Since the compiler knows (or at least is supposed to know) the low level details of the machine, if you can express things in that fashion, the compiler has a better chance of optimizing it.

Some cpus have explicit sign extend instructions, which the compiler can do the operation in a single instruction.

If they don't, often times you can do sign extension by shifting the value to the left and and then using an arithmetic right shift to duplicate the sign bit.

If they don't have a fast arithmetic shift right, but have a fast conditional move and bit test operation, they could use conditional move to do the sign extension: (((x) & 0x80) ? -x : x).

On the machines that don't have convenient arithmetic right shifts or fast conditional move (like the Data General MV/Eclipse that I worked on 30+ years ago), you can use: (((x & 0xff) ^ 0x80) - 0x80) to sign extend a byte (and if you have a load byte instruction that zero extends, you can eliminate the & 0xff).

Hi Michael,

Wow, you sound like you have been doing coding for a while... I'm just a beginner at it. I did a bit of MATLAB programming in my engineering degree but I really think Australian universities should offer C/C++.

I got away with using

((int16_t)MSB << 8) | LSB;

Would be nice for Arduino to post the full set of C methods and data types their IDE/Compiler supports.

For people starting out it's hard know what's in and what's out.

The issue you are running into is because with the AVR cpus ints are 16 bits vs 64 bits on the Due (ARM). So a number such as 65508 is a positive number as an int on the Due. The upper bit isn't set in this situation. (bit 63) However, This number is a negative on a AVR based board because the upper bit is set with this value. (bit 15) On the Due if you want a 16 bit signed number, use short.

short ADXL345_read() 
{
  Wire.beginTransmission(ADXL345_I2C_ADDRESS);
  Wire.write(DATAZ0);
  Wire.endTransmission();
  Wire.requestFrom(ADXL345_I2C_ADDRESS, 2);
  byte LSB_z = Wire.read();
  byte MSB_z = Wire.read();
  return ((MSB_z << 8) | LSB_z);
}

Something else to observe. Unless there is a reason, storing a byte in an int (16 or 64 bits) is wasting space. Assuming that these won't go beyond a byte in size.
e.g.
int POWER_CTL = 0x2D;
int DATA_FORMAT = 0x31;
int DATAZ0 = 0x36;
int DATAZ1 = 0x37;

Greg

adamatcooranbong:
Hi Michael,

Wow, you sound like you have been doing coding for a while... I'm just a beginner at it. I did a bit of MATLAB programming in my engineering degree but I really think Australian universities should offer C/C++.

I have been working professionally as a compiler developer since 1979. I've worked on various GCC ports since 1988 with a 2 year gap when I was at Metrowerks (currently I'm working on the powerpc). I was on the original ANSI C standards committee (X3J11). So yes, I've been around the track a few times.