4

I'm doing a small embedded project where I have 40 bits transferred through a SPI type interface. I pull these bits off of a 32 bit bus and place the upper 32 bits into a uint32_t variable and the lower 8 bits into a uint8_t variable. I'm trying to combine them into a single uint64_t. However when I shift by 8, it drops the top 8 bits. Here is my code.

uint64_t getError()
{
    uint32_t * disp_addr = (uint32_t*)(MYDISPLAY);

    uint64_t error_upper;
    uint8_t error_lower;
    uint64_t error= 0;

    error_lower = *(disp_addr+1);
    error_upper = *(disp_addr+0);
    error = ((uint64_t) error_upper) <<8 | error_lower;
    return error;
}

This code is working except for the fact that it's dropping my top 8 bits. Any thoughts or hints would be greatly appreciated. Thanks.

edit

uint64_t getError()
{
    uint32_t * disp_addr = (uint32_t*)(MYDISPLAY);

    uint64_t error_upper;
    uint8_t error_lower;
    uint64_t error= 0;

    error_lower = 0x34;
    error_upper = 0xABCDEF12;
    error = ((uint64_t) error_upper) <<8 | error_lower;
    printf("%010x", error);
    //return error;
}

Results: 00cdef1234

SparkyGuru
  • 43
  • 4
  • 1
    What would be the desired behavior? – moffeltje May 20 '15 at 14:46
  • 2
    Of course it is dropping.. Where would they go? – Eugene Sh. May 20 '15 at 14:53
  • Cast the upper bits var before shifting. – Weather Vane May 20 '15 at 14:53
  • It should concatenate error_upper and error_lower in the lower 40 bits of my uint64_t error. I get that if I left shift a 32 bit int the top bits would drop off. but if I typecast it as a 64 bit int and shift it, shouldn't it move those bits into positions 33-40? – SparkyGuru May 20 '15 at 14:56
  • @WeatherVane Cast to what? – Eugene Sh. May 20 '15 at 14:56
  • my original code was error = ((uint64_t) error_upper<<8) | error_lower; but that didn't work either. – SparkyGuru May 20 '15 at 14:58
  • Maybe your hardware doesn't handle 64-bit values as a whole but splits them somehow and loses the bits? Can you do a shift of **only** `error_upper` without the `| error_lower` and see if you still lose the top bits? – Andrew Henle May 20 '15 at 15:02
  • @EugeneSh. mybad - misread it. – Weather Vane May 20 '15 at 15:03
  • @AndrewHenle, that is an interesting idea. I've already tried doing a shift strictly on error_upper and it still drops the bits, which would suggest maybe my hardware doesn't support it. – SparkyGuru May 20 '15 at 15:04
  • I doubt it is a hardware issue. It would be helpful if you hardcode some values, run the code and show the results. – Eugene Sh. May 20 '15 at 15:05
  • @SparkyGuru Please try to provide more information about which CPU and compiler that is used. The error cannot be reproduced with the code posted. I would suspect some compiler oddity on the given system, maybe a glitchy `uint64_t` handling mechanism on a 32 bit compiler. – Lundin May 20 '15 at 15:07
  • As @Lundin, cannot reproduce. Were the bits there in the first place? – Weather Vane May 20 '15 at 15:07
  • Btw the endianess of your SPI interface and/or display is not necessarily the same as the endianess of your CPU. SPI data is often in big endian format and MCUs may have any endianess. But I don't see how this would cause the mentioned issue. – Lundin May 20 '15 at 15:11
  • Your printf format needs to be `%0llX`. – Weather Vane May 20 '15 at 15:12
  • You are just not printing it right. use `%llx` – Eugene Sh. May 20 '15 at 15:13
  • @WeatherVane right, I had a typo before, my printf format was %010x. – SparkyGuru May 20 '15 at 15:14
  • 1
    `printf("%010x", error);` is printing `00cedf1234`. `printf("%010llx", error);` is printing `abcedf1234`. I think we can close the question here.. – Eugene Sh. May 20 '15 at 15:17
  • Indeed... the printf functions are evil. Before posting questions like this, view your actual data in the debugger maybe? – Lundin May 20 '15 at 15:20

3 Answers3

4

The printf format specifier is incorrect.

#include <stdio.h>

typedef unsigned __int64 uint64_t;
typedef unsigned char uint8_t;

int main(void) {
    uint64_t error_upper, error;
    uint8_t error_lower;

    error_lower = 0x34;
    error_upper = 0xABCEDF12;
    error = (error_upper << 8) | error_lower;
    printf("%x\n", error);
    printf("%llx\n", error);
    return 0;
}

Program output:

cedf1234
abcedf1234
Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • when I try printf("%llx\n", error); I get 500000ffffffffff. – SparkyGuru May 20 '15 at 15:29
  • @WeatherVane `double L` is what I used. – SparkyGuru May 20 '15 at 15:34
  • @SparkyGuru if I run that exact code that's the output from the two formats. LPs deleted his recent comment, I deleted my reply. – Weather Vane May 20 '15 at 15:36
  • 1
    `%x` requires an argument of type `unsigned int`; using it with a `uint64_t` argument has undefined behavior. `%llx` requires an argument of type `unsigned long long int`; using it with a `uint64_t` argument has undefined behavior *unless* `uint64_t` is defined as `unsigned long long int` (which it commonly is). `` defines macros for `printf` formats for the types defined in `` -- or you can convert to `uintmax_t` and use `%ju`: `printf("%ju\n", (uintmax_t)x);` – Keith Thompson May 20 '15 at 15:38
  • @KeithThompson Thanks, I think your comment on expected arguments helped the most. I'm kind of a noob programmer. I'm an EE student thrown into a programming role. – SparkyGuru May 20 '15 at 15:47
0

Why are you saying that the upper byte is cut? If I use your code and print the result is ok:

uint32_t temp = 0x01020304;
uint32_t *disp_addr = &temp;
uint64_t error_upper;
uint8_t error_lower;
uint64_t error= 0;

error_lower = *(disp_addr+1);
error_upper = *(disp_addr+0);
error = (error_upper<<8) | error_lower;

printf("\n%08X%08X\n",  (uint32_t)(error>>32), error);

Output is

0000000102030401

Are you using %d to printout the value?

LPs
  • 16,045
  • 8
  • 30
  • 61
-1

Look carefully at

uint32_t *disp_addr = &temp;
...
error_lower = *(disp_addr+0);

Because disp_addr is a ptr to uint32_t you are storing a 32bit value in a 8bit variable. Depending on your machine endiannes and how you load disp_addr you may be loading the wrong data.

You probably wanted to do:

uint32_t *disp_addr = &temp;
...
error_lower = *(uint8_t *)(disp_addr+0);

which is not the same.