4

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!

  • 3
    Left shift 8 bits, then bitwise OR. Turns two 8-bit values into one 16-bit value. – Majenko Mar 27 '17 at 20:46
  • Just trying to find the duplicate I know is there (cus I answered it). Closest I can find right now is: arduino.stackexchange.com/questions/19243/and-syntax-in-arduino/… – Majenko Mar 27 '17 at 20:48
  • 4
    One important thing to note though is that the use of Wire.read() inside the shifting and ORing like that is not good. There is no guarantee that the Wire.read() functions will be called in the right order. You should read into variables first then work with those variables. en.cppreference.com/w/cpp/language/eval_order – Majenko Mar 27 '17 at 20:52
  • I see, it's a point I'll have to considerate with my own design. Though, actually, I prefer to work with variables instead of functions. Thanks for the advice! – Julio Mar 27 '17 at 21:04
  • 1
    In practice it is usually ok, but there is no guarantees. And what works in one version of the compiler may stop in another. Also Arduino playground isn't the most reliable of sources for good quality code. – Majenko Mar 27 '17 at 21:23
6

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.

  • Such a great explanation! – opu 웃 Aug 8 '18 at 3:43
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.

  • I got it, it seems as you said that it composes a word with those two bytes, because item's datasheet indicates that in each pair, one register has the high bits and the other one the low bits. Thank you. – Julio Mar 27 '17 at 21:15
  • You are welcome, and yes, the high 8 bits go into the first part of the pair, and the next (low) 8 bits go in the second part. – Michel Keijzers Mar 27 '17 at 21:47
  • 3
    “For the first line: Acx = Wire.read() << 8 | wire.read() means the first byte is read, it is shifted to the left one byte and the next byte read is placed in the right” is incorrect. It should instead say, “For the first line: Acx = wire.read() << 8 | wire.read() means that two bytes are read, one of them is used as MSB, and the other as LSB”. That is, according to the C standard N1570 §6.5.2, without parenthesization it is undefined which wire.read() is first and which is second. Of course it often is the case they are used in the order you mentioned, but it's undefined here. – James Waldby - jwpat7 Mar 27 '17 at 22:27
  • @jwpat7 yes you are right ... I will update the answer – Michel Keijzers Mar 27 '17 at 23:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.