1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/fxos8700/fxos8700.c
2021-08-13 19:50:38 +02:00

231 lines
7.5 KiB
C

/*
* Copyright (C) 2018 UC Berkeley
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @file
* @brief Driver for the FXOS8700 3-axis accelerometer/magnetometer
*
* @author Michael Andersen <m.andersen@cs.berkeley.edu>
* @author Hyung-Sin Kim <hs.kim@cs.berkeleyedu>
*/
#include "periph/i2c.h"
#include "xtimer.h"
#include "fxos8700.h"
#include "kernel_defines.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define I2C_SPEED I2C_SPEED_FAST
#define FXOS8700_FULLDATA_LENGTH (12)
static int fxos8700_read_regs(const fxos8700_t* dev, uint8_t reg, uint8_t* data, size_t len)
{
i2c_acquire(dev->p.i2c);
if (i2c_read_regs(dev->p.i2c, dev->p.addr, reg, (char*) data, len, 0) < 0) {
DEBUG("[fxos8700] Can't read register 0x%x\n", reg);
i2c_release(dev->p.i2c);
return FXOS8700_BUSERR;
}
i2c_release(dev->p.i2c);
return FXOS8700_OK;
}
static int fxos8700_write_regs(const fxos8700_t* dev, uint8_t reg, uint8_t* data, size_t len)
{
i2c_acquire(dev->p.i2c);
if (i2c_write_regs(dev->p.i2c, dev->p.addr, reg, (char*) data, len, 0) < 0) {
DEBUG("[fxos8700] Can't write to register 0x%x\n", reg);
i2c_release(dev->p.i2c);
return FXOS8700_BUSERR;
}
i2c_release(dev->p.i2c);
return FXOS8700_OK;
}
int fxos8700_init(fxos8700_t* dev, const fxos8700_params_t *params)
{
uint8_t config;
if ((params->addr < 0x1C) || (params->addr > 0x1F)) {
DEBUG("[fxos8700] Invalid address\n");
return FXOS8700_ADDRERR;
}
dev->p.addr = params->addr;
dev->p.i2c = params->i2c;
dev->p.acc_range = params->acc_range;
dev->p.renew_interval = params->renew_interval;
i2c_acquire(dev->p.i2c);
if (i2c_read_regs(dev->p.i2c, dev->p.addr, FXOS8700_REG_WHO_AM_I, &config, 1, 0) < 0) {
i2c_release(dev->p.i2c);
DEBUG("[fxos8700] Could not read WHOAMI\n");
return FXOS8700_NOBUS;
}
if (config != FXOS8700_WHO_AM_I_VAL) {
i2c_release(dev->p.i2c);
DEBUG("[fxos8700] WHOAMI is wrong (%2x)\n", config);
return FXOS8700_NODEV;
}
i2c_release(dev->p.i2c);
/* Configure the ODR to maximum (400Hz in hybrid mode) */
config = FXOS8700_REG_CTRL_REG1_ODR__400HZ & FXOS8700_REG_CTRL_REG1_MASK__ODR;
if (fxos8700_write_regs(dev, FXOS8700_REG_CTRL_REG1, &config, 1) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
dev->config = config;
/* Activate hybrid mode */
config = FXOS8700_REG_M_CTRL_REG1_HMS__HYBRID & FXOS8700_REG_M_CTRL_REG1_MASK__HMS;
if (fxos8700_write_regs(dev, FXOS8700_REG_M_CTRL_REG1, &config, 1) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
/* Set burst read mode (accel + magnet together) */
config = FXOS8700_REG_M_CTRL_REG2_MASK__HYB_AUTOINC_MODE;
if (fxos8700_write_regs(dev, FXOS8700_REG_M_CTRL_REG2, &config, 1) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
/* Set accelerator's full-scale range */
if (fxos8700_read_regs(dev, FXOS8700_REG_XYZ_DATA_CFG, &config, 1) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
config &= ~FXOS8700_REG_XYZ_DATA_CFG_MASK__FS;
config |= dev->p.acc_range & FXOS8700_REG_XYZ_DATA_CFG_MASK__FS;
if (fxos8700_write_regs(dev, FXOS8700_REG_XYZ_DATA_CFG, &config, 1) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
/* initial read for caching operation */
if (fxos8700_read(dev, &dev->acc_cached, &dev->mag_cached) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
dev->last_read_time = xtimer_now_usec();
return FXOS8700_OK;
}
int fxos8700_set_active(const fxos8700_t* dev)
{
uint8_t config = dev->config | FXOS8700_REG_CTRL_REG1_MASK__ACTIVE;
if (fxos8700_write_regs(dev, FXOS8700_REG_CTRL_REG1, &config, 1) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
return FXOS8700_OK;
}
int fxos8700_set_idle(const fxos8700_t* dev)
{
uint8_t config = dev->config & ~FXOS8700_REG_CTRL_REG1_MASK__ACTIVE;
if (fxos8700_write_regs(dev, FXOS8700_REG_CTRL_REG1, &config, 1) != FXOS8700_OK) {
return FXOS8700_BUSERR;
}
return FXOS8700_OK;
}
int fxos8700_read(const fxos8700_t* dev, fxos8700_measurement_t* acc,
fxos8700_measurement_t* mag)
{
uint8_t data[12];
uint8_t ready = 0;
if (fxos8700_set_active(dev)) {
return FXOS8700_BUSERR;
}
while (!(ready & FXOS8700_REG_STATUS_MASK__XYZ_READY)) {
fxos8700_read_regs(dev, FXOS8700_REG_STATUS, &ready, 1);
}
while (!(ready & FXOS8700_REG_M_DR_STATUS_MASK__XYZ_READY)) {
fxos8700_read_regs(dev, FXOS8700_REG_M_DR_STATUS, &ready, 1);
}
/* Read all data at once */
if (fxos8700_read_regs(dev, FXOS8700_REG_OUT_X_MSB, &data[0], FXOS8700_FULLDATA_LENGTH)) {
return FXOS8700_BUSERR;
}
if (fxos8700_set_idle(dev)) {
return FXOS8700_BUSERR;
}
/* Read accelerometer */
if (acc) {
if (IS_ACTIVE(CONFIG_FXOS8700_USE_ACC_RAW_VALUES)) {
acc->x = (int16_t) ((data[0] << 8) | data[1]) >> 2;
acc->y = (int16_t) ((data[2] << 8) | data[3]) >> 2;
acc->z = (int16_t) ((data[4] << 8) | data[5]) >> 2;
}
else {
int32_t acc_raw_x = (int16_t) ((data[0] << 8) | data[1]) >> 2;
int32_t acc_raw_y = (int16_t) ((data[2] << 8) | data[3]) >> 2;
int32_t acc_raw_z = (int16_t) ((data[4] << 8) | data[5]) >> 2;
switch(dev->p.acc_range) {
case FXOS8700_REG_XYZ_DATA_CFG_FS__2G:
acc->x = (int16_t) ((acc_raw_x * 244) / 100);
acc->y = (int16_t) ((acc_raw_y * 244) / 100);
acc->z = (int16_t) ((acc_raw_z * 244) / 100);
break;
case FXOS8700_REG_XYZ_DATA_CFG_FS__4G:
acc->x = (int16_t) ((acc_raw_x * 488) / 1000);
acc->y = (int16_t) ((acc_raw_y * 488) / 1000);
acc->z = (int16_t) ((acc_raw_z * 488) / 1000);
break;
case FXOS8700_REG_XYZ_DATA_CFG_FS__8G:
acc->x = (int16_t) ((acc_raw_x * 976) / 1000);
acc->y = (int16_t) ((acc_raw_y * 976) / 1000);
acc->z = (int16_t) ((acc_raw_z * 976) / 1000);
break;
default:
return FXOS8700_NODEV;
}
}
}
/* Read magnetometer */
if (mag) {
mag->x = (int16_t) ((data[6] << 8) | data[7]);
mag->y = (int16_t) ((data[8] << 8) | data[9]);
mag->z = (int16_t) ((data[10] << 8) | data[11]);
}
return FXOS8700_OK;
}
int fxos8700_read_cached(const void *dev, fxos8700_measurement_t* acc,
fxos8700_measurement_t* mag)
{
fxos8700_t* fxos_dev = (fxos8700_t *)dev;
uint32_t now = xtimer_now_usec();
/* check if readings are outdated */
if (now - fxos_dev->last_read_time > fxos_dev->p.renew_interval) {
/* refresh cache and update last_read_time */
if (fxos8700_read(fxos_dev, &fxos_dev->acc_cached, &fxos_dev->mag_cached)
!= FXOS8700_OK) {
return FXOS8700_BUSERR;
}
fxos_dev->last_read_time = now;
}
/* Read cached data */
if (acc) {
acc->x = fxos_dev->acc_cached.x;
acc->y = fxos_dev->acc_cached.y;
acc->z = fxos_dev->acc_cached.z;
}
if (mag) {
mag->x = fxos_dev->mag_cached.x;
mag->y = fxos_dev->mag_cached.y;
mag->z = fxos_dev->mag_cached.z;
}
return FXOS8700_OK;
}