6

I'm using the Arduino's site test code for getting raw values from a MPU-6050 accelerometer + gyro. In the loop, there are this lines for getting the next data from its registers:

Wire.requestFrom(MPU_addr, 14, true);  // request a total of 14 registers

AcX = Wire.read()<<8 | Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
AcY = Wire.read()<<8 | Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
AcZ = Wire.read()<<8 | Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
Tmp = Wire.read()<<8 | Wire.read();  // 0x41 (TEMP_OUT_H)   & 0x42 (TEMP_OUT_L)

GyX = Wire.read()<<8 | Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
GyY = Wire.read()<<8 | Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
GyZ = Wire.read()<<8 | Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

Hex numbers indicates, of course, the address of each register. However, I can't get how, if the first instruction only requests from the MPU_addr (constant), it changes to the consecutive registers. Is it that <<8| makes something like a sum or a logic OR to switch the register to read?

Regards!

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81
Julio
  • 113
  • 2
  • 3
  • 11

2 Answers2

5

Just adding a couple of points to Michel Keijzers's answer.

The expression Wire.read() << 8 takes the value returned by Wire.read(), which is an int, and shifts it 8 bits to the left, which is equivalent to multiplying by 256. If you are using a 32-bit microcontroller (e.g. an Arduino Due), this is safe. However, most Arduinos are based on an 8-bit AVR chip. On them, an int is 16-bits and can only hold values up to 32767. If the initial value is larger than 127, then the bit shift causes a signed integer overflow, which in C and C++ is undefined behavior (read: “illegal operation”). It is likely that everything runs fine and you get the expected result, but it is unwise to ever rely on undefined behavior in C or C++.

The proper way to do the shift is to cast the initial value to an unsigned data type, which is always safe v.s. overflows, and then do the shift, like in

((uint16_t) Wire.read()) << 8

You may omit the outer parentheses if you know your operator priorities.

The second point is about the already mentioned fact that the order of the reads is not specified. The simplest fix is to do one read per statement, as in:

AcX = (uint16_t) Wire.read() << 8;  // first read MSB
AcX |= Wire.read();                 // then LSB

It is worth noting that the first statement has an implicit cast from uint16_t (the result of the shift) to int16_t (the type of AcX). This can also overflow if the shifted value is larger than 32767. However, an overflow happening during a conversion in not undefined, but rather implementation defined behavior. This means that the authors of the compiler must decide and document what happens. Arduino code is usually compiled by gcc, and the gcc docs document this behavior as

The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 and C11 6.3.1.3).

For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.

This is exactly what we want here, and since it is the most efficient choice on a two’s complement architecture, it seems unlikely that another compiler would make a different choice.

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81
2

1 << 3 means the value 1 shifted 3 times to the left. So 1 in binary is 0000001, shifted left 3 places is 00001000. And 0001101 << 3 will be 1101000.

The | command is a logic or (where bits will be 1 if one of the bits is 1, e.g. 001100 | 001001 = 001101.

Probably every Wire.read() gives the next byte. For the first line: Acx = Wire.read() << 8 | wire.read() means one (mostly the first byte) is read, it is shifted to the left one byte and the other (mostly next byte) read is placed in the right. So it results in the first two read bytes to be positioned in AcX. It is semantically the same as Wire.read() * 256 + Wire.read(), but normally a bit shift is easier for a computer, same as | and the + operator.

Michel Keijzers
  • 13,014
  • 7
  • 41
  • 58