| /* 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->i2c_spi_addr_flags, |
| 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->i2c_spi_addr_flags, |
| 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->i2c_spi_addr_flags, |
| BMP280_CONFIG_REG, &val); |
| |
| if (ret == EC_SUCCESS) { |
| val = (val & 0xE0) | ((durn << 5) & 0xE0); |
| /* write the standby duration*/ |
| ret = i2c_write8(s->port, s->i2c_spi_addr_flags, |
| 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->i2c_spi_addr_flags, |
| 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->i2c_spi_addr_flags, |
| 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 */ |