6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
784 lines
19 KiB
C
784 lines
19 KiB
C
/****************************************************************************
|
|
* drivers/sensors/lps25h.c
|
|
*
|
|
* Copyright (C) 2014-2017 Haltian Ltd. All rights reserved.
|
|
*
|
|
* 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 <nuttx/arch.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
#include <sys/types.h>
|
|
#include <debug.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/random.h>
|
|
|
|
#include <nuttx/sensors/lps25h.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-Processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_DEBUG_LPS25H
|
|
# define lps25h_dbg(x, ...) _info(x, ##__VA_ARGS__)
|
|
#else
|
|
# define lps25h_dbg(x, ...) sninfo(x, ##__VA_ARGS__)
|
|
#endif
|
|
|
|
#ifndef CONFIG_LPS25H_I2C_FREQUENCY
|
|
# define CONFIG_LPS25H_I2C_FREQUENCY 400000
|
|
#endif
|
|
|
|
#define LPS25H_PRESSURE_INTERNAL_DIVIDER 4096
|
|
|
|
/* 'AN4450 - Hardware and software guidelines for use of LPS25H pressure
|
|
* sensors' - '6.2 One-shot mode conversion time estimation' gives estimates
|
|
* for conversion times:
|
|
*
|
|
* Typical conversion time ≈ 62*(Pavg+Tavg) + 975 μs
|
|
* ex: Tavg = 64; Pavg = 512; Typ. conversation time ≈ 36.7 ms (compatible with
|
|
* ODT=25 Hz)
|
|
* ex: Tavg = 32; Pavg = 128; Typ. conversation time ≈ 10.9 ms
|
|
* The formula is accurate within +/- 3% at room temperature
|
|
*
|
|
* Set timeout to 2 * max.conversation time (2*36.7*1.03 = 76 ms).
|
|
*/
|
|
|
|
#define LPS25H_RETRY_TIMEOUT_MSECS 76
|
|
#define LPS25H_MAX_RETRIES 5
|
|
|
|
#define LPS25H_I2C_RETRIES 10
|
|
|
|
/* Registers */
|
|
|
|
#define LPS25H_REF_P_XL 0x08
|
|
#define LPS25H_REF_P_L 0x09
|
|
#define LPS25H_REF_P_H 0x0a
|
|
#define LPS25H_WHO_AM_I 0x0f
|
|
#define LPS25H_RES_CONF 0x10
|
|
#define LPS25H_CTRL_REG1 0x20
|
|
#define LPS25H_CTRL_REG2 0x21
|
|
#define LPS25H_CTRL_REG3 0x22
|
|
#define LPS25H_CTRL_REG4 0x23
|
|
#define LPS25H_INT_CFG 0x24
|
|
#define LPS25H_INT_SOURCE 0x25
|
|
#define LPS25H_STATUS_REG 0x27
|
|
#define LPS25H_PRESS_POUT_XL 0x28
|
|
#define LPS25H_PRESS_OUT_L 0x29
|
|
#define LPS25H_PRESS_OUT_H 0x2a
|
|
#define LPS25H_TEMP_OUT_L 0x2b
|
|
#define LPS25H_TEMP_OUT_H 0x2c
|
|
#define LPS25H_FIFO_CTRL 0x2e
|
|
#define LPS25H_FIFO_STATUS 0x2f
|
|
#define LPS25H_THS_P_L 0x30
|
|
#define LPS25H_THS_P_H 0x31
|
|
#define LPS25H_RPDS_L 0x39
|
|
#define LPS25H_RPDS_H 0x3a
|
|
|
|
/* Bits in registers */
|
|
|
|
#define LPS25H_AUTO_ZERO (1 << 2)
|
|
#define LPS25H_BDU (1 << 2)
|
|
#define LPS25H_DIFF_EN (1 << 3)
|
|
#define LPS25H_FIFO_EN (1 << 6)
|
|
#define LPS25H_WTM_EN (1 << 5)
|
|
#define LPS25H_FIFO_MEAN_DEC (1 << 4)
|
|
#define LPS25H_PD (1 << 7)
|
|
#define LPS25H_ONE_SHOT (1 << 0)
|
|
#define LPS25H_INT_H_L (1 << 7)
|
|
#define LPS25H_PP_OD (1 << 6)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct lps25h_dev_s
|
|
{
|
|
struct i2c_master_s *i2c;
|
|
uint8_t addr;
|
|
bool irqenabled;
|
|
volatile bool int_pending;
|
|
sem_t devsem;
|
|
sem_t waitsem;
|
|
lps25h_config_t *config;
|
|
};
|
|
|
|
enum LPS25H_RES_CONF_AVG_PRES
|
|
{
|
|
PRES_AVG_8 = 0,
|
|
PRES_AVG_32,
|
|
PRES_AVG_128,
|
|
PRES_AVG_512
|
|
};
|
|
|
|
enum LPS25H_RES_CONF_AVG_TEMP
|
|
{
|
|
TEMP_AVG_8 = 0,
|
|
TEMP_AVG_16,
|
|
TEMP_AVG_32,
|
|
TEMP_AVG_64
|
|
};
|
|
|
|
enum LPS25H_CTRL_REG1_ODR
|
|
{
|
|
CTRL_REG1_ODR_ONE_SHOT = 0,
|
|
CTRL_REG1_ODR_1Hz,
|
|
CTRL_REG1_ODR_7Hz,
|
|
CTRL_REG1_ODR_12_5Hz,
|
|
CTRL_REG1_ODR_25Hz
|
|
};
|
|
|
|
enum LPS25H_CTRL_REG4_P1
|
|
{
|
|
P1_DRDY = 0x1,
|
|
P1_OVERRUN = 0x02,
|
|
P1_WTM = 0x04,
|
|
P1_EMPTY = 0x08
|
|
};
|
|
|
|
enum LPS25H_FIFO_CTRL_MODE
|
|
{
|
|
BYPASS_MODE = 0x0,
|
|
FIFO_STOP_WHEN_FULL,
|
|
STREAM_NEWEST_IN_FIFO,
|
|
STREAM_DEASSERTED,
|
|
BYPASS_DEASSERTED_STREAM,
|
|
FIFO_MEAN = 0x06,
|
|
BYPASS_DEASSERTED_FIFO
|
|
};
|
|
|
|
enum LPS25H_FIFO_CTRL_WTM
|
|
{
|
|
SAMPLE_2 = 0x01,
|
|
SAMPLE_4 = 0x03,
|
|
SAMPLE_8 = 0x07,
|
|
SAMPLE_16 = 0x0f,
|
|
SAMPLE_32 = 0x1f
|
|
};
|
|
|
|
enum LPS25H_INT_CFG_OP
|
|
{
|
|
PH_E = 0x1,
|
|
PL_E = 0x2,
|
|
LIR = 0x4
|
|
};
|
|
|
|
/************************************************************************************
|
|
* Private Function Prototypes
|
|
************************************************************************************/
|
|
|
|
static int lps25h_open(FAR struct file *filep);
|
|
static int lps25h_close(FAR struct file *filep);
|
|
static ssize_t lps25h_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t lps25h_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen);
|
|
static int lps25h_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
|
|
|
|
static int lps25h_configure_dev(FAR struct lps25h_dev_s *dev);
|
|
static int lps25h_read_pressure(FAR struct lps25h_dev_s *dev,
|
|
FAR lps25h_pressure_data_t *pres);
|
|
static int lps25h_read_temper(FAR struct lps25h_dev_s *dev,
|
|
FAR lps25h_temper_data_t *temper);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_lps25hops =
|
|
{
|
|
lps25h_open, /* open */
|
|
lps25h_close, /* close */
|
|
lps25h_read, /* read */
|
|
lps25h_write, /* write */
|
|
NULL, /* seek */
|
|
lps25h_ioctl, /* ioctl */
|
|
NULL /* poll */
|
|
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
|
|
, NULL /* unlink */
|
|
#endif
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
static int lps25h_do_transfer(FAR struct lps25h_dev_s *dev,
|
|
FAR struct i2c_msg_s *msgv,
|
|
size_t nmsg)
|
|
{
|
|
int ret = -EIO;
|
|
int retries;
|
|
|
|
for (retries = 0; retries < LPS25H_I2C_RETRIES; retries++)
|
|
{
|
|
ret = I2C_TRANSFER(dev->i2c, msgv, nmsg);
|
|
if (ret >= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
/* Some error. Try to reset I2C bus and keep trying. */
|
|
|
|
#ifdef CONFIG_I2C_RESET
|
|
if (retries == LPS25H_I2C_RETRIES - 1)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ret = I2C_RESET(dev->i2c);
|
|
if (ret < 0)
|
|
{
|
|
lps25h_dbg("I2C_RESET failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
lps25h_dbg("xfer failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int lps25h_write_reg8(struct lps25h_dev_s *dev, uint8_t reg_addr,
|
|
const uint8_t value)
|
|
{
|
|
struct i2c_msg_s msgv[2] =
|
|
{
|
|
{
|
|
.frequency = CONFIG_LPS25H_I2C_FREQUENCY,
|
|
.addr = dev->addr,
|
|
.flags = 0,
|
|
.buffer = ®_addr,
|
|
.length = 1
|
|
},
|
|
{
|
|
.frequency = CONFIG_LPS25H_I2C_FREQUENCY,
|
|
.addr = dev->addr,
|
|
.flags = I2C_M_NOSTART,
|
|
.buffer = (void *)&value,
|
|
.length = 1
|
|
}
|
|
};
|
|
|
|
return lps25h_do_transfer(dev, msgv, 2);
|
|
}
|
|
|
|
static int lps25h_read_reg8(FAR struct lps25h_dev_s *dev,
|
|
FAR uint8_t *reg_addr,
|
|
FAR uint8_t *value)
|
|
{
|
|
struct i2c_msg_s msgv[2] =
|
|
{
|
|
{
|
|
.frequency = CONFIG_LPS25H_I2C_FREQUENCY,
|
|
.addr = dev->addr,
|
|
.flags = 0,
|
|
.buffer = reg_addr,
|
|
.length = 1
|
|
},
|
|
{
|
|
.frequency = CONFIG_LPS25H_I2C_FREQUENCY,
|
|
.addr = dev->addr,
|
|
.flags = I2C_M_READ,
|
|
.buffer = value,
|
|
.length = 1
|
|
}
|
|
};
|
|
|
|
return lps25h_do_transfer(dev, msgv, 2);
|
|
}
|
|
|
|
static int lps25h_power_on_off(FAR struct lps25h_dev_s *dev, bool on)
|
|
{
|
|
int ret;
|
|
uint8_t value;
|
|
|
|
value = on ? LPS25H_PD : 0;
|
|
ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG1, value);
|
|
return ret;
|
|
}
|
|
|
|
static int lps25h_open(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct lps25h_dev_s *dev = inode->i_private;
|
|
uint8_t value = 0;
|
|
uint8_t addr = LPS25H_WHO_AM_I;
|
|
int32_t ret;
|
|
|
|
/* Get exclusive access */
|
|
|
|
nxsem_wait_uninterruptible(&dev->devsem);
|
|
|
|
dev->config->set_power(dev->config, true);
|
|
ret = lps25h_read_reg8(dev, &addr, &value);
|
|
if (ret < 0)
|
|
{
|
|
lps25h_dbg("Cannot read device's ID\n");
|
|
dev->config->set_power(dev->config, false);
|
|
goto out;
|
|
}
|
|
|
|
lps25h_dbg("WHO_AM_I: 0x%2x\n", value);
|
|
|
|
dev->config->irq_enable(dev->config, true);
|
|
dev->irqenabled = true;
|
|
|
|
out:
|
|
nxsem_post(&dev->devsem);
|
|
return ret;
|
|
}
|
|
|
|
static int lps25h_close(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct lps25h_dev_s *dev = inode->i_private;
|
|
int ret;
|
|
|
|
/* Get exclusive access */
|
|
|
|
nxsem_wait_uninterruptible(&dev->devsem);
|
|
|
|
dev->config->irq_enable(dev->config, false);
|
|
dev->irqenabled = false;
|
|
ret = lps25h_power_on_off(dev, false);
|
|
dev->config->set_power(dev->config, false);
|
|
lps25h_dbg("CLOSED\n");
|
|
|
|
nxsem_post(&dev->devsem);
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t lps25h_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct lps25h_dev_s *dev = inode->i_private;
|
|
lps25h_pressure_data_t data;
|
|
ssize_t length = 0;
|
|
int ret;
|
|
|
|
/* Get exclusive access */
|
|
|
|
nxsem_wait_uninterruptible(&dev->devsem);
|
|
|
|
ret = lps25h_configure_dev(dev);
|
|
if (ret < 0)
|
|
{
|
|
lps25h_dbg("cannot configure sensor: %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
ret = lps25h_read_pressure(dev, &data);
|
|
if (ret < 0)
|
|
{
|
|
lps25h_dbg("cannot read data: %d\n", ret);
|
|
}
|
|
else
|
|
{
|
|
/* This interface is mainly intended for easy debugging in nsh. */
|
|
|
|
length = snprintf(buffer, buflen, "%u\n", data.pressure_Pa);
|
|
if (length > buflen)
|
|
{
|
|
length = buflen;
|
|
}
|
|
}
|
|
|
|
out:
|
|
|
|
nxsem_post(&dev->devsem);
|
|
return length;
|
|
}
|
|
|
|
static ssize_t lps25h_write(FAR struct file *filep, FAR const char *buffer,
|
|
size_t buflen)
|
|
{
|
|
ssize_t length = 0;
|
|
|
|
return length;
|
|
}
|
|
|
|
static void lps25h_notify(FAR struct lps25h_dev_s *dev)
|
|
{
|
|
DEBUGASSERT(dev != NULL);
|
|
|
|
dev->int_pending = true;
|
|
nxsem_post(&dev->waitsem);
|
|
}
|
|
|
|
static int lps25h_int_handler(int irq, FAR void *context, FAR void *arg)
|
|
{
|
|
FAR struct lps25h_dev_s *dev = (FAR struct lps25h_dev_s *)arg;
|
|
|
|
DEBUGASSERT(dev != NULL);
|
|
|
|
lps25h_notify(dev);
|
|
lps25h_dbg("lps25h interrupt\n");
|
|
return OK;
|
|
}
|
|
|
|
static int lps25h_configure_dev(FAR struct lps25h_dev_s *dev)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = lps25h_power_on_off(dev, false);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Enable FIFO */
|
|
|
|
ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG2, LPS25H_FIFO_EN);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = lps25h_write_reg8(dev, LPS25H_FIFO_CTRL, (BYPASS_MODE << 5));
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG4, P1_DRDY);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Write CTRL_REG1 to turn device on */
|
|
|
|
ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG1,
|
|
LPS25H_PD | (CTRL_REG1_ODR_1Hz << 4));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lps25h_one_shot(FAR struct lps25h_dev_s *dev)
|
|
{
|
|
int ret = ERROR;
|
|
int retries;
|
|
struct timespec abstime;
|
|
irqstate_t flags;
|
|
|
|
if (!dev->irqenabled)
|
|
{
|
|
lps25h_dbg("IRQ disabled!\n");
|
|
}
|
|
|
|
/* Retry one-shot measurement multiple times. */
|
|
|
|
for (retries = 0; retries < LPS25H_MAX_RETRIES; retries++)
|
|
{
|
|
/* Power off so we start from a known state. */
|
|
|
|
ret = lps25h_power_on_off(dev, false);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Initiate a one shot mode measurement */
|
|
|
|
ret = lps25h_write_reg8(dev, LPS25H_CTRL_REG2, LPS25H_ONE_SHOT);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Power on to start measurement. */
|
|
|
|
ret = lps25h_power_on_off(dev, true);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
clock_gettime(CLOCK_REALTIME, &abstime);
|
|
abstime.tv_sec += (LPS25H_RETRY_TIMEOUT_MSECS / 1000);
|
|
abstime.tv_nsec += (LPS25H_RETRY_TIMEOUT_MSECS % 1000) * 1000 * 1000;
|
|
while (abstime.tv_nsec >= (1000 * 1000 * 1000))
|
|
{
|
|
abstime.tv_sec++;
|
|
abstime.tv_nsec -= 1000 * 1000 * 1000;
|
|
}
|
|
|
|
ret = nxsem_timedwait_uninterruptible(&dev->waitsem, &abstime);
|
|
if (ret == OK)
|
|
{
|
|
break;
|
|
}
|
|
else if (ret == -ETIMEDOUT)
|
|
{
|
|
uint8_t reg = LPS25H_CTRL_REG2;
|
|
uint8_t value;
|
|
|
|
/* In 'AN4450 - Hardware and software guidelines for use of
|
|
* LPS25H pressure sensors' - '4.3 One-shot mode measurement
|
|
* sequence', one-shot mode example is given where interrupt line
|
|
* is not used, but CTRL_REG2 is polled until ONE_SHOT bit is
|
|
* unset (as it is self-clearing). Check ONE_SHOT bit status here
|
|
* to see if we just missed interrupt.
|
|
*/
|
|
|
|
ret = lps25h_read_reg8(dev, ®, &value);
|
|
if (ret == OK && (value & LPS25H_ONE_SHOT) == 0)
|
|
{
|
|
/* One-shot completed. */
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Some unknown mystery error */
|
|
|
|
DEBUGASSERT(false);
|
|
return ret;
|
|
}
|
|
|
|
lps25h_dbg("Retrying one-shot measurement: retries=%d\n", retries);
|
|
}
|
|
|
|
if (ret != OK)
|
|
{
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
flags = enter_critical_section();
|
|
dev->int_pending = false;
|
|
leave_critical_section(flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lps25h_read_pressure(FAR struct lps25h_dev_s *dev,
|
|
FAR lps25h_pressure_data_t *pres)
|
|
{
|
|
int ret;
|
|
uint8_t pres_addr_h = LPS25H_PRESS_OUT_H;
|
|
uint8_t pres_addr_l = LPS25H_PRESS_OUT_L;
|
|
uint8_t pres_addr_xl = LPS25H_PRESS_POUT_XL;
|
|
uint8_t pres_value_h = 0;
|
|
uint8_t pres_value_l = 0;
|
|
uint8_t pres_value_xl = 0;
|
|
int32_t pres_res = 0;
|
|
|
|
ret = lps25h_one_shot(dev);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = lps25h_read_reg8(dev, &pres_addr_h, &pres_value_h);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = lps25h_read_reg8(dev, &pres_addr_l, &pres_value_l);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = lps25h_read_reg8(dev, &pres_addr_xl, &pres_value_xl);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
pres_res = ((int32_t) pres_value_h << 16) |
|
|
((int16_t) pres_value_l << 8) |
|
|
pres_value_xl;
|
|
|
|
/* Add to entropy pool. */
|
|
|
|
add_sensor_randomness(pres_res);
|
|
|
|
/* Convert to more usable format. */
|
|
|
|
pres->pressure_int_hP = pres_res / LPS25H_PRESSURE_INTERNAL_DIVIDER;
|
|
pres->pressure_Pa = (uint64_t)pres_res * 100000 / LPS25H_PRESSURE_INTERNAL_DIVIDER;
|
|
pres->raw_data = pres_res;
|
|
lps25h_dbg("Pressure: %u Pa\n", pres->pressure_Pa);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lps25h_read_temper(FAR struct lps25h_dev_s *dev,
|
|
FAR lps25h_temper_data_t *temper)
|
|
{
|
|
int ret;
|
|
uint8_t temper_addr_h = LPS25H_TEMP_OUT_H;
|
|
uint8_t temper_addr_l = LPS25H_TEMP_OUT_L;
|
|
uint8_t temper_value_h = 0;
|
|
uint8_t temper_value_l = 0;
|
|
int32_t temper_res;
|
|
int16_t raw_data;
|
|
|
|
ret = lps25h_read_reg8(dev, &temper_addr_h, &temper_value_h);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = lps25h_read_reg8(dev, &temper_addr_l, &temper_value_l);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
raw_data = (temper_value_h << 8) | temper_value_l;
|
|
|
|
/* Add to entropy pool. */
|
|
|
|
add_sensor_randomness(raw_data);
|
|
|
|
/* T(⁰C) = 42.5 + (raw / 480)
|
|
* =>
|
|
* T(⁰C) * scale = (425 * 48 + raw) * scale / 480;
|
|
*/
|
|
|
|
temper_res = (425 * 48 + raw_data);
|
|
temper_res *= LPS25H_TEMPER_DIVIDER;
|
|
temper_res /= 480;
|
|
|
|
temper->int_temper = temper_res;
|
|
temper->raw_data = raw_data;
|
|
lps25h_dbg("Temperature: %d\n", temper_res);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lps25h_who_am_i(struct lps25h_dev_s *dev,
|
|
lps25h_who_am_i_data * who_am_i_data)
|
|
{
|
|
uint8_t who_addr = LPS25H_WHO_AM_I;
|
|
return lps25h_read_reg8(dev, &who_addr, &who_am_i_data->who_am_i);
|
|
}
|
|
|
|
static int lps25h_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct lps25h_dev_s *dev = inode->i_private;
|
|
int ret;
|
|
|
|
/* Get exclusive access */
|
|
|
|
nxsem_wait_uninterruptible(&dev->devsem);
|
|
|
|
switch (cmd)
|
|
{
|
|
case SNIOC_CFGR:
|
|
ret = lps25h_configure_dev(dev);
|
|
break;
|
|
|
|
case SNIOC_PRESSURE_OUT:
|
|
ret = lps25h_read_pressure(dev, (lps25h_pressure_data_t *) arg);
|
|
break;
|
|
|
|
case SNIOC_TEMPERATURE_OUT:
|
|
/* NOTE: call SNIOC_PRESSURE_OUT before this one,
|
|
* or results are bogus.
|
|
*/
|
|
|
|
ret = lps25h_read_temper(dev, (lps25h_temper_data_t *) arg);
|
|
break;
|
|
|
|
case SNIOC_SENSOR_OFF:
|
|
ret = lps25h_power_on_off(dev, false);
|
|
break;
|
|
|
|
case SNIOC_GET_DEV_ID:
|
|
ret = lps25h_who_am_i(dev, (lps25h_who_am_i_data *) arg);
|
|
break;
|
|
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
nxsem_post(&dev->devsem);
|
|
return ret;
|
|
}
|
|
|
|
int lps25h_register(FAR const char *devpath, FAR struct i2c_master_s *i2c,
|
|
uint8_t addr, FAR lps25h_config_t *config)
|
|
{
|
|
int ret = 0;
|
|
FAR struct lps25h_dev_s *dev;
|
|
|
|
dev = (struct lps25h_dev_s *)kmm_zalloc(sizeof(struct lps25h_dev_s));
|
|
if (!dev)
|
|
{
|
|
lps25h_dbg("Memory cannot be allocated for LPS25H sensor\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
nxsem_init(&dev->devsem, 0, 1);
|
|
nxsem_init(&dev->waitsem, 0, 0);
|
|
|
|
dev->addr = addr;
|
|
dev->i2c = i2c;
|
|
dev->config = config;
|
|
|
|
if (dev->config->irq_clear)
|
|
{
|
|
dev->config->irq_clear(dev->config);
|
|
}
|
|
|
|
ret = register_driver(devpath, &g_lps25hops, 0666, dev);
|
|
|
|
lps25h_dbg("Registered with %d\n", ret);
|
|
|
|
if (ret < 0)
|
|
{
|
|
kmm_free(dev);
|
|
lps25h_dbg("Error occurred during the driver registering\n");
|
|
return ERROR;
|
|
}
|
|
|
|
dev->config->irq_attach(config, lps25h_int_handler, dev);
|
|
dev->config->irq_enable(config, false);
|
|
dev->irqenabled = false;
|
|
return OK;
|
|
}
|