This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Get X,Y,Z values from LIS3DH using nrf52 development board

Hi!

I am trying to interface LIS3DH module (https://www.adafruit.com/product/2809) with nRF52 DK, SDK 11, via i2c/spi. I tested on Arduino UNO & it gave x,y,z values. So, I decided to write nrf driver for LIS3DH using Adafruit LIS3DH driver(https://github.com/adafruit/Adafruit_LIS3DH/) but I am getting errors like i2c begin & how/what to write to control registers to read data again.

Please suggest me how to write driver or if general LIS3DH driver is available then reply ASAP.

LIS3DH.h

#include <LIS3DH.h>
#include "nrf_drv_twi.h"
#include "nrf_delay.h"

/** Sensor types */
typedef enum
{
  SENSOR_TYPE_ACCELEROMETER         = (1),   /**< Gravity + linear acceleration */
  SENSOR_TYPE_MAGNETIC_FIELD        = (2),
  SENSOR_TYPE_ORIENTATION           = (3),
  SENSOR_TYPE_GYROSCOPE             = (4),
  SENSOR_TYPE_LIGHT                 = (5),
  SENSOR_TYPE_PRESSURE              = (6),
  SENSOR_TYPE_PROXIMITY             = (8),
  SENSOR_TYPE_GRAVITY               = (9),
  SENSOR_TYPE_LINEAR_ACCELERATION   = (10),  /**< Acceleration not including gravity */
  SENSOR_TYPE_ROTATION_VECTOR       = (11),
  SENSOR_TYPE_RELATIVE_HUMIDITY     = (12),
  SENSOR_TYPE_AMBIENT_TEMPERATURE   = (13),
  SENSOR_TYPE_VOLTAGE               = (15),
  SENSOR_TYPE_CURRENT               = (16),
  SENSOR_TYPE_COLOR                 = (17)
} sensors_type_t;

/** struct sensors_vec_s is used to return a vector in a common format. */
typedef struct {
 
        float v[3];
        struct {
            float x;
            float y;
            float z;
    
            float roll;    /**< Rotation around the longitudinal axis (the plane body, 'X axis'). Roll is positive and increasing when moving downward. -90�<=roll<=90� */
            float pitch;   /**< Rotation around the lateral axis (the wing span, 'Y axis'). Pitch is positive and increasing when moving upwards. -180�<=pitch<=180�) */
            float heading; /**< Angle between the longitudinal axis (the plane body) and magnetic north, measured clockwise when viewing from the top of the device. 0-359� */
    
					int8_t status;
    uint8_t reserved[3];
} sensors_vec_t;

/** struct sensors_color_s is used to return color data in a common format. */
typedef struct {

        float c[3];
        /* RGB color space */

            float r;       /**< Red component */
            float g;       /**< Green component */
            float b;       /**< Blue component */
  
  
    uint32_t rgba;         /**< 24-bit RGBA value */
} sensors_color_t;

/* Sensor event (36 bytes) */
/** struct sensor_event_s is used to provide a single sensor event in a common format. */
typedef struct
{
    int32_t version;                          /**< must be sizeof(struct sensors_event_t) */
    int32_t sensor_id;                        /**< unique sensor identifier */
    int32_t type;                             /**< sensor type */
    int32_t reserved0;                        /**< reserved */
    int32_t timestamp;                        /**< time is in milliseconds */

        float           data[4];
        float           temperature;          /**< temperature is in degrees centigrade (Celsius) */
        float           distance;             /**< distance in centimeters */
        float           light;                /**< light in SI lux units */
        float           pressure;             /**< pressure in hectopascal (hPa) */
        float           relative_humidity;    /**< relative humidity in percent */
        float           current;              /**< current in milliamps (mA) */
        float           voltage;              /**< voltage in volts (V) */
     
 
} sensors_event_t;

/* Sensor details (40 bytes) */
/** struct sensor_s is used to describe basic information about a specific sensor. */
typedef struct
{
    char     name[12];                        /**< sensor name */
    int32_t  version;                         /**< version of the hardware + driver */
    int32_t  sensor_id;                       /**< unique sensor identifier */
    int32_t  type;                            /**< this sensor's type (ex. SENSOR_TYPE_LIGHT) */
    float    max_value;                       /**< maximum value of this sensor's value in SI units */
    float    min_value;                       /**< minimum value of this sensor's value in SI units */
    float    resolution;                      /**< smallest difference between two values reported by this sensor */
    int32_t  min_delay;                       /**< min delay in microseconds between events. zero = not a constant rate */
} sensor_t;




typedef enum
{
  LIS3DH_RANGE_16_G         = 3,   // +/- 16g
  LIS3DH_RANGE_8_G           = 2,   // +/- 8g
  LIS3DH_RANGE_4_G           = 1,   // +/- 4g
  LIS3DH_RANGE_2_G           = 0    // +/- 2g (default value)
} lis3dh_range_t;

typedef enum
{
  LIS3DH_AXIS_X         = 0x0,
  LIS3DH_AXIS_Y         = 0x1,
  LIS3DH_AXIS_Z         = 0x2,
} lis3dh_axis_t;


  // These must be defined by the subclass
  void enableAutoRange(bool enabled) {};
 

  int16_t readADC(uint8_t a);

  void setRange(lis3dh_range_t range);
  lis3dh_range_t getRange(void);

  void setDataRate(lis3dh_dataRate_t dataRate);
  lis3dh_dataRate_t getDataRate(void);

  bool getEvent(sensors_event_t *event);
  void getSensor(sensor_t *sensor);

  uint8_t getOrientation(void);

  void setClick(uint8_t c, uint8_t clickthresh, uint8_t timelimit = 10, uint8_t timelatency = 20, uint8_t timewindow = 255);
  uint8_t getClick(void);

  int16_t x, y, z;
  float x_g, y_g, z_g;


  uint8_t readRegister8(uint8_t reg);
  void writeRegister8(uint8_t reg, uint8_t value);
  uint8_t spixfer(uint8_t x = 0xFF);


  int32_t _sensorID;
  int8_t  _i2caddr;

  // SPI
  int8_t _cs, _mosi, _miso, _sck;

/**************************************************************************/
/*!
    @brief  Setups the HW (reads coefficients values, etc.)
*/
/**************************************************************************/
bool begin() {



  /* Check connection */
  uint8_t deviceid = readRegister8(LIS3DH_REG_WHOAMI);
  if (deviceid != 0x33)
  {
    /* No LIS3DH detected ... return false */
    //Serial.println(deviceid, HEX);
    return false;
  }

  // enable all axes, normal mode
  writeRegister8(LIS3DH_REG_CTRL1, 0x07);
  // 400Hz rate
  setDataRate(LIS3DH_DATARATE_400_HZ);

  // High res & BDU enabled
  writeRegister8(LIS3DH_REG_CTRL4, 0x88);

  // DRDY on INT1
  writeRegister8(LIS3DH_REG_CTRL3, 0x10);

  // Turn on orientation config
  //writeRegister8(LIS3DH_REG_PL_CFG, 0x40);

  // enable adcs
  writeRegister8(LIS3DH_REG_TEMPCFG, 0x80);

  /*
  for (uint8_t i=0; i<0x30; i++) {
    Serial.print("$");
    Serial.print(i, HEX); Serial.print(" = 0x");
    Serial.println(readRegister8(i), HEX);
  }
  */

  return true;
}


void read(void) {
  // read x y z at once

  if (_cs == -1) {
    // i2c
	nrf_drv_twi_tx(&p_twi_instance,LIS3DH_DEFAULT_ADDRESS,LIS3DH_REG_OUT_X_L,sizeof(LIS3DH_REG_OUT_X_L), true);// PEDO_WRITE(data,6);
	nrf_delay_ms(5);
	do{
	nrf_drv_twi_rx(&p_twi_instance,LIS3DH_DEFAULT_ADDRESS,LIS3DH_REG_OUT_X_L, sizeof(LIS3DH_REG_OUT_X_L), false);
		
	}while(LIS3DH_REG_OUT_X_L[1]!=0x80);
	
    I2Cinterface->requestFrom(_i2caddr, 6);
    x = I2Cinterface->read(); x |= ((uint16_t)I2Cinterface->read()) << 8;
    y = I2Cinterface->read(); y |= ((uint16_t)I2Cinterface->read()) << 8;
    z = I2Cinterface->read(); z |= ((uint16_t)I2Cinterface->read()) << 8;
  } 
  #ifndef __AVR_ATtiny85__
  else {
    if (_sck == -1)
      SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
    digitalWrite(_cs, LOW);
    spixfer(LIS3DH_REG_OUT_X_L | 0x80 | 0x40); // read multiple, bit 7&6 high

    x = spixfer(); x |= ((uint16_t)spixfer()) << 8;
    y = spixfer(); y |= ((uint16_t)spixfer()) << 8;
    z = spixfer(); z |= ((uint16_t)spixfer()) << 8;

    digitalWrite(_cs, HIGH);
    if (_sck == -1)
      SPI.endTransaction();              // release the SPI bus

  }
  #endif
  uint8_t range = getRange();
  uint16_t divider = 1;
  if (range == LIS3DH_RANGE_16_G) divider = 1365; // different sensitivity at 16g
  if (range == LIS3DH_RANGE_8_G) divider = 4096;
  if (range == LIS3DH_RANGE_4_G) divider = 8190;
  if (range == LIS3DH_RANGE_2_G) divider = 16380;

  x_g = (float)x / divider;
  y_g = (float)y / divider;
  z_g = (float)z / divider;

}

/**************************************************************************/
/*!
    @brief  Read the auxilary ADC
*/
/**************************************************************************/

int16_t readADC(uint8_t adc) {
  if ((adc < 1) || (adc > 3)) return 0;
  uint16_t value;

  adc--;

  uint8_t reg = LIS3DH_REG_OUTADC1_L + adc*2;

  if (_cs == -1) {
    // i2c
    I2Cinterface->beginTransmission(_i2caddr);
    I2Cinterface->write(reg | 0x80);   // 0x80 for autoincrement
    I2Cinterface->endTransmission();
    I2Cinterface->requestFrom(_i2caddr, 2);
    value = I2Cinterface->read();  value |= ((uint16_t)I2Cinterface->read()) << 8;
  } 
  #ifndef __AVR_ATtiny85__
  else {
    if (_sck == -1)
      SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
    digitalWrite(_cs, LOW);
    spixfer(reg | 0x80 | 0x40); // read multiple, bit 7&6 high

    value = spixfer(); value |= ((uint16_t)spixfer()) << 8;

    digitalWrite(_cs, HIGH);
    if (_sck == -1)
      SPI.endTransaction();              // release the SPI bus
  }
  #endif

  return value;
}


/**************************************************************************/
/*!
    @brief  Set INT to output for single or double click
*/
/**************************************************************************/

void setClick(uint8_t c, uint8_t clickthresh, uint8_t timelimit, uint8_t timelatency, uint8_t timewindow) {
  if (!c) {
    //disable int
    uint8_t r = readRegister8(LIS3DH_REG_CTRL3);
    r &= ~(0x80); // turn off I1_CLICK
    writeRegister8(LIS3DH_REG_CTRL3, r);
    writeRegister8(LIS3DH_REG_CLICKCFG, 0);
    return;
  }
  // else...

  writeRegister8(LIS3DH_REG_CTRL3, 0x80); // turn on int1 click
  writeRegister8(LIS3DH_REG_CTRL5, 0x08); // latch interrupt on int1

  if (c == 1)
    writeRegister8(LIS3DH_REG_CLICKCFG, 0x15); // turn on all axes & singletap
  if (c == 2)
    writeRegister8(LIS3DH_REG_CLICKCFG, 0x2A); // turn on all axes & doubletap


  writeRegister8(LIS3DH_REG_CLICKTHS, clickthresh); // arbitrary
  writeRegister8(LIS3DH_REG_TIMELIMIT, timelimit); // arbitrary
  writeRegister8(LIS3DH_REG_TIMELATENCY, timelatency); // arbitrary
  writeRegister8(LIS3DH_REG_TIMEWINDOW, timewindow); // arbitrary
}

uint8_t getClick(void) {
  return readRegister8(LIS3DH_REG_CLICKSRC);
}


/**************************************************************************/
/*!
    @brief  Sets the g range for the accelerometer
*/
/**************************************************************************/
void setRange(lis3dh_range_t range)
{
  uint8_t r = readRegister8(LIS3DH_REG_CTRL4);
  r &= ~(0x30);
  r |= range << 4;
  writeRegister8(LIS3DH_REG_CTRL4, r);
}

/**************************************************************************/
/*!
    @brief  Sets the g range for the accelerometer
*/
/**************************************************************************/
lis3dh_range_t getRange(void)
{
  /* Read the data format register to preserve bits */
  return (lis3dh_range_t)((readRegister8(LIS3DH_REG_CTRL4) >> 4) & 0x03);
}

/**************************************************************************/
/*!
    @brief  Sets the data rate for the LIS3DH (controls power consumption)
*/
/**************************************************************************/
void setDataRate(lis3dh_dataRate_t dataRate)
{
  uint8_t ctl1 = readRegister8(LIS3DH_REG_CTRL1);
  ctl1 &= ~(0xF0); // mask off bits
  ctl1 |= (dataRate << 4);
  writeRegister8(LIS3DH_REG_CTRL1, ctl1);
}

/**************************************************************************/
/*!
    @brief  Sets the data rate for the LIS3DH (controls power consumption)
*/
/**************************************************************************/
lis3dh_dataRate_t getDataRate(void)
{
  return (lis3dh_dataRate_t)((readRegister8(LIS3DH_REG_CTRL1) >> 4)& 0x0F);
}

/**************************************************************************/
/*!
    @brief  Gets the most recent sensor event
*/
/**************************************************************************/
bool getEvent(sensors_event_t *event) {
  /* Clear the event */
  memset(event, 0, sizeof(sensors_event_t));

  event->version   = sizeof(sensors_event_t);
  event->sensor_id = _sensorID;
  event->type      = SENSOR_TYPE_ACCELEROMETER;
  event->timestamp = 0;

  read();

  event->acceleration.x = x_g * SENSORS_GRAVITY_STANDARD;
  event->acceleration.y = y_g * SENSORS_GRAVITY_STANDARD;
  event->acceleration.z = z_g * SENSORS_GRAVITY_STANDARD;
}

/**************************************************************************/
/*!
    @brief  Gets the sensor_t data
*/
/**************************************************************************/
void getSensor(sensor_t *sensor) {
  /* Clear the sensor_t object */
  memset(sensor, 0, sizeof(sensor_t));

  /* Insert the sensor name in the fixed length char array */
  strncpy (sensor->name, "LIS3DH", sizeof(sensor->name) - 1);
  sensor->name[sizeof(sensor->name)- 1] = 0;
  sensor->version     = 1;
  sensor->sensor_id   = _sensorID;
  sensor->type        = SENSOR_TYPE_ACCELEROMETER;
  sensor->min_delay   = 0;
  sensor->max_value   = 0;
  sensor->min_value   = 0;
  sensor->resolution  = 0;
}


/**************************************************************************/
/*!
    @brief  Low level SPI
*/
/**************************************************************************/

uint8_t spixfer(uint8_t x) {
  #ifndef __AVR_ATtiny85__
  if (_sck == -1)
    return SPI.transfer(x);

  // software spi
  //Serial.println("Software SPI");
  uint8_t reply = 0;
  for (int i=7; i>=0; i--) {
    reply <<= 1;
    digitalWrite(_sck, LOW);
    digitalWrite(_mosi, x & (1<<i));
    digitalWrite(_sck, HIGH);
    if (digitalRead(_miso))
      reply |= 1;
  }
  return reply;
  #endif
}


/**************************************************************************/
/*!
    @brief  Writes 8-bits to the specified destination register
*/
/**************************************************************************/
void writeRegister8(uint8_t reg, uint8_t value) {
  if (_cs == -1) {
    I2Cinterface->beginTransmission((uint8_t)_i2caddr);
    I2Cinterface->write((uint8_t)reg);
    I2Cinterface->write((uint8_t)value);
    I2Cinterface->endTransmission();
  } 
  #ifndef __AVR_ATtiny85__
  else {
    if (_sck == -1)
      SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
    digitalWrite(_cs, LOW);
    spixfer(reg & ~0x80); // write, bit 7 low
    spixfer(value);
    digitalWrite(_cs, HIGH);
    if (_sck == -1)
      SPI.endTransaction();              // release the SPI bus
  }
  #endif
}

/**************************************************************************/
/*!
    @brief  Reads 8-bits from the specified register
*/
/**************************************************************************/
uint8_t readRegister8(uint8_t reg) {
  uint8_t value;

  if (_cs == -1) {
    I2Cinterface->beginTransmission(_i2caddr);
    I2Cinterface->write((uint8_t)reg);
    I2Cinterface->endTransmission();

    I2Cinterface->requestFrom(_i2caddr, 1);
    value = I2Cinterface->read();
  }  
  #ifndef __AVR_ATtiny85__
  else {
    if (_sck == -1)
      SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
    digitalWrite(_cs, LOW);
    spixfer(reg | 0x80); // read, bit 7 high
    value = spixfer(0);
    digitalWrite(_cs, HIGH);
    if (_sck == -1)
      SPI.endTransaction();              // release the SPI bus
  }
  #endif
  return value;
}

Parents
  • Hello,

    I think you should be able to find a driver for the LIS3DH in a couple of cases here on DevZone. However, we have a driver that is included in a separate SDK. However, this SDK is not open source, but I can give you the driver. See if you can use this one.

    /* Copyright (c) 2016 Nordic Semiconductor. All Rights Reserved.
     *
     * The information contained herein is property of Nordic Semiconductor ASA.
     * Terms and conditions of usage are described in detail in NORDIC
     * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
     *
     * Licensees are granted free, non-transferable use of the information. NO
     * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
     * the file.
     *
     */
    
    #include <string.h>
    #ifndef __ICCARM__
    #include <alloca.h>
    #endif
    
    #include "nordic_common.h"
    #include "nrf.h"
    #include "nrf_assert.h"
    #include "nrf_error.h"
    #include "nrf_delay.h"
    #include "app_debug.h"
    #include "app_error.h"
    #include "app_gpiote.h"
    #include "app_twi.h"
    
    #include "drv_acc_lis3dh.h"
    #include "drv_acc.h"
    
    #include "resources.h"
    #include "twi_common.h"
    #include "sr3_config.h"
    
    #define NRF_LOG_MODULE_NAME drv_acc
    #define NRF_LOG_LEVEL CONFIG_ACC_DRV_LOG_LEVEL
    #include "nrf_log.h"
    NRF_LOG_MODULE_REGISTER();
    
    #if CONFIG_ACC_ENABLED
    
    // Check pin configuration.
    STATIC_ASSERT(IS_IO_VALID(CONFIG_IO_ACC_IRQ));
    
    #define ACC_TWI_INSTANCE        (g_twi[CONFIG_ACC_TWI_BUS])
    
    static drv_acc_mode_t           m_acc_mode = DRV_ACC_MODE_UNDEFINED;
    static app_gpiote_user_id_t     m_acc_gpiote;
    
    #if CONFIG_ACC_USE_CLICK_DETECTION
    static drv_acc_callback_t       m_acc_callback;
    #endif
    
    /**@brief Reads one or more consecutive registers from the device.
     *
     * @note This function uses unscheduled I2C transactions - must be called only in MAIN CONTEXT.
     *
     * @param[in]  reg      Register address.
     * @param[out] contents Pointer to data buffer.
     * @param[in]  num_regs Number of registers to be read.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_read_regs(uint8_t reg, uint8_t* contents, uint8_t num_regs)
    {
        app_twi_transfer_t transfers[2];
    
        if (num_regs > 1)
        {
            // Setting auto increment
            reg |= BIT_7;
        }
    
        transfers[0].p_data     = &reg;
        transfers[0].length     = 1;
        transfers[0].operation  = APP_TWI_WRITE_OP(LIS3DH_I2C_ADDRESS);
        transfers[0].flags      = APP_TWI_NO_STOP;
    
        transfers[1].p_data     = contents;
        transfers[1].length     = num_regs;
        transfers[1].operation  = APP_TWI_READ_OP(LIS3DH_I2C_ADDRESS);
        transfers[1].flags      = 0;
    
        return app_twi_perform(ACC_TWI_INSTANCE, transfers, ARRAY_SIZE(transfers), twi_idle_func);
    }
    
    /**@brief Writes one or more consecutive registers to the device. This function uses unscheduled I2C transactions - must be called only in MAIN CONTEXT.
     *
     * @param[in] reg      Register address.
     * @param[in] contents Data to write.
     * @param[in] num_regs Length of data/number of registers that should be written.
     *
     * @return NRF_SUCCESS on success, otherwise error code.
     */
    static ret_code_t lis3dh_write_regs(uint8_t reg, uint8_t* contents, uint8_t num_regs)
    {
        app_twi_transfer_t transfers[1];
    
    #ifndef __ICCARM__
        uint8_t *write_buffer = alloca(num_regs + 1);
    #else
        uint8_t write_buffer[6];
    
        ASSERT(sizeof(write_buffer) >= (num_regs + 1));
    #endif
    
        if (num_regs > 1)
        {
            // Setting auto increment
            reg |= BIT_7;
        }
    
        write_buffer[0] = reg;
        memcpy(&write_buffer[1], contents, num_regs);
    
        transfers[0].p_data     = write_buffer;
        transfers[0].length     = num_regs + 1;
        transfers[0].operation  = APP_TWI_WRITE_OP(LIS3DH_I2C_ADDRESS);
        transfers[0].flags      = 0;
    
        return app_twi_perform(ACC_TWI_INSTANCE, transfers, ARRAY_SIZE(transfers), twi_idle_func);
    }
    
    /**@brief Resets the memory contents to its default state.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_reset(void)
    {
        ret_code_t status;
        uint8_t reg_val;
    
        // Resetting takes time - time-out to complete depends on ODR - see AN3308 application note - page 11.
        // Set fast ODR - 1.6 kHz.
        reg_val = 0x80;
        status = lis3dh_write_regs(CTRL_REG1, &reg_val, 1);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Reboot
        reg_val = 0x80;
        status = lis3dh_write_regs(CTRL_REG5, &reg_val, 1);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        nrf_delay_ms(1);
    
        return NRF_SUCCESS;
    }
    
    /**@brief Read and verify ID.
     *
     * @return NRF_SUCCESS if LIS3DH chip was found, otherwise an error code.
     */
    static ret_code_t lis3dh_verify_id(void)
    {
        ret_code_t status;
        uint8_t reg_val;
    
        status = lis3dh_read_regs(WHO_AM_I, &reg_val, 1);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        return (reg_val == I_AM_LIS3DH) ? NRF_SUCCESS : NRF_ERROR_NOT_FOUND;
    }
    
    /**@brief Reads interrupt source registers, which clears interrupt flag.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_int_clear(void)
    {
        ret_code_t status;
        uint8_t reg_val;
    
        status = lis3dh_read_regs(INT1_SRC, &reg_val, 1);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        status = lis3dh_read_regs(CLICK_SRC, &reg_val, 1);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        /*
         * The LIS3DH needs 1/ODR time to clear the interrupt.
         * As we are using 100 Hz ODR, we have to wait 10 ms here to make sure
         * that the interrupt is no longer signaled.
         */
        nrf_delay_ms(10);
    
        return NRF_SUCCESS;
    }
    
    /**@brief Enables selected interrupts.
     *
     * @param[in] int1_cfg  Contents of the INT1_CFG register.
     * @param[in] click_cfg Contents of the CLICK_CFG register.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_int_enable(uint8_t int1_cfg, uint8_t click_cfg)
    {
        ret_code_t status;
    
        status = lis3dh_write_regs(INT1_CFG, &int1_cfg, 1);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
       return lis3dh_write_regs(CLICK_CFG, &click_cfg, 1);
    }
    
    /**@brief Accelerometer Interrupt handler.
     */
    static void m_acc_interupt_handler(uint32_t const *p_event_pins_low_to_high,
                                       uint32_t const *p_event_pins_high_to_low)
    {
    #if CONFIG_ACC_USE_CLICK_DETECTION
        if ((m_acc_mode == DRV_ACC_MODE_CLICK_DETECT) && m_acc_callback)
        {
            NRF_LOG_DEBUG("<Accelerometer Interrupt>");
            m_acc_callback();
        }
    #endif /* CONFIG_ACC_USE_CLICK_DETECTION */
    }
    
    /**@brief Sets the accelerometer in idle mode.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_idle_mode_set(void)
    {
        uint8_t ctrl_regs[] = {0x58, 0x00, 0x00, 0x00, 0x00};
        ret_code_t status;
        uint8_t reg_val;
    
        NRF_LOG_INFO("DRV_ACC_MODE_IDLE");
    
        // Disable all interrupts.
        status = lis3dh_int_enable(0x00, 0x00);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Update configuration.
        status = lis3dh_write_regs(CTRL_REG1, ctrl_regs, sizeof(ctrl_regs));
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Clear interrupts.
        status = lis3dh_int_clear();
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Set Power Down mode.
        reg_val = 0x08;
        return lis3dh_write_regs(CTRL_REG1, &reg_val, 1);
    }
    
    /**@brief Sets the accelerometer in wakeup mode - see AN3308 application note - page 23.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_wakeup_mode_set(void)
    {
        uint8_t ctrl_regs[] = {0x5F, 0x01, 0x40, 0x00, 0x00};
        uint8_t int1_ths[]  = {CONFIG_ACC_WAKEUP_THRESHOLD, 0x00};
        ret_code_t status;
        uint8_t reg_val;
    
        NRF_LOG_INFO("DRV_ACC_MODE_WAKE_UP");
    
        // Disable all interrupts.
        status = lis3dh_int_enable(0x00, 0x00);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Update configuration.
        status = lis3dh_write_regs(CTRL_REG1, ctrl_regs, sizeof(ctrl_regs));
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        status = lis3dh_write_regs(INT1_THS, int1_ths, sizeof(int1_ths));
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // This read resets the LIS3DH internal HP filter.
        status = lis3dh_read_regs(REFERENCE_REG, &reg_val, 1);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Clear interrupts.
        status = lis3dh_int_clear();
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Enable selected interrupts.
        return lis3dh_int_enable(0x2A, 0x00);
    }
    
    #if CONFIG_ACC_USE_CLICK_DETECTION
    /**@brief Sets the accelerometer in click detect mode  - see AN3308 application note - page 35.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_click_detect_mode_set(void)
    {
        uint8_t ctrl_regs[] = {0x57, 0x04, 0x80, 0x00, 0x00};
        uint8_t click_cfg[] = {CONFIG_ACC_CLICK_THRESHOLD,
                               CONFIG_ACC_CLICK_TIMELIMIT,
                               CONFIG_ACC_CLICK_LATENCY};
        ret_code_t status;
    
        NRF_LOG_INFO("DRV_ACC_MODE_CLICK_DETECT");
    
        // Disable all interrupts.
        status = lis3dh_int_enable(0x00, 0x00);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Update configuration.
        status = lis3dh_write_regs(CTRL_REG1, ctrl_regs, sizeof(ctrl_regs));
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        status = lis3dh_write_regs(CLICK_THS, click_cfg, sizeof(click_cfg));
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Clear interrupts.
        status = lis3dh_int_clear();
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        // Enable selected interrupts.
        return lis3dh_int_enable(0x00, CONFIG_ACC_CLICK_AXES);
    }
    #endif /* CONFIG_ACC_USE_CLICK_DETECTION */
    
    /**@brief Sets accelerometer in the given mode.
     *
     * @param[in]   mode    Mode of accelerometer operation.
     *
     * @return NRF_SUCCESS on success, otherwise an error code.
     */
    static ret_code_t lis3dh_mode_set(drv_acc_mode_t mode)
    {
        switch (mode)
        {
            case DRV_ACC_MODE_WAKE_UP:
                return lis3dh_wakeup_mode_set();
    #if CONFIG_ACC_USE_CLICK_DETECTION
            case DRV_ACC_MODE_CLICK_DETECT:
                return lis3dh_click_detect_mode_set();
    #endif
            case DRV_ACC_MODE_IDLE:
                return lis3dh_idle_mode_set();
            default:
                return NRF_ERROR_NOT_SUPPORTED;
        }
    
        /* Not reached */
    }
    
    /**@brief Enables PIO interrupt. */
    static ret_code_t lis3dh_enable_pio_interrupts(void)
    {
        return app_gpiote_user_enable(m_acc_gpiote);
    }
    
    /**@brief Disables PIO interrupt. */
    static ret_code_t lis3dh_disable_pio_interrupts(void)
    {
        return app_gpiote_user_disable(m_acc_gpiote);
    }
    
    /*--- drv_acc.h API ---*/
    
    ret_code_t drv_acc_mode_set(drv_acc_mode_t mode)
    {
        ret_code_t status = NRF_SUCCESS;
    
        if (m_acc_mode != mode)
        {
            status = lis3dh_disable_pio_interrupts();
            if (status != NRF_SUCCESS)
            {
                return status;
            }
    
            status = lis3dh_mode_set(mode);
            if (status != NRF_SUCCESS)
            {
                return status;
            }
    
            switch (mode)
            {
    #if CONFIG_ACC_USE_CLICK_DETECTION
                case DRV_ACC_MODE_CLICK_DETECT:
    #endif
                case DRV_ACC_MODE_WAKE_UP:
                    status = lis3dh_enable_pio_interrupts();
                    if (status != NRF_SUCCESS)
                    {
                        return status;
                    }
                    break;
    
                default:
                    /* Do nothing */
                    break;
            }
    
            m_acc_mode = mode;
        }
    
        return status;
    }
    
    ret_code_t drv_acc_init(drv_acc_callback_t click_handler)
    {
        uint32_t low_to_high_mask = (1 << CONFIG_IO_ACC_IRQ);
        uint32_t high_to_low_mask = (0 << CONFIG_IO_ACC_IRQ);
        ret_code_t status;
    
    #if CONFIG_ACC_USE_CLICK_DETECTION
        m_acc_callback = click_handler;
    #endif
    
        status = app_gpiote_user_register(&m_acc_gpiote,
                                          &low_to_high_mask,
                                          &high_to_low_mask,
                                          m_acc_interupt_handler);
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        status = lis3dh_verify_id();
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        status = lis3dh_reset();
        if (status != NRF_SUCCESS)
        {
            return status;
        }
    
        return drv_acc_mode_set(DRV_ACC_MODE_DEFAULT);
    }
    
    #endif /* CONFIG_ACC_ENABLED */
    

    drv_acc_lis3dh.h

     

    Best regards,

    Edvin

  • Please reply with the function to call for X,Y,Z values from main.c ?

  • I don't have any sensor to test this on. Maybe someone else in this forum has done this before, and can help you.

    See if you e.g. can use something from this case:

     

    BR,

    Edvin

  • I may have a setting or two wrong in my project, but it looks like this driver file relies on a few other files (bolded in the #include statements below) that are not in the v15 SDK. Also, is there any example code available that utilizes this driver?


    #ifndef __ICCARM__
    // #include <alloca.h>
    #endif

    #include "nordic_common.h"
    #include "nrf.h"
    #include "nrf_assert.h"
    #include "nrf_error.h"
    #include "nrf_delay.h"
    // #include "app_debug.h"
    #include "app_error.h"
    #include "app_gpiote.h"
    // #include "app_twi.h"

    #include "drv_acc_lis3dh.h"
    // #include "drv_acc.h"

    // #include "resources.h"
    // #include "twi_common.h"
    // #include "sr3_config.h"

  • I did this uncomment & driver code compiled successfully but how I can read X,Y,Z values there's no such function in the driver? 

    I tried calling this function but it's not working:

    lis3dh_read_regs();

  • I see that the projects where the driver is used, it only uses the LIS3DH to wake up the chip from sleep, so maybe the drivers that you initially found would be a better approach.

     

    Can you please specify what kind of issues you see when you try to use it? You say: " I am getting errors like i2c begin". What do you mean by that?

     

    Best regards,

    Edvin

     

  • So, this (drv_acc_lis3dh.c) will only wake-up LIS3DH. I am not getting any error while compiling your files but I can't find any function to read X,Y,Z values defined in drv_acc_lis3dh.h file:

    typedef struct
    {
    int16_t AXIS_X;
    int16_t AXIS_Y;
    int16_t AXIS_Z;
    } AccAxesRaw_t;

    Is driver incomplete? Please suggest me how to read the registers for LIS3DH data like here below:

    static uint8_t mosiBuf [2];
    static uint8_t misoBuf [2];
    void writeLisReg(uint8_t subAddr, uint8_t value)
    {
    uint32_t err_code;
    while(spi_master_get_state(SPI_MASTER_0) != SPI_MASTER_STATE_IDLE);
    mosiBuf[0] = subAddr & ~0xc0;
    mosiBuf[1] = value;
    err_code = spi_master_send_recv(SPI_MASTER_0, mosiBuf, 2, misoBuf,2);
    APP_ERROR_CHECK(err_code);
    }

    uint8_t readLisReg(uint8_t subAddr)
    {
    uint32_t err_code;
    while(spi_master_get_state(SPI_MASTER_0) != SPI_MASTER_STATE_IDLE);
    mosiBuf[0] = subAddr | 0x80;
    mosiBuf[1] = 0;
    misoBuf[0] = 0x55;
    misoBuf[1] = 0xAA;
    err_code = spi_master_send_recv(SPI_MASTER_0, mosiBuf, 2, misoBuf,2);
    APP_ERROR_CHECK(err_code);
    while(spi_master_get_state(SPI_MASTER_0) != SPI_MASTER_STATE_IDLE);
    return misoBuf[1];
    }

    void init_lis3dh() {
    writeLisReg(LIS3DH_CTRL_REG1, 0x57); //All axes, normal, 100Hz
    writeLisReg(LIS3DH_CTRL_REG2, 0x00); // No HighPass filter
    writeLisReg(LIS3DH_CTRL_REG3, 0x00); // No interrupts
    writeLisReg(LIS3DH_CTRL_REG4, 0x0); // all defaults
    writeLisReg(LIS3DH_CTRL_REG5, 0x0); // all defaults
    writeLisReg(LIS3DH_CTRL_REG6, 0x0); // all defaults
    }

    void readLisData()
    {
    int16_t x,y,z;
    double dx, dy, dz;
    while(readLisReg(LIS3DH_STATUS_REG) & 0x8 == 0 );
    x = readLisReg(LIS3DH_OUT_X_H) * 256 + readLisReg(LIS3DH_OUT_X_L);
    y = readLisReg(LIS3DH_OUT_Y_H) * 256 + readLisReg(LIS3DH_OUT_Y_L);
    z = readLisReg(LIS3DH_OUT_Z_H) * 256 + readLisReg(LIS3DH_OUT_Z_L);
    dx = x/16384.0;
    dy = y/16384.0;
    dz = z/16384.0;
    printf("X: %f; Y: %f; Z: %f; SUM: %f\n\r", dx , dy, dz, sqrt(dx*dx + dy*dy + dz*dz));
    }

    This is SPI example found here: https://github.com/elmot/nRF51822xxAA-LIS3DH/blob/master/main.c but I need data via i2c communication

Reply
  • So, this (drv_acc_lis3dh.c) will only wake-up LIS3DH. I am not getting any error while compiling your files but I can't find any function to read X,Y,Z values defined in drv_acc_lis3dh.h file:

    typedef struct
    {
    int16_t AXIS_X;
    int16_t AXIS_Y;
    int16_t AXIS_Z;
    } AccAxesRaw_t;

    Is driver incomplete? Please suggest me how to read the registers for LIS3DH data like here below:

    static uint8_t mosiBuf [2];
    static uint8_t misoBuf [2];
    void writeLisReg(uint8_t subAddr, uint8_t value)
    {
    uint32_t err_code;
    while(spi_master_get_state(SPI_MASTER_0) != SPI_MASTER_STATE_IDLE);
    mosiBuf[0] = subAddr & ~0xc0;
    mosiBuf[1] = value;
    err_code = spi_master_send_recv(SPI_MASTER_0, mosiBuf, 2, misoBuf,2);
    APP_ERROR_CHECK(err_code);
    }

    uint8_t readLisReg(uint8_t subAddr)
    {
    uint32_t err_code;
    while(spi_master_get_state(SPI_MASTER_0) != SPI_MASTER_STATE_IDLE);
    mosiBuf[0] = subAddr | 0x80;
    mosiBuf[1] = 0;
    misoBuf[0] = 0x55;
    misoBuf[1] = 0xAA;
    err_code = spi_master_send_recv(SPI_MASTER_0, mosiBuf, 2, misoBuf,2);
    APP_ERROR_CHECK(err_code);
    while(spi_master_get_state(SPI_MASTER_0) != SPI_MASTER_STATE_IDLE);
    return misoBuf[1];
    }

    void init_lis3dh() {
    writeLisReg(LIS3DH_CTRL_REG1, 0x57); //All axes, normal, 100Hz
    writeLisReg(LIS3DH_CTRL_REG2, 0x00); // No HighPass filter
    writeLisReg(LIS3DH_CTRL_REG3, 0x00); // No interrupts
    writeLisReg(LIS3DH_CTRL_REG4, 0x0); // all defaults
    writeLisReg(LIS3DH_CTRL_REG5, 0x0); // all defaults
    writeLisReg(LIS3DH_CTRL_REG6, 0x0); // all defaults
    }

    void readLisData()
    {
    int16_t x,y,z;
    double dx, dy, dz;
    while(readLisReg(LIS3DH_STATUS_REG) & 0x8 == 0 );
    x = readLisReg(LIS3DH_OUT_X_H) * 256 + readLisReg(LIS3DH_OUT_X_L);
    y = readLisReg(LIS3DH_OUT_Y_H) * 256 + readLisReg(LIS3DH_OUT_Y_L);
    z = readLisReg(LIS3DH_OUT_Z_H) * 256 + readLisReg(LIS3DH_OUT_Z_L);
    dx = x/16384.0;
    dy = y/16384.0;
    dz = z/16384.0;
    printf("X: %f; Y: %f; Z: %f; SUM: %f\n\r", dx , dy, dz, sqrt(dx*dx + dy*dy + dz*dz));
    }

    This is SPI example found here: https://github.com/elmot/nRF51822xxAA-LIS3DH/blob/master/main.c but I need data via i2c communication

Children
Related