nuttx/drivers/sensors/bmp180.c
Gregory Nutt 936df1bcb5 Adds new OS internal functions nxsig_sleep() and nxsig_usleep. These differ from the standard sleep() and usleep() in that (1) they don't cause cancellation points, and (2) don't set the errno variable (if applicable). All calls to sleep() and usleep() changed to calls to nxsig_sleep() and nxsig_usleep().
Squashed commit of the following:

    Change all calls to usleep() in the OS proper to calls to nxsig_usleep()

    sched/signal:  Add a new OS internal function nxsig_usleep() that is functionally equivalent to usleep() but does not cause a cancellaption point and does not modify the errno variable.

    sched/signal:  Add a new OS internal function nxsig_sleep() that is functionally equivalent to sleep() but does not cause a cancellaption point.
2017-10-06 10:15:01 -06:00

647 lines
18 KiB
C

/****************************************************************************
* drivers/sensors/bmp180.c
* Character driver for the Freescale BMP1801 Barometer Sensor
*
* Copyright (C) 2015 Alan Carvalho de Assis
* Author: Alan Carvalho de Assis <acassis@gmail.com>
*
* Copyright (C) 2015-2016 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name NuttX nor the names of its 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 THE
* COPYRIGHT OWNER 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdlib.h>
#include <fixedmath.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/signal.h>
#include <nuttx/fs/fs.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/sensors/bmp180.h>
#include <nuttx/random.h>
#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_BMP180)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BMP180_ADDR 0x77
#define BMP180_FREQ 100000
#define DEVID 0x55
#define BMP180_AC1_MSB 0xaa
#define BMP180_AC1_LSB 0xab
#define BMP180_AC2_MSB 0xac
#define BMP180_AC2_LSB 0xad
#define BMP180_AC3_MSB 0xae
#define BMP180_AC3_LSB 0xaf
#define BMP180_AC4_MSB 0xb0
#define BMP180_AC4_LSB 0xb1
#define BMP180_AC5_MSB 0xb2
#define BMP180_AC5_LSB 0xb3
#define BMP180_AC6_MSB 0xb4
#define BMP180_AC6_LSB 0xb5
#define BMP180_B1_MSB 0xb6
#define BMP180_B1_LSB 0xb7
#define BMP180_B2_MSB 0xb8
#define BMP180_B2_LSB 0xb9
#define BMP180_MB_MSB 0xba
#define BMP180_MB_LSB 0xbb
#define BMP180_MC_MSB 0xbc
#define BMP180_MC_LSB 0xbd
#define BMP180_MD_MSB 0xbe
#define BMP180_MD_LSB 0xbf
#define BMP180_DEVID 0xd0
#define BMP180_SOFT_RESET 0xe0
#define BMP180_CTRL_MEAS 0xf4
#define BMP180_ADC_OUT_MSB 0xf6
#define BMP180_ADC_OUT_LSB 0xf7
#define BMP180_ADC_OUT_XLSB 0xf8
#define BMP180_READ_TEMP 0x2e
#define BMP180_READ_PRESS 0x34
#define BMP180_NOOVERSAMPLE 0x00
#define BMP180_OVERSAMPLE2X 0x70
#define BMP180_OVERSAMPLE4X 0xb0
#define BMP180_OVERSAMPLE8X 0xc0
/* Current Oversampling */
#define CURRENT_OSS (BMP180_OVERSAMPLE8X)
/****************************************************************************
* Private Type Definitions
****************************************************************************/
struct bmp180_dev_s
{
FAR struct i2c_master_s *i2c; /* I2C interface */
uint8_t addr; /* BMP180 I2C address */
int freq; /* BMP180 Frequency <= 3.4MHz */
int16_t bmp180_cal_ac1; /* Calibration coefficients */
int16_t bmp180_cal_ac2;
int16_t bmp180_cal_ac3;
uint16_t bmp180_cal_ac4;
uint16_t bmp180_cal_ac5;
uint16_t bmp180_cal_ac6;
int16_t bmp180_cal_b1;
int16_t bmp180_cal_b2;
int16_t bmp180_cal_mb;
int16_t bmp180_cal_mc;
int16_t bmp180_cal_md;
int32_t bmp180_utemp; /* Uncompensated temperature read from BMP180 */
int32_t bmp180_upress; /* Uncompensated pressure read from BMP180 */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static uint8_t bmp180_getreg8(FAR struct bmp180_dev_s *priv, uint8_t regaddr);
static uint16_t bmp180_getreg16(FAR struct bmp180_dev_s *priv, uint8_t regaddr);
static void bmp180_putreg8(FAR struct bmp180_dev_s *priv, uint8_t regaddr,
uint8_t regval);
static void bmp180_updatecaldata(FAR struct bmp180_dev_s *priv);
static void bmp180_read_press_temp(FAR struct bmp180_dev_s *priv);
static int bmp180_getpressure(FAR struct bmp180_dev_s *priv);
/* Character driver methods */
static int bmp180_open(FAR struct file *filep);
static int bmp180_close(FAR struct file *filep);
static ssize_t bmp180_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t bmp180_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_bmp180fops =
{
bmp180_open, /* open */
bmp180_close, /* close */
bmp180_read, /* read */
bmp180_write, /* write */
0, /* seek */
0, /* ioctl */
#ifndef CONFIG_DISABLE_POLL
0, /* poll */
#endif
0 /* unlink */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: bmp180_getreg8
*
* Description:
* Read from an 8-bit BMP180 register
*
****************************************************************************/
static uint8_t bmp180_getreg8(FAR struct bmp180_dev_s *priv, uint8_t regaddr)
{
struct i2c_config_s config;
uint8_t regval = 0;
int ret;
/* Set up the I2C configuration */
config.frequency = priv->freq;
config.address = priv->addr;
config.addrlen = 7;
/* Write the register address */
ret = i2c_write(priv->i2c, &config, &regaddr, 1);
if (ret < 0)
{
snerr("ERROR: i2c_write failed: %d\n", ret);
return ret;
}
/* Read the register value */
ret = i2c_read(priv->i2c, &config, &regval, 1);
if (ret < 0)
{
snerr("ERROR: i2c_read failed: %d\n", ret);
return ret;
}
return regval;
}
/****************************************************************************
* Name: bmp180_getreg16
*
* Description:
* Read two 8-bit from a BMP180 register
*
****************************************************************************/
static uint16_t bmp180_getreg16(FAR struct bmp180_dev_s *priv, uint8_t regaddr)
{
struct i2c_config_s config;
uint16_t msb, lsb;
uint16_t regval = 0;
int ret;
/* Set up the I2C configuration */
config.frequency = priv->freq;
config.address = priv->addr;
config.addrlen = 7;
/* Register to read */
ret = i2c_write(priv->i2c, &config, &regaddr, 1);
if (ret < 0)
{
snerr("ERROR: i2c_write failed: %d\n", ret);
return ret;
}
/* Read register */
ret = i2c_read(priv->i2c, &config, (uint8_t *)&regval, 2);
if (ret < 0)
{
snerr("ERROR: i2c_read failed: %d\n", ret);
return ret;
}
/* MSB and LSB are inverted */
msb = (regval & 0xFF);
lsb = (regval & 0xFF00) >> 8;
regval = (msb << 8) | lsb;
return regval;
}
/****************************************************************************
* Name: bmp180_putreg8
*
* Description:
* Write to an 8-bit BMP180 register
*
****************************************************************************/
static void bmp180_putreg8(FAR struct bmp180_dev_s *priv, uint8_t regaddr,
uint8_t regval)
{
struct i2c_config_s config;
uint8_t data[2];
int ret;
/* Set up the I2C configuration */
config.frequency = priv->freq;
config.address = priv->addr;
config.addrlen = 7;
data[0] = regaddr;
data[1] = regval;
/* Write the register address and value */
ret = i2c_write(priv->i2c, &config, (uint8_t *) &data, 2);
if (ret < 0)
{
snerr("ERROR: i2c_write failed: %d\n", ret);
return;
}
return;
}
/****************************************************************************
* Name: bmp180_checkid
*
* Description:
* Read and verify the BMP180 chip ID
*
****************************************************************************/
static int bmp180_checkid(FAR struct bmp180_dev_s *priv)
{
uint8_t devid = 0;
/* Read device ID */
devid = bmp180_getreg8(priv, BMP180_DEVID);
sninfo("devid: 0x%02x\n", devid);
if (devid != (uint16_t) DEVID)
{
/* ID is not Correct */
snerr("ERROR: Wrong Device ID!\n");
return -ENODEV;
}
return OK;
}
/****************************************************************************
* Name: bmp180_updatecaldata
*
* Description:
* Update Calibration Coefficient Data
*
****************************************************************************/
static void bmp180_updatecaldata(FAR struct bmp180_dev_s *priv)
{
/* AC1 */
priv->bmp180_cal_ac1 = (int16_t) bmp180_getreg16(priv, BMP180_AC1_MSB);
/* AC2 */
priv->bmp180_cal_ac2 = (int16_t) bmp180_getreg16(priv, BMP180_AC2_MSB);
/* AC3 */
priv->bmp180_cal_ac3 = (int16_t) bmp180_getreg16(priv, BMP180_AC3_MSB);
/* AC4 */
priv->bmp180_cal_ac4 = bmp180_getreg16(priv, BMP180_AC4_MSB);
/* AC5 */
priv->bmp180_cal_ac5 = bmp180_getreg16(priv, BMP180_AC5_MSB);
/* AC6 */
priv->bmp180_cal_ac6 = bmp180_getreg16(priv, BMP180_AC6_MSB);
/* B1 */
priv->bmp180_cal_b1 = (int16_t) bmp180_getreg16(priv, BMP180_B1_MSB);
/* B2 */
priv->bmp180_cal_b2 = (int16_t) bmp180_getreg16(priv, BMP180_B2_MSB);
/* MB */
priv->bmp180_cal_mb = (int16_t) bmp180_getreg16(priv, BMP180_MB_MSB);
/* MC */
priv->bmp180_cal_mc = (int16_t) bmp180_getreg16(priv, BMP180_MC_MSB);
/* MD */
priv->bmp180_cal_md = (int16_t) bmp180_getreg16(priv, BMP180_MD_MSB);
}
/****************************************************************************
* Name: bmp180_read_press_temp
*
* Description:
* Read raw pressure and temperature from BMP180 and store it in the
* bmp180_dev_s structure.
*
****************************************************************************/
static void bmp180_read_press_temp(FAR struct bmp180_dev_s *priv)
{
uint8_t oss = CURRENT_OSS;
/* Issue a read temperature command */
bmp180_putreg8(priv, BMP180_CTRL_MEAS, BMP180_READ_TEMP);
/* Wait 5ms */
nxsig_usleep(5000);
/* Read temperature */
priv->bmp180_utemp = bmp180_getreg16(priv, BMP180_ADC_OUT_MSB);
/* Issue a read pressure command */
bmp180_putreg8(priv, BMP180_CTRL_MEAS, (BMP180_READ_PRESS | oss));
/* Delay 25.5ms (to OverSampling 8X) */
nxsig_usleep(25500);
/* Read pressure */
priv->bmp180_upress = bmp180_getreg16(priv, BMP180_ADC_OUT_MSB) << 8;
priv->bmp180_upress |= bmp180_getreg8(priv, BMP180_ADC_OUT_XLSB);
priv->bmp180_upress = priv->bmp180_upress >> (8 - (oss >> 6));
sninfo("Uncompensated temperature = %d\n", priv->bmp180_utemp);
sninfo("Uncompensated pressure = %d\n", priv->bmp180_upress);
}
/****************************************************************************
* Name: bmp180_getpressure
*
* Description:
* Calculate the Barometric Pressure using the temperature compensated
* See Freescale AN3785 and BMP1801 data sheet for details
*
****************************************************************************/
static int bmp180_getpressure(FAR struct bmp180_dev_s *priv)
{
int32_t x1;
int32_t x2;
int32_t x3;
int32_t b3;
int32_t b5;
int32_t b6;
int32_t press;
int32_t temp;
uint32_t b4;
uint32_t b7;
uint8_t oss = (CURRENT_OSS >> 6);
/* Check if coefficient data were read correctly */
if ((priv->bmp180_cal_ac1 == 0) || (priv->bmp180_cal_ac2 == 0) ||
(priv->bmp180_cal_ac3 == 0) || (priv->bmp180_cal_ac4 == 0) ||
(priv->bmp180_cal_ac5 == 0) || (priv->bmp180_cal_ac6 == 0) ||
(priv->bmp180_cal_b1 == 0) || (priv->bmp180_cal_b2 == 0) ||
(priv->bmp180_cal_mb == 0) || (priv->bmp180_cal_mc == 0) ||
(priv->bmp180_cal_md == 0))
{
bmp180_updatecaldata(priv);
}
/* Read temperature and pressure */
bmp180_read_press_temp(priv);
/* Feed raw sensor data to entropy pool */
add_sensor_randomness((priv->bmp180_utemp << 16) ^ priv->bmp180_upress);
/* Calculate true temperature */
x1 = ((priv->bmp180_utemp - priv->bmp180_cal_ac6) * priv->bmp180_cal_ac5) >> 15;
x2 = (priv->bmp180_cal_mc << 11) / (x1 + priv->bmp180_cal_md);
b5 = x1 + x2;
temp = (b5 + 8) >> 4;
sninfo("Compensated temperature = %d\n", temp);
/* Calculate true pressure */
b6 = b5 - 4000;
x1 = (priv->bmp180_cal_b2 * ((b6 * b6) >> 12)) >> 11;
x2 = (priv->bmp180_cal_ac2 * b6) >> 11;
x3 = x1 + x2;
b3 = (((((int32_t) priv->bmp180_cal_ac1) * 4 + x3) << oss) + 2) >> 2;
x1 = (priv->bmp180_cal_ac3 * b6) >> 13;
x2 = (priv->bmp180_cal_b1 * ((b6 * b6) >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (priv->bmp180_cal_ac4 * (uint32_t) (x3 + 32768)) >> 15;
b7 = ((uint32_t) (priv->bmp180_upress - b3) * (50000 >> oss));
if (b7 < 0x80000000)
{
press = (b7 << 1) / b4;
}
else
{
press = (b7 / b4) << 1;
}
x1 = (press >> 8) * (press >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * press) >> 16;
press = press + ((x1 + x2 + 3791) >> 4);
sninfo("Compressed pressure = %d\n", press);
return press;
}
/****************************************************************************
* Name: bmp180_open
*
* Description:
* This function is called whenever the BMP1801 device is opened.
*
****************************************************************************/
static int bmp180_open(FAR struct file *filep)
{
return OK;
}
/****************************************************************************
* Name: bmp180_close
*
* Description:
* This routine is called when the BMP180 device is closed.
*
****************************************************************************/
static int bmp180_close(FAR struct file *filep)
{
return OK;
}
/****************************************************************************
* Name: bmp180_read
****************************************************************************/
static ssize_t bmp180_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct bmp180_dev_s *priv = inode->i_private;
FAR uint32_t *press = (FAR uint32_t *) buffer;
if (!buffer)
{
snerr("ERROR: Buffer is null\n");
return -1;
}
if (buflen != 4)
{
snerr("ERROR: You can't read something other than 32 bits (4 bytes)\n");
return -1;
}
/* Get the pressure compensated */
*press = (int32_t) bmp180_getpressure(priv);
/* Return size of uint32_t (4 bytes) */
return 4;
}
/****************************************************************************
* Name: bmp180_write
****************************************************************************/
static ssize_t bmp180_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
return -ENOSYS;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bmp180_register
*
* Description:
* Register the BMP180 character device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/press0"
* i2c - An instance of the I2C interface to use to communicate with
* BMP180
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int bmp180_register(FAR const char *devpath, FAR struct i2c_master_s *i2c)
{
FAR struct bmp180_dev_s *priv;
int ret;
/* Initialize the BMP180 device structure */
priv = (FAR struct bmp180_dev_s *)kmm_malloc(sizeof(struct bmp180_dev_s));
if (!priv)
{
snerr("ERROR: Failed to allocate instance\n");
return -ENOMEM;
}
priv->i2c = i2c;
priv->addr = BMP180_ADDR;
priv->freq = BMP180_FREQ;
/* Check Device ID */
ret = bmp180_checkid(priv);
if (ret < 0)
{
snerr("ERROR: Failed to register driver: %d\n", ret);
kmm_free(priv);
return ret;
}
/* Read the coefficient value */
bmp180_updatecaldata(priv);
/* Register the character driver */
ret = register_driver(devpath, &g_bmp180fops, 0666, priv);
if (ret < 0)
{
snerr("ERROR: Failed to register driver: %d\n", ret);
kmm_free(priv);
}
sninfo("BMP180 driver loaded successfully!\n");
return ret;
}
#endif /* CONFIG_I2C && CONFIG_SENSORS_BMP180 */