I2C help needed

I am tring to understand and learn I2C better and in doing so have come accross the following question.
I am using part of the I2C example code on the Arduino Learning website for the WIre Library SFR_Ranger reader.
My particular I2C device has a 14-bit reolution and the data is stored in Registers 254 and 255. In 255 the bits 6 and 7 are not used, hence the 14 bits.
Question 1)
I am confused about why I read register 255 in the code with 2 bytes, I would have thought it should read register 254 first but that just gives garbage.

Question 2)
In the statement reading = reading << 6;    // shift high byte to be high 8 bits I read the correct 14 bit data with range of 0 - 16383.
If I change the 6 to an 8 as in original code, which i understand is 16 bit, the output range is 0 - 65535. While I assume this is just the 14 bit data interpolated into the 16 bit range so resolution would not be any greater, My question is how does it do this? It is now reading 8 bits from each register but 2 of the bits are unused in the first register.

The answer is probably staring me in the face but i dont get it.

here is the working code.

#include <Wire.h>

void setup()
{
  Wire.begin();                // join i2c bus (address optional for master)
  Serial.begin(9600);          // start serial communication at 9600bps
}

unsigned int reading = 0;

void loop()
{
 
 Wire.beginTransmission(66); // transmit to device #112
 Wire.write(byte(255));      // sets register pointer to echo #1 register (0x02)
 Wire.endTransmission();      // stop transmitting

  // step 4: request reading from sensor
  Wire.requestFrom(66, 2);    // request 2 bytes from slave device #112

  // step 5: receive reading from sensor
  if(2 <= Wire.available())    // if two bytes were received
  {
    reading = Wire.read();  // receive high byte (overwrites previous reading)
    reading = reading << 6;    // shift high byte to be high 8 bits
    reading |= Wire.read(); // receive low byte as lower 8 bits
    Serial.println(reading);   // print the reading
  }

  delay(300);                  // wait a bit since people have to read the output :)
    
}

A lot depends on the specifications of your device. Not all 14-bit resolution device are exactly alike. Can you point to the datasheet?

Hi John
Thank you.
http://www.ams.com/eng/Products/Magnetic-Position-Sensors/Magnetic-Rotary-Position-Sensors/AS5048B

btw, the I2C table is on pg21. The device also does SPI and PWM but the I2C information is very minimal.

Wire.beginTransmission(66); // transmit to device #112

Is it 66 or 112?
The datasheet says that some of the I2C address bits are set by OTP so without actually getting it to respond you don't know what any individual device's address will be.
Have you run an I2C bus scanner to see what responds?
Run Nick's I2C scanner sketch which is near the bottom of this page http://www.gammon.com.au/forum/?id=10896

Pete

The datasheet doesn't show the ability to do sequential reads. I would read the two registers separately:

    Wire.beginTransmission(66); // transmit to device #112
    Wire.write(byte(254));      // sets register pointer to angle MSB
    Wire.endTransmission();      // stop transmitting
    Wire.requestFrom(66, 1);    // request 2 bytes from slave device #112
    if(Wire.available())
        reading = Wire.read();  // receive high byte
    reading <<= 6;    // shift MSB to be bits 6-13.

    Wire.beginTransmission(66); // transmit to device #112
    Wire.write(byte(255));      // sets register pointer to angle LSB
    Wire.endTransmission();      // stop transmitting
    Wire.requestFrom(66, 1);    // request 2 bytes from slave device #112
    if(Wire.available())
        reading |= (Wire.read() & 0x3F); // receive low six bits

    Serial.println(reading);   // print the reading

    delay(300);                  // wait a bit since people have to read the output :)

Is it 66 or 112?

Sorry, the comments were from original code. It is 66 that can communicate with. I have the manual bits set to A1=0 and A2 = 1 so according to the data sheet the defalt address is 10000 plus A2 A1 which is 1000010 = 66 and this works.
I tried the I2C scanner but it does not find any device.

The datasheet doesn't show the ability to do sequential reads. I would read the two registers separately:

When I try that code, it seems to be random data that doesnt change logically when I rotate the device.
3543
407
2826
2425
2930
2610
2482
2610
3183
1571
1630
990
1758
1306
656
716
2827
2186
767
53
3891
3699
3571
807
1058
801
736
601
538
730
794
602
538

Using my posted code I get good data smoothly changing as i rotate the device
14
74
76
75
220
579
801
1160
1473
1691
2005
2256
2427
2663
2832
3024
3251
3520
4038
4303
4624
4914
5220
5922
6707
7407
7946
8529
9568
10686
11401
12014
12632
12637
12640
12718
13130
13520
14093
14244
14463
14802
14697
14698
I am just trying to understand why I only get this result when reading register 255 with 2 bytes? How does it get the readings from 254 in there.

I tried the I2C scanner but it does not find any device.

The scanner tries every possible valid address so if it doesn't find anything how can you be getting a valid response?

Pete

I really do not know, All i can tell you is that it says not found. If I try the scanner with an LCD I have i see it operates correctly and finds the LCD Address.
On the data sheet is says you can program the address but it only allows 1 time. I have not done this as yet so is it possible that it will not answer an inquiry (Whatever the scanner is sending) but will respond to a write command if the correct default address is used?

I'm a bit confused about the relationship between the numbers 66 and 112.

Your explanation of which two bits are not used seems implausible. Are you sure it is the most
significant 2 bits of the low byte which are unused ?

