1
\$\begingroup\$

I am programming an AVR chip (atmega1284p) with c code, I am using SPI to read in 4 bytes from a chip on my circuit. I would like to take those 4 bytes and return them as an unsigned long.

I can print the bytes to the serial port and I have received logical values, which I can calculate by hand to get the correct value. I then try to do this with programming and I get very peculiar results. Here is the code snippet.

Note: returnBytes is a 4 character array with the correct character values. Also, I am using a bitmask to make sure that the shift doesn't have anything weird going on -- just incase (works the same with and without bitmask).

unsigned long returnLong = (returnBytes[3]&0xFF)<<24;
returnLong |= (returnBytes[2]&0xFF)<<16;
returnLong |= (returnBytes[1]&0xFF)<<8;
returnLong |= (returnBytes[0]&0xFF);

return returnLong;

What I get from this is peculiar, in that it should be counting from 4.2bil to 0. It starts at around 4.2 bil, generally counts down and then drops to 30,000 after a couple of counts, then jumps back to 4.2bil after a couple of counts. (Yes the counter will wrap back around once it hits 0).

I am certain that the bytes are correct and my method of printing the unsigned long is correct.

Here is the output of this code (First line is bytes 3 2 1 0, Second line is the value of the unsigned long)

255 254 230 157
Counter Value: 4294960797
255 253 206 25
Counter Value: 4294954521
255 252 182 90
Counter Value: 4294948442
255 251 158 153
Counter Value: 4294942361
255 250 134 159
Counter Value: 4294936223
255 249 110 139
Counter Value: 28299
255 248 87 207
Counter Value: 22479
255 247 65 170
Counter Value: 16810

Hopefully this is enough info.

\$\endgroup\$
  • \$\begingroup\$ Just create a UNION in c. Read the bytes in as bytes, and addres the long as a long. No conversions necessary \$\endgroup\$ – Scott Seidman Aug 10 '13 at 17:57
6
\$\begingroup\$

There are two possible answers to this as a processor can be bigendian meaning that the byte with the lowest address is the most significant or littlendian meaning that this byte is the least significant.

I'll assume the the bytes are stored in an array

unsigned char a[4];

and we want the result in

unsigned long retval;

If the system is bigendian

retval  = (unsigned long) a[0] << 24 | (unsigned long) a[1] << 16;
retval |= (unsigned long) a[2] << 8 | a[3]; 

If the system is littlendian

retval  = (unsigned long) a[3] << 24 | (unsigned long) a[2] << 16;
retval |= (unsigned long) a[1] << 8 | a[0];

Note the casts are necessary because otherwise you lose bits. For example

retval = a[0] << 8;

Should always be zero assuming that an unsigned char is 8 bit because

a[0] << 8

Will shift all 8 bits out of the 8 bit variable and they will be lost be lost because the compiler is doing 8 bit arithmetic. The resulting answer 0 will then be written into the 32bit long.

Having the cast changes the number in a[0] into a 32bit value first so the shift does not lose these bits.

\$\endgroup\$
  • \$\begingroup\$ I continued working on this after asking the question and I figure out what the problem was -- your note hits it exactly. My chip is littlendian, so that is all correct, however the cast was my problem. I figured out that when I shifted, it dumps the bits because the char is only 8 bits. I added casts and it works perfectly, thanks. \$\endgroup\$ – Jake Aug 10 '13 at 18:31

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.