Search code examples
c++sensors

C++: Calculate acceleration value from 2 complement


I'm switching from the Arduino IDE to ESP IDF and I want to test an accelerometer (LIS2DW12). Initially, I had problems with the hardware-related programming. In the meantime I'm getting good values, but I need a factor of 10 for this. It`s a 14 bit value, stored in 2 8bit registers. I've searched some forums and have been working on it for a few days. But I haven't been able to find out, why I'm wrong by a factor of 10. What am I doing wrong? I am grateful for any reference! Register LIS2DW12 Datasheet

#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
#include "Arduino.h"
#include "string.h"

static const char *TAG = "LIS2DW12: ";

#define I2C_MASTER_SCL_IO           22             /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO           21             /*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM              1              /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ          400000         /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE   0              /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE   0              /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS       1000


// Sensor Reg
#define SENSOR_ADDR                 0x19        /*!< Slave address of the MPU9250 sensor */
#define WHO_AM_I_REG_ADDR           0x0F        /*!< Register addresses of the "who am I" register */

#define Temp_L 0x0D
#define Temp_H 0x0E
#define WhoAmI 0x0F
#define CTRL1  0x20
#define CTRL2  0x21
#define CTRL3  0x22
#define CTRL4  0x23
#define CTRL5  0x24
#define CTRL6  0x25

#define X_LSB 0x28
#define X_MSB 0x29
#define Y_LSB 0x2A
#define Y_MSB 0x2B
#define Z_LSB 0x2C
#define Z_MSB 0x2D

uint16_t X_raw, Y_raw, Z_raw;
float X, Y, Z;
int ix,iy,iz;

void Sensor_data_test(){
    
    byte buf[2];
    
    // read register
    register_read(Z_LSB, buf, 2);

    Z_raw = buf[1]<<8 | buf[0];        // buf = MSB <<8 | LSB;

    // print buffer
    Serial.println("Sensor Data:");   
    Serial.print("buf0: ");Serial.print(buf[0]); 
    Serial.println();
    Serial.print("buf1: ");Serial.print(buf[1]); 
    Serial.println();
    Serial.print("Z uint16_t : ");Serial.print(Z_raw); 
    Serial.println();

    // calculate binary and print
    uint16_t Zh = Z_raw;
    String hz;
    while(Zh!=0) 
    {
        hz=(Zh%2==0 ?"0":"1")+hz; 
        Zh/=2; 
    }
    Serial.print("Z bin : ");Serial.println(hz); 


    //calculate 2'er complement
    int k = 0;
    if(Z_raw & (((uint16_t)0x1) << 15))     //check if binary is negativ
    {                                       //if negativ, do
        Z_raw = ~Z_raw;                     //invert bits
        Z_raw = Z_raw + 1;                  //add one bit
        Z_raw = Z_raw >> 2;                 //shift 2 spaces to the rigt (caused by Sensor register)          
        k = Z_raw * -1;                     //save as Integer an reverse sign
    }
    else{                                   //if positiv, do
        Z_raw = Z_raw >> 2;                 //shift to spaces to the rigt (caused by Sensor register)
        k = Z_raw;                          //save as Integer
    }

    if (k){ 
        Serial.print("Z int: ");Serial.print(k); 
        Serial.println();
    }

    //acceleration value ============================================

    Z= (float)k *16/8191;   // factor 10 needed?

    Serial.print("Z accel: ");Serial.print(Z); 
    Serial.println();

}

void Set_Sensor_Config(){
    register_write_byte(CTRL1,0x55);
    register_write_byte(CTRL2,0x04);
    register_write_byte(CTRL3,0x00);
    register_write_byte(CTRL4,0x00);
    register_write_byte(CTRL5,0x00);
    register_write_byte(CTRL6,0x34);
}

void Show_Sensor_Config(){
    uint8_t buf[1];
    ESP_LOGI(TAG, "LIS12DW Config: ");
    register_read(CTRL1, buf, 1);
    ESP_LOGI(TAG, "CTRL1 = %X", buf[0]);
    register_read(CTRL2, buf, 1);
    ESP_LOGI(TAG, "CTRL2 = %X", buf[0]);
    register_read(CTRL3, buf, 1);
    ESP_LOGI(TAG, "CTRL3 = %X", buf[0]);
    register_read(CTRL4, buf, 1);
    ESP_LOGI(TAG, "CTRL4 = %X", buf[0]);
    register_read(CTRL5, buf, 1);
    ESP_LOGI(TAG, "CTRL5 = %X", buf[0]);
    register_read(CTRL6, buf, 1);
    ESP_LOGI(TAG, "CTRL6 = %X", buf[0]);
}

extern "C" void app_main()
{
    Serial.begin(115200);

    ESP_ERROR_CHECK(i2c_master_init());
    ESP_LOGI(TAG, "I2C initialized successfully");

    Set_Sensor_Config();
    Show_Sensor_Config();

    Sensor_data_test();

    ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM));
    ESP_LOGI(TAG, "I2C de-initialized successfully");
    
    
}

Output:

I (336) LIS2DW12: : LIS12DW Config: 
I (336) LIS2DW12: : CTRL1 = 55
I (337) LIS2DW12: : CTRL2 = 4
I (341) LIS2DW12: : CTRL3 = 0
I (344) LIS2DW12: : CTRL4 = 0
I (348) LIS2DW12: : CTRL5 = 0
I (351) LIS2DW12: : CTRL6 = 34
Sensor Data:
buf0: 40
buf1: 248
Z uint16_t : 63528
Z bin : 1111100000101000
Z int: -502
Z accel: -0.98
I (367) LIS2DW12: : I2C de-initialized successfully

I wrote a function that reads out the Z acceleration registers and converts the data into a value [m/s²]. I expect a value of about 9.81 but I get 0.98 (acceleration of gravity).

Edit: I forgot to mention that I set the range to 16g. And the resolution are 13bit (8191).

Z= (float)k *16/8191;   // factor 10 needed?

Solution

  • I was about to write that it still doesn't work. But while I was trying to write my questions and formatting my code, I found my mistake. Thanks to deviantgeek for your help. I followed the sample code and finally got it to work.

    You cannot/should not approach the matter manually. Because even I can't understand the factors yet. If someone gets an incorrect value, then look at this function "lis2dw12_from_fs2_to_mg". There are various factors that depend, for example, on the range (2g, 4g,...) I used this code as "template": lis2dw12_read_data_single.c

    This code works for 16g. It meassures only Z, but the other directions are the same

    int32_t accel_z_raw_get(int16_t *val)
    {
        uint8_t buff[2];
        int32_t ret;
        if(STATUS & 0x01){     //check if Data ready to read // #define STATUS 0x27
    
            ret = register_read(Z_LSB, buff, 2);
            *val = (int16_t)buff[1];
            *val = (*val * 256) + (int16_t)buff[0];
          }
          return ret;
    }
    
    float_t lis2dw12_from_fs2_lp1_to_mg(int16_t lsb)
    {
      return ((float_t)lsb) * 0.488f;
    }
    
    static int16_t data_raw_z[1];
    static float accel_z_mg[1];
    
    void get_sensor_data(){
    
        char str[25];
    
        accel_z_raw_get(data_raw_z);
    
        accel_z_mg[0] = lis2dw12_from_fs2_lp1_to_mg(data_raw_z[0]);
    
        sprintf((char *)str, "acceleration Z: % 4.2f\t\r\n", accel_z_mg[0]);
    
        puts(str);
    
    }
    
    extern "C" void app_main()
    {
        Serial.begin(115200);
    
        ESP_ERROR_CHECK(i2c_master_init());
        ESP_LOGI(TAG, "I2C initialized successfully");
    
        Set_Sensor_Config();
        
        Show_Sensor_Config();
    
        get_sensor_data();
    
        ESP_ERROR_CHECK(i2c_driver_delete(I2C_MASTER_NUM));
        ESP_LOGI(TAG, "I2C de-initialized successfully");
    }
    

    The output is: -981.86 That´s correct, because the sensor measures in mg`s. The negativ sign is ok, caus the sensor is upsidedown...

    I (336) LIS2DW12-Register: LIS12DW Config: 
    I (336) LIS2DW12-Register: CTRL1 = 55
    I (338) LIS2DW12-Register: CTRL2 = 4
    I (342) LIS2DW12-Register: CTRL3 = 0
    I (347) LIS2DW12-Register: CTRL4 = 0
    I (351) LIS2DW12-Register: CTRL5 = 0
    I (355) LIS2DW12-Register: CTRL6 = 34
    I (359) LIS2DW12-Register: STATUS = 1
    acceleration Z: -981.86
    

    Thank you for your help! This forum is great!