3

I am trying to connect an Arduino to a digital accelerometer. The Arduino receives commands from a mobile app and transmits over Bluetooth through hardware serial. I also posted this question here.

Code:

 #include < SPI.h >

const int X8 = 0x06;
const int Y8 = 0x07;
const int Z8 = 0x08;

/*
bits
1 0  mode 00: standby, 01: measurement
5     1: spi 3 wire, 0: 4 wire
4      self test
3     2 glevel: 01 2g
1 0  mode, 00 standby, 01 measurement 
*/
const int MODE = 0x16;

const int STATUS = 0x09;

//7 digital filter band width, 0 62.5 Hz, 1 125 Hz
const int CONTROL = 0x18;

bool isTesting = false;

const int chipSelectPin = 7;

void setup() {
    Serial.begin(9600);
    SPI.begin();
    pinMode(chipSelectPin, OUTPUT);

    //set up standby, spi 4 wire, 2g range, 
    writeRegister(MODE, 0x04);

    //set up speed
    writeRegister(CONTROL, 0x00);

    delay(100);
}
void loop() {
    if (getSerial() == 't') {
        isTesting = true;
        writeRegister(MODE, 0x05); // 0000 0101- 0001 0101 0x15
        testing();

    }

}
void testing() {
    while (isTesting) {
        if ((readRegister(STATUS) & 1) == 1) {
            Serial.print((char) readRegister(X8), DEC);
            Serial.print("x");
            Serial.print((char) readRegister(Y8), DEC);
            Serial.print("y");
            Serial.print((char) readRegisterChar(Z8), DEC);
            Serial.print("z");
        }

        if (getSerial() == 'u') {
            isTesting = false;
            writeRegister(MODE, 0x04);
        }
    }
}
char getSerial() {
    if (Serial.available()) {
        char c = Serial.read();
        return c;
    }
    return 'x';
}
byte readRegister(byte thisRegister) {
    byte inByte = 0;
    SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); // 500khz clock
    digitalWrite(chipSelectPin, LOW);
    SPI.transfer(thisRegister << 1);
    inByte = SPI.transfer(0x00);
    digitalWrite(chipSelectPin, HIGH);
    return inByte;
}
char readRegisterChar(byte thisRegister) {
    char inChar = 0;
    SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); // 500khz clock
    digitalWrite(chipSelectPin, LOW);
    SPI.transfer(thisRegister << 1);
    inChar = SPI.transfer(0x00);
    digitalWrite(chipSelectPin, HIGH);
    return inChar;
}
void writeRegister(byte thisRegister, byte value) {
    SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); // 500khz clock
    digitalWrite(chipSelectPin, LOW);
    SPI.transfer(128 | thisRegister << 1);
    SPI.transfer(value);
    digitalWrite(chipSelectPin, HIGH);
}

The sensor allows for data values of 10 bits but for simplicity I am getting only the 8 bit data. The data received when the sensor is on a flat surface is:

x y z

5 -11 74

4 -19 74

2 -9 69

0 -8 68

7 -16 68

7 -11 73

enter image description here

When the self test is run, the results are:

-5 -20 122

-4 -22 122

-1 -20 123

-4 -20 123

-5 -21 121

-3 -18 123

-8 -22 127

From the data sheet: "When the self-test function is initiated through the mode control register ($16), accessing the "self-test" bit, an electrostatic force is applied to each axis to cause it to deflect. The Z-axis is trimmed to deflect 1g."

enter image description here

Is the data correct? How is it interpreted? If the z data during self test is 1g, shouldn't it have the same magnitude as when it is on a flat surface?

I adapted to code to get the 10 bits of data:

const int X10_LSB = 0x00;
const int X10_MSB = 0x01;
const int Y10_LSB = 0x02;
const int Y10_MSB = 0x03;
const int Z10_LSB = 0x04;
const int Z10_MSB = 0x05;

//[...]