You may need to be shifting the first byte you read by 8 bits, not 6 bits, if that is the case.
If you only shift the first byte you read by six bits, then the second byte you read will overwrite
the least significant two bits of the first byte, which is probably not what you intended.

I'd suggest printing the actual hex value of the two bytes which you read to the serial monitor
and examining them closely, to check.

You may need to be shifting the first byte you read by 8 bits,  not 6 bits,

If i do that then the output goes to 16-bit (0 - 65535) which is clearly incorrect.

I'm a bit confused about the relationship between the numbers 66 and 112.

As I said above, the 112 is part of the comments from the arduino code example, I changed the address to 66

So I gather you are getting valid readings, and your question is "why am I getting valid readings?"

I've used this device using SPI and you ask for address 0xFFFF and then read 2 bytes, which looks like what is happening for you.

So I gather you are getting valid readings, and your question is "why am I getting valid readings?"

Hi Nick. Yes exactly. At least it appears i am getting valid readings. I just dont understand that if the data is contained in 254 and 255, why it only works when I call 255? I am not calling 254 at all but it seems to be getting the data from it.

Here is the HEX output with the bits shifted 6
0, E
0, 34
1, 6C
3, C7
3, F8
5, 153
6, 1B6
9, 242
C, 312
F, 3DD
12, 483
15, 543
16, 5A1
17, 5D6
1A, 699
1B, 6F7
1F, 7C9
23, 8D4
25, 964
28, A29
2A, AA7
2D, B7C
33, CCC
35, D70
39, E46
3D, F4E
41, 1065
45, 1144
4A, 1292
4E, 139F
55, 1575
5E, 17BF
63, 18CA
6C, 1B06
72, 1C93
78, 1E2B
78, 1E2B
78, 1E17
82, 2083
8D, 2378
96, 259B
A1, 2844
AA, 2A82
B0, 2C00
B5, 2D58
BD, 2F40
C8, 322F
D4, 3518
E1, 386C
ED, 3B4A
F3, 3CD7
F4, 3D10
F4, 3D0E
F9, 3E4D
'
Here is the decimal output
0, 18
2, 130
8, 522
15, 975
24, 1582
34, 2237
45, 2936
58, 3723
72, 4640
89, 5730
98, 6315
112, 7223
128, 8231
132, 8470
132, 8471
139, 8936
153, 9849
170, 10894
181, 11597
190, 12190
200, 12846
215, 13766
225, 14435
228, 14642
233, 14968
240, 15399
247, 15836
251, 16116
255, 16347

with this code

#include <Wire.h>

void setup()
{
  Wire.begin();                // join i2c bus (address optional for master)
  Serial.begin(9600);          // start serial communication at 9600bps
}

unsigned int reading = 0;
int reading1 = 0;
void loop()
{
 
 Wire.beginTransmission(66); // transmit to device #66
 Wire.write(byte(255));      // sets register pointer to echo #1 register
 Wire.endTransmission();      // stop transmitting

  // step 4: request reading from sensor
  Wire.requestFrom(66, 2);    // request 2 bytes from slave device #112

  // step 5: receive reading from sensor
  if(2 <= Wire.available())    // if two bytes were received
  {
    reading = Wire.read();  // receive high byte (overwrites previous reading)
    Serial.print(reading);   // print the reading
    Serial.print(", ");
    reading = reading << 6;    // shift high byte to be high 8 bits
    reading |= Wire.read(); // receive low byte as lower 8 bits
    Serial.println(reading);   // print the reading
  }

  delay(300);                  // wait a bit since people have to read the output :)
}

Their documentation is pretty obscure, and your results agree, as I say, with the way that the SPI interface works. This is my SPI code, and it reads two bytes:

// for parity checks
const unsigned int masks [16] = { 
      0x01, 0x02, 0x04, 0x08, 
      0x10, 0x20, 0x40, 0x80, 
      0x100, 0x200, 0x400, 0x800, 
      0x1000, 0x2000, 0x4000, 0x8000 };

unsigned int AS5048A::Transfer (const unsigned int command)
  {
  byte hi, lo;

  SPI.setDataMode (SPI_MODE1);
  digitalWrite (chipSelect_, LOW);
  hi = SPI.transfer (highByte (command));
  lo = SPI.transfer (lowByte (command));
  digitalWrite (chipSelect_, HIGH);
   
  unsigned int result = makeWord (hi, lo);
  
  // calculate parity
  byte parity = 0;
  for (byte i = 0; i < 16; i++)
    {
    if (result & masks [i])
      parity++;
    }
  // even parity, so low-order bit should be zero
  if (parity & 1)
    {
    parityFlag_ = true;
    errorFlag_ = false;  // we actually don't know the error flag state
    }
  else
    {
    parityFlag_ = false;
    errorFlag_ = (result & EF) != 0;
    }
  return result;
    
  } // end of AS5048A::Transfer

Thanks Nick.
As usual you are such an asset to this forum.
So it seems my output must be correct then. It sure seems correct as I rotate the magnet.
Does your I2C Scanner find your device?

Lsnyman:
Thanks Nick.
As usual you are such an asset to this forum.
So it seems my output must be correct then. It sure seems correct as I rotate the magnet.
Does your I2C Scanner find your device?

Thanks! :slight_smile:

No, in my case it was configurecd as an SPI device (and soldered onto a board that I can't change). I'm a little surprised that the scanner doesn't find it, however if the figures are correct presumably it is there.