sensor: add driver for HP206C sensor

This sensor is a high precision barometer, altimeter and temperature
sensor. It can also be found on the Grove Barometer module (v1.0).

Sensor datasheet:
http://www.hoperf.com/upload/sensor/HP206C_DataSheet_EN_V2.0.pdf

Grove link:
http://www.seeedstudio.com/depot/Grove-Barometer-HighAccuracy-p-1865.html

Origin: Original
Change-Id: I99986b26b6b4447e31e12a1fc17ede3e6bb4c586
Signed-off-by: Laurentiu Palcu <laurentiu.palcu@intel.com>
This commit is contained in:
Laurentiu Palcu 2016-05-17 17:17:33 +03:00 committed by Anas Nashif
commit 3ef2e059c3
5 changed files with 535 additions and 0 deletions

View file

@ -41,6 +41,8 @@ source "drivers/sensor/Kconfig.hdc1008"
source "drivers/sensor/Kconfig.hmc5883l"
source "drivers/sensor/Kconfig.hp206c"
source "drivers/sensor/Kconfig.hts221"
source "drivers/sensor/Kconfig.isl29035"

View file

@ -0,0 +1,88 @@
# Kconfig.hp206c - HopeRF Electronic HP206C precision barometer and
# altimeter configuration options
#
# Copyright (c) 2016 Intel Corporation
#
# Licensed 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.
#
menuconfig HP206C
bool "HopeRF HP206C precision barometer and altimeter sensor"
depends on SENSOR
depends on I2C
default n
help
Enable HopeRF HP206C barometer and altimeter support.
config HP206C_SYS_LOG_LEVEL
int "HP206C Log level"
depends on SYS_LOG && HP206C
default 0
range 0 4
help
Sets log level for HP206C driver.
Levels are:
0 OFF, do not write
1 ERROR, only write SYS_LOG_ERR
2 WARNING, write SYS_LOG_WRN in adition to previous level
3 INFO, write SYS_LOG_INF in adition to previous levels
4 DEBUG, write SYS_LOG_DBG in adition to previous levels
config HP206C_INIT_PRIORITY
int
prompt "HP206C Init priority"
default 70
depends on HP206C
help
Device driver initialization priority.
As the device is connected to SPI bus, its driver has
to be initialized after the SPI one.
config HP206C_DRV_NAME
string "Driver's name"
depends on HP206C
default "hp206c"
help
Name for the HP206C driver which will be used for binding.
config HP206C_I2C_PORT_NAME
string "I2C master controller port name"
depends on HP206C
default "I2C_0"
help
Master I2C port name through which HP206C chip is accessed.
config HP206C_OSR_RUNTIME
bool "Oversampling rate set at runtime"
depends on HP206C
default y
config HP206C_OSR
int "Oversampling rate"
depends on HP206C && !HP206C_OSR_RUNTIME
default 4096
help
Allowed values: 4096, 2048, 1024, 512, 256, 128
config HP206C_ALT_OFFSET_RUNTIME
bool "Altitude offset set at runtime"
depends on HP206C
default y
config HP206C_ALT_OFFSET
int "Altitude offset (in cm)"
depends on HP206C && !HP206C_ALT_OFFSET_RUNTIME
default 0
help
Value, in cm, that will be used to compensate altitude calculation.
For more info on how to choose this value, consult section 6.1.1 in
the datasheet.

View file

@ -14,6 +14,7 @@ obj-$(CONFIG_DHT) += sensor_dht.o
obj-$(CONFIG_HDC1008) += sensor_hdc1008.o
obj-$(CONFIG_HMC5883L) += sensor_hmc5883l.o
obj-$(CONFIG_HMC5883L_TRIGGER) += sensor_hmc5883l_trigger.o
obj-$(CONFIG_HP206C) += sensor_hp206c.o
obj-$(CONFIG_HTS221) += sensor_hts221.o
obj-$(CONFIG_HTS221_TRIGGER) += sensor_hts221_trigger.o
obj-$(CONFIG_ISL29035) += sensor_isl29035.o

View file

