after finally finding out how to create a software SPI connection i now can successfully read the data from an ADXL345 acceleration sensor. But they look rather strange to me, this are some sample values:

X: -252 Y: -532 Z: 9616

These values are measured in +-16g mode with full precision (so basically we have 4mg/LSB). These values are in mG (e.g a value of 1000 would represent 1G). I don't quite understand why the Z-value is so high?

I initialize the ADXL345 with this code:

write_command = ((0x00 | 0x31) << 8) | 0b01001011;//set SPI 3wire & 16g range & full-res
write_command = ((0x00 | 0x2D) << 8) | 0x08; //Enable measurements


I send the values over uart to an UART / USB converter. The values are changing if i start to move the sensor board so i think my reading part is correct:

//read X-Axis

//convert in milli-G [4.0mg/LSB] in +/- 16g mode @ full-res
x = ((x1 << 8) | x0);
y = ((y1 << 8) | y0);
z = ((z1 << 8) | z0);

*x_val = x * 4;
*y_val = y * 4;
*z_val = z * 4;


Do you have any ideas what i do wrong at the data processing part?

• It seems a reading in m/s^2... verify that the registers got the correct configuration. – NicolaSysnet Apr 29 '16 at 14:47
• What do you mean? Which registers? – binaryBigInt Apr 29 '16 at 14:56
• You did show only the evaluation of the write_command. You should have called something like writeRegister(write_command) to issue that command. Then you could, eventually, read the value of the register with x0 = a=readRegister(0x80 | 0x31); and see if it agrees with the value you wrote. – NicolaSysnet Apr 29 '16 at 15:02
• We need to know the format of the numbers being read in, especially byte order. Be careful about the endianness of your variables. – Scott Seidman Apr 29 '16 at 15:12
• Post your entire code. For example, is your example printout the raw value "z" or the scaled value "z_val?" We can't tell. Also, be careful with your units - you mention "1000 would be 1g," but that is 1000 mg is 1g, 1g would be represented by 250 LSB (1000mg / 4) as the raw readout from the ADXL345. Also, check your datatypes. If z0 and z1 are uint8_t types (since your reads will always return 8 bits), you will lose all z1 data in your (z1 << 8) | z0 conversion and you'll need to typecast first. Lastly, check axis orientation. Your X printout looks suspiciously like 1g on its side. :) – Joel Wigton Apr 29 '16 at 16:10

Putting my original comment into an answer now that you've pasted your code here.

1. My suspicion was right that you are losing your upper byte by shifting them away, because you've declared z0/z1 as a uint8_t. So (z0 << 8) always turns into 0x00. Make these uint16_t instead.
2. I think your communication is probably OK, like you mentioned, but one way to test it for sure is to check the WHO_AM_I register. Most devices have something like this (might name it differently). The ADXL345 WHO_AM_I register is at 0x00 and it's hardcoded to 0xE5 (or 345 octal, yuk yuk, clever Analog Devices engineers). First thing your enableADXL() function should do is check this register and look for the hard-coded answer as expected. If it doesn't match, your communications channel is broken so no point continuing.
3. Since it appears you are printing out x, y, and z (not x_val, y_val, and z_val), 1g will be about 250 LSB, so again double-check that your device isn't on its side because your X-axis value is -252.

But I think #1 will fix you.

• Ha! I didn't notice the 345 octal :) – bitsmack Apr 29 '16 at 19:28
• Oh, and the OP is printing x_val, y_val, and z_val, but has used local (to main.c) x, y, and z variable names in his call to the readXYZ() function. – bitsmack Apr 29 '16 at 19:31
• As I said in a comment before i can read the DEVID successfully. And i use x,y,z as parameters for the function readXYZ. So basically the function stores the x/y/z axis values in the parameters i call it with. – binaryBigInt Apr 29 '16 at 22:29
• Good call, I missed that but see it now. So how did it go trying my first suggestion of making all the xyz/01 variables as uint16_t? The problem is still that your shift occurs before the assignment to "z," so z1 data is already lost by the time you OR in z0. – Joel Wigton Apr 29 '16 at 23:21
• I changed my code to this: pastebin.com/DLSDKvbc but it is still not working. Do you have any ideas why? – binaryBigInt Apr 30 '16 at 15:38

I think that your implicit conversions might be messing you up. This is from the code in your link:

    uint8_t x0 = 0;
uint8_t x1 = 0;

int16_t x = 0;

//convert in milli-G [4.0mg/LSB] in +/- 16g mode @ full-res
x = ((x1 << 8) | x0);

*x_val = x * 4;


Since x1 is an uint8_t, the (x1 << 8) statement shifts all the bits away, leaving only zeros. Instead, try this typecast:

    x = (((uint16_t)(x1) << 8) | x0);


Or, you could just declare x1 as a uint16_t.

Similarly, depending on your compiler, you may need to explicitly typecast the uint16_t value to a int16_t value when assigning it to x. Altogether, this would look like:

x = (int16_t)(((uint16_t)(x1) << 8) | x0);

• I changed the datatype from x1, y1 and z1 to uint16_t but that didn't help. Also casting the value to int16_t didn't change anything :( – binaryBigInt Apr 29 '16 at 22:31
• @bismack Any ideas what i could do to fix the wrong number problem? – binaryBigInt Apr 30 '16 at 12:06