void testing() {
        while (isTesting) {
            if ((readRegister(STATUS) & 1) == 1) {

                int xData = (readRegister(X10_MSB) << 8) | readRegister(X10_LSB);

                // checks if bit 9 is 1 and sign extends
                if ((xData & 512) == 512)
                    xData = (0b1111110000000000 | xData);
                int yData = (readRegister(Y10_MSB) << 8) | readRegister(Y10_LSB);
                if ((yData & 512) == 512)
                    yData = (0b1111110000000000 | yData);
                int zData = (readRegister(Z10_MSB) << 8) | readRegister(Z10_LSB);
                if ((zData & 512) == 512)
                    zData = (0b1111110000000000 | zData);

                Serial.print(xData);
                Serial.print("x");
                Serial.print(yData);
                Serial.print("y");
                Serial.print(zData);
                Serial.print("z");

            }

The outputs for 2g measurement is:

4 246 -183

5 245 -183

5 245 -182

6 245 -181

For the self test:

252 -20 -134

253 -21 -133

253 -21 -135

253 -20 -135

the values for 8 and 10 bits of data don't correspond. What am I doing wrong?

Update: I rewrote the code to get the 10 bits of data with the LSB bits fisrt as per a comment:

Code:

     int xData =(readRegister (X10_LSB));
        xData = xData | (readRegister (X10_MSB)<<8);
        xData = xData << 6;
        int yData =(readRegister (Y10_LSB));
        yData = yData | (readRegister (Y10_MSB)<<8);
        yData = yData << 6;
        int zData = (readRegister (Z10_LSB));
        zData = zData | (readRegister (Z10_MSB)<<8);
        zData = zData << 6;

        xData /= 64;
        yData /= 64;
        zData /= 64;

The results for the sensor when on a flat surface:

0 -4 71

4 -11 70

5 -13 70

2 -12 70

The results when self-test is applied:

-2 -19 123

-2 -19 122

-4 -20 122

-3 -21 122

Why would the 8 bit and 10 bit results return similar values during a self test (around 128)? Should the 10 bit value be 4X greater?

2
  • Why didn't you tell us that it is about a MMA7455L sensor and that you are also asking the question here: forum.arduino.cc/index.php?topic=465471.0 Have you tried a library that is known to be working well ? You could try to shift the 10 bits 6 bits to the left for a 2's complement 16 bit integer. You read MSB first, but according to the datasheet: "XOUTH should be read directly following XOUTL read".
    – Jot
    Commented Mar 25, 2017 at 22:26
  • Have you tried this link sketch from the Arduino Playground?
    – chrisl
    Commented Jan 25, 2018 at 11:10

1 Answer 1

2

The sensor allows for data values of 10 bits but for simplicity I am getting only the 8 bit data

you are using it in 2g mode, so you have only 8bit of data.

The data received when the sensor is on a flat surface is:

this is correct. You have a 8bit range (256 value, from -128 to +128) that map a range from +2g to -2g. So we have 256lsb/4g = 64lsb/g.
Motionless, the lenght of the gravity vector (sqrt(xx+yy+z*z)) must be close to 64. Of corse you need some offset and non-linearity calibration but the data seems fine.
Also if you look at the piece of datasheet you posted in table "Sensitivity", you will see they call the LSB "count", and they are in the range we calculated!

When the self test is run, the results are:

again this is correct, if your sensor is flat in way the Gravity apply on the Z; what you see is the normal gravity (64) summing up to the self-test offset (64), giving you a nice ~128

I adapted to code to get the 10 bits of data:

Why would the 8 bit and 10 bit results return similar values during a self test (around 128)? Should the 10 bit value be 4X greater?

to use 10bit, you must set the full scale range to +-8g. If you look at the piece of datasheet you posted, table "Output Signal", you see how many bit are avaiable for each range.
If you think about it, how does the sensor know if you want to read 8 or 10bit? (remeber the DS say you must read the low register BEFORE high register!) if you read only 8bit, you would be loosing the 2msb, one rapresenting the sign and the other the most important bit that can change your result by 2^9=512 btw also you first 10 bit conversion is wrong as you flip the bit for 2's complement, but does nothing to the value bit.
The solution with bit shitf of 6 follow by divising is working but a bit strange to see. I would go for something more "standard" like

int xData = readRegister (X10_LSB) | (readRegister (X10_MSB) << 8);
if (xData | 0b0000 0010 0000 0000){ //if negative
    //keep 9 bit raspresenting the value and multiply by -1 to keep the sign
    xData = (xData & 0b0000 0001 1111 1111) * -1;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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