@ -0,0 +1,345 @@
/* HopeRF Electronic HP206C precision barometer and altimeter driver
*
* Copyright (c) 2016 Intel Corporation
*
* Licensed 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.
*
* Datasheet:
* http://www.hoperf.com/upload/sensor/HP206C_DataSheet_EN_V2.0.pdf
*/
#include <init.h>
#include <sensor.h>
#include <i2c.h>
#include <misc/byteorder.h>
#include <nanokernel.h>
#include <gpio.h>
#include "sensor_hp206c.h"
static inline int hp206c_bus_config(struct device *dev)
{
struct hp206c_device_data *hp206c = dev->driver_data;
union dev_config i2c_cfg;
i2c_cfg.raw = 0;
i2c_cfg.bits.is_master_device = 1;
i2c_cfg.bits.speed = I2C_SPEED_STANDARD;
return i2c_configure(hp206c->i2c, i2c_cfg.raw);
}
static int hp206c_read(struct device *dev, uint8_t cmd, uint8_t *data,
uint8_t len)
{
struct hp206c_device_data *hp206c = dev->driver_data;
hp206c_bus_config(dev);
if (i2c_burst_read(hp206c->i2c, HP206C_I2C_ADDRESS,
cmd, data, len) < 0) {
return -EIO;
}
return 0;
}
static int hp206c_read_reg(struct device *dev, uint8_t reg_addr,
uint8_t *reg_val)
{
uint8_t cmd = HP206C_CMD_READ_REG | (reg_addr & HP206C_REG_ADDR_MASK);
return hp206c_read(dev, cmd, reg_val, 1);
}
static int hp206c_write(struct device *dev, uint8_t cmd, uint8_t *data,
uint8_t len)
{
struct hp206c_device_data *hp206c = dev->driver_data;
hp206c_bus_config(dev);
if (i2c_burst_write(hp206c->i2c, HP206C_I2C_ADDRESS,
cmd, data, len) < 0) {
return -EIO;
}
return 0;
}
static int hp206c_write_reg(struct device *dev, uint8_t reg_addr,
uint8_t reg_val)
{
uint8_t cmd = HP206C_CMD_WRITE_REG | (reg_addr & HP206C_REG_ADDR_MASK);
return hp206c_write(dev, cmd, &reg_val, 1);
}
static int hp206c_cmd_send(struct device *dev, uint8_t cmd)
{
struct hp206c_device_data *hp206c = dev->driver_data;
hp206c_bus_config(dev);
return i2c_write(hp206c->i2c, &cmd, 1, HP206C_I2C_ADDRESS);
}
/*
* The conversion times in this map were rounded up. The reason for doing that
* is merely to spare 24 bytes that, otherwise, would've been taken by having
* the times converted to microseconds. The trade-off is 900us added to the
* conversion wait time which looks like a good compromise provided the highest
* precision computation takes 131.1ms.
*/
static uint8_t hp206c_adc_time_ms[] = {
/* conversion time(ms), OSR */
132, /* 4096 */
66, /* 2048 */
34, /* 1024 */
17, /* 512 */
9, /* 256 */
5, /* 128 */
};
static int hp206c_osr_set(struct device *dev, uint16_t osr)
{
struct hp206c_device_data *hp206c = dev->driver_data;
uint8_t i;
/* the following code translates OSR values to an index */
for (i = 0; i < 6 && BIT(12 - i) != osr; i++) {
;
}
if (i == 6) {
return -ENOTSUP;
}
hp206c->osr = i;
return 0;
}
static int hp206c_altitude_offs_set(struct device *dev, int16_t offs)
{
uint8_t reg_val;
reg_val = offs & 0xff;
if (hp206c_write_reg(dev, HP206C_REG_ALT_OFF_LSB, reg_val) < 0) {
return -EIO;
}
reg_val = (offs & 0xff00) >> 8;
if (hp206c_write_reg(dev, HP206C_REG_ALT_OFF_MSB, reg_val) < 0) {
return -EIO;
}
return hp206c_write_reg(dev, HP206C_REG_PARA, HP206C_COMPENSATION_EN);
}
static int hp206c_attr_set(struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
if (val->type != SENSOR_VALUE_TYPE_INT) {
return -EINVAL;
}
#ifdef CONFIG_HP206C_OSR_RUNTIME
if (attr == SENSOR_ATTR_OVERSAMPLING) {
return hp206c_osr_set(dev, val->val1);
}
#endif
#ifdef CONFIG_HP206C_ALT_OFFSET_RUNTIME
if (attr == SENSOR_ATTR_OFFSET) {
if (chan != SENSOR_CHAN_ALTITUDE) {
return -ENOTSUP;
}
return hp206c_altitude_offs_set(dev, val->val1);
}
#endif
return -ENOTSUP;
}
static int hp206c_wait_dev_ready(struct device *dev, uint32_t timeout_ms)
{
struct hp206c_device_data *hp206c = dev->driver_data;
uint8_t int_src;
#ifdef CONFIG_NANO_TIMERS
nano_timer_start(&hp206c->tmr, MSEC(timeout_ms));
nano_timer_test(&hp206c->tmr, TICKS_UNLIMITED);
#else
sys_thread_busy_wait(timeout_ms * 1000);
#endif
if (hp206c_read_reg(dev, HP206C_REG_INT_SRC, &int_src) < 0) {
return -EIO;
}
if (int_src & HP206C_DEV_RDY) {
return 0;
}
return -EBUSY;
}
static int hp206c_adc_aquire(struct device *dev, enum sensor_channel chan)
{
struct hp206c_device_data *hp206c = dev->driver_data;
if (hp206c_cmd_send(dev, HP206C_CMD_ADC_CVT | (hp206c->osr << 2)) < 0) {
return -EIO;
}
return hp206c_wait_dev_ready(dev, hp206c_adc_time_ms[hp206c->osr]);
}
static int32_t hp206c_buf_convert(uint8_t *buf, bool signed_val)
{
int32_t tmp = 0;
if (signed_val && (buf[0] & 0x08)) {
tmp |= (0xff << 24) | (0xf0 << 16);
}
tmp |= ((buf[0] & 0x0f) << 16) | (buf[1] << 8) | buf[2];
return tmp;
}
static int hp206c_val_get(struct device *dev,
uint8_t cmd, struct sensor_value *val)
{
uint8_t buf[3];
int32_t temp = 0;
if (hp206c_read(dev, cmd, buf, 3) < 0) {
return -EIO;
}
/*
* According to documentation, pressure and altitude are 20 bit unsigned
* values whereas temperature is a signed.
*/
if (cmd == HP206C_CMD_READ_T) {
temp = hp206c_buf_convert(buf, true);
} else {
temp = hp206c_buf_convert(buf, false);
}
val->type = SENSOR_VALUE_TYPE_INT_PLUS_MICRO;
if (cmd == HP206C_CMD_READ_P) {
val->val1 = temp / 1000;
val->val2 = temp % 1000 * 1000;
} else {
val->val1 = temp / 100;
val->val2 = temp % 100 * 10000;
}
return 0;
}
static inline int hp206c_pressure_get(struct device *dev,
struct sensor_value *val)
{
return hp206c_val_get(dev, HP206C_CMD_READ_P, val);
}
static inline int hp206c_altitude_get(struct device *dev,
struct sensor_value *val)
{
return hp206c_val_get(dev, HP206C_CMD_READ_A, val);
}
static inline int hp206c_temperature_get(struct device *dev,
struct sensor_value *val)
{
return hp206c_val_get(dev, HP206C_CMD_READ_T, val);
}
static int hp206c_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
switch (chan) {
case SENSOR_CHAN_TEMP:
return hp206c_temperature_get(dev, val);
case SENSOR_CHAN_PRESS:
return hp206c_pressure_get(dev, val);
case SENSOR_CHAN_ALTITUDE:
return hp206c_altitude_get(dev, val);
default:
return -ENOTSUP;
}
return 0;
}
struct sensor_driver_api hp206c_api = {
.attr_set = hp206c_attr_set,
#ifdef CONFIG_HP206C_TRIGGER
.trigger_set = hp206c_trigger_set,
#endif
.sample_fetch = hp206c_adc_aquire,
.channel_get = hp206c_channel_get,
};
int hp206c_init(struct device *dev)
{
struct hp206c_device_data *hp206c = dev->driver_data;
hp206c->i2c = device_get_binding(CONFIG_HP206C_I2C_PORT_NAME);
if (!hp206c->i2c) {
SYS_LOG_ERR("I2C master controller not found!");
return -EINVAL;
}
/* reset the chip */
if (hp206c_cmd_send(dev, HP206C_CMD_SOFT_RST) < 0) {
SYS_LOG_ERR("Cannot reset chip.");
return -EIO;
}
#ifdef CONFIG_NANO_TIMERS
nano_timer_init(&hp206c->tmr, NULL);
#endif
sys_thread_busy_wait(500);
if (hp206c_osr_set(dev, HP206C_DEFAULT_OSR) < 0) {
SYS_LOG_ERR("OSR value is not supported.");
return -ENOTSUP;
}
if (hp206c_altitude_offs_set(dev, HP206C_DEFAULT_ALT_OFFSET) < 0) {
return -EIO;
}
dev->driver_api = &hp206c_api;
return 0;
}
static struct hp206c_device_data hp206c_data;
DEVICE_INIT(hp206c, CONFIG_HP206C_DRV_NAME, hp206c_init, &hp206c_data,
NULL, NANOKERNEL, CONFIG_HP206C_INIT_PRIORITY);

