0

Just to illustrate my work here, I found a tutorial on how to code an accelerometer, I wanted to understand how does it work, literally get how it works, and not simply using the code without understanding it.

In the beginning of the program,there's a char variable declared as :

char values[10];

then later on the code, there was couple lines of getting the information stored in this variable, I'll give this as as example :

x = ((int)values[1]<<8)|(int)values[0];

we want X to receive values[0] and values[1] , that's what that line does up there.

My question is : can someone explain to me how does that line work, and why can't we simply write it as x = ((int)values[1])|(int)values[0]; ?

  • char values[10] is actually declaring an array of 10 chars - but none of them are assigned values (presumably the values get set when reading data from the sensor). Those array values are accessed using values[0] ..to.. values[9]. So when it uses (int)values[1] it is referring to the second char in the array (and casting it to an int). – KennetRunner Sep 14 '16 at 13:33
3

To understand this you have to start thinking in binary.

The | operator is "Perform a binary OR on these two values".

The <<8 is "Shift this value 8 bits to the left".

If you have two 8-bit values, say (in hex) 0x37 and 0x42, and you OR them together you get:

0x37 = 00110111 
0x42 = 01000010 
--------------- |
0x77 = 01110111

In each column where there is a 1 in either value you get a 1 in the result.

Now if you shift the first number 8 bits to the left, you end up with:

0x3700 = 0011011100000000 
0x0042 =         01000010 
------------------------- |
0x3742 = 0011011101000010

You do this when a 16-bit value has been split into two 8-bit values and you want to join them back together again into the original 16-bit value.

2

up in the beginning of the program, there's a char variable declared as: char values[10];

This is not a “char variable”, it's an “array of 10 chars”.

((int)values[1]<<8)|(int)values[0]

Majenko's answer perfectly explains the intent of the author of the tutorial, but that is not necessarily what the code actually does. As it stands, this piece of code is buggy. Or, to say the least, it has a serious portability problem: it will probably work fine on ARM-based boards, but it will fail on AVR-based boards, and on x86.

The reason is that, in C and C++, the type char can be either signed or unsigned, depending on the platform. On most platforms, including AVR, it is signed. This implies that, when a char is converted to int, it undergoes sign extension instead of being just zero padded. To put it graphically, let's assume the sign bit (most significant bit) of values[0] is s, then:

values[0]      =         sxxxxxxx
(int)values[0] = sssssssssxxxxxxx

Here I am assuming an AVR (16-bit ints). If the sign bit happens to be zero, this will work as intended. This is the case in Majenko's example. But if we replace 0x42 with 0xa2, we will have:

values[1]         =   0x37 =         00110111
(int)values[1]    = 0x0037 = 0000000000110111
(int)values[1]<<8 = 0x3700 = 0011011100000000
values[0]         =   0xa2 =         10100010
(int)values[0]    = 0xffa2 = 1111111110100010

Then, the bitwise OR gives

0x3700 = 0011011100000000 
0xffa2 = 1111111110100010 
------------------------- |
0xffa2 = 1111111110100010

and the data in values[1] is lost.

Conclusion: values should be declared as an array of unsigned chars, as this will enforce zero-padding instead of sign extension. Always use unsigned types when doing bit manipulations. And you probably should find a better tutorial to learn this kind of stuff.

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.