blob: f1bfbb1bfcc27b0deb4612e7fc7f6d592913f286 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* resarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include "os/mynewt.h"
#include "bma2xx/bma2xx.h"
#include "bma2xx_priv.h"
#include "defs/error.h"
#include "hal/hal_gpio.h"
#include "hal/hal_i2c.h"
#include "hal/hal_spi.h"
#include "i2cn/i2cn.h"
#if MYNEWT_VAL(BMA2XX_LOG)
#include "modlog/modlog.h"
#endif
#define BMA2XX_LOG(lvl_, ...) \
MODLOG_ ## lvl_(MYNEWT_VAL(BMA2XX_LOG_MODULE), __VA_ARGS__)
#else
#define BMA2XX_LOG(lvl_, ...)
#endif
#define BMA2XX_NOTIFY_MASK 0x01
#define BMA2XX_READ_MASK 0x02
#if MYNEWT_VAL(SPI_0_MASTER) || MYNEWT_VAL(SPI_1_MASTER)
static struct hal_spi_settings spi_bma2xx_settings = {
.data_order = HAL_SPI_MSB_FIRST,
.data_mode = HAL_SPI_MODE0,
.baudrate = 4000,
.word_size = HAL_SPI_WORD_SIZE_8BIT,
};
#endif
static void
delay_msec(uint32_t delay)
{
delay = (delay * OS_TICKS_PER_SEC) / 1000 + 1;
os_time_delay(delay);
}
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
static void
init_interrupt(struct bma2xx_int * interrupt, struct sensor_int *ints)
{
os_error_t error;
error = os_sem_init(&interrupt->wait, 0);
assert(error == OS_OK);
interrupt->active = false;
interrupt->asleep = false;
interrupt->ints = ints;
}
static void
undo_interrupt(struct bma2xx_int * interrupt)
{
OS_ENTER_CRITICAL(interrupt->lock);
interrupt->active = false;
interrupt->asleep = false;
OS_EXIT_CRITICAL(interrupt->lock);
}
static void
wait_interrupt(struct bma2xx_int * interrupt, enum bma2xx_int_num int_num)
{
bool wait;
OS_ENTER_CRITICAL(interrupt->lock);
/* Check if we did not missed interrupt */
if (hal_gpio_read(interrupt->ints[int_num].host_pin) ==
interrupt->ints[int_num].active) {
OS_EXIT_CRITICAL(interrupt->lock);
return;
}
if (interrupt->active) {
interrupt->active = false;
wait = false;
} else {
interrupt->asleep = true;
wait = true;
}
OS_EXIT_CRITICAL(interrupt->lock);
if (wait) {
os_error_t error;
error = os_sem_pend(&interrupt->wait, -1);
assert(error == OS_OK);
}
}
static void
wake_interrupt(struct bma2xx_int * interrupt)
{
bool wake;
OS_ENTER_CRITICAL(interrupt->lock);
if (interrupt->asleep) {
interrupt->asleep = false;
wake = true;
} else {
interrupt->active = true;
wake = false;
}
OS_EXIT_CRITICAL(interrupt->lock);
if (wake) {
os_error_t error;
error = os_sem_release(&interrupt->wait);
assert(error == OS_OK);
}
}
static void
interrupt_handler(void * arg)
{
struct sensor *sensor = arg;
struct bma2xx *bma2xx;
bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
if (bma2xx->pdd.interrupt) {
wake_interrupt(bma2xx->pdd.interrupt);
}
sensor_mgr_put_interrupt_evt(sensor);
}
#endif
/**
* Read multiple length data over SPI
*
* @param register address
* @param variable length payload
* @param length of the payload to read
*
* @return 0 on success, non-zero on failure
*/
int
spi_readlen(struct sensor_itf * itf, uint8_t addr, uint8_t *payload,
uint8_t len)
{
int i;
uint16_t retval;
int rc;
rc = 0;
/* Select the device */
hal_gpio_write(itf->si_cs_pin, 0);
/* Send the address */
retval = hal_spi_tx_val(itf->si_num, addr | BMA2XX_SPI_READ_CMD_BIT);
if (retval == 0xFFFF) {
rc = SYS_EINVAL;
BMA2XX_LOG(ERROR, "SPI_%u register write failed addr:0x%02X\n",
itf->si_num, addr);
goto err;
}
for (i = 0; i < len; i++) {
/* Read data */
retval = hal_spi_tx_val(itf->si_num, 0);
if (retval == 0xFFFF) {
rc = SYS_EINVAL;
BMA2XX_LOG(ERROR, "SPI_%u read failed addr:0x%02X\n",
itf->si_num, addr);
goto err;
}
payload[i] = retval;
}
rc = 0;
err:
/* De-select the device */
hal_gpio_write(itf->si_cs_pin, 1);
return rc;
}
/**
* Write multiple length data SPI
*
* @param register address
* @param variable length payload
* @param length of the payload to write
*
* @return 0 on success, non-zero on failure
*/
int
spi_writereg(struct sensor_itf * itf, uint8_t addr, uint8_t payload,
uint8_t len)
{
int i;
int rc;
/* Select the device */
hal_gpio_write(itf->si_cs_pin, 0);
/* Send the address */
rc = hal_spi_tx_val(itf->si_num, addr);
if (rc == 0xFFFF) {
rc = SYS_EINVAL;
BMA2XX_LOG(ERROR, "SPI_%u register write failed addr:0x%02X\n",
itf->si_num, addr);
goto err;
}
for (i = 0; i < len; i++) {
/* Read data */
rc = hal_spi_tx_val(itf->si_num, payload);
if (rc == 0xFFFF) {
rc = SYS_EINVAL;
BMA2XX_LOG(ERROR, "SPI_%u write failed addr:0x%02X:0x%02X\n",
itf->si_num, addr);
goto err;
}
}
rc = 0;
err:
/* De-select the device */
hal_gpio_write(itf->si_cs_pin, 1);
os_time_delay((OS_TICKS_PER_SEC * 30)/1000 + 1);
return rc;
}
int
i2c_readlen(struct sensor_itf * itf, uint8_t addr, uint8_t *payload,
uint8_t len)
{
struct hal_i2c_master_data oper;
int rc;
oper.address = itf->si_addr;
oper.len = 1;
oper.buffer = &addr;
rc = i2cn_master_write(itf->si_num, &oper, OS_TICKS_PER_SEC / 10, 1,
MYNEWT_VAL(BMA2XX_I2C_RETRIES));
if (rc != 0) {
BMA2XX_LOG(ERROR, "I2C access failed at address 0x%02X\n", addr);
return rc;
}
oper.address = itf->si_addr;
oper.len = len;
oper.buffer = payload;
rc = i2cn_master_read(itf->si_num, &oper, OS_TICKS_PER_SEC / 10, 1,
MYNEWT_VAL(BMA2XX_I2C_RETRIES));
if (rc != 0) {
BMA2XX_LOG(ERROR, "I2C read failed at address 0x%02X length %u\n",
addr, len);
return rc;
}
return 0;
}
int
i2c_writereg(struct sensor_itf * itf, uint8_t addr, uint8_t data)
{
uint8_t tuple[2];
struct hal_i2c_master_data oper;
int rc;
tuple[0] = addr;
tuple[1] = data;
oper.address = itf->si_addr;
oper.len = 2;
oper.buffer = tuple;
rc = i2cn_master_write(itf->si_num, &oper, OS_TICKS_PER_SEC / 10, 1,
MYNEWT_VAL(BMA2XX_I2C_RETRIES));
if (rc != 0) {
BMA2XX_LOG(ERROR, "I2C write failed at address 0x%02X single byte\n",
addr);
return rc;
}
return 0;
}
static int
get_register(struct bma2xx *bma2xx,
uint8_t addr,
uint8_t * data)
{
int rc;
struct sensor_itf * itf;
itf = SENSOR_GET_ITF(&bma2xx->sensor);
rc = sensor_itf_lock(itf, MYNEWT_VAL(BMA2XX_ITF_LOCK_TMO));
if (rc) {
return rc;
}
if (itf->si_type == SENSOR_ITF_SPI) {
rc = spi_readlen(itf, addr, data, 1);
} else if (itf->si_type == SENSOR_ITF_I2C){
rc = i2c_readlen(itf, addr, data, 1);
} else {
rc = SYS_EINVAL;
}
sensor_itf_unlock(itf);
return rc;
}
static int
get_registers(struct bma2xx *bma2xx,
uint8_t addr,
uint8_t * data,
uint8_t size)
{
int rc;
struct sensor_itf * itf;
itf = SENSOR_GET_ITF(&bma2xx->sensor);
rc = sensor_itf_lock(itf, MYNEWT_VAL(BMA2XX_ITF_LOCK_TMO));
if (rc) {
return rc;
}
if (itf->si_type == SENSOR_ITF_SPI) {
rc = spi_readlen(itf, addr, data, size);
} else if (itf->si_type == SENSOR_ITF_I2C){
rc = i2c_readlen(itf, addr, data, size);
} else {
rc = SYS_EINVAL;
}
sensor_itf_unlock(itf);
return rc;
}
static int
set_register(struct bma2xx *bma2xx,
uint8_t addr,
uint8_t data)
{
int rc;
struct sensor_itf * itf;
itf = SENSOR_GET_ITF(&bma2xx->sensor);
rc = sensor_itf_lock(itf, MYNEWT_VAL(BMA2XX_ITF_LOCK_TMO));
if (rc) {
return rc;
}
if (itf->si_type == SENSOR_ITF_SPI) {
rc = spi_writereg(itf, addr, data, 1);
} else if (itf->si_type == SENSOR_ITF_I2C){
rc = i2c_writereg(itf, addr, data);
} else {
rc = SYS_EINVAL;
}
switch (bma2xx->power) {
case BMA2XX_POWER_MODE_SUSPEND:
case BMA2XX_POWER_MODE_LPM_1:
delay_msec(1);
break;
default:
break;
}
sensor_itf_unlock(itf);
return rc;
}
int
bma2xx_get_chip_id(struct bma2xx *bma2xx,
uint8_t * chip_id)
{
return get_register((struct bma2xx *)bma2xx, REG_ADDR_BGW_CHIPID, chip_id);
}
static void
compute_accel_data(struct accel_data * accel_data,
enum bma2xx_model model,
const uint8_t * raw_data,
float accel_scale)
{
int16_t raw_accel;
uint8_t model_shift = 0;
switch (model) {
case BMA2XX_BMA280:
model_shift = BMA280_ACCEL_BIT_SHIFT;
break;
case BMA2XX_BMA253:
model_shift = BMA253_ACCEL_BIT_SHIFT;
break;
default:
break;
}
raw_accel = (int16_t)(raw_data[0] & 0xFC) | (int16_t)(raw_data[1] << 8);
raw_accel >>= model_shift;
accel_data->accel_g = (float)raw_accel * accel_scale;
accel_data->new_data = raw_data[0] & 0x01;
}
static int
get_accel_scale(enum bma2xx_model model,
enum bma2xx_g_range g_range,
float *accel_scale)
{
switch (g_range) {
case BMA2XX_G_RANGE_2:
switch(model){
case BMA2XX_BMA280:
*accel_scale = BMA280_G_SCALE_2;
break;
case BMA2XX_BMA253:
*accel_scale = BMA253_G_SCALE_2;
break;
default:
return SYS_EINVAL;
}
break;
case BMA2XX_G_RANGE_4:
switch(model){
case BMA2XX_BMA280:
*accel_scale = BMA280_G_SCALE_4;
break;
case BMA2XX_BMA253:
*accel_scale = BMA253_G_SCALE_4;
break;
default:
return SYS_EINVAL;
}
break;
case BMA2XX_G_RANGE_8:
switch(model){
case BMA2XX_BMA280:
*accel_scale = BMA280_G_SCALE_8;
break;
case BMA2XX_BMA253:
*accel_scale = BMA253_G_SCALE_8;
break;
default:
return SYS_EINVAL;
}
break;
case BMA2XX_G_RANGE_16:
switch(model){
case BMA2XX_BMA280:
*accel_scale = BMA280_G_SCALE_16;
break;
case BMA2XX_BMA253:
*accel_scale = BMA253_G_SCALE_16;
break;
default:
return SYS_EINVAL;
}
break;
default:
return SYS_EINVAL;
}
return 0;
}
int
bma2xx_get_accel(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
enum axis axis,
struct accel_data * accel_data)
{
float accel_scale;
uint8_t base_addr;
uint8_t data[2];
int rc;
rc = get_accel_scale(bma2xx->cfg.model, g_range, &accel_scale);
if (rc != 0){
return rc;
}
switch (axis) {
case AXIS_X:
base_addr = REG_ADDR_ACCD_X_LSB;
break;
case AXIS_Y:
base_addr = REG_ADDR_ACCD_Y_LSB;
break;
case AXIS_Z:
base_addr = REG_ADDR_ACCD_Z_LSB;
break;
default:
return SYS_EINVAL;
}
rc = get_registers((struct bma2xx *)bma2xx, base_addr,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
compute_accel_data(accel_data, bma2xx->cfg.model, data, accel_scale);
return 0;
}
int
bma2xx_get_temp(struct bma2xx *bma2xx,
float * temp_c)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_ACCD_TEMP, &data);
if (rc != 0) {
return rc;
}
*temp_c = (float)(int8_t)data * 0.5 + 23.0;
return 0;
}
static void
quad_to_axis_trigger(struct axis_trigger * axis_trigger,
uint8_t quad_bits,
const char * name_bits)
{
axis_trigger->sign = (quad_bits >> 3) & 0x01;
switch (quad_bits & 0x07) {
default:
BMA2XX_LOG(ERROR, "unknown %s quad bits 0x%02X\n",
name_bits, quad_bits);
case 0x00:
axis_trigger->axis = -1;
axis_trigger->axis_known = false;
break;
case 0x01:
axis_trigger->axis = AXIS_X;
axis_trigger->axis_known = true;
break;
case 0x02:
axis_trigger->axis = AXIS_Y;
axis_trigger->axis_known = true;
break;
case 0x03:
axis_trigger->axis = AXIS_Z;
axis_trigger->axis_known = true;
break;
}
}
int
bma2xx_get_int_status(struct bma2xx *bma2xx,
struct int_status * int_status)
{
uint8_t data[4];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_STATUS_0,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
int_status->flat_int_active = data[0] & 0x80;
int_status->orient_int_active = data[0] & 0x40;
int_status->s_tap_int_active = data[0] & 0x20;
int_status->d_tap_int_active = data[0] & 0x10;
int_status->slow_no_mot_int_active = data[0] & 0x08;
int_status->slope_int_active = data[0] & 0x04;
int_status->high_g_int_active = data[0] & 0x02;
int_status->low_g_int_active = data[0] & 0x01;
int_status->data_int_active = data[1] & 0x80;
int_status->fifo_wmark_int_active = data[1] & 0x40;
int_status->fifo_full_int_active = data[1] & 0x20;
quad_to_axis_trigger(&int_status->tap_trigger,
(data[2] >> 4) & 0x0F, "tap");
quad_to_axis_trigger(&int_status->slope_trigger,
(data[2] >> 0) & 0x0F, "slope");
int_status->device_is_flat = data[3] & 0x80;
int_status->device_is_down = data[3] & 0x40;
int_status->device_orientation = (data[3] >> 4) & 0x03;
quad_to_axis_trigger(&int_status->high_g_trigger,
(data[3] >> 0) & 0x0F, "high_g");
return 0;
}
int
bma2xx_get_fifo_status(struct bma2xx *bma2xx,
bool * overrun,
uint8_t * frame_counter)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_FIFO_STATUS, &data);
if (rc != 0) {
return rc;
}
*overrun = data & 0x80;
*frame_counter = data & 0x7F;
return 0;
}
int
bma2xx_get_g_range(struct bma2xx *bma2xx,
enum bma2xx_g_range * g_range)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_RANGE, &data);
if (rc != 0) {
return rc;
}
switch (data & 0x0F) {
default:
BMA2XX_LOG(ERROR, "unknown PMU_RANGE reg value 0x%02X\n", data);
*g_range = BMA2XX_G_RANGE_16;
break;
case 0x03:
*g_range = BMA2XX_G_RANGE_2;
break;
case 0x05:
*g_range = BMA2XX_G_RANGE_4;
break;
case 0x08:
*g_range = BMA2XX_G_RANGE_8;
break;
case 0x0C:
*g_range = BMA2XX_G_RANGE_16;
break;
}
return 0;
}
int
bma2xx_set_g_range(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range)
{
uint8_t data;
switch (g_range) {
case BMA2XX_G_RANGE_2:
data = 0x03;
break;
case BMA2XX_G_RANGE_4:
data = 0x05;
break;
case BMA2XX_G_RANGE_8:
data = 0x08;
break;
case BMA2XX_G_RANGE_16:
data = 0x0C;
break;
default:
return SYS_EINVAL;
}
return set_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_RANGE, data);
}
int
bma2xx_get_filter_bandwidth(struct bma2xx *bma2xx,
enum bma2xx_filter_bandwidth * filter_bandwidth)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_BW, &data);
if (rc != 0) {
return rc;
}
switch (data & 0x1F) {
case 0x00 ... 0x08:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_7_81_HZ;
break;
case 0x09:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_15_63_HZ;
break;
case 0x0A:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_31_25_HZ;
break;
case 0x0B:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_62_5_HZ;
break;
case 0x0C:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_125_HZ;
break;
case 0x0D:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_250_HZ;
break;
case 0x0E:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_500_HZ;
break;
case 0x0F ... 0x1F:
*filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_1000_HZ;
break;
}
return 0;
}
int
bma2xx_set_filter_bandwidth(struct bma2xx *bma2xx,
enum bma2xx_filter_bandwidth filter_bandwidth)
{
uint8_t data;
switch (filter_bandwidth) {
case BMA2XX_FILTER_BANDWIDTH_7_81_HZ:
data = 0x08;
break;
case BMA2XX_FILTER_BANDWIDTH_15_63_HZ:
data = 0x09;
break;
case BMA2XX_FILTER_BANDWIDTH_31_25_HZ:
data = 0x0A;
break;
case BMA2XX_FILTER_BANDWIDTH_62_5_HZ:
data = 0x0B;
break;
case BMA2XX_FILTER_BANDWIDTH_125_HZ:
data = 0x0C;
break;
case BMA2XX_FILTER_BANDWIDTH_250_HZ:
data = 0x0D;
break;
case BMA2XX_FILTER_BANDWIDTH_500_HZ:
data = 0x0E;
break;
case BMA2XX_FILTER_BANDWIDTH_1000_HZ:
switch( bma2xx->cfg.model) {
case BMA2XX_BMA253:
data = 0x0F;
break;
case BMA2XX_BMA280:
return SYS_EINVAL;
default:
return SYS_EINVAL;
}
break;
case BMA2XX_FILTER_BANDWIDTH_ODR_MAX:
switch( bma2xx->cfg.model) {
case BMA2XX_BMA253:
return SYS_EINVAL;
case BMA2XX_BMA280:
data = 0x0F;
break;
default:
return SYS_EINVAL;
}
break;
default:
return SYS_EINVAL;
}
return set_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_BW, data);
}
int
bma2xx_get_power_settings(struct bma2xx *bma2xx,
struct power_settings * power_settings)
{
uint8_t data[2];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_PMU_LPW,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
switch ((data[0] >> 5) & 0x07) {
default:
BMA2XX_LOG(ERROR, "unknown PMU_LPW reg value 0x%02X\n", data[0]);
power_settings->power_mode = BMA2XX_POWER_MODE_NORMAL;
break;
case 0x00:
power_settings->power_mode = BMA2XX_POWER_MODE_NORMAL;
break;
case 0x01:
power_settings->power_mode = BMA2XX_POWER_MODE_DEEP_SUSPEND;
break;
case 0x02:
if ((data[1] & 0x40) == 0) {
power_settings->power_mode = BMA2XX_POWER_MODE_LPM_1;
} else {
power_settings->power_mode = BMA2XX_POWER_MODE_LPM_2;
}
break;
case 0x04:
if ((data[1] & 0x40) == 0) {
power_settings->power_mode = BMA2XX_POWER_MODE_SUSPEND;
} else {
power_settings->power_mode = BMA2XX_POWER_MODE_STANDBY;
}
break;
}
switch ((data[0] >> 1) & 0x0F) {
case 0x00 ... 0x05:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_0_5_MS;
break;
case 0x06:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_1_MS;
break;
case 0x07:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_2_MS;
break;
case 0x08:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_4_MS;
break;
case 0x09:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_6_MS;
break;
case 0x0A:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_10_MS;
break;
case 0x0B:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_25_MS;
break;
case 0x0C:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_50_MS;
break;
case 0x0D:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_100_MS;
break;
case 0x0E:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_500_MS;
break;
case 0x0F:
power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_1_S;
break;
}
if ((data[1] & 0x20) != 0) {
power_settings->sleep_timer = SLEEP_TIMER_EQUIDISTANT_SAMPLING;
} else {
power_settings->sleep_timer = SLEEP_TIMER_EVENT_DRIVEN;
}
return 0;
}
int
bma2xx_set_power_settings(struct bma2xx *bma2xx,
struct power_settings * power_settings)
{
uint8_t data[2];
int rc;
data[0] = 0;
data[1] = 0;
switch (power_settings->power_mode) {
case BMA2XX_POWER_MODE_NORMAL:
data[0] |= 0x00 << 5;
break;
case BMA2XX_POWER_MODE_DEEP_SUSPEND:
data[0] |= 0x01 << 5;
break;
case BMA2XX_POWER_MODE_SUSPEND:
data[0] |= 0x04 << 5;
data[1] |= 0x00 << 6;
break;
case BMA2XX_POWER_MODE_STANDBY:
data[0] |= 0x04 << 5;
data[1] |= 0x01 << 6;
break;
case BMA2XX_POWER_MODE_LPM_1:
data[0] |= 0x02 << 5;
data[1] |= 0x00 << 6;
break;
case BMA2XX_POWER_MODE_LPM_2:
data[0] |= 0x02 << 5;
data[1] |= 0x01 << 6;
break;
default:
return SYS_EINVAL;
}
switch (power_settings->sleep_duration) {
case BMA2XX_SLEEP_DURATION_0_5_MS:
data[0] |= 0x05 << 1;
break;
case BMA2XX_SLEEP_DURATION_1_MS:
data[0] |= 0x06 << 1;
break;
case BMA2XX_SLEEP_DURATION_2_MS:
data[0] |= 0x07 << 1;
break;
case BMA2XX_SLEEP_DURATION_4_MS:
data[0] |= 0x08 << 1;
break;
case BMA2XX_SLEEP_DURATION_6_MS:
data[0] |= 0x09 << 1;
break;
case BMA2XX_SLEEP_DURATION_10_MS:
data[0] |= 0x0A << 1;
break;
case BMA2XX_SLEEP_DURATION_25_MS:
data[0] |= 0x0B << 1;
break;
case BMA2XX_SLEEP_DURATION_50_MS:
data[0] |= 0x0C << 1;
break;
case BMA2XX_SLEEP_DURATION_100_MS:
data[0] |= 0x0D << 1;
break;
case BMA2XX_SLEEP_DURATION_500_MS:
data[0] |= 0x0E << 1;
break;
case BMA2XX_SLEEP_DURATION_1_S:
data[0] |= 0x0F << 1;
break;
default:
return SYS_EINVAL;
}
switch (power_settings->sleep_timer) {
case SLEEP_TIMER_EVENT_DRIVEN:
data[1] |= 0x00 << 5;
break;
case SLEEP_TIMER_EQUIDISTANT_SAMPLING:
data[1] |= 0x01 << 5;
break;
default:
return SYS_EINVAL;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_LOW_POWER, data[1]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_LPW, data[0]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_data_acquisition(struct bma2xx *bma2xx,
bool * unfiltered_reg_data,
bool * disable_reg_shadow)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_ACCD_HBW, &data);
if (rc != 0) {
return rc;
}
*unfiltered_reg_data = data & 0x80;
*disable_reg_shadow = data & 0x40;
return 0;
}
int
bma2xx_set_data_acquisition(struct bma2xx *bma2xx,
bool unfiltered_reg_data,
bool disable_reg_shadow)
{
uint8_t data;
data = (unfiltered_reg_data << 7) |
(disable_reg_shadow << 6);
return set_register((struct bma2xx *)bma2xx, REG_ADDR_ACCD_HBW, data);
}
int
bma2xx_set_softreset(struct bma2xx *bma2xx)
{
int rc;
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_BGW_SOFTRESET, REG_VALUE_SOFT_RESET);
if (rc != 0) {
return rc;
}
delay_msec(2);
return 0;
}
int
bma2xx_get_int_enable(struct bma2xx *bma2xx,
struct int_enable * int_enable)
{
uint8_t data[3];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_EN_0,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
int_enable->flat_int_enable = data[0] & 0x80;
int_enable->orient_int_enable = data[0] & 0x40;
int_enable->s_tap_int_enable = data[0] & 0x20;
int_enable->d_tap_int_enable = data[0] & 0x10;
int_enable->slope_z_int_enable = data[0] & 0x04;
int_enable->slope_y_int_enable = data[0] & 0x02;
int_enable->slope_x_int_enable = data[0] & 0x01;
int_enable->fifo_wmark_int_enable = data[1] & 0x40;
int_enable->fifo_full_int_enable = data[1] & 0x20;
int_enable->data_int_enable = data[1] & 0x10;
int_enable->low_g_int_enable = data[1] & 0x08;
int_enable->high_g_z_int_enable = data[1] & 0x04;
int_enable->high_g_y_int_enable = data[1] & 0x02;
int_enable->high_g_x_int_enable = data[1] & 0x01;
int_enable->no_motion_select = data[2] & 0x08;
int_enable->slow_no_mot_z_int_enable = data[2] & 0x04;
int_enable->slow_no_mot_y_int_enable = data[2] & 0x02;
int_enable->slow_no_mot_x_int_enable = data[2] & 0x01;
return 0;
}
int
bma2xx_set_int_enable(struct bma2xx *bma2xx,
struct int_enable * int_enable)
{
uint8_t data[3];
int rc;
data[0] = (int_enable->flat_int_enable << 7) |
(int_enable->orient_int_enable << 6) |
(int_enable->s_tap_int_enable << 5) |
(int_enable->d_tap_int_enable << 4) |
(int_enable->slope_z_int_enable << 2) |
(int_enable->slope_y_int_enable << 1) |
(int_enable->slope_x_int_enable << 0);
data[1] = (int_enable->fifo_wmark_int_enable << 6) |
(int_enable->fifo_full_int_enable << 5) |
(int_enable->data_int_enable << 4) |
(int_enable->low_g_int_enable << 3) |
(int_enable->high_g_z_int_enable << 2) |
(int_enable->high_g_y_int_enable << 1) |
(int_enable->high_g_x_int_enable << 0);
data[2] = (int_enable->no_motion_select << 3) |
(int_enable->slow_no_mot_z_int_enable << 2) |
(int_enable->slow_no_mot_y_int_enable << 1) |
(int_enable->slow_no_mot_x_int_enable << 0);
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_EN_0, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_EN_1, data[1]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_EN_2, data[2]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_int_routes(struct bma2xx *bma2xx,
struct int_routes * int_routes)
{
uint8_t data[3];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_MAP_0,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
int_routes->flat_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x80) != 0) {
int_routes->flat_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x80) != 0) {
int_routes->flat_int_route |= INT_ROUTE_PIN_2;
}
int_routes->orient_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x40) != 0) {
int_routes->orient_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x40) != 0) {
int_routes->orient_int_route |= INT_ROUTE_PIN_2;
}
int_routes->s_tap_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x20) != 0) {
int_routes->s_tap_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x20) != 0) {
int_routes->s_tap_int_route |= INT_ROUTE_PIN_2;
}
int_routes->d_tap_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x10) != 0) {
int_routes->d_tap_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x10) != 0) {
int_routes->d_tap_int_route |= INT_ROUTE_PIN_2;
}
int_routes->slow_no_mot_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x08) != 0) {
int_routes->slow_no_mot_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x08) != 0) {
int_routes->slow_no_mot_int_route |= INT_ROUTE_PIN_2;
}
int_routes->slope_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x04) != 0) {
int_routes->slope_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x04) != 0) {
int_routes->slope_int_route |= INT_ROUTE_PIN_2;
}
int_routes->high_g_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x02) != 0) {
int_routes->high_g_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x02) != 0) {
int_routes->high_g_int_route |= INT_ROUTE_PIN_2;
}
int_routes->low_g_int_route = INT_ROUTE_NONE;
if ((data[0] & 0x01) != 0) {
int_routes->low_g_int_route |= INT_ROUTE_PIN_1;
}
if ((data[2] & 0x01) != 0) {
int_routes->low_g_int_route |= INT_ROUTE_PIN_2;
}
int_routes->fifo_wmark_int_route = INT_ROUTE_NONE;
if ((data[1] & 0x02) != 0) {
int_routes->fifo_wmark_int_route |= INT_ROUTE_PIN_1;
}
if ((data[1] & 0x40) != 0) {
int_routes->fifo_wmark_int_route |= INT_ROUTE_PIN_2;
}
int_routes->fifo_full_int_route = INT_ROUTE_NONE;
if ((data[1] & 0x04) != 0) {
int_routes->fifo_full_int_route |= INT_ROUTE_PIN_1;
}
if ((data[1] & 0x20) != 0) {
int_routes->fifo_full_int_route |= INT_ROUTE_PIN_2;
}
int_routes->data_int_route = INT_ROUTE_NONE;
if ((data[1] & 0x01) != 0) {
int_routes->data_int_route |= INT_ROUTE_PIN_1;
}
if ((data[1] & 0x80) != 0) {
int_routes->data_int_route |= INT_ROUTE_PIN_2;
}
return 0;
}
int
bma2xx_set_int_routes(struct bma2xx *bma2xx,
struct int_routes * int_routes)
{
uint8_t data[3];
int rc;
data[0] = (((int_routes->flat_int_route & INT_ROUTE_PIN_1) != 0) << 7) |
(((int_routes->orient_int_route & INT_ROUTE_PIN_1) != 0) << 6) |
(((int_routes->s_tap_int_route & INT_ROUTE_PIN_1) != 0) << 5) |
(((int_routes->d_tap_int_route & INT_ROUTE_PIN_1) != 0) << 4) |
(((int_routes->slow_no_mot_int_route & INT_ROUTE_PIN_1) != 0) << 3) |
(((int_routes->slope_int_route & INT_ROUTE_PIN_1) != 0) << 2) |
(((int_routes->high_g_int_route & INT_ROUTE_PIN_1) != 0) << 1) |
(((int_routes->low_g_int_route & INT_ROUTE_PIN_1) != 0) << 0);
data[1] = (((int_routes->data_int_route & INT_ROUTE_PIN_2) != 0) << 7) |
(((int_routes->fifo_wmark_int_route & INT_ROUTE_PIN_2) != 0) << 6) |
(((int_routes->fifo_full_int_route & INT_ROUTE_PIN_2) != 0) << 5) |
(((int_routes->fifo_full_int_route & INT_ROUTE_PIN_1) != 0) << 2) |
(((int_routes->fifo_wmark_int_route & INT_ROUTE_PIN_1) != 0) << 1) |
(((int_routes->data_int_route & INT_ROUTE_PIN_1) != 0) << 0);
data[2] = (((int_routes->flat_int_route & INT_ROUTE_PIN_2) != 0) << 7) |
(((int_routes->orient_int_route & INT_ROUTE_PIN_2) != 0) << 6) |
(((int_routes->s_tap_int_route & INT_ROUTE_PIN_2) != 0) << 5) |
(((int_routes->d_tap_int_route & INT_ROUTE_PIN_2) != 0) << 4) |
(((int_routes->slow_no_mot_int_route & INT_ROUTE_PIN_2) != 0) << 3) |
(((int_routes->slope_int_route & INT_ROUTE_PIN_2) != 0) << 2) |
(((int_routes->high_g_int_route & INT_ROUTE_PIN_2) != 0) << 1) |
(((int_routes->low_g_int_route & INT_ROUTE_PIN_2) != 0) << 0);
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_MAP_0, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_MAP_1, data[1]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_MAP_2, data[2]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_int_filters(struct bma2xx *bma2xx,
struct int_filters * int_filters)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_INT_SRC, &data);
if (rc != 0) {
return rc;
}
int_filters->unfiltered_data_int = data & 0x20;
int_filters->unfiltered_tap_int = data & 0x10;
int_filters->unfiltered_slow_no_mot_int = data & 0x08;
int_filters->unfiltered_slope_int = data & 0x04;
int_filters->unfiltered_high_g_int = data & 0x02;
int_filters->unfiltered_low_g_int = data & 0x01;
return 0;
}
int
bma2xx_set_int_filters(struct bma2xx *bma2xx,
struct int_filters * int_filters)
{
uint8_t data;
data = (int_filters->unfiltered_data_int << 5) |
(int_filters->unfiltered_tap_int << 4) |
(int_filters->unfiltered_slow_no_mot_int << 3) |
(int_filters->unfiltered_slope_int << 2) |
(int_filters->unfiltered_high_g_int << 1) |
(int_filters->unfiltered_low_g_int << 0);
return set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_SRC, data);
}
int
bma2xx_get_int_pin_electrical(struct bma2xx *bma2xx,
struct int_pin_electrical * electrical)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_INT_OUT_CTRL, &data);
if (rc != 0) {
return rc;
}
if ((data & 0x02) != 0) {
electrical->pin1_output = INT_PIN_OUTPUT_OPEN_DRAIN;
} else {
electrical->pin1_output = INT_PIN_OUTPUT_PUSH_PULL;
}
if ((data & 0x01) != 0) {
electrical->pin1_active = INT_PIN_ACTIVE_HIGH;
} else {
electrical->pin1_active = INT_PIN_ACTIVE_LOW;
}
if ((data & 0x08) != 0) {
electrical->pin2_output = INT_PIN_OUTPUT_OPEN_DRAIN;
} else {
electrical->pin2_output = INT_PIN_OUTPUT_PUSH_PULL;
}
if ((data & 0x04) != 0) {
electrical->pin2_active = INT_PIN_ACTIVE_HIGH;
} else {
electrical->pin2_active = INT_PIN_ACTIVE_LOW;
}
return 0;
}
int
bma2xx_set_int_pin_electrical(struct bma2xx *bma2xx,
struct int_pin_electrical * electrical)
{
uint8_t data;
data = 0;
switch (electrical->pin1_output) {
case INT_PIN_OUTPUT_OPEN_DRAIN:
data |= 0x02;
break;
case INT_PIN_OUTPUT_PUSH_PULL:
data |= 0x00;
break;
default:
return SYS_EINVAL;
}
switch (electrical->pin1_active) {
case INT_PIN_ACTIVE_HIGH:
data |= 0x01;
break;
case INT_PIN_ACTIVE_LOW:
data |= 0x00;
break;
default:
return SYS_EINVAL;
}
switch (electrical->pin2_output) {
case INT_PIN_OUTPUT_OPEN_DRAIN:
data |= 0x08;
break;
case INT_PIN_OUTPUT_PUSH_PULL:
data |= 0x00;
break;
default:
return SYS_EINVAL;
}
switch (electrical->pin2_active) {
case INT_PIN_ACTIVE_HIGH:
data |= 0x04;
break;
case INT_PIN_ACTIVE_LOW:
data |= 0x00;
break;
default:
return SYS_EINVAL;
}
return set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_OUT_CTRL, data);
}
int
bma2xx_get_int_latch(struct bma2xx *bma2xx,
enum int_latch * int_latch)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_INT_RST_LATCH, &data);
if (rc != 0) {
return rc;
}
switch (data & 0x0F) {
case 0x00:
*int_latch = INT_LATCH_NON_LATCHED;
break;
case 0x01:
*int_latch = INT_LATCH_TEMPORARY_250_MS;
break;
case 0x02:
*int_latch = INT_LATCH_TEMPORARY_500_MS;
break;
case 0x03:
*int_latch = INT_LATCH_TEMPORARY_1_S;
break;
case 0x04:
*int_latch = INT_LATCH_TEMPORARY_2_S;
break;
case 0x05:
*int_latch = INT_LATCH_TEMPORARY_4_S;
break;
case 0x06:
*int_latch = INT_LATCH_TEMPORARY_8_S;
break;
case 0x07:
*int_latch = INT_LATCH_LATCHED;
break;
case 0x08:
*int_latch = INT_LATCH_NON_LATCHED;
break;
case 0x09:
*int_latch = INT_LATCH_TEMPORARY_250_US;
break;
case 0x0A:
*int_latch = INT_LATCH_TEMPORARY_500_US;
break;
case 0x0B:
*int_latch = INT_LATCH_TEMPORARY_1_MS;
break;
case 0x0C:
*int_latch = INT_LATCH_TEMPORARY_12_5_MS;
break;
case 0x0D:
*int_latch = INT_LATCH_TEMPORARY_25_MS;
break;
case 0x0E:
*int_latch = INT_LATCH_TEMPORARY_50_MS;
break;
case 0x0F:
*int_latch = INT_LATCH_LATCHED;
break;
}
return 0;
}
int
bma2xx_set_int_latch(struct bma2xx *bma2xx,
bool reset_ints,
enum int_latch int_latch)
{
uint8_t data;
data = 0;
data |= reset_ints << 7;
switch (int_latch) {
case INT_LATCH_NON_LATCHED:
data |= 0x00;
break;
case INT_LATCH_LATCHED:
data |= 0x0F;
break;
case INT_LATCH_TEMPORARY_250_US:
data |= 0x09;
break;
case INT_LATCH_TEMPORARY_500_US:
data |= 0x0A;
break;
case INT_LATCH_TEMPORARY_1_MS:
data |= 0x0B;
break;
case INT_LATCH_TEMPORARY_12_5_MS:
data |= 0x0C;
break;
case INT_LATCH_TEMPORARY_25_MS:
data |= 0x0D;
break;
case INT_LATCH_TEMPORARY_50_MS:
data |= 0x0E;
break;
case INT_LATCH_TEMPORARY_250_MS:
data |= 0x01;
break;
case INT_LATCH_TEMPORARY_500_MS:
data |= 0x02;
break;
case INT_LATCH_TEMPORARY_1_S:
data |= 0x03;
break;
case INT_LATCH_TEMPORARY_2_S:
data |= 0x04;
break;
case INT_LATCH_TEMPORARY_4_S:
data |= 0x05;
break;
case INT_LATCH_TEMPORARY_8_S:
data |= 0x06;
break;
default:
return SYS_EINVAL;
}
return set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_RST_LATCH, data);
}
int
bma2xx_get_low_g_int_cfg(struct bma2xx *bma2xx,
struct low_g_int_cfg * low_g_int_cfg)
{
uint8_t data[3];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_0,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
low_g_int_cfg->delay_ms = (data[0] + 1) << 1;
low_g_int_cfg->thresh_g = (float)data[1] * 0.00781;
low_g_int_cfg->hyster_g = (float)(data[2] & 0x03) * 0.125;
low_g_int_cfg->axis_summing = data[2] & 0x04;
return 0;
}
int
bma2xx_set_low_g_int_cfg(struct bma2xx *bma2xx,
struct low_g_int_cfg * low_g_int_cfg)
{
uint8_t data[3];
int rc;
if (low_g_int_cfg->delay_ms < 2 ||
low_g_int_cfg->delay_ms > 512) {
return SYS_EINVAL;
}
if (low_g_int_cfg->thresh_g < 0.0 ||
low_g_int_cfg->thresh_g > 1.992) {
return SYS_EINVAL;
}
if (low_g_int_cfg->hyster_g < 0.0 ||
low_g_int_cfg->hyster_g > 0.375) {
return SYS_EINVAL;
}
data[0] = (low_g_int_cfg->delay_ms >> 1) - 1;
data[1] = low_g_int_cfg->thresh_g / 0.00781;
data[2] = (low_g_int_cfg->axis_summing << 2) |
(((uint8_t)(low_g_int_cfg->hyster_g / 0.125) & 0x03) << 0);
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_0, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_1, data[1]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_2, data[2]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_high_g_int_cfg(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
struct high_g_int_cfg * high_g_int_cfg)
{
float hyster_scale;
float thresh_scale;
uint8_t data[3];
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
hyster_scale = 0.125;
thresh_scale = 0.00781;
break;
case BMA2XX_G_RANGE_4:
hyster_scale = 0.25;
thresh_scale = 0.01563;
break;
case BMA2XX_G_RANGE_8:
hyster_scale = 0.5;
thresh_scale = 0.03125;
break;
case BMA2XX_G_RANGE_16:
hyster_scale = 1.0;
thresh_scale = 0.0625;
break;
default:
return SYS_EINVAL;
}
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_2,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
high_g_int_cfg->hyster_g = (float)((data[0] >> 6) & 0x03) * hyster_scale;
high_g_int_cfg->delay_ms = (data[1] + 1) << 1;
high_g_int_cfg->thresh_g = (float)data[2] * thresh_scale;
return 0;
}
int
bma2xx_set_high_g_int_cfg(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
struct high_g_int_cfg * high_g_int_cfg)
{
float hyster_scale;
float thresh_scale;
uint8_t data[3];
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
hyster_scale = 0.125;
thresh_scale = 0.00781;
break;
case BMA2XX_G_RANGE_4:
hyster_scale = 0.25;
thresh_scale = 0.01563;
break;
case BMA2XX_G_RANGE_8:
hyster_scale = 0.5;
thresh_scale = 0.03125;
break;
case BMA2XX_G_RANGE_16:
hyster_scale = 1.0;
thresh_scale = 0.0625;
break;
default:
return SYS_EINVAL;
}
if (high_g_int_cfg->hyster_g < 0.0 ||
high_g_int_cfg->hyster_g > hyster_scale * 3.0) {
return SYS_EINVAL;
}
if (high_g_int_cfg->delay_ms < 2 ||
high_g_int_cfg->delay_ms > 512) {
return SYS_EINVAL;
}
if (high_g_int_cfg->thresh_g < 0.0 ||
high_g_int_cfg->thresh_g > thresh_scale * 255.0) {
return SYS_EINVAL;
}
data[0] = ((uint8_t)(high_g_int_cfg->hyster_g / hyster_scale) & 0x03) << 6;
data[1] = (high_g_int_cfg->delay_ms >> 1) - 1;
data[2] = high_g_int_cfg->thresh_g / thresh_scale;
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_2, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_3, data[1]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_4, data[2]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_slow_no_mot_int_cfg(struct bma2xx *bma2xx,
bool no_motion_select,
enum bma2xx_g_range g_range,
struct slow_no_mot_int_cfg * slow_no_mot_int_cfg)
{
float thresh_scale;
uint8_t data[2];
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
thresh_scale = 0.00391;
break;
case BMA2XX_G_RANGE_4:
thresh_scale = 0.00781;
break;
case BMA2XX_G_RANGE_8:
thresh_scale = 0.01563;
break;
case BMA2XX_G_RANGE_16:
thresh_scale = 0.03125;
break;
default:
return SYS_EINVAL;
}
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_INT_5, data + 0);
if (rc != 0) {
return rc;
}
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_INT_7, data + 1);
if (rc != 0) {
return rc;
}
if (no_motion_select) {
if ((data[0] & 0x80) == 0) {
if ((data[0] & 0x40) == 0) {
slow_no_mot_int_cfg->duration_p_or_s =
((data[0] >> 2) & 0x0F) + 1;
} else {
slow_no_mot_int_cfg->duration_p_or_s =
(((data[0] >> 2) & 0x0F) << 2) + 20;
}
} else {
slow_no_mot_int_cfg->duration_p_or_s =
(((data[0] >> 2) & 0x1F) << 3) + 88;
}
} else {
slow_no_mot_int_cfg->duration_p_or_s =
((data[0] >> 2) & 0x03) + 1;
}
slow_no_mot_int_cfg->thresh_g = (float)data[1] * thresh_scale;
return 0;
}
int
bma2xx_set_slow_no_mot_int_cfg(struct bma2xx *bma2xx,
bool no_motion_select,
enum bma2xx_g_range g_range,
struct slow_no_mot_int_cfg * slow_no_mot_int_cfg)
{
float thresh_scale;
uint8_t data[2];
uint16_t duration;
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
thresh_scale = 0.00391;
break;
case BMA2XX_G_RANGE_4:
thresh_scale = 0.00781;
break;
case BMA2XX_G_RANGE_8:
thresh_scale = 0.01563;
break;
case BMA2XX_G_RANGE_16:
thresh_scale = 0.03125;
break;
default:
return SYS_EINVAL;
}
if (no_motion_select) {
if (slow_no_mot_int_cfg->duration_p_or_s < 1 ||
slow_no_mot_int_cfg->duration_p_or_s > 336) {
return SYS_EINVAL;
}
} else {
if (slow_no_mot_int_cfg->duration_p_or_s < 1 ||
slow_no_mot_int_cfg->duration_p_or_s > 4) {
return SYS_EINVAL;
}
}
if (slow_no_mot_int_cfg->thresh_g < 0.0 ||
slow_no_mot_int_cfg->thresh_g > thresh_scale * 255.0) {
return SYS_EINVAL;
}
duration = slow_no_mot_int_cfg->duration_p_or_s;
if (no_motion_select) {
if (duration > 80) {
if (duration < 88) {
duration = 88;
}
data[0] = (((duration - 88) >> 3) << 2) | 0x80;
} else if (duration > 16) {
if (duration < 20) {
duration = 20;
}
data[0] = (((duration - 20) >> 2) << 2) | 0x40;
} else {
data[0] = (duration - 1) << 2;
}
} else {
data[0] = (duration - 1) << 2;
}
data[1] = slow_no_mot_int_cfg->thresh_g / thresh_scale;
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_5, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_7, data[1]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_slope_int_cfg(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
struct slope_int_cfg * slope_int_cfg)
{
float thresh_scale;
uint8_t data[2];
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
thresh_scale = 0.00391;
break;
case BMA2XX_G_RANGE_4:
thresh_scale = 0.00781;
break;
case BMA2XX_G_RANGE_8:
thresh_scale = 0.01563;
break;
case BMA2XX_G_RANGE_16:
thresh_scale = 0.03125;
break;
default:
return SYS_EINVAL;
}
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_5,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
slope_int_cfg->duration_p = (data[0] & 0x03) + 1;
slope_int_cfg->thresh_g = (float)data[1] * thresh_scale;
return 0;
}
int
bma2xx_set_slope_int_cfg(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
struct slope_int_cfg * slope_int_cfg)
{
float thresh_scale;
uint8_t data[2];
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
thresh_scale = 0.00391;
break;
case BMA2XX_G_RANGE_4:
thresh_scale = 0.00781;
break;
case BMA2XX_G_RANGE_8:
thresh_scale = 0.01563;
break;
case BMA2XX_G_RANGE_16:
thresh_scale = 0.03125;
break;
default:
return SYS_EINVAL;
}
if (slope_int_cfg->duration_p < 1 ||
slope_int_cfg->duration_p > 4) {
return SYS_EINVAL;
}
if (slope_int_cfg->thresh_g < 0.0 ||
slope_int_cfg->thresh_g > thresh_scale * 255.0) {
return SYS_EINVAL;
}
data[0] = (slope_int_cfg->duration_p - 1) & 0x03;
data[1] = slope_int_cfg->thresh_g / thresh_scale;
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_5, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_6, data[1]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_tap_int_cfg(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
struct tap_int_cfg * tap_int_cfg)
{
float thresh_scale;
uint8_t data[2];
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
thresh_scale = 0.0625;
break;
case BMA2XX_G_RANGE_4:
thresh_scale = 0.125;
break;
case BMA2XX_G_RANGE_8:
thresh_scale = 0.25;
break;
case BMA2XX_G_RANGE_16:
thresh_scale = 0.5;
break;
default:
return SYS_EINVAL;
}
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_8,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
if ((data[0] & 0x80) == 0) {
tap_int_cfg->tap_quiet = BMA2XX_TAP_QUIET_30_MS;
} else {
tap_int_cfg->tap_quiet = BMA2XX_TAP_QUIET_20_MS;
}
if ((data[0] & 0x40) == 0) {
tap_int_cfg->tap_shock = BMA2XX_TAP_SHOCK_50_MS;
} else {
tap_int_cfg->tap_shock = BMA2XX_TAP_SHOCK_75_MS;
}
switch (data[0] & 0x07) {
case 0x00:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_50_MS;
break;
case 0x01:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_100_MS;
break;
case 0x02:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_150_MS;
break;
case 0x03:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_200_MS;
break;
case 0x04:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_250_MS;
break;
case 0x05:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_375_MS;
break;
case 0x06:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_500_MS;
break;
case 0x07:
tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_700_MS;
break;
}
switch ((data[1] >> 6) & 0x03) {
case 0x00:
tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_2;
break;
case 0x01:
tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_4;
break;
case 0x02:
tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_8;
break;
case 0x03:
tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_16;
break;
}
tap_int_cfg->thresh_g = (float)(data[1] & 0x1F) * thresh_scale;
return 0;
}
int
bma2xx_set_tap_int_cfg(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
struct tap_int_cfg * tap_int_cfg)
{
float thresh_scale;
uint8_t data[2];
int rc;
switch (g_range) {
case BMA2XX_G_RANGE_2:
thresh_scale = 0.0625;
break;
case BMA2XX_G_RANGE_4:
thresh_scale = 0.125;
break;
case BMA2XX_G_RANGE_8:
thresh_scale = 0.25;
break;
case BMA2XX_G_RANGE_16:
thresh_scale = 0.5;
break;
default:
return SYS_EINVAL;
}
if (tap_int_cfg->thresh_g < 0.0 ||
tap_int_cfg->thresh_g > thresh_scale * 31.0) {
return SYS_EINVAL;
}
data[0] = 0;
data[1] = 0;
switch (tap_int_cfg->tap_quiet) {
case BMA2XX_TAP_QUIET_20_MS:
data[0] |= 0x80;
break;
case BMA2XX_TAP_QUIET_30_MS:
data[0] |= 0x00;
break;
default:
return SYS_EINVAL;
}
switch (tap_int_cfg->tap_shock) {
case BMA2XX_TAP_SHOCK_50_MS:
data[0] |= 0x00;
break;
case BMA2XX_TAP_SHOCK_75_MS:
data[0] |= 0x40;
break;
default:
return SYS_EINVAL;
}
switch (tap_int_cfg->d_tap_window) {
case BMA2XX_D_TAP_WINDOW_50_MS:
data[0] |= 0x00;
break;
case BMA2XX_D_TAP_WINDOW_100_MS:
data[0] |= 0x01;
break;
case BMA2XX_D_TAP_WINDOW_150_MS:
data[0] |= 0x02;
break;
case BMA2XX_D_TAP_WINDOW_200_MS:
data[0] |= 0x03;
break;
case BMA2XX_D_TAP_WINDOW_250_MS:
data[0] |= 0x04;
break;
case BMA2XX_D_TAP_WINDOW_375_MS:
data[0] |= 0x05;
break;
case BMA2XX_D_TAP_WINDOW_500_MS:
data[0] |= 0x06;
break;
case BMA2XX_D_TAP_WINDOW_700_MS:
data[0] |= 0x07;
break;
default:
return SYS_EINVAL;
}
switch (tap_int_cfg->tap_wake_samples) {
case BMA2XX_TAP_WAKE_SAMPLES_2:
data[1] |= 0x00 << 6;
break;
case BMA2XX_TAP_WAKE_SAMPLES_4:
data[1] |= 0x01 << 6;
break;
case BMA2XX_TAP_WAKE_SAMPLES_8:
data[1] |= 0x02 << 6;
break;
case BMA2XX_TAP_WAKE_SAMPLES_16:
data[1] |= 0x03 << 6;
break;
default:
return SYS_EINVAL;
}
data[1] |= (uint8_t)(tap_int_cfg->thresh_g / thresh_scale) & 0x1F;
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_8, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_9, data[1]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_orient_int_cfg(struct bma2xx *bma2xx,
struct orient_int_cfg * orient_int_cfg)
{
uint8_t data[2];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_A,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
orient_int_cfg->hyster_g = (float)((data[0] >> 4) & 0x07) * 0.0625;
switch ((data[0] >> 2) & 0x03) {
case 0x00:
orient_int_cfg->orient_blocking =
BMA2XX_ORIENT_BLOCKING_NONE;
break;
case 0x01:
orient_int_cfg->orient_blocking =
BMA2XX_ORIENT_BLOCKING_ACCEL_ONLY;
break;
case 0x02:
orient_int_cfg->orient_blocking =
BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE;
break;
case 0x03:
orient_int_cfg->orient_blocking =
BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE_AND_STABLE;
break;
}
switch (data[0] & 0x03) {
case 0x00:
orient_int_cfg->orient_mode =
BMA2XX_ORIENT_MODE_SYMMETRICAL;
break;
case 0x01:
orient_int_cfg->orient_mode =
BMA2XX_ORIENT_MODE_HIGH_ASYMMETRICAL;
break;
case 0x02:
orient_int_cfg->orient_mode =
BMA2XX_ORIENT_MODE_LOW_ASYMMETRICAL;
break;
case 0x03:
orient_int_cfg->orient_mode =
BMA2XX_ORIENT_MODE_SYMMETRICAL;
break;
}
orient_int_cfg->signal_up_dn = data[1] & 0x40;
orient_int_cfg->blocking_angle = data[1] & 0x3F;
return 0;
}
int
bma2xx_set_orient_int_cfg(struct bma2xx *bma2xx,
struct orient_int_cfg * orient_int_cfg)
{
uint8_t data[2];
int rc;
if (orient_int_cfg->hyster_g < 0.0 ||
orient_int_cfg->hyster_g > (0.0625 * 7.0)) {
return SYS_EINVAL;
}
if (orient_int_cfg->blocking_angle > 0x3F) {
return SYS_EINVAL;
}
data[0] = (uint8_t)(orient_int_cfg->hyster_g / 0.0625) << 4;
switch (orient_int_cfg->orient_blocking) {
case BMA2XX_ORIENT_BLOCKING_NONE:
data[0] |= 0x00 << 2;
break;
case BMA2XX_ORIENT_BLOCKING_ACCEL_ONLY:
data[0] |= 0x01 << 2;
break;
case BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE:
data[0] |= 0x02 << 2;
break;
case BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE_AND_STABLE:
data[0] |= 0x03 << 2;
break;
default:
return SYS_EINVAL;
}
switch (orient_int_cfg->orient_mode) {
case BMA2XX_ORIENT_MODE_SYMMETRICAL:
data[0] |= 0x00;
break;
case BMA2XX_ORIENT_MODE_HIGH_ASYMMETRICAL:
data[0] |= 0x01;
break;
case BMA2XX_ORIENT_MODE_LOW_ASYMMETRICAL:
data[0] |= 0x02;
break;
default:
return SYS_EINVAL;
}
data[1] = (orient_int_cfg->signal_up_dn << 6) |
((orient_int_cfg->blocking_angle & 0x3F) << 0);
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_A, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_B, data[1]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_flat_int_cfg(struct bma2xx *bma2xx,
struct flat_int_cfg * flat_int_cfg)
{
uint8_t data[2];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_INT_C,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
flat_int_cfg->flat_angle = data[0] & 0x3F;
switch ((data[1] >> 4) & 0x03) {
case 0x00:
flat_int_cfg->flat_hold = FLAT_HOLD_0_MS;
break;
case 0x01:
flat_int_cfg->flat_hold = FLAT_HOLD_512_MS;
break;
case 0x02:
flat_int_cfg->flat_hold = FLAT_HOLD_1024_MS;
break;
case 0x03:
flat_int_cfg->flat_hold = FLAT_HOLD_2048_MS;
break;
}
flat_int_cfg->flat_hyster = data[1] & 0x07;
flat_int_cfg->hyster_enable = (data[1] & 0x07) != 0x00;
return 0;
}
int
bma2xx_set_flat_int_cfg(struct bma2xx *bma2xx,
struct flat_int_cfg * flat_int_cfg)
{
uint8_t data[2];
int rc;
if (flat_int_cfg->flat_angle > 0x3F) {
return SYS_EINVAL;
}
if (flat_int_cfg->flat_hyster == 0x00 &&
flat_int_cfg->hyster_enable) {
return SYS_EINVAL;
}
data[0] = flat_int_cfg->flat_angle & 0x3F;
data[1] = 0;
switch (flat_int_cfg->flat_hold) {
case FLAT_HOLD_0_MS:
data[1] |= 0x00 << 4;
break;
case FLAT_HOLD_512_MS:
data[1] |= 0x01 << 4;
break;
case FLAT_HOLD_1024_MS:
data[1] |= 0x02 << 4;
break;
case FLAT_HOLD_2048_MS:
data[1] |= 0x03 << 4;
break;
}
if (flat_int_cfg->hyster_enable) {
data[1] |= flat_int_cfg->flat_hyster & 0x07;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_C, data[0]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_INT_D, data[1]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_fifo_wmark_level(struct bma2xx *bma2xx,
uint8_t * wmark_level)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_FIFO_CONFIG_0, &data);
if (rc != 0) {
return rc;
}
*wmark_level = data & 0x3F;
return 0;
}
int
bma2xx_set_fifo_wmark_level(struct bma2xx *bma2xx,
uint8_t wmark_level)
{
uint8_t data;
if (wmark_level > 32) {
return SYS_EINVAL;
}
data = wmark_level & 0x3F;
return set_register((struct bma2xx *)bma2xx, REG_ADDR_FIFO_CONFIG_0, data);
}
int
bma2xx_get_self_test_cfg(struct bma2xx *bma2xx,
struct self_test_cfg * self_test_cfg)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_SELF_TEST, &data);
if (rc != 0) {
return rc;
}
if ((data & 0x10) == 0) {
self_test_cfg->self_test_ampl = SELF_TEST_AMPL_LOW;
} else {
self_test_cfg->self_test_ampl = SELF_TEST_AMPL_HIGH;
}
if ((data & 0x04) == 0) {
self_test_cfg->self_test_sign = SELF_TEST_SIGN_NEGATIVE;
} else {
self_test_cfg->self_test_sign = SELF_TEST_SIGN_POSITIVE;
}
switch (data & 0x03) {
case 0x00:
self_test_cfg->self_test_axis = -1;
self_test_cfg->self_test_enabled = false;
break;
case 0x01:
self_test_cfg->self_test_axis = AXIS_X;
self_test_cfg->self_test_enabled = true;
break;
case 0x02:
self_test_cfg->self_test_axis = AXIS_Y;
self_test_cfg->self_test_enabled = true;
break;
case 0x03:
self_test_cfg->self_test_axis = AXIS_Z;
self_test_cfg->self_test_enabled = true;
break;
}
return 0;
}
int
bma2xx_set_self_test_cfg(struct bma2xx *bma2xx,
struct self_test_cfg * self_test_cfg)
{
uint8_t data;
data = 0;
switch (self_test_cfg->self_test_ampl) {
case SELF_TEST_AMPL_HIGH:
data |= 0x10;
break;
case SELF_TEST_AMPL_LOW:
data |= 0x00;
break;
default:
return SYS_EINVAL;
}
switch (self_test_cfg->self_test_sign) {
case SELF_TEST_SIGN_NEGATIVE:
data |= 0x00;
break;
case SELF_TEST_SIGN_POSITIVE:
data |= 0x04;
break;
default:
return SYS_EINVAL;
}
if (self_test_cfg->self_test_enabled) {
switch (self_test_cfg->self_test_axis) {
case AXIS_X:
data |= 0x01;
break;
case AXIS_Y:
data |= 0x02;
break;
case AXIS_Z:
data |= 0x03;
break;
default:
return SYS_EINVAL;
}
}
return set_register((struct bma2xx *)bma2xx, REG_ADDR_PMU_SELF_TEST, data);
}
int
bma2xx_get_nvm_control(struct bma2xx *bma2xx,
uint8_t * remaining_cycles,
bool * load_from_nvm,
bool * nvm_is_ready,
bool * nvm_unlocked)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_TRIM_NVM_CTRL, &data);
if (rc != 0) {
return rc;
}
*remaining_cycles = (data >> 4) & 0x0F;
*load_from_nvm = data & 0x08;
*nvm_is_ready = data & 0x04;
*nvm_unlocked = data & 0x01;
return 0;
}
int
bma2xx_set_nvm_control(struct bma2xx *bma2xx,
bool load_from_nvm,
bool store_into_nvm,
bool nvm_unlocked)
{
uint8_t data;
data = (load_from_nvm << 3) |
(store_into_nvm << 1) |
(nvm_unlocked << 0);
return set_register((struct bma2xx *)bma2xx, REG_ADDR_TRIM_NVM_CTRL, data);
}
int
bma2xx_get_i2c_watchdog(struct bma2xx *bma2xx,
enum i2c_watchdog * i2c_watchdog)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_BGW_SPI3_WDT, &data);
if (rc != 0) {
return rc;
}
if ((data & 0x04) != 0) {
if ((data & 0x02) != 0) {
*i2c_watchdog = I2C_WATCHDOG_50_MS;
} else {
*i2c_watchdog = I2C_WATCHDOG_1_MS;
}
} else {
*i2c_watchdog = I2C_WATCHDOG_DISABLED;
}
return 0;
}
int
bma2xx_set_i2c_watchdog(struct bma2xx *bma2xx,
enum i2c_watchdog i2c_watchdog)
{
uint8_t data;
switch (i2c_watchdog) {
case I2C_WATCHDOG_DISABLED:
data = 0x00;
break;
case I2C_WATCHDOG_1_MS:
data = 0x04;
break;
case I2C_WATCHDOG_50_MS:
data = 0x06;
break;
default:
return SYS_EINVAL;
}
return set_register((struct bma2xx *)bma2xx, REG_ADDR_BGW_SPI3_WDT, data);
}
int
bma2xx_get_fast_ofc_cfg(struct bma2xx *bma2xx,
bool * fast_ofc_ready,
enum bma2xx_offset_comp_target * ofc_target_z,
enum bma2xx_offset_comp_target * ofc_target_y,
enum bma2xx_offset_comp_target * ofc_target_x)
{
uint8_t data[2];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_OFC_CTRL,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
*fast_ofc_ready = data[0] & 0x10;
switch ((data[1] >> 5) & 0x03) {
case 0x00:
*ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_0_G;
break;
case 0x01:
*ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_POS_1_G;
break;
case 0x02:
*ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_NEG_1_G;
break;
case 0x03:
*ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_0_G;
break;
}
switch ((data[1] >> 3) & 0x03) {
case 0x00:
*ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_0_G;
break;
case 0x01:
*ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_POS_1_G;
break;
case 0x02:
*ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_NEG_1_G;
break;
case 0x03:
*ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_0_G;
break;
}
switch ((data[1] >> 1) & 0x03) {
case 0x00:
*ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_0_G;
break;
case 0x01:
*ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_POS_1_G;
break;
case 0x02:
*ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_NEG_1_G;
break;
case 0x03:
*ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_0_G;
break;
}
return 0;
}
int
bma2xx_set_fast_ofc_cfg(struct bma2xx *bma2xx,
enum axis fast_ofc_axis,
enum bma2xx_offset_comp_target fast_ofc_target,
bool trigger_fast_ofc)
{
uint8_t data[2];
uint8_t axis_value;
uint8_t axis_shift;
int rc;
data[0] = 0;
data[1] = 0;
switch (fast_ofc_axis) {
case AXIS_X:
axis_value = 0x01;
axis_shift = 1;
break;
case AXIS_Y:
axis_value = 0x02;
axis_shift = 3;
break;
case AXIS_Z:
axis_value = 0x03;
axis_shift = 5;
break;
default:
return SYS_EINVAL;
}
switch (fast_ofc_target) {
case BMA2XX_OFFSET_COMP_TARGET_0_G:
data[1] |= 0x00 << axis_shift;
break;
case BMA2XX_OFFSET_COMP_TARGET_NEG_1_G:
data[1] |= 0x02 << axis_shift;
break;
case BMA2XX_OFFSET_COMP_TARGET_POS_1_G:
data[1] |= 0x01 << axis_shift;
break;
default:
return SYS_EINVAL;
}
if (trigger_fast_ofc) {
data[0] |= axis_value << 5;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_OFC_SETTING, data[1]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_OFC_CTRL, data[0]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_get_slow_ofc_cfg(struct bma2xx *bma2xx,
struct slow_ofc_cfg * slow_ofc_cfg)
{
uint8_t data[2];
int rc;
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_OFC_CTRL,
data, sizeof(data) / sizeof(*data));
if (rc != 0) {
return rc;
}
slow_ofc_cfg->ofc_z_enabled = data[0] & 0x04;
slow_ofc_cfg->ofc_y_enabled = data[0] & 0x02;
slow_ofc_cfg->ofc_x_enabled = data[0] & 0x01;
slow_ofc_cfg->high_bw_cut_off = data[1] & 0x01;
return 0;
}
int
bma2xx_set_slow_ofc_cfg(struct bma2xx *bma2xx,
struct slow_ofc_cfg * slow_ofc_cfg)
{
uint8_t data[2];
int rc;
data[0] = (slow_ofc_cfg->ofc_z_enabled << 2) |
(slow_ofc_cfg->ofc_y_enabled << 1) |
(slow_ofc_cfg->ofc_x_enabled << 0);
data[1] = slow_ofc_cfg->high_bw_cut_off << 0;
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_OFC_SETTING, data[1]);
if (rc != 0) {
return rc;
}
rc = set_register((struct bma2xx *)bma2xx, REG_ADDR_OFC_CTRL, data[0]);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_set_ofc_reset(struct bma2xx *bma2xx)
{
return set_register((struct bma2xx *)bma2xx, REG_ADDR_OFC_CTRL, 0x80);
}
int
bma2xx_get_ofc_offset(struct bma2xx *bma2xx,
enum axis axis,
float * offset_g)
{
uint8_t reg_addr;
uint8_t data;
int rc;
switch (axis) {
case AXIS_X:
reg_addr = REG_ADDR_OFC_OFFSET_X;
break;
case AXIS_Y:
reg_addr = REG_ADDR_OFC_OFFSET_Y;
break;
case AXIS_Z:
reg_addr = REG_ADDR_OFC_OFFSET_Z;
break;
default:
return SYS_EINVAL;
}
rc = get_register((struct bma2xx *)bma2xx, reg_addr, &data);
if (rc != 0) {
return rc;
}
*offset_g = (float)(int8_t)data * 0.00781;
return 0;
}
int
bma2xx_set_ofc_offset(struct bma2xx *bma2xx,
enum axis axis,
float offset_g)
{
uint8_t reg_addr;
uint8_t data;
switch (axis) {
case AXIS_X:
reg_addr = REG_ADDR_OFC_OFFSET_X;
break;
case AXIS_Y:
reg_addr = REG_ADDR_OFC_OFFSET_Y;
break;
case AXIS_Z:
reg_addr = REG_ADDR_OFC_OFFSET_Z;
break;
default:
return SYS_EINVAL;
}
data = (int8_t)(offset_g / 0.00781);
return set_register((struct bma2xx *)bma2xx, reg_addr, data);
}
int
bma2xx_get_saved_data(struct bma2xx *bma2xx,
enum saved_data_addr saved_data_addr,
uint8_t * saved_data_val)
{
uint8_t reg_addr;
switch (saved_data_addr) {
case SAVED_DATA_ADDR_0:
reg_addr = REG_ADDR_TRIM_GP0;
break;
case SAVED_DATA_ADDR_1:
reg_addr = REG_ADDR_TRIM_GP1;
break;
default:
return SYS_EINVAL;
}
return get_register((struct bma2xx *)bma2xx, reg_addr, saved_data_val);
}
int
bma2xx_set_saved_data(struct bma2xx *bma2xx,
enum saved_data_addr saved_data_addr,
uint8_t saved_data_val)
{
uint8_t reg_addr;
switch (saved_data_addr) {
case SAVED_DATA_ADDR_0:
reg_addr = REG_ADDR_TRIM_GP0;
break;
case SAVED_DATA_ADDR_1:
reg_addr = REG_ADDR_TRIM_GP1;
break;
default:
return SYS_EINVAL;
}
return set_register((struct bma2xx *)bma2xx, reg_addr, saved_data_val);
}
int
bma2xx_get_fifo_cfg(struct bma2xx *bma2xx,
struct fifo_cfg * fifo_cfg)
{
uint8_t data;
int rc;
rc = get_register((struct bma2xx *)bma2xx, REG_ADDR_FIFO_CONFIG_1, &data);
if (rc != 0) {
return rc;
}
switch ((data >> 6) & 0x03) {
case 0x03:
BMA2XX_LOG(ERROR, "unknown FIFO_CONFIG_1 reg value 0x%02X\n", data);
case 0x00:
fifo_cfg->fifo_mode = FIFO_MODE_BYPASS;
break;
case 0x01:
fifo_cfg->fifo_mode = FIFO_MODE_FIFO;
break;
case 0x02:
fifo_cfg->fifo_mode = FIFO_MODE_STREAM;
break;
}
switch ((data >> 0) & 0x03) {
case 0x00:
fifo_cfg->fifo_data = FIFO_DATA_X_AND_Y_AND_Z;
break;
case 0x01:
fifo_cfg->fifo_data = FIFO_DATA_X_ONLY;
break;
case 0x02:
fifo_cfg->fifo_data = FIFO_DATA_Y_ONLY;
break;
case 0x03:
fifo_cfg->fifo_data = FIFO_DATA_Z_ONLY;
break;
}
return 0;
}
int
bma2xx_set_fifo_cfg(struct bma2xx *bma2xx,
struct fifo_cfg * fifo_cfg)
{
uint8_t data;
data = 0;
switch (fifo_cfg->fifo_mode) {
case FIFO_MODE_BYPASS:
data |= 0x00 << 6;
break;
case FIFO_MODE_FIFO:
data |= 0x01 << 6;
break;
case FIFO_MODE_STREAM:
data |= 0x02 << 6;
break;
default:
return SYS_EINVAL;
}
switch (fifo_cfg->fifo_data) {
case FIFO_DATA_X_AND_Y_AND_Z:
data |= 0x00 << 0;
break;
case FIFO_DATA_X_ONLY:
data |= 0x01 << 0;
break;
case FIFO_DATA_Y_ONLY:
data |= 0x02 << 0;
break;
case FIFO_DATA_Z_ONLY:
data |= 0x03 << 0;
break;
}
return set_register((struct bma2xx *)bma2xx, REG_ADDR_FIFO_CONFIG_1, data);
}
int
bma2xx_get_fifo(struct bma2xx *bma2xx,
enum bma2xx_g_range g_range,
enum fifo_data fifo_data,
struct accel_data * accel_data)
{
float accel_scale;
uint8_t size, iter;
uint8_t data[AXIS_ALL << 1];
int rc;
rc = get_accel_scale(bma2xx->cfg.model, g_range, &accel_scale);
if (rc != 0){
return SYS_EINVAL;
}
switch (fifo_data) {
case FIFO_DATA_X_AND_Y_AND_Z:
size = AXIS_ALL << 1;
break;
case FIFO_DATA_X_ONLY:
case FIFO_DATA_Y_ONLY:
case FIFO_DATA_Z_ONLY:
size = 1 << 1;
break;
default:
return SYS_EINVAL;
}
rc = get_registers((struct bma2xx *)bma2xx, REG_ADDR_FIFO_DATA, data, size);
if (rc != 0) {
return rc;
}
for (iter = 0; iter < size; iter += 2) {
compute_accel_data(accel_data + (iter >> 1),
bma2xx->cfg.model,
data + iter,
accel_scale);
}
return 0;
}
static int
reset_and_recfg(struct bma2xx *bma2xx)
{
struct bma2xx_cfg *cfg;
int rc;
enum int_route int_route;
struct int_routes int_routes;
struct int_filters int_filters;
struct int_pin_electrical int_pin_electrical;
struct low_g_int_cfg low_g_int_cfg;
struct high_g_int_cfg high_g_int_cfg;
struct tap_int_cfg tap_int_cfg;
struct orient_int_cfg orient_int_cfg;
enum i2c_watchdog i2c_watchdog;
struct fifo_cfg fifo_cfg;
struct bma2xx_private_driver_data *pdd;
cfg = &bma2xx->cfg;
pdd = &bma2xx->pdd;
bma2xx->power = BMA2XX_POWER_MODE_NORMAL;
rc = bma2xx_set_softreset(bma2xx);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_g_range(bma2xx, cfg->g_range);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_filter_bandwidth(bma2xx, cfg->filter_bandwidth);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_data_acquisition(bma2xx,
cfg->use_unfiltered_data,
false);
if (rc != 0) {
return rc;
}
int_route = INT_ROUTE_NONE;
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
int_route = pdd->int_route;
#endif
int_routes.flat_int_route = INT_ROUTE_NONE;
int_routes.orient_int_route = int_route;
int_routes.s_tap_int_route = INT_ROUTE_NONE;
int_routes.d_tap_int_route = INT_ROUTE_NONE;
int_routes.slow_no_mot_int_route = INT_ROUTE_NONE;
int_routes.slope_int_route = INT_ROUTE_NONE;
int_routes.high_g_int_route = int_route;
int_routes.low_g_int_route = int_route;
int_routes.fifo_wmark_int_route = INT_ROUTE_NONE;
int_routes.fifo_full_int_route = INT_ROUTE_NONE;
int_routes.data_int_route = int_route;
rc = bma2xx_set_int_routes(bma2xx, &int_routes);
if (rc != 0) {
return rc;
}
int_filters.unfiltered_data_int = cfg->use_unfiltered_data;
int_filters.unfiltered_tap_int = cfg->use_unfiltered_data;
int_filters.unfiltered_slow_no_mot_int = cfg->use_unfiltered_data;
int_filters.unfiltered_slope_int = cfg->use_unfiltered_data;
int_filters.unfiltered_high_g_int = cfg->use_unfiltered_data;
int_filters.unfiltered_low_g_int = cfg->use_unfiltered_data;
rc = bma2xx_set_int_filters(bma2xx, &int_filters);
if (rc != 0) {
return rc;
}
#if MYNEWT_VAL(BMA2XX_INT_CFG_OUTPUT)
int_pin_electrical.pin1_output = INT_PIN_OUTPUT_OPEN_DRAIN;
int_pin_electrical.pin2_output = INT_PIN_OUTPUT_OPEN_DRAIN;
#else
int_pin_electrical.pin1_output = INT_PIN_OUTPUT_PUSH_PULL;
int_pin_electrical.pin2_output = INT_PIN_OUTPUT_PUSH_PULL;
#endif
#if MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)
int_pin_electrical.pin1_active = INT_PIN_ACTIVE_HIGH;
int_pin_electrical.pin2_active = INT_PIN_ACTIVE_HIGH;
#else
int_pin_electrical.pin1_active = INT_PIN_ACTIVE_LOW;
int_pin_electrical.pin2_active = INT_PIN_ACTIVE_LOW;
#endif
rc = bma2xx_set_int_pin_electrical(bma2xx, &int_pin_electrical);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_int_latch(bma2xx, false, INT_LATCH_NON_LATCHED);
if (rc != 0) {
return rc;
}
low_g_int_cfg.delay_ms = cfg->low_g_delay_ms;
low_g_int_cfg.thresh_g = cfg->low_g_thresh_g;
low_g_int_cfg.hyster_g = cfg->low_g_hyster_g;
low_g_int_cfg.axis_summing = false;
rc = bma2xx_set_low_g_int_cfg(bma2xx, &low_g_int_cfg);
if (rc != 0) {
return rc;
}
high_g_int_cfg.hyster_g = cfg->high_g_hyster_g;
high_g_int_cfg.delay_ms = cfg->high_g_delay_ms;
high_g_int_cfg.thresh_g = cfg->high_g_thresh_g;
rc = bma2xx_set_high_g_int_cfg(bma2xx, cfg->g_range, &high_g_int_cfg);
if (rc != 0) {
return rc;
}
tap_int_cfg.tap_quiet = cfg->tap_quiet;
tap_int_cfg.tap_shock = cfg->tap_shock;
tap_int_cfg.d_tap_window = cfg->d_tap_window;
tap_int_cfg.tap_wake_samples = cfg->tap_wake_samples;
tap_int_cfg.thresh_g = cfg->tap_thresh_g;
rc = bma2xx_set_tap_int_cfg(bma2xx, cfg->g_range, &tap_int_cfg);
if (rc != 0) {
return rc;
}
orient_int_cfg.hyster_g = cfg->orient_hyster_g;
orient_int_cfg.orient_blocking = cfg->orient_blocking;
orient_int_cfg.orient_mode = cfg->orient_mode;
orient_int_cfg.signal_up_dn = cfg->orient_signal_ud;
orient_int_cfg.blocking_angle = 0x08;
rc = bma2xx_set_orient_int_cfg(bma2xx, &orient_int_cfg);
if (rc != 0) {
return rc;
}
#if MYNEWT_VAL(BMA2XX_I2C_WDT)
i2c_watchdog = I2C_WATCHDOG_50_MS;
#else
i2c_watchdog = I2C_WATCHDOG_DISABLED;
#endif
rc = bma2xx_set_i2c_watchdog(bma2xx, i2c_watchdog);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_ofc_offset(bma2xx, AXIS_X, cfg->offset_x_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Y, cfg->offset_y_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Z, cfg->offset_z_g);
if (rc != 0) {
return rc;
}
fifo_cfg.fifo_mode = FIFO_MODE_BYPASS;
fifo_cfg.fifo_data = FIFO_DATA_X_AND_Y_AND_Z;
rc = bma2xx_set_fifo_cfg(bma2xx, &fifo_cfg);
if (rc != 0) {
return rc;
}
return 0;
}
static int
change_power(struct bma2xx *bma2xx,
enum bma2xx_power_mode target)
{
struct bma2xx_cfg *cfg;
int rc;
bool step1_move;
bool step2_move;
enum bma2xx_power_mode step1_mode;
enum bma2xx_power_mode step2_mode;
struct power_settings power_settings;
cfg = &bma2xx->cfg;
if (bma2xx->power == BMA2XX_POWER_MODE_DEEP_SUSPEND) {
rc = reset_and_recfg(bma2xx);
if (rc != 0) {
return rc;
}
}
step1_move = false;
switch (bma2xx->power) {
case BMA2XX_POWER_MODE_SUSPEND:
case BMA2XX_POWER_MODE_LPM_1:
switch (target) {
case BMA2XX_POWER_MODE_STANDBY:
case BMA2XX_POWER_MODE_LPM_2:
step1_mode = BMA2XX_POWER_MODE_NORMAL;
step1_move = true;
break;
default:
break;
}
break;
case BMA2XX_POWER_MODE_STANDBY:
case BMA2XX_POWER_MODE_LPM_2:
switch (target) {
case BMA2XX_POWER_MODE_SUSPEND:
case BMA2XX_POWER_MODE_LPM_1:
step1_mode = BMA2XX_POWER_MODE_NORMAL;
step1_move = true;
break;
default:
break;
}
break;
default:
break;
}
if (bma2xx->power != target) {
step2_mode = target;
step2_move = true;
} else {
step2_move = false;
}
if (step1_move) {
power_settings.power_mode = step1_mode;
power_settings.sleep_duration = cfg->sleep_duration;
power_settings.sleep_timer = SLEEP_TIMER_EVENT_DRIVEN;
rc = bma2xx_set_power_settings(bma2xx, &power_settings);
if (rc != 0) {
return rc;
}
bma2xx->power = step1_mode;
}
if (step2_move) {
power_settings.power_mode = step2_mode;
power_settings.sleep_duration = cfg->sleep_duration;
power_settings.sleep_timer = SLEEP_TIMER_EVENT_DRIVEN;
rc = bma2xx_set_power_settings(bma2xx, &power_settings);
if (rc != 0) {
return rc;
}
bma2xx->power = step2_mode;
}
return 0;
}
static int
interim_power(struct bma2xx *bma2xx,
const enum bma2xx_power_mode * reqs,
uint8_t size)
{
uint8_t i;
if (size == 0) {
return SYS_EINVAL;
}
for (i = 0; i < size; i++) {
if (reqs[i] == bma2xx->power) {
return 0;
}
}
return change_power(bma2xx, reqs[0]);
}
static int
default_power(struct bma2xx *bma2xx)
{
struct bma2xx_cfg *cfg;
cfg = &bma2xx->cfg;
if (cfg->power_mode == bma2xx->power) {
return 0;
}
return change_power(bma2xx, cfg->power_mode);
}
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
static int
init_intpin(struct bma2xx *bma2xx,
hal_gpio_irq_handler_t handler,
void * arg)
{
struct bma2xx_private_driver_data *pdd = &bma2xx->pdd;
hal_gpio_irq_trig_t trig;
int pin = -1;
int rc;
int i;
for (i = 0; i < MYNEWT_VAL(SENSOR_MAX_INTERRUPTS_PINS); i++){
pin = bma2xx->sensor.s_itf.si_ints[i].host_pin;
if (pin > 0) {
break;
}
}
if (pin < 0) {
BMA2XX_LOG(ERROR, "Interrupt pin not configured\n");
return SYS_EINVAL;
}
pdd->int_num = i;
if (bma2xx->sensor.s_itf.si_ints[pdd->int_num].active) {
trig = HAL_GPIO_TRIG_RISING;
} else {
trig = HAL_GPIO_TRIG_FALLING;
}
if (bma2xx->sensor.s_itf.si_ints[pdd->int_num].device_pin == 1) {
pdd->int_route = INT_ROUTE_PIN_1;
} else if (bma2xx->sensor.s_itf.si_ints[pdd->int_num].device_pin == 2) {
pdd->int_route = INT_ROUTE_PIN_2;
} else {
BMA2XX_LOG(ERROR, "Route not configured\n");
return SYS_EINVAL;
}
rc = hal_gpio_irq_init(pin,
handler,
arg,
trig,
HAL_GPIO_PULL_NONE);
if (rc != 0) {
return rc;
}
return 0;
}
static void
enable_intpin(struct bma2xx *bma2xx)
{
struct bma2xx_private_driver_data *pdd = &bma2xx->pdd;
enum bma2xx_int_num int_num = pdd->int_num;
pdd->int_ref_cnt++;
if (pdd->int_ref_cnt == 1) {
hal_gpio_irq_enable(bma2xx->sensor.s_itf.si_ints[int_num].host_pin);
}
}
static void
disable_intpin(struct bma2xx *bma2xx)
{
struct bma2xx_private_driver_data *pdd = &bma2xx->pdd;
enum bma2xx_int_num int_num = pdd->int_num;
if (pdd->int_ref_cnt == 0) {
return;
}
pdd->int_ref_cnt--;
if (pdd->int_ref_cnt == 0) {
hal_gpio_irq_disable(bma2xx->sensor.s_itf.si_ints[int_num].host_pin);
}
}
#endif
static int
self_test_enable(struct bma2xx *bma2xx,
enum self_test_ampl ampl,
enum self_test_sign sign,
enum axis axis)
{
struct self_test_cfg self_test_cfg;
self_test_cfg.self_test_ampl = ampl;
self_test_cfg.self_test_sign = sign;
self_test_cfg.self_test_axis = axis;
self_test_cfg.self_test_enabled = true;
return bma2xx_set_self_test_cfg(bma2xx, &self_test_cfg);
}
static int
self_test_disable(struct bma2xx *bma2xx)
{
struct self_test_cfg self_test_cfg;
self_test_cfg.self_test_ampl = SELF_TEST_AMPL_LOW;
self_test_cfg.self_test_sign = SELF_TEST_SIGN_NEGATIVE;
self_test_cfg.self_test_axis = -1;
self_test_cfg.self_test_enabled = false;
return bma2xx_set_self_test_cfg(bma2xx, &self_test_cfg);
}
static int
self_test_nudge(struct bma2xx *bma2xx,
enum self_test_ampl ampl,
enum self_test_sign sign,
enum axis axis,
enum bma2xx_g_range g_range,
struct accel_data * accel_data)
{
int rc;
rc = self_test_enable(bma2xx, ampl, sign, axis);
if (rc != 0) {
return rc;
}
delay_msec(50);
rc = bma2xx_get_accel(bma2xx, g_range, axis, accel_data);
if (rc != 0) {
return rc;
}
rc = self_test_disable(bma2xx);
if (rc != 0) {
return rc;
}
delay_msec(50);
return 0;
}
static int
self_test_axis(struct bma2xx *bma2xx,
enum axis axis,
enum bma2xx_g_range g_range,
float * delta_hi_g,
float * delta_lo_g)
{
struct accel_data accel_neg_hi;
struct accel_data accel_neg_lo;
struct accel_data accel_pos_hi;
struct accel_data accel_pos_lo;
int rc;
rc = self_test_nudge(bma2xx,
SELF_TEST_AMPL_HIGH,
SELF_TEST_SIGN_NEGATIVE,
axis,
g_range,
&accel_neg_hi);
if (rc != 0) {
return rc;
}
rc = self_test_nudge(bma2xx,
SELF_TEST_AMPL_LOW,
SELF_TEST_SIGN_NEGATIVE,
axis,
g_range,
&accel_neg_lo);
if (rc != 0) {
return rc;
}
rc = self_test_nudge(bma2xx,
SELF_TEST_AMPL_HIGH,
SELF_TEST_SIGN_POSITIVE,
axis,
g_range,
&accel_pos_hi);
if (rc != 0) {
return rc;
}
rc = self_test_nudge(bma2xx,
SELF_TEST_AMPL_LOW,
SELF_TEST_SIGN_POSITIVE,
axis,
g_range,
&accel_pos_lo);
if (rc != 0) {
return rc;
}
*delta_hi_g = accel_pos_hi.accel_g - accel_neg_hi.accel_g;
*delta_lo_g = accel_pos_lo.accel_g - accel_neg_lo.accel_g;
return 0;
}
int
bma2xx_self_test(struct bma2xx *bma2xx,
float delta_high_mult,
float delta_low_mult,
bool * self_test_fail)
{
struct bma2xx_cfg *cfg;
enum bma2xx_power_mode request_power;
int rc;
float delta_hi_x_g;
float delta_lo_x_g;
float delta_hi_y_g;
float delta_lo_y_g;
float delta_hi_z_g;
float delta_lo_z_g;
bool fail;
cfg = &bma2xx->cfg;
request_power = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx, &request_power, 1);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_g_range(bma2xx, BMA2XX_G_RANGE_8);
if (rc != 0) {
return rc;
}
rc = self_test_axis(bma2xx,
AXIS_X,
BMA2XX_G_RANGE_8,
&delta_hi_x_g,
&delta_lo_x_g);
if (rc != 0) {
return rc;
}
rc = self_test_axis(bma2xx,
AXIS_Y,
BMA2XX_G_RANGE_8,
&delta_hi_y_g,
&delta_lo_y_g);
if (rc != 0) {
return rc;
}
rc = self_test_axis(bma2xx,
AXIS_Z,
BMA2XX_G_RANGE_8,
&delta_hi_z_g,
&delta_lo_z_g);
if (rc != 0) {
return rc;
}
rc = self_test_disable(bma2xx);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_g_range(bma2xx, cfg->g_range);
if (rc != 0) {
return rc;
}
delay_msec(50);
rc = default_power(bma2xx);
if (rc != 0) {
return rc;
}
fail = false;
if (delta_hi_x_g < delta_high_mult * 0.8) {
fail = true;
}
if (delta_lo_x_g < delta_low_mult * 0.8) {
fail = true;
}
if (delta_hi_y_g < delta_high_mult * 0.8) {
fail = true;
}
if (delta_lo_y_g < delta_low_mult * 0.8) {
fail = true;
}
if (delta_hi_z_g < delta_high_mult * 0.4) {
fail = true;
}
if (delta_lo_z_g < delta_low_mult * 0.4) {
fail = true;
}
*self_test_fail = fail;
return 0;
}
static int
axis_offset_compensation(struct bma2xx *bma2xx,
enum axis axis,
enum bma2xx_offset_comp_target target)
{
int rc;
bool ready;
enum bma2xx_offset_comp_target target_z;
enum bma2xx_offset_comp_target target_y;
enum bma2xx_offset_comp_target target_x;
uint32_t count;
rc = bma2xx_get_fast_ofc_cfg(bma2xx,
&ready,
&target_z,
&target_y,
&target_x);
if (rc != 0) {
return rc;
}
if (!ready) {
BMA2XX_LOG(ERROR, "offset compensation already in progress\n");
return SYS_ETIMEOUT;
}
rc = bma2xx_set_fast_ofc_cfg(bma2xx, axis, target, true);
if (rc != 0) {
return rc;
}
for (count = 1000; count != 0; count--) {
rc = bma2xx_get_fast_ofc_cfg(bma2xx,
&ready,
&target_z,
&target_y,
&target_x);
if (rc != 0) {
return rc;
}
if (ready) {
break;
}
}
if (count == 0) {
BMA2XX_LOG(ERROR, "offset compensation did not complete\n");
return SYS_ETIMEOUT;
}
return 0;
}
int
bma2xx_offset_compensation(struct bma2xx *bma2xx,
enum bma2xx_offset_comp_target target_x,
enum bma2xx_offset_comp_target target_y,
enum bma2xx_offset_comp_target target_z)
{
struct bma2xx_cfg *cfg;
enum bma2xx_power_mode request_power;
int rc;
cfg = &bma2xx->cfg;
request_power = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx, &request_power, 1);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_g_range(bma2xx, BMA2XX_G_RANGE_2);
if (rc != 0) {
return rc;
}
rc = axis_offset_compensation(bma2xx, AXIS_X, target_x);
if (rc != 0) {
return rc;
}
rc = axis_offset_compensation(bma2xx, AXIS_Y, target_y);
if (rc != 0) {
return rc;
}
rc = axis_offset_compensation(bma2xx, AXIS_Z, target_z);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_ofc_offset(bma2xx, AXIS_X, &bma2xx->cfg.offset_x_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Y, &bma2xx->cfg.offset_y_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Z, &bma2xx->cfg.offset_z_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_g_range(bma2xx, cfg->g_range);
if (rc != 0) {
return rc;
}
rc = default_power(bma2xx);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_query_offsets(struct bma2xx *bma2xx,
float * offset_x_g,
float * offset_y_g,
float * offset_z_g)
{
struct bma2xx_cfg *cfg;
enum bma2xx_power_mode request_power[5];
int rc;
float val_offset_x_g;
float val_offset_y_g;
float val_offset_z_g;
bool mismatch;
cfg = &bma2xx->cfg;
request_power[0] = BMA2XX_POWER_MODE_SUSPEND;
request_power[1] = BMA2XX_POWER_MODE_STANDBY;
request_power[2] = BMA2XX_POWER_MODE_LPM_1;
request_power[3] = BMA2XX_POWER_MODE_LPM_2;
request_power[4] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
return rc;
}
rc = bma2xx_get_ofc_offset(bma2xx, AXIS_X, &val_offset_x_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Y, &val_offset_y_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Z, &val_offset_z_g);
if (rc != 0) {
return rc;
}
rc = default_power(bma2xx);
if (rc != 0) {
return rc;
}
mismatch = false;
if (cfg->offset_x_g != val_offset_x_g) {
BMA2XX_LOG(ERROR, "X compensation offset value mismatch\n");
mismatch = true;
}
if (cfg->offset_y_g != val_offset_y_g) {
BMA2XX_LOG(ERROR, "Y compensation offset value mismatch\n");
mismatch = true;
}
if (cfg->offset_z_g != val_offset_z_g) {
BMA2XX_LOG(ERROR, "Z compensation offset value mismatch\n");
mismatch = true;
}
if (mismatch) {
return SYS_EINVAL;
}
*offset_x_g = val_offset_x_g;
*offset_y_g = val_offset_y_g;
*offset_z_g = val_offset_z_g;
return 0;
}
int
bma2xx_write_offsets(struct bma2xx *bma2xx,
float offset_x_g,
float offset_y_g,
float offset_z_g)
{
struct bma2xx_cfg *cfg;
enum bma2xx_power_mode request_power[5];
int rc;
cfg = &bma2xx->cfg;
request_power[0] = BMA2XX_POWER_MODE_SUSPEND;
request_power[1] = BMA2XX_POWER_MODE_STANDBY;
request_power[2] = BMA2XX_POWER_MODE_LPM_1;
request_power[3] = BMA2XX_POWER_MODE_LPM_2;
request_power[4] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
return rc;
}
rc = bma2xx_set_ofc_offset(bma2xx, AXIS_X, offset_x_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Y, offset_y_g);
if (rc != 0) {
return rc;
}
rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Z, offset_z_g);
if (rc != 0) {
return rc;
}
cfg->offset_x_g = offset_x_g;
cfg->offset_y_g = offset_y_g;
cfg->offset_z_g = offset_z_g;
return 0;
}
int
bma2xx_stream_read(struct bma2xx *bma2xx,
bma2xx_stream_read_func_t read_func,
void * read_arg,
uint32_t time_ms)
{
struct bma2xx_cfg *cfg;
int rc;
enum bma2xx_power_mode request_power;
struct int_enable int_enable_org;
struct int_enable int_enable = { 0 };
os_time_t time_ticks;
os_time_t stop_ticks;
struct accel_data accel_data[AXIS_ALL];
struct sensor_accel_data sad;
struct bma2xx_private_driver_data *pdd;
cfg = &bma2xx->cfg;
pdd = &bma2xx->pdd;
stop_ticks = 0;
request_power = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx, &request_power, 1);
if (rc != 0) {
return rc;
}
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
undo_interrupt(&bma2xx->intr);
if (pdd->interrupt) {
return SYS_EBUSY;
}
pdd->interrupt = &bma2xx->intr;
enable_intpin(bma2xx);
#endif
rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
goto done;
}
/* Leave tap configured as it is since it is on int2*/
int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
int_enable.data_int_enable = true;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
if (rc != 0) {
goto done;
}
if (time_ms != 0) {
rc = os_time_ms_to_ticks(time_ms, &time_ticks);
if (rc != 0) {
goto done;
}
stop_ticks = os_time_get() + time_ticks;
}
for (;;) {
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
wait_interrupt(&bma2xx->intr, pdd->int_num);
#else
switch (cfg->filter_bandwidth) {
case BMA2XX_FILTER_BANDWIDTH_7_81_HZ:
delay_msec(128);
break;
case BMA2XX_FILTER_BANDWIDTH_15_63_HZ:
delay_msec(64);
break;
case BMA2XX_FILTER_BANDWIDTH_31_25_HZ:
delay_msec(32);
break;
case BMA2XX_FILTER_BANDWIDTH_62_5_HZ:
delay_msec(16);
break;
case BMA2XX_FILTER_BANDWIDTH_125_HZ:
delay_msec(8);
break;
case BMA2XX_FILTER_BANDWIDTH_250_HZ:
delay_msec(4);
break;
case BMA2XX_FILTER_BANDWIDTH_500_HZ:
delay_msec(2);
break;
case BMA2XX_FILTER_BANDWIDTH_1000_HZ:
case BMA2XX_FILTER_BANDWIDTH_ODR_MAX:
delay_msec(1);
break;
default:
delay_msec(1000);
break;
}
#endif
rc = bma2xx_get_fifo(bma2xx,
cfg->g_range,
FIFO_DATA_X_AND_Y_AND_Z,
accel_data);
if (rc != 0) {
goto done;
}
sad.sad_x = accel_data[AXIS_X].accel_g;
sad.sad_y = accel_data[AXIS_Y].accel_g;
sad.sad_z = accel_data[AXIS_Z].accel_g;
sad.sad_x_is_valid = 1;
sad.sad_y_is_valid = 1;
sad.sad_z_is_valid = 1;
if (read_func(read_arg, &sad)) {
break;
}
if (time_ms != 0 && OS_TIME_TICK_GT(os_time_get(), stop_ticks)) {
break;
}
}
rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
goto done;
}
rc = default_power(bma2xx);
if (rc != 0) {
goto done;
}
done:
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
pdd->interrupt = NULL;
disable_intpin(bma2xx);
#endif
return rc;
}
int
bma2xx_current_temp(struct bma2xx *bma2xx,
float * temp_c)
{
enum bma2xx_power_mode request_power[3];
int rc;
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
return rc;
}
rc = bma2xx_get_temp(bma2xx, temp_c);
if (rc != 0) {
return rc;
}
rc = default_power(bma2xx);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_current_orient(struct bma2xx *bma2xx,
struct bma2xx_orient_xyz * orient_xyz)
{
enum bma2xx_power_mode request_power[3];
int rc;
struct int_enable int_enable_org;
struct int_enable int_enable = { 0 };
struct int_status int_status;
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
return rc;
}
rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
return rc;
}
/* Leave tap configured as it is since it is on int2*/
int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
int_enable.orient_int_enable = true;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_int_status(bma2xx, &int_status);
if (rc != 0) {
return rc;
}
/* Back to original interrupts */
rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
return rc;
}
rc = default_power(bma2xx);
if (rc != 0) {
return rc;
}
orient_xyz->orient_xy = int_status.device_orientation;
orient_xyz->downward_z = int_status.device_is_down;
return 0;
}
int
bma2xx_wait_for_orient(struct bma2xx *bma2xx,
struct bma2xx_orient_xyz * orient_xyz)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
int rc;
enum bma2xx_power_mode request_power[3];
struct int_enable int_enable_org;
struct int_enable int_enable = {0};
struct int_status int_status;
struct bma2xx_private_driver_data *pdd;
pdd = &bma2xx->pdd;
if (pdd->interrupt) {
BMA2XX_LOG(ERROR, "Interrupt used\n");
return SYS_EINVAL;
}
pdd->interrupt = &bma2xx->intr;
enable_intpin(bma2xx);
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
goto done;
}
undo_interrupt(&bma2xx->intr);
rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
return rc;
}
/* Leave tap configured as it is since it is on int2*/
int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
int_enable.orient_int_enable = true;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
if (rc != 0) {
goto done;
}
wait_interrupt(&bma2xx->intr, pdd->int_num);
rc = bma2xx_get_int_status(bma2xx, &int_status);
if (rc != 0) {
goto done;
}
/* Back to original interrupts */
rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
goto done;
}
rc = default_power(bma2xx);
if (rc != 0) {
goto done;
}
orient_xyz->orient_xy = int_status.device_orientation;
orient_xyz->downward_z = int_status.device_is_down;
done:
pdd->interrupt = NULL;
disable_intpin(bma2xx);
return rc;
#else
return SYS_ENODEV;
#endif
}
int
bma2xx_wait_for_high_g(struct bma2xx *bma2xx)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
int rc;
enum bma2xx_power_mode request_power[3];
struct int_enable int_enable_org;
struct int_enable int_enable = { 0 };
struct bma2xx_private_driver_data *pdd;
pdd = &bma2xx->pdd;
if (pdd->interrupt) {
BMA2XX_LOG(ERROR, "Interrupt used\n");
return SYS_EINVAL;
}
pdd->interrupt = &bma2xx->intr;
enable_intpin(bma2xx);
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
goto done;
}
undo_interrupt(&bma2xx->intr);
rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
return rc;
}
/* Leave tap configured as it is since it is on int2*/
int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
int_enable.high_g_z_int_enable = true;
int_enable.high_g_y_int_enable = true;
int_enable.high_g_x_int_enable = true;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
if (rc != 0) {
goto done;
}
wait_interrupt(&bma2xx->intr, pdd->int_num);
rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
goto done;
}
rc = default_power(bma2xx);
if (rc != 0) {
goto done;
}
done:
pdd->interrupt = NULL;
disable_intpin(bma2xx);
return rc;
#else
return SYS_ENODEV;
#endif
}
int
bma2xx_wait_for_low_g(struct bma2xx *bma2xx)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
int rc;
enum bma2xx_power_mode request_power[3];
struct int_enable int_enable_org;
struct int_enable int_enable = { 0 };
struct bma2xx_private_driver_data *pdd;
pdd = &bma2xx->pdd;
if (pdd->interrupt) {
BMA2XX_LOG(ERROR, "Interrupt used\n");
return SYS_EINVAL;
}
pdd->interrupt = &bma2xx->intr;
enable_intpin(bma2xx);
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
goto done;
}
undo_interrupt(&bma2xx->intr);
rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
return rc;
}
/* Leave tap configured as it is since it is on int2 */
int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
int_enable.low_g_int_enable = true;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
if (rc != 0) {
goto done;
}
wait_interrupt(&bma2xx->intr, pdd->int_num);
rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
goto done;
}
rc = default_power(bma2xx);
if (rc != 0) {
goto done;
}
done:
pdd->interrupt = NULL;
disable_intpin(bma2xx);
return 0;
#else
return SYS_ENODEV;
#endif
}
int
bma2xx_wait_for_tap(struct bma2xx *bma2xx,
enum bma2xx_tap_type tap_type)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
int rc = 0;
enum bma2xx_power_mode request_power[3];
struct int_enable int_enable_org;
struct int_enable int_enable = { 0 };
struct int_routes int_routes;
struct int_routes int_routes_org;
struct bma2xx_private_driver_data *pdd;
pdd = &bma2xx->pdd;
switch (tap_type) {
case BMA2XX_TAP_TYPE_DOUBLE:
case BMA2XX_TAP_TYPE_SINGLE:
break;
default:
return SYS_EINVAL;
}
rc = bma2xx_get_int_routes(bma2xx, &int_routes_org);
if (rc != 0) {
return rc;
}
int_routes = int_routes_org;
if (tap_type == BMA2XX_TAP_TYPE_DOUBLE) {
/* According to BMA2XX when single tap shall not be used we should not
* route it to any INTX
*/
int_routes.d_tap_int_route = pdd->int_route;
int_routes.s_tap_int_route = INT_ROUTE_NONE;
} else {
int_routes.d_tap_int_route = INT_ROUTE_NONE;
int_routes.s_tap_int_route = pdd->int_route;
}
rc = bma2xx_set_int_routes(bma2xx, &int_routes);
if (rc != 0) {
return rc;
}
if (pdd->interrupt) {
BMA2XX_LOG(ERROR, "Interrupt used\n");
return SYS_EINVAL;
}
pdd->interrupt = &bma2xx->intr;
enable_intpin(bma2xx);
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
goto done;
}
undo_interrupt(&bma2xx->intr);
rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
return rc;
}
int_enable.s_tap_int_enable = tap_type == BMA2XX_TAP_TYPE_SINGLE;
int_enable.d_tap_int_enable = tap_type == BMA2XX_TAP_TYPE_DOUBLE;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
if (rc != 0) {
goto done;
}
wait_interrupt(&bma2xx->intr, pdd->int_num);
rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
if (rc != 0) {
goto done;
}
rc = default_power(bma2xx);
done:
pdd->interrupt = NULL;
disable_intpin(bma2xx);
/* Restore previous routing */
rc = bma2xx_set_int_routes(bma2xx, &int_routes_org);
return rc;
#else
return SYS_ENODEV;
#endif
}
int
bma2xx_power_settings(struct bma2xx *bma2xx,
enum bma2xx_power_mode power_mode,
enum bma2xx_sleep_duration sleep_duration)
{
struct bma2xx_cfg *cfg;
cfg = &bma2xx->cfg;
cfg->power_mode = power_mode;
cfg->sleep_duration = sleep_duration;
return default_power(bma2xx);
}
static int
sensor_driver_read(struct sensor * sensor,
sensor_type_t sensor_type,
sensor_data_func_t data_func,
void * data_arg,
uint32_t timeout)
{
struct bma2xx *bma2xx;
struct bma2xx_cfg *cfg;
enum bma2xx_power_mode request_power[3];
int rc;
struct accel_data accel_data[AXIS_ALL];
struct sensor_accel_data sad;
float temp_c;
struct sensor_temp_data std;
if ((sensor_type & ~(SENSOR_TYPE_ACCELEROMETER |
SENSOR_TYPE_AMBIENT_TEMPERATURE)) != 0) {
return SYS_EINVAL;
}
bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
cfg = &bma2xx->cfg;
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
return rc;
}
if ((sensor_type & SENSOR_TYPE_ACCELEROMETER) != 0) {
rc = bma2xx_get_accel(bma2xx,
cfg->g_range,
AXIS_X,
accel_data + AXIS_X);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_accel(bma2xx,
cfg->g_range,
AXIS_Y,
accel_data + AXIS_Y);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_accel(bma2xx,
cfg->g_range,
AXIS_Z,
accel_data + AXIS_Z);
if (rc != 0) {
return rc;
}
sad.sad_x = accel_data[AXIS_X].accel_g;
sad.sad_y = accel_data[AXIS_Y].accel_g;
sad.sad_z = accel_data[AXIS_Z].accel_g;
sad.sad_x_is_valid = 1;
sad.sad_y_is_valid = 1;
sad.sad_z_is_valid = 1;
rc = data_func(sensor,
data_arg,
&sad,
SENSOR_TYPE_ACCELEROMETER);
if (rc != 0) {
return rc;
}
}
if ((sensor_type & SENSOR_TYPE_AMBIENT_TEMPERATURE) != 0) {
rc = bma2xx_get_temp(bma2xx, &temp_c);
if (rc != 0) {
return rc;
}
std.std_temp = temp_c;
std.std_temp_is_valid = 1;
rc = data_func(sensor,
data_arg,
&std,
SENSOR_TYPE_AMBIENT_TEMPERATURE);
if (rc != 0) {
return rc;
}
}
rc = default_power(bma2xx);
if (rc != 0) {
return rc;
}
return 0;
}
static int
sensor_driver_get_config(struct sensor * sensor,
sensor_type_t sensor_type,
struct sensor_cfg * cfg)
{
if ((sensor_type & ~(SENSOR_TYPE_ACCELEROMETER |
SENSOR_TYPE_AMBIENT_TEMPERATURE)) != 0) {
return SYS_EINVAL;
}
if ((sensor_type & (sensor_type - 1)) != 0) {
return SYS_EINVAL;
}
if ((sensor_type & SENSOR_TYPE_ACCELEROMETER) != 0) {
cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT_TRIPLET;
}
if ((sensor_type & SENSOR_TYPE_AMBIENT_TEMPERATURE) != 0) {
cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT;
}
return 0;
}
static int
sensor_driver_set_trigger_thresh(struct sensor * sensor,
sensor_type_t sensor_type,
struct sensor_type_traits * stt)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
struct bma2xx *bma2xx;
struct bma2xx_cfg *cfg;
int rc;
enum bma2xx_power_mode request_power[3];
struct sensor_accel_data * low_thresh;
struct sensor_accel_data * high_thresh;
struct int_enable int_enable;
float thresh;
struct low_g_int_cfg low_g_int_cfg;
struct high_g_int_cfg high_g_int_cfg;
struct bma2xx_private_driver_data *pdd;
if (sensor_type != SENSOR_TYPE_ACCELEROMETER) {
return SYS_EINVAL;
}
bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
cfg = &bma2xx->cfg;
pdd = &bma2xx->pdd;
pdd->read_ctx.srec_type |= sensor_type;
pdd->registered_mask |= BMA2XX_READ_MASK;
enable_intpin(bma2xx);
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
goto done;
}
low_thresh = stt->stt_low_thresh.sad;
high_thresh = stt->stt_high_thresh.sad;
rc = bma2xx_get_int_enable(bma2xx, &int_enable);
if (rc != 0) {
goto done;
}
if (low_thresh->sad_x_is_valid |
low_thresh->sad_y_is_valid |
low_thresh->sad_z_is_valid) {
thresh = INFINITY;
if (low_thresh->sad_x_is_valid) {
if (thresh > low_thresh->sad_x) {
thresh = low_thresh->sad_x;
}
}
if (low_thresh->sad_y_is_valid) {
if (thresh > low_thresh->sad_y) {
thresh = low_thresh->sad_y;
}
}
if (low_thresh->sad_z_is_valid) {
if (thresh > low_thresh->sad_z) {
thresh = low_thresh->sad_z;
}
}
low_g_int_cfg.delay_ms = 20;
low_g_int_cfg.thresh_g = thresh;
low_g_int_cfg.hyster_g = 0.125;
low_g_int_cfg.axis_summing = false;
rc = bma2xx_set_low_g_int_cfg(bma2xx,
&low_g_int_cfg);
if (rc != 0) {
goto done;
}
int_enable.low_g_int_enable = true;
}
if (high_thresh->sad_x_is_valid |
high_thresh->sad_y_is_valid |
high_thresh->sad_z_is_valid) {
thresh = 0.0;
if (high_thresh->sad_x_is_valid) {
if (thresh < high_thresh->sad_x) {
thresh = high_thresh->sad_x;
}
}
if (high_thresh->sad_y_is_valid) {
if (thresh < high_thresh->sad_y) {
thresh = high_thresh->sad_y;
}
}
if (high_thresh->sad_z_is_valid) {
if (thresh < high_thresh->sad_z) {
thresh = high_thresh->sad_z;
}
}
high_g_int_cfg.hyster_g = 0.25;
high_g_int_cfg.delay_ms = 32;
high_g_int_cfg.thresh_g = thresh;
rc = bma2xx_set_high_g_int_cfg(bma2xx,
cfg->g_range,
&high_g_int_cfg);
if (rc != 0) {
goto done;
}
int_enable.high_g_z_int_enable = high_thresh->sad_z_is_valid;
int_enable.high_g_y_int_enable = high_thresh->sad_y_is_valid;
int_enable.high_g_x_int_enable = high_thresh->sad_x_is_valid;
}
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
done:
if (rc != 0) {
/* Something went wrong, unregister from interrupt */
pdd->read_ctx.srec_type &= ~sensor_type;
pdd->registered_mask &= ~BMA2XX_READ_MASK;
disable_intpin(bma2xx);
}
return rc;
#else
return SYS_ENODEV;
#endif
}
static int
sensor_driver_unset_notification(struct sensor * sensor,
sensor_event_type_t sensor_event_type)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
struct bma2xx *bma2xx;
enum bma2xx_power_mode request_power[3];
struct int_enable int_enable;
struct int_routes int_routes;
struct bma2xx_private_driver_data *pdd;
int rc;
if ((sensor_event_type & ~(SENSOR_EVENT_TYPE_DOUBLE_TAP |
SENSOR_EVENT_TYPE_SINGLE_TAP)) != 0) {
return SYS_EINVAL;
}
/*XXX for now we do not support registering for both events */
if (sensor_event_type == (SENSOR_EVENT_TYPE_DOUBLE_TAP |
SENSOR_EVENT_TYPE_SINGLE_TAP)) {
return SYS_EINVAL;
}
bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
pdd = &bma2xx->pdd;
pdd->notify_ctx.snec_evtype &= ~sensor_event_type;
pdd->registered_mask &= ~BMA2XX_NOTIFY_MASK;
disable_intpin(bma2xx);
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
return rc;
}
/* Clear route and interrupts. We can do it for single and double as driver
* supports notification only for one of them at the time
*/
rc = bma2xx_get_int_routes(bma2xx, &int_routes);
if (rc != 0) {
return rc;
}
if (sensor_event_type & SENSOR_EVENT_TYPE_SINGLE_TAP) {
int_routes.s_tap_int_route = INT_ROUTE_NONE;
}
if (sensor_event_type & SENSOR_EVENT_TYPE_DOUBLE_TAP) {
int_routes.d_tap_int_route = INT_ROUTE_NONE;
}
rc = bma2xx_set_int_routes(bma2xx, &int_routes);
if (rc != 0) {
return rc;
}
rc = bma2xx_get_int_enable(bma2xx, &int_enable);
if (rc != 0) {
return rc;
}
int_enable.d_tap_int_enable = false;
int_enable.s_tap_int_enable = false;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
if (rc != 0) {
return rc;
}
return 0;
#else
return SYS_ENODEV;
#endif
}
static int
sensor_driver_set_notification(struct sensor * sensor,
sensor_event_type_t sensor_event_type)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
struct bma2xx *bma2xx;
int rc;
enum bma2xx_power_mode request_power[3];
struct int_enable int_enable;
struct int_routes int_routes;
struct bma2xx_private_driver_data *pdd;
if ((sensor_event_type & ~(SENSOR_EVENT_TYPE_DOUBLE_TAP |
SENSOR_EVENT_TYPE_SINGLE_TAP)) != 0) {
return SYS_EINVAL;
}
/*XXX for now we do not support registering for both events */
if (sensor_event_type == (SENSOR_EVENT_TYPE_DOUBLE_TAP |
SENSOR_EVENT_TYPE_SINGLE_TAP)) {
return SYS_EINVAL;
}
bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
pdd = &bma2xx->pdd;
if (pdd->registered_mask & BMA2XX_NOTIFY_MASK) {
return SYS_EBUSY;
}
pdd->notify_ctx.snec_evtype |= sensor_event_type;
pdd->registered_mask |= BMA2XX_NOTIFY_MASK;
enable_intpin(bma2xx);
request_power[0] = BMA2XX_POWER_MODE_LPM_1;
request_power[1] = BMA2XX_POWER_MODE_LPM_2;
request_power[2] = BMA2XX_POWER_MODE_NORMAL;
rc = interim_power(bma2xx,
request_power,
sizeof(request_power) / sizeof(*request_power));
if (rc != 0) {
goto done;
}
/* Configure route */
rc = bma2xx_get_int_routes(bma2xx, &int_routes);
if (rc != 0) {
return rc;
}
if (sensor_event_type & SENSOR_EVENT_TYPE_DOUBLE_TAP) {
int_routes.d_tap_int_route = pdd->int_route;
}
if (sensor_event_type & SENSOR_EVENT_TYPE_SINGLE_TAP) {
int_routes.s_tap_int_route = pdd->int_route;
}
rc = bma2xx_set_int_routes(bma2xx, &int_routes);
if (rc != 0) {
return rc;
}
/* Configure enable event*/
rc = bma2xx_get_int_enable(bma2xx, &int_enable);
if (rc != 0) {
goto done;
}
/* Enable tap event*/
int_enable.s_tap_int_enable = sensor_event_type &
SENSOR_EVENT_TYPE_SINGLE_TAP;
int_enable.d_tap_int_enable = sensor_event_type &
SENSOR_EVENT_TYPE_DOUBLE_TAP;
rc = bma2xx_set_int_enable(bma2xx, &int_enable);
done:
if (rc != 0) {
pdd->notify_ctx.snec_evtype &= ~sensor_event_type;
pdd->registered_mask &= ~BMA2XX_NOTIFY_MASK;
disable_intpin(bma2xx);
}
return rc;
#else
return SYS_ENODEV;
#endif
}
static int
sensor_driver_handle_interrupt(struct sensor * sensor)
{
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
struct bma2xx *bma2xx;
struct bma2xx_private_driver_data *pdd;
struct int_status int_status;
int rc;
bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
pdd = &bma2xx->pdd;
rc = bma2xx_get_int_status(bma2xx, &int_status);
if (rc != 0) {
BMA2XX_LOG(ERROR, "Cound not read int status err=0x%02x\n", rc);
return rc;
}
if (pdd->registered_mask & BMA2XX_NOTIFY_MASK) {
if (int_status.s_tap_int_active) {
sensor_mgr_put_notify_evt(&pdd->notify_ctx, SENSOR_EVENT_TYPE_SINGLE_TAP);
}
if (int_status.d_tap_int_active) {
sensor_mgr_put_notify_evt(&pdd->notify_ctx, SENSOR_EVENT_TYPE_DOUBLE_TAP);
}
}
if ((pdd->registered_mask & BMA2XX_READ_MASK) &&
(int_status.high_g_int_active || int_status.low_g_int_active)) {
sensor_mgr_put_read_evt(&pdd->read_ctx);
}
return 0;
#else
return SYS_ENODEV;
#endif
}
static struct sensor_driver bma2xx_sensor_driver = {
.sd_read = sensor_driver_read,
.sd_get_config = sensor_driver_get_config,
.sd_set_trigger_thresh = sensor_driver_set_trigger_thresh,
.sd_set_notification = sensor_driver_set_notification,
.sd_unset_notification = sensor_driver_unset_notification,
.sd_handle_interrupt = sensor_driver_handle_interrupt,
};
int
bma2xx_config(struct bma2xx *bma2xx, struct bma2xx_cfg *cfg)
{
struct sensor * sensor;
int rc;
uint8_t chip_id;
uint8_t model_chip_id;
bma2xx->cfg = *cfg;
sensor = &bma2xx->sensor;
rc = bma2xx_get_chip_id(bma2xx, &chip_id);
if (rc != 0) {
return rc;
}
switch (cfg->model) {
case BMA2XX_BMA280:
model_chip_id = BMA280_REG_VALUE_CHIP_ID;
break;
case BMA2XX_BMA253:
model_chip_id = BMA253_REG_VALUE_CHIP_ID;
break;
default:
return SYS_EINVAL;
}
if (chip_id != model_chip_id) {
BMA2XX_LOG(ERROR, "received incorrect chip ID 0x%02X\n", chip_id);
return SYS_EINVAL;
}
rc = reset_and_recfg(bma2xx);
if (rc != 0) {
return rc;
}
rc = default_power(bma2xx);
if (rc != 0) {
return rc;
}
rc = sensor_set_type_mask(sensor, cfg->sensor_mask);
if (rc != 0) {
return rc;
}
return 0;
}
int
bma2xx_init(struct os_dev * dev, void * arg)
{
struct bma2xx *bma2xx;
struct sensor * sensor;
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
struct bma2xx_private_driver_data *pdd;
#endif
int rc;
if (!dev || !arg) {
return SYS_ENODEV;
}
bma2xx = (struct bma2xx *)dev;
sensor = &bma2xx->sensor;
rc = sensor_init(sensor, dev);
if (rc != 0) {
return rc;
}
rc = sensor_set_driver(sensor,
SENSOR_TYPE_ACCELEROMETER |
SENSOR_TYPE_AMBIENT_TEMPERATURE,
&bma2xx_sensor_driver);
if (rc != 0) {
return rc;
}
rc = sensor_set_interface(sensor, arg);
if (rc != 0) {
return rc;
}
sensor->s_next_run = OS_TIMEOUT_NEVER;
rc = sensor_mgr_register(sensor);
if (rc != 0) {
return rc;
}
#if MYNEWT_VAL(SPI_0_MASTER) || MYNEWT_VAL(SPI_1_MASTER)
rc = hal_spi_config(sensor->s_itf.si_num, &spi_bma2xx_settings);
if (rc == EINVAL) {
/* If spi is already enabled, for nrf52, it returns -1, We should not
* fail if the spi is already enabled
*/
return rc;
}
rc = hal_spi_enable(sensor->s_itf.si_num);
if (rc) {
return rc;
}
rc = hal_gpio_init_out(sensor->s_itf.si_cs_pin, 1);
if (rc) {
return rc;
}
#endif
#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
init_interrupt(&bma2xx->intr, bma2xx->sensor.s_itf.si_ints);
pdd = &bma2xx->pdd;
pdd->read_ctx.srec_sensor = sensor;
pdd->notify_ctx.snec_sensor = sensor;
rc = init_intpin(bma2xx, interrupt_handler, sensor);
if (rc != 0) {
return rc;
}
#endif
bma2xx->power = BMA2XX_POWER_MODE_NORMAL;
return 0;
}