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.
#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; }