/* Copyright 2016 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* **************************************************************************** * Copyright (C) 2012 - 2015 Bosch Sensortec GmbH * * File : bmp280.h * * Date : 2015/03/27 * * Revision : 2.0.4(Pressure and Temperature compensation code revision is 1.1) * * Usage: Sensor Driver for BMP280 sensor * **************************************************************************** * * \section License * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the copyright holder nor the names of the * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER * OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE * * The information provided is believed to be accurate and reliable. * The copyright holder assumes no responsibility * for the consequences of use * of such information nor for any infringement of patents or * other rights of third parties which may result from its use. * No license is granted by implication or otherwise under any patent or * patent rights of the copyright holder. **************************************************************************/ #include "accelgyro.h" #include "common.h" #include "console.h" #include "driver/baro_bmp280.h" #include "i2c.h" #include "timer.h" #define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) #define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args) static const uint16_t standby_durn[] = {1, 63, 125, 250, 500, 1000, 2000, 4000}; /* * This function is used to get calibration parameters used for * calculation in the registers * * parameter | Register address | bit *------------|------------------|---------------- * dig_T1 | 0x88 and 0x89 | from 0 : 7 to 8: 15 * dig_T2 | 0x8A and 0x8B | from 0 : 7 to 8: 15 * dig_T3 | 0x8C and 0x8D | from 0 : 7 to 8: 15 * dig_P1 | 0x8E and 0x8F | from 0 : 7 to 8: 15 * dig_P2 | 0x90 and 0x91 | from 0 : 7 to 8: 15 * dig_P3 | 0x92 and 0x93 | from 0 : 7 to 8: 15 * dig_P4 | 0x94 and 0x95 | from 0 : 7 to 8: 15 * dig_P5 | 0x96 and 0x97 | from 0 : 7 to 8: 15 * dig_P6 | 0x98 and 0x99 | from 0 : 7 to 8: 15 * dig_P7 | 0x9A and 0x9B | from 0 : 7 to 8: 15 * dig_P8 | 0x9C and 0x9D | from 0 : 7 to 8: 15 * dig_P9 | 0x9E and 0x9F | from 0 : 7 to 8: 15 * * @return results of bus communication function * @retval 0 -> Success * */ static int bmp280_get_calib_param(const struct motion_sensor_t *s) { int ret; uint8_t a_data_u8[BMP280_CALIB_DATA_SIZE] = {0}; struct bmp280_drv_data_t *data = BMP280_GET_DATA(s); ret = i2c_read_block(s->port, s->addr, BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG, a_data_u8, BMP280_CALIB_DATA_SIZE); if (ret) return ret; /* read calibration values*/ data->calib_param.dig_T1 = (a_data_u8[1] << 8) | a_data_u8[0]; data->calib_param.dig_T2 = (a_data_u8[3] << 8 | a_data_u8[2]); data->calib_param.dig_T3 = (a_data_u8[5] << 8) | a_data_u8[4]; data->calib_param.dig_P1 = (a_data_u8[7] << 8) | a_data_u8[6]; data->calib_param.dig_P2 = (a_data_u8[9] << 8) | a_data_u8[8]; data->calib_param.dig_P3 = (a_data_u8[11] << 8) | a_data_u8[10]; data->calib_param.dig_P4 = (a_data_u8[13] << 8) | a_data_u8[12]; data->calib_param.dig_P5 = (a_data_u8[15] << 8) | a_data_u8[14]; data->calib_param.dig_P6 = (a_data_u8[17] << 8) | a_data_u8[16]; data->calib_param.dig_P7 = (a_data_u8[19] << 8) | a_data_u8[18]; data->calib_param.dig_P8 = (a_data_u8[21] << 8) | a_data_u8[20]; data->calib_param.dig_P9 = (a_data_u8[23] << 8) | a_data_u8[22]; return EC_SUCCESS; } static int bmp280_read_uncomp_pressure(const struct motion_sensor_t *s, int *uncomp_pres) { int ret; uint8_t a_data_u8[BMP280_PRESSURE_DATA_SIZE] = {0}; ret = i2c_read_block(s->port, s->addr, BMP280_PRESSURE_MSB_REG, a_data_u8, BMP280_PRESSURE_DATA_SIZE); if (ret) return ret; *uncomp_pres = (int32_t)((a_data_u8[0] << 12) | (a_data_u8[1] << 4) | (a_data_u8[2] >> 4)); return EC_SUCCESS; } /* * Reads actual pressure from uncompensated pressure * and returns the value in Pascal(Pa) * @note Output value of "96386" equals 96386 Pa = * 963.86 hPa = 963.86 millibar * * Algorithm from BMP280 Datasheet Rev 1.15 Section 8.2 * */ static int bmp280_compensate_pressure(const struct motion_sensor_t *s, int uncomp_pressure) { int var1, var2; uint32_t p; struct bmp280_drv_data_t *data = BMP280_GET_DATA(s); /* calculate x1 */ var1 = (((int32_t)data->calib_param.t_fine) >> 1) - 64000; /* calculate x2 */ var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t)data->calib_param.dig_P6); var2 = var2 + ((var1 * ((int32_t)data->calib_param.dig_P5)) << 1); var2 = (var2 >> 2) + (((int32_t)data->calib_param.dig_P4) << 16); /* calculate x1 */ var1 = (((data->calib_param.dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((int32_t)data->calib_param.dig_P2) * var1) >> 1)) >> 18; var1 = ((((32768 + var1)) * ((int32_t)data->calib_param.dig_P1)) >> 15); /* Avoid exception caused by division by zero */ if (!var1) return 0; /* calculate pressure */ p = (((uint32_t)((1048576) - uncomp_pressure) - (var2 >> 12))) * 3125; /* check overflow */ if (p < 0x80000000) p = (p << 1) / ((uint32_t)var1); else p = (p / (uint32_t)var1) << 1; /* calculate x1 */ var1 = (((int32_t)data->calib_param.dig_P9) * ((int32_t)(((p >> 3) * (p >> 3)) >> 13))) >> 12; /* calculate x2 */ var2 = (((int32_t)(p >> 2)) * ((int32_t)data->calib_param.dig_P8)) >> 13; /* calculate true pressure */ return (uint32_t)((int32_t)p + ((var1 + var2 + data->calib_param.dig_P7) >> 4)); } /* * Set the standby duration * standby_durn: The standby duration time value. * value | standby duration * ----------|-------------------- * 0x00 | 1_MS * 0x01 | 63_MS * 0x02 | 125_MS * 0x03 | 250_MS * 0x04 | 500_MS * 0x05 | 1000_MS * 0x06 | 2000_MS * 0x07 | 4000_MS */ static int bmp280_set_standby_durn(const struct motion_sensor_t *s, uint8_t durn) { int ret, val; ret = i2c_read8(s->port, s->addr, BMP280_CONFIG_REG, &val); if (ret == EC_SUCCESS) { val = (val & 0xE0) | ((durn << 5) & 0xE0); /* write the standby duration*/ ret = i2c_write8(s->port, s->addr, BMP280_CONFIG_REG, val); } return ret; } static int bmp280_set_power_mode(const struct motion_sensor_t *s, uint8_t power_mode) { int val; val = (BMP280_OVERSAMP_TEMP << 5) + (BMP280_OVERSAMP_PRES << 2) + power_mode; return i2c_write8(s->port, s->addr, BMP280_CTRL_MEAS_REG, val); } static int bmp280_set_range(const struct motion_sensor_t *s, int range, int rnd) { struct bmp280_drv_data_t *data = BMP280_GET_DATA(s); /* * ->range contains the number of bit to right shift in order for the * measurment to fit into 16 bits (or less if the AP wants to). */ data->range = 15 - __builtin_clz(range); return EC_SUCCESS; } static int bmp280_get_range(const struct motion_sensor_t *s) { struct bmp280_drv_data_t *data = BMP280_GET_DATA(s); return 1 << (16 + data->range); } /* * bmp280_init() - Used to initialize barometer with default config * * @return results of bus communication function * @retval 0 -> Success */ static int bmp280_init(const struct motion_sensor_t *s) { int val, ret; if (!s) return EC_ERROR_INVAL; /* Read chip id */ ret = i2c_read8(s->port, s->addr, BMP280_CHIP_ID_REG, &val); if (ret) return ret; if (val != BMP280_CHIP_ID) return EC_ERROR_INVAL; /* set power mode */ ret = bmp280_set_power_mode(s, BMP280_SLEEP_MODE); if (ret) return ret; /* Read bmp280 calibration parameter */ ret = bmp280_get_calib_param(s); if (ret) return ret; return sensor_init_done(s); } static int bmp280_read(const struct motion_sensor_t *s, intv3_t v) { int ret, pres; struct bmp280_drv_data_t *data = BMP280_GET_DATA(s); ret = bmp280_read_uncomp_pressure(s, &pres); if (ret) return ret; v[0] = bmp280_compensate_pressure(s, pres) >> data->range; v[1] = v[2] = 0; return EC_SUCCESS; } /* * Set data rate, rate in mHz. * Calculate the delay (in ms) to apply. */ static int bmp280_set_data_rate(const struct motion_sensor_t *s, int rate, int roundup) { struct bmp280_drv_data_t *data = BMP280_GET_DATA(s); int durn, i, ret; int period; /* Period in ms */ if (rate == 0) { /* Set to sleep mode */ data->rate = 0; return bmp280_set_power_mode(s, BMP280_SLEEP_MODE); } else period = 1000000 / rate; /* reset power mode, waking from sleep */ if (!data->rate) { ret = bmp280_set_power_mode(s, BMP280_NORMAL_MODE); if (ret) return ret; } durn = 0; for (i = BMP280_STANDBY_CNT-1; i > 0; i--) { if (period >= standby_durn[i] + BMP280_COMPUTE_TIME) { durn = i; break; } else if (period > standby_durn[i-1] + BMP280_COMPUTE_TIME) { durn = roundup ? i-1 : i; break; } } ret = bmp280_set_standby_durn(s, durn); if (ret == EC_SUCCESS) /* * The maximum frequency is around 76Hz. Be sure it fits in 16 * bits by shifting by one bit. */ data->rate = (1000000 >> BMP280_RATE_SHIFT) / (standby_durn[durn] + BMP280_COMPUTE_TIME); return ret; } static int bmp280_get_data_rate(const struct motion_sensor_t *s) { struct bmp280_drv_data_t *data = BMP280_GET_DATA(s); return data->rate << BMP280_RATE_SHIFT; } const struct accelgyro_drv bmp280_drv = { .init = bmp280_init, .read = bmp280_read, .set_range = bmp280_set_range, .get_range = bmp280_get_range, .set_data_rate = bmp280_set_data_rate, .get_data_rate = bmp280_get_data_rate, }; #ifdef CONFIG_CMD_I2C_STRESS_TEST_ACCEL struct i2c_stress_test_dev bmp280_i2c_stress_test_dev = { .reg_info = { .read_reg = BMP280_CHIP_ID_REG, .read_val = BMP280_CHIP_ID, .write_reg = BMP280_CONFIG_REG, }, .i2c_read = &i2c_read8, .i2c_write = &i2c_write8, }; #endif /* CONFIG_CMD_I2C_STRESS_TEST_ACCEL */