View file

@ -0,0 +1,99 @@
/* HopeRF Electronic HP206C precision barometer and altimeter header
*
* Copyright (c) 2016 Intel Corporation
*
* Licensed 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.
*
*/
#ifndef _SENSOR_HP206C_H_
#define _SENSOR_HP206C_H_
#include <misc/util.h>
#define HP206C_I2C_ADDRESS 0x76
/* HP206C configuration registers */
#define HP206C_REG_ALT_OFF_LSB 0x00
#define HP206C_REG_ALT_OFF_MSB 0x01
#define HP206C_REG_PA_H_TH_LSB 0x02
#define HP206C_REG_PA_H_TH_MSB 0x03
#define HP206C_REG_PA_M_TH_LSB 0x04
#define HP206C_REG_PA_M_TH_MSB 0x05
#define HP206C_REG_PA_L_TH_LSB 0x06
#define HP206C_REG_PA_L_TH_MSB 0x07
#define HP206C_REG_T_H_TH 0x08
#define HP206C_REG_T_M_TH 0x09
#define HP206C_REG_T_L_TH 0x0A
#define HP206C_REG_INT_EN 0x0B
#define HP206C_REG_INT_GFG 0x0C
#define HP206C_REG_INT_SRC 0x0D
#define HP206C_REG_INT_DIR 0x0E
#define HP206C_REG_PARA 0x0F
/* HP206C commands */
#define HP206C_CMD_SOFT_RST 0x06
#define HP206C_CMD_ADC_CVT 0x40
#define HP206C_CMD_READ_PT 0x10
#define HP206C_CMD_READ_AT 0x11
#define HP206C_CMD_READ_P 0x30
#define HP206C_CMD_READ_A 0x31
#define HP206C_CMD_READ_T 0x32
#define HP206C_CMD_ANA_CAL 0x28
#define HP206C_CMD_READ_REG 0x80
#define HP206C_CMD_WRITE_REG 0xC0
#define HP206C_REG_ADDR_MASK 0x3F
/* HP206C_REG_INT_SRC */
#define HP206C_T_WIN BIT(0)
#define HP206C_PA_WIN BIT(1)
#define HP206C_T_TRAV BIT(2)
#define HP206C_PA_TRAV BIT(3)
#define HP206C_T_RDY BIT(4)
#define HP206C_PA_RDY BIT(5)
#define HP206C_DEV_RDY BIT(6)
#define HP206C_TH_ERR BIT(7)
/* HP206C_REG_PARA */
#define HP206C_COMPENSATION_EN BIT(7)
/* default settings, based on menuconfig options */
#if defined(CONFIG_HP206C_OSR_RUNTIME)
# define HP206C_DEFAULT_OSR 4096
#else
# define HP206C_DEFAULT_OSR CONFIG_HP206C_OSR
#endif
#if defined(CONFIG_HP206C_ALT_OFFSET_RUNTIME)
# define HP206C_DEFAULT_ALT_OFFSET 0
#else
# define HP206C_DEFAULT_ALT_OFFSET CONFIG_HP206C_ALT_OFFSET
#endif
/* end of default settings */
struct hp206c_device_data {
struct device *i2c;
#ifdef CONFIG_NANO_TIMERS
#if CONFIG_SYS_CLOCK_TICKS_PER_SEC < 2000
#error "SYS_CLOCK_TICKS_PER_SEC > 2000 needed for better timeouts granularity."
#endif
struct nano_timer tmr;
#endif
uint8_t osr;
};
#define SYS_LOG_DOMAIN "HP206C"
#define SYS_LOG_LEVEL CONFIG_HP206C_SYS_LOG_LEVEL
#include <misc/sys_log.h>
#endif /* _SENSOR_HP206C_H_ */