mirror of
https://github.com/eclipse/upm.git
synced 2025-03-17 22:17:28 +03:00
418 lines
11 KiB
C
418 lines
11 KiB
C
/*
|
|
* Author: Jon Trulson <jtrulson@ics.com>
|
|
* Copyright (c) 2017 Intel Corporation.
|
|
*
|
|
* The MIT License
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
|
|
#include "upm_utilities.h"
|
|
|
|
#include "lsm303d.h"
|
|
|
|
|
|
// some useful macros to save on typing and text wrapping
|
|
#undef _SHIFT
|
|
#define _SHIFT(x) (_LSM303D_##x##_SHIFT)
|
|
|
|
#undef _MASK
|
|
#define _MASK(x) (_LSM303D_##x##_MASK)
|
|
|
|
#undef _SHIFTMASK
|
|
#define _SHIFTMASK(x) (_MASK(x) << _SHIFT(x))
|
|
|
|
|
|
// init
|
|
lsm303d_context lsm303d_init(int bus, int addr)
|
|
{
|
|
lsm303d_context dev =
|
|
(lsm303d_context)malloc(sizeof(struct _lsm303d_context));
|
|
|
|
if (!dev)
|
|
return NULL;
|
|
|
|
// zero out context
|
|
memset((void *)dev, 0, sizeof(struct _lsm303d_context));
|
|
|
|
// make sure MRAA is initialized
|
|
if (mraa_init() != MRAA_SUCCESS)
|
|
{
|
|
printf("%s: mraa_init() failed.\n", __FUNCTION__);
|
|
lsm303d_close(dev);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(dev->i2c = mraa_i2c_init(bus)))
|
|
{
|
|
printf("%s: mraa_i2c_init() failed.\n", __FUNCTION__);
|
|
lsm303d_close(dev);
|
|
return NULL;
|
|
}
|
|
|
|
if (mraa_i2c_address(dev->i2c, addr))
|
|
{
|
|
printf("%s: mraa_i2c_address() failed.\n", __FUNCTION__);
|
|
lsm303d_close(dev);
|
|
return NULL;
|
|
}
|
|
|
|
// check the chip id
|
|
uint8_t chipID = lsm303d_read_reg(dev, LSM303D_REG_WHO_AM_I);
|
|
|
|
if (chipID != LSM303D_CHIPID)
|
|
{
|
|
printf("%s: invalid chip id: %02x. Expected %02x\n",
|
|
__FUNCTION__, chipID, LSM303D_CHIPID);
|
|
lsm303d_close(dev);
|
|
return NULL;
|
|
}
|
|
|
|
// call devinit with a default high resolution mode
|
|
if (lsm303d_devinit(dev, LSM303D_M_RES_HIGH))
|
|
{
|
|
printf("%s: lsm303d_devinit() failed.\n", __FUNCTION__);
|
|
lsm303d_close(dev);
|
|
return NULL;
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
void lsm303d_close(lsm303d_context dev)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
if (dev->i2c)
|
|
mraa_i2c_stop(dev->i2c);
|
|
|
|
free(dev);
|
|
}
|
|
|
|
upm_result_t lsm303d_devinit(const lsm303d_context dev,
|
|
LSM303D_M_RES_T res)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
// enable all axes and BDU
|
|
uint8_t reg = lsm303d_read_reg(dev, LSM303D_REG_CTRL1);
|
|
|
|
reg |= LSM303D_CTRL1_AXEN
|
|
| LSM303D_CTRL1_AYEN
|
|
| LSM303D_CTRL1_AZEN
|
|
| LSM303D_CTRL1_BDU;
|
|
|
|
if (lsm303d_write_reg(dev, LSM303D_REG_CTRL1, reg))
|
|
{
|
|
printf("%s: lsm303d_write_reg() failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
// enable temperature measurement and set mag resolution
|
|
reg = lsm303d_read_reg(dev, LSM303D_REG_CTRL5);
|
|
reg &= ~_SHIFTMASK(CTRL5_MRES);
|
|
reg |= LSM303D_CTRL5_TEMP_EN
|
|
| (res << _SHIFT(CTRL5_MRES));
|
|
|
|
if (lsm303d_write_reg(dev, LSM303D_REG_CTRL5, reg))
|
|
{
|
|
printf("%s: lsm303d_write_reg() failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
// set magnetometer to continuous mode
|
|
reg = lsm303d_read_reg(dev, LSM303D_REG_CTRL7);
|
|
reg &= ~_SHIFTMASK(CTRL7_MD);
|
|
reg |= (LSM303D_MD_CONTINUOUS << _SHIFT(CTRL7_MD));
|
|
|
|
if (lsm303d_write_reg(dev, LSM303D_REG_CTRL7, reg))
|
|
{
|
|
printf("%s: lsm303d_write_reg() failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
// set ACC ODR to 100Hz by default
|
|
if (lsm303d_set_acc_odr(dev, LSM303D_AODR_100HZ))
|
|
{
|
|
printf("%s: lsm303d_set_acc_odr() failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
// set MAG ODR to 12.5Hz by default
|
|
if (lsm303d_set_mag_odr(dev, LSM303D_MODR_12_5HZ))
|
|
{
|
|
printf("%s: lsm303d_set_acc_odr() failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
// default to 2G acc sensitivity
|
|
if (lsm303d_set_acc_full_scale(dev, LSM303D_AFS_2G))
|
|
{
|
|
printf("%s: lsm303d_set_acc_full_scale() failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
// default to 2 Gauss mag sensitivity
|
|
if (lsm303d_set_mag_full_scale(dev, LSM303D_MFS_2))
|
|
{
|
|
printf("%s: lsm303d_set_acc_full_scale() failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
upm_delay_ms(10);
|
|
|
|
return UPM_SUCCESS;
|
|
}
|
|
|
|
upm_result_t lsm303d_set_acc_full_scale(const lsm303d_context dev,
|
|
LSM303D_AFS_T fs)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
uint8_t reg = lsm303d_read_reg(dev, LSM303D_REG_CTRL2);
|
|
|
|
reg &= ~_SHIFTMASK(CTRL2_AFS);
|
|
reg |= (fs << _SHIFT(CTRL2_AFS));
|
|
|
|
if (lsm303d_write_reg(dev, LSM303D_REG_CTRL2, reg))
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
|
|
upm_delay_ms(50);
|
|
|
|
// set our scaling factor depending on current FS
|
|
switch(fs)
|
|
{
|
|
case LSM303D_AFS_2G:
|
|
dev->accScale = 0.061;
|
|
break;
|
|
|
|
case LSM303D_AFS_4G:
|
|
dev->accScale = 0.122;
|
|
break;
|
|
|
|
case LSM303D_AFS_6G:
|
|
dev->accScale = 0.183;
|
|
break;
|
|
|
|
case LSM303D_AFS_8G:
|
|
dev->accScale = 0.320;
|
|
break;
|
|
|
|
case LSM303D_AFS_16G:
|
|
dev->accScale = 0.732;
|
|
break;
|
|
}
|
|
|
|
return UPM_SUCCESS;
|
|
}
|
|
|
|
upm_result_t lsm303d_set_mag_full_scale(const lsm303d_context dev,
|
|
LSM303D_MFS_T fs)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
uint8_t reg = lsm303d_read_reg(dev, LSM303D_REG_CTRL6);
|
|
|
|
reg &= ~_SHIFTMASK(CTRL6_MFS);
|
|
reg |= (fs << _SHIFT(CTRL6_MFS));
|
|
|
|
if (lsm303d_write_reg(dev, LSM303D_REG_CTRL6, reg))
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
|
|
upm_delay_ms(50);
|
|
|
|
// set our scaling factor depending on current FS
|
|
switch(fs)
|
|
{
|
|
case LSM303D_MFS_2:
|
|
dev->magScale = 0.080;
|
|
break;
|
|
|
|
case LSM303D_MFS_4:
|
|
dev->magScale = 0.160;
|
|
break;
|
|
|
|
case LSM303D_MFS_8:
|
|
dev->magScale = 0.320;
|
|
break;
|
|
|
|
case LSM303D_MFS_12:
|
|
dev->magScale = 0.479;
|
|
break;
|
|
}
|
|
|
|
return UPM_SUCCESS;
|
|
}
|
|
|
|
upm_result_t lsm303d_update(const lsm303d_context dev)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
const int maxLen = 6;
|
|
uint8_t buf[maxLen];
|
|
|
|
// get the temperature first, only 2 bytes
|
|
if (lsm303d_read_regs(dev, LSM303D_REG_TEMP_OUT_L, buf, 2) != 2)
|
|
{
|
|
printf("%s: lsm303d_read_regs(temp) failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
dev->temperature = (float)( (int16_t)(buf[0] | (buf[1] << 8)) << 4);
|
|
|
|
// next, acc data
|
|
if (lsm303d_read_regs(dev, LSM303D_REG_OUT_X_L_A, buf,
|
|
maxLen) != maxLen)
|
|
{
|
|
printf("%s: lsm303d_read_regs(acc) failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
dev->accX = (float)((int16_t)(buf[0] | (buf[1] << 8)));
|
|
dev->accY = (float)((int16_t)(buf[2] | (buf[3] << 8)));
|
|
dev->accZ = (float)((int16_t)(buf[4] | (buf[5] << 8)));
|
|
|
|
// now mag data
|
|
if (lsm303d_read_regs(dev, LSM303D_REG_OUT_X_L_M, buf,
|
|
maxLen) != maxLen)
|
|
{
|
|
printf("%s: lsm303d_read_regs(mag) failed.\n", __FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
dev->magX = (float)((int16_t)(buf[0] | (buf[1] << 8)));
|
|
dev->magY = (float)((int16_t)(buf[2] | (buf[3] << 8)));
|
|
dev->magZ = (float)((int16_t)(buf[4] | (buf[5] << 8)));
|
|
|
|
return UPM_SUCCESS;
|
|
}
|
|
|
|
uint8_t lsm303d_read_reg(const lsm303d_context dev, uint8_t reg)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
int rv = mraa_i2c_read_byte_data(dev->i2c, reg);
|
|
if (rv < 0)
|
|
{
|
|
printf("%s: mraa_i2c_read_byte_data() failed\n", __FUNCTION__);
|
|
return 0xff;
|
|
}
|
|
|
|
return (uint8_t)rv;
|
|
}
|
|
|
|
int lsm303d_read_regs(const lsm303d_context dev, uint8_t reg,
|
|
uint8_t *buffer, int len)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
reg |= 0x80; // enable auto-increment
|
|
if (mraa_i2c_read_bytes_data(dev->i2c, reg, buffer, len) != len)
|
|
return -1;
|
|
|
|
return len;
|
|
}
|
|
|
|
upm_result_t lsm303d_write_reg(const lsm303d_context dev,
|
|
uint8_t reg, uint8_t val)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
if (mraa_i2c_write_byte_data(dev->i2c, val, reg))
|
|
{
|
|
printf("%s: mraa_i2c_write_byte_data() failed.\n",
|
|
__FUNCTION__);
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
}
|
|
|
|
return UPM_SUCCESS;
|
|
}
|
|
|
|
float lsm303d_get_temperature(const lsm303d_context dev)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
// It's not clear how to compute this from the datasheet, but this
|
|
// seems to give a reasonably accurate result.
|
|
return (dev->temperature / 128.0) + 25.0;
|
|
}
|
|
|
|
void lsm303d_get_magnetometer(const lsm303d_context dev,
|
|
float *x, float *y, float *z)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
// Output is in milli-Gauss - we convert and return it in uT (SI
|
|
// micro-teslas) instead.
|
|
if (x)
|
|
*x = (dev->magX * dev->magScale) / 10.0;
|
|
if (y)
|
|
*y = (dev->magY * dev->magScale) / 10.0;
|
|
if (z)
|
|
*z = (dev->magZ * dev->magScale) / 10.0;
|
|
}
|
|
|
|
void lsm303d_get_accelerometer(const lsm303d_context dev,
|
|
float *x, float *y, float *z)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
if (x)
|
|
*x = (dev->accX * dev->accScale) / 1000.0;
|
|
if (y)
|
|
*y = (dev->accY * dev->accScale) / 1000.0;
|
|
if (z)
|
|
*z = (dev->accZ * dev->accScale) / 1000.0;
|
|
}
|
|
|
|
upm_result_t lsm303d_set_acc_odr(const lsm303d_context dev,
|
|
LSM303D_AODR_T odr)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
uint8_t reg = lsm303d_read_reg(dev, LSM303D_REG_CTRL1);
|
|
reg &= ~_SHIFTMASK(CTRL1_AODR);
|
|
reg |= (odr << _SHIFT(CTRL1_AODR));
|
|
|
|
if (lsm303d_write_reg(dev, LSM303D_REG_CTRL1, reg))
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
|
|
return UPM_SUCCESS;
|
|
}
|
|
|
|
upm_result_t lsm303d_set_mag_odr(const lsm303d_context dev,
|
|
LSM303D_MODR_T odr)
|
|
{
|
|
assert(dev != NULL);
|
|
|
|
uint8_t reg = lsm303d_read_reg(dev, LSM303D_REG_CTRL5);
|
|
reg &= ~_SHIFTMASK(CTRL5_MODR);
|
|
reg |= (odr << _SHIFT(CTRL5_MODR));
|
|
|
|
if (lsm303d_write_reg(dev, LSM303D_REG_CTRL5, reg))
|
|
return UPM_ERROR_OPERATION_FAILED;
|
|
|
|
return UPM_SUCCESS;
|
|
}
|