From 976cf30281ea77f06b2bc79032fc7388e21b0721 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Fri, 15 Jan 2016 17:44:02 +0100 Subject: [PATCH] drivers/hdc1000: remodeled driver - cleaned up interface - made read functions return phyical values - made resolution configurable at initialization time - added default parameter configuration file - added SAUL support for the driver --- drivers/Makefile.dep | 4 + drivers/Makefile.include | 3 + drivers/hdc1000/hdc1000.c | 184 +++++++---------------- drivers/hdc1000/hdc1000_saul.c | 60 ++++++++ drivers/hdc1000/include/hdc1000_params.h | 76 ++++++++++ drivers/hdc1000/include/hdc1000_regs.h | 73 +++++++++ drivers/include/hdc1000.h | 159 +++++++++++--------- 7 files changed, 360 insertions(+), 199 deletions(-) create mode 100644 drivers/hdc1000/hdc1000_saul.c create mode 100644 drivers/hdc1000/include/hdc1000_params.h create mode 100644 drivers/hdc1000/include/hdc1000_regs.h diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 0b6ace63c6..ffa950cae2 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -73,6 +73,10 @@ ifneq (,$(filter ethos,$(USEMODULE))) USEMODULE += tsrb endif +ifneq (,$(filter hdc1000,$(USEMODULE))) + USEMODULE += xtimer +endif + ifneq (,$(filter hih6130,$(USEMODULE))) USEMODULE += xtimer endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 7c1e2836cf..d2b40de3c2 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -82,3 +82,6 @@ endif ifneq (,$(filter si70xx,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/si70xx/include endif +ifneq (,$(filter hdc1000,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/hdc1000/include +endif diff --git a/drivers/hdc1000/hdc1000.c b/drivers/hdc1000/hdc1000.c index 480ab68bcd..791d02f35b 100644 --- a/drivers/hdc1000/hdc1000.c +++ b/drivers/hdc1000/hdc1000.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 PHYTEC Messtechnik GmbH + * 2017 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -15,176 +16,107 @@ * @brief Driver for the TI HDC1000 Humidity and Temperature Sensor. * * @author Johann Fischer + * @author Hauke Petersen * * @} */ -#include -#include +#include + +#include "assert.h" +#include "xtimer.h" #include "periph/i2c.h" #include "hdc1000.h" #define ENABLE_DEBUG (0) #include "debug.h" -/** - * @brief Register Map - */ -#define HDC1000_TEMPERATURE 0x00 -#define HDC1000_HUMIDITY 0x01 -#define HDC1000_CONFG 0x02 -#define HDCD1000_SID1 0xFB -#define HDCD1000_SID2 0xFC -#define HDCD1000_SID3 0xFD -#define HDC1000_MANUFACTURER_ID 0xFE -#define HDC1000_DEVICE_ID 0xFF - -/** - * @brief Configuration Register Description - */ -#define HDC1000_CONFG_RST (1 << 15) -#define HDC1000_CONFG_HEAT (1 << 13) -#define HDC1000_CONFG_SEQ_MOD (1 << 12) -#define HDC1000_CONFG_BTST_LOW (1 << 11) -#define HDC1000_CONFG_TRES_MSK (1 << 10) -#define HDC1000_CONFG_TRES_11 (1 << 10) -#define HDC1000_CONFG_TRES_14 0 -#define HDC1000_CONFG_HRES_MSK (1 << 9 | 1 << 8) -#define HDC1000_CONFG_HRES_14 0 -#define HDC1000_CONFG_HRES_11 (1 << 8) -#define HDC1000_CONFG_HRES_8 (1 << 9) - -/** - * @brief Manufacturer and Device ID - */ -#define HDC1000_MID_VALUE 0x5449 -#define HDC1000_DID_VALUE 0x1000 - #define I2C_SPEED I2C_SPEED_FAST -int hdc1000_test(hdc1000_t *dev) +int hdc1000_init(hdc1000_t *dev, const hdc1000_params_t *params) { uint8_t reg[2]; uint16_t tmp; - i2c_acquire(dev->i2c); - if (i2c_read_regs(dev->i2c, dev->addr, HDC1000_MANUFACTURER_ID, reg, 2) != 2) { - i2c_release(dev->i2c); - return -1; + /* write device descriptor */ + memcpy(&dev->p, params, sizeof(hdc1000_params_t)); + + /* initialize the I2C bus */ + i2c_acquire(dev->p.i2c); + if (i2c_init_master(dev->p.i2c, I2C_SPEED) < 0) { + i2c_release(dev->p.i2c); + return HDC1000_NOBUS; + } + + /* try if we can interact with the device by reading its manufacturer ID */ + if (i2c_read_regs(dev->p.i2c, dev->p.addr, + HDC1000_MANUFACTURER_ID, reg, 2) != 2) { + i2c_release(dev->p.i2c); + return HDC1000_NOBUS; } - i2c_release(dev->i2c); tmp = ((uint16_t)reg[0] << 8) | reg[1]; if (tmp != HDC1000_MID_VALUE) { - return -1; + i2c_release(dev->p.i2c); + return HDC1000_NODEV; } - return 0; -} - -int hdc1000_init(hdc1000_t *dev, i2c_t i2c, uint8_t address) -{ - uint8_t reg[2]; - - /* write device descriptor */ - dev->i2c = i2c; - dev->addr = address; - dev->initialized = false; - - i2c_acquire(dev->i2c); - /* initialize the I2C bus */ - if (i2c_init_master(i2c, I2C_SPEED) < 0) { - i2c_release(dev->i2c); - return -1; - } - i2c_release(dev->i2c); - - if (hdc1000_test(dev)) { - return -2; - } - - /* set 14 bit resolution for both sensors and sequence mode */ - uint16_t tmp = HDC1000_CONFG_SEQ_MOD; + /* set resolution for both sensors and sequence mode */ + tmp = (HDC1000_SEQ_MOD | dev->p.res); reg[0] = (tmp >> 8); reg[1] = tmp; - i2c_acquire(dev->i2c); - if (i2c_write_regs(dev->i2c, dev->addr, HDC1000_CONFG, reg, 2) != 2) { - i2c_release(dev->i2c); - return -3; + if (i2c_write_regs(dev->p.i2c, dev->p.addr, HDC1000_CONFIG, reg, 2) != 2) { + i2c_release(dev->p.i2c); + return HDC1000_NOBUS; } - dev->initialized = true; + i2c_release(dev->p.i2c); - i2c_release(dev->i2c); - return 0; + /* all set */ + return HDC1000_OK; } -int hdc1000_reset(hdc1000_t *dev) +void hdc1000_trigger_conversion(hdc1000_t *dev) { - uint8_t reg[2]; - uint16_t tmp = HDC1000_CONFG_RST; - reg[0] = (tmp >> 8); - reg[1] = tmp; - dev->initialized = false; + assert(dev); - i2c_acquire(dev->i2c); - if (i2c_write_regs(dev->i2c, dev->addr, HDC1000_CONFG, reg, 2) != 2) { - i2c_release(dev->i2c); - return -1; - } + i2c_acquire(dev->p.i2c); - i2c_release(dev->i2c); - return 0; -} - -int hdc1000_startmeasure(hdc1000_t *dev) -{ - if (dev->initialized == false) { - return -1; - } - - i2c_acquire(dev->i2c); /* Trigger the measurements by executing a write access * to the address 0x00 (HDC1000_TEMPERATURE). - * Conversion Time is 6.50ms by 14 bit resolution. + * Conversion Time is 6.50ms for each value for 14 bit resolution. */ - if (i2c_write_byte(dev->i2c, dev->addr, HDC1000_TEMPERATURE) != 1) { - i2c_release(dev->i2c); - return -1; - } + i2c_write_byte(dev->p.i2c, dev->p.addr, HDC1000_TEMPERATURE); - i2c_release(dev->i2c); - return 0; + i2c_release(dev->p.i2c); } -int hdc1000_read(hdc1000_t *dev, uint16_t *rawtemp, uint16_t *rawhum) +void hdc1000_get_results(hdc1000_t *dev, int16_t *temp, int16_t *hum) { + assert(dev); + uint8_t buf[4]; + uint16_t traw, hraw; - if (dev->initialized == false) { - return -1; + /* first we read the RAW results from the device */ + i2c_acquire(dev->p.i2c); + i2c_read_bytes(dev->p.i2c, dev->p.addr, buf, 4); + i2c_release(dev->p.i2c); + + /* and finally we convert the values to their physical representation */ + if (temp) { + traw = ((uint16_t)buf[0] << 8) | buf[1]; + *temp = (int16_t)((((int32_t)traw * 16500) >> 16) - 4000); } - - i2c_acquire(dev->i2c); - if (i2c_read_bytes(dev->i2c, dev->addr, buf, 4) != 4) { - i2c_release(dev->i2c); - return -1; + if (hum) { + hraw = ((uint16_t)buf[2] << 8) | buf[3]; + *hum = (int16_t)(((int32_t)hraw * 10000) >> 16); } - /* Register bytes are sent MSB first. */ - *rawtemp = ((uint16_t)buf[0] << 8) | buf[1]; - *rawhum = ((uint16_t)buf[2] << 8) | buf[3]; - - i2c_release(dev->i2c); - return 0; } -void hdc1000_convert(uint16_t rawtemp, uint16_t rawhum, int *temp, int *hum) +void hdc1000_read(hdc1000_t *dev, int16_t *temp, int16_t *hum) { - /* calculate temperature*100 [°C] */ - *temp = (int)((((int32_t)rawtemp * 16500) >> 16) - 4000); - DEBUG("hdc1000 : T: %d\n", *temp); - - /* calculate relative humidity*100 [%RH] */ - *hum = (int)(((int32_t)rawhum * 10000) >> 16); - DEBUG("hdc1000 : RH: %d\n", *hum); + hdc1000_trigger_conversion(dev); + xtimer_usleep(HDC1000_CONVERSION_TIME); + hdc1000_get_results(dev, temp, hum); } diff --git a/drivers/hdc1000/hdc1000_saul.c b/drivers/hdc1000/hdc1000_saul.c new file mode 100644 index 0000000000..691b464d4d --- /dev/null +++ b/drivers/hdc1000/hdc1000_saul.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup driver_hdc1000 + * @{ + * + * @file + * @brief HDC1000 adaption to the RIOT actuator/sensor interface + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "saul.h" +#include "hdc1000.h" + +static int read_temp(void *dev, phydat_t *res) +{ + hdc1000_t *d = (hdc1000_t *)dev; + + hdc1000_read(d, &(res->val[0]), NULL); + memset(&(res->val[1]), 0, 2 * sizeof(int16_t)); + res->unit = UNIT_TEMP_C; + res->scale = -2; + + return 1; +} + +static int read_hum(void *dev, phydat_t *res) +{ + hdc1000_t *d = (hdc1000_t *)dev; + + hdc1000_read(d, NULL, &(res->val[0])); + memset(&(res->val[1]), 0, 2 * sizeof(int16_t)); + res->unit = UNIT_PERCENT; + res->scale = -2; + + return 1; +} + +const saul_driver_t hdc1000_saul_temp_driver = { + .read = read_temp, + .write = saul_notsup, + .type = SAUL_SENSE_TEMP, +}; + +const saul_driver_t hdc1000_saul_hum_driver = { + .read = read_hum, + .write = saul_notsup, + .type = SAUL_SENSE_HUM, +}; diff --git a/drivers/hdc1000/include/hdc1000_params.h b/drivers/hdc1000/include/hdc1000_params.h new file mode 100644 index 0000000000..70904a6d57 --- /dev/null +++ b/drivers/hdc1000/include/hdc1000_params.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_hdc1000 + * + * @{ + * @file + * @brief Default configuration for HDC1000 devices + * + * @author Hauke Petersen + */ + +#ifndef HDC1000_PARAMS_H +#define HDC1000_PARAMS_H + +#include "board.h" +#include "hdc1000.h" +#include "saul_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set default configuration parameters for the HDC1000 driver + * @{ + */ +#ifndef HDC1000_PARAM_I2C +#define HDC1000_PARAM_I2C I2C_DEV(0) +#endif +#ifndef HDC1000_PARAM_ADDR +#define HDC1000_PARAM_ADDR (HDC1000_I2C_ADDRESS) +#endif +#ifndef HDC1000_PARAM_RES +#define HDC1000_PARAM_RES HDC1000_14BIT +#endif + +#define HDC1000_PARAMS_DEFAULT { .i2c = HDC1000_PARAM_I2C, \ + .addr = HDC1000_PARAM_ADDR, \ + .res = HDC1000_PARAM_RES } +/**@}*/ + +/** + * @brief HDC1000 configuration + */ +static const hdc1000_params_t hdc1000_params[] = +{ +#ifdef HDC1000_PARAMS_BOARD + HDC1000_PARAMS_BOARD, +#else + HDC1000_PARAMS_DEFAULT, +#endif +}; + +/** + * @brief Additional meta information to keep in the SAUL registry + */ +static const saul_reg_info_t hdc1000_saul_info[] = +{ + { + .name = "hdc1000", + }, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* HDC1000_PARAMS_H */ +/** @} */ diff --git a/drivers/hdc1000/include/hdc1000_regs.h b/drivers/hdc1000/include/hdc1000_regs.h new file mode 100644 index 0000000000..c6a95b6263 --- /dev/null +++ b/drivers/hdc1000/include/hdc1000_regs.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 PHYTEC Messtechnik GmbH + * 2017 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_hdc1000 + * @{ + * + * @file + * @brief Register definitions for HDC1000 devices + * + * @author Johann Fischer + * @author Hauke Petersen + */ + +#ifndef HDC1000_REGS_H_ +#define HDC1000_REGS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief Manufacturer and Device IDs + * @{ + */ +#define HDC1000_MID_VALUE 0x5449 +#define HDC1000_DID_VALUE 0x1000 +/** @} */ + +/** + * @brief Register Map + * @{ + */ +#define HDC1000_TEMPERATURE (0x00) +#define HDC1000_HUMIDITY (0x01) +#define HDC1000_CONFIG (0x02) +#define HDC1000_SID1 (0xFB) +#define HDC1000_SID2 (0xFC) +#define HDC1000_SID3 (0xFD) +#define HDC1000_MANUFACTURER_ID (0xFE) +#define HDC1000_DEVICE_ID (0xFF) +/** @} */ + +/** + * @brief Configuration register bitmap + * @{ + */ +#define HDC1000_RST (1 << 15) +#define HDC1000_HEAT (1 << 13) +#define HDC1000_SEQ_MOD (1 << 12) +#define HDC1000_BTST_LOW (1 << 11) +#define HDC1000_TRES_MSK (1 << 10) +#define HDC1000_TRES11 (1 << 10) +#define HDC1000_TRES14 (0) +#define HDC1000_HRES_MSK (1 << 9 | 1 << 8) +#define HDC1000_HRES14 (0) +#define HDC1000_HRES11 (1 << 8) +#define HDC1000_HRES8 (1 << 9) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* HDC1000_REGS_H_ */ +/** @} */ diff --git a/drivers/include/hdc1000.h b/drivers/include/hdc1000.h index 69872395bc..340bc81b86 100644 --- a/drivers/include/hdc1000.h +++ b/drivers/include/hdc1000.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 PHYTEC Messtechnik GmbH + * 2017 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -9,126 +10,138 @@ /** * @defgroup drivers_hdc1000 HDC1000 Humidity and Temperature Sensor * @ingroup drivers_sensors - * @brief Driver for the Texas Instruments HDC1000 - * Humidity and Temperature Sensor. - * The driver will initialize the sensor for best - * resolution (14 bit). Currently the driver doesn't use the heater. - * Temperature and humidity are acquired in sequence. - * The sensor is always in sleep mode. The measurement must - * be started by a write access to the address 0x00 - * (HDC1000_TEMPERATURE). After completing the measurement - * the sensor will return to sleep mode. Typical - * Conversion Time by 14 bit resolution is 6.50ms - * for humidity and 6.35ms for temperature. - * HDC1000_CONVERSION_TIME is twice as large to prevent - * the problems with timer resolution. + * @brief Driver for the TI HDC1000 Humidity and Temperature Sensor + * + * The driver will initialize the sensor for best resolution (14 bit). Currently + * the driver doesn't use the heater. Temperature and humidity are acquired in + * sequence. The sensor is always in sleep mode. + * + * The temperature and humidity values can either be acquired using the + * simplified `hdc1000_read()` function, or the conversion can be triggered + * manually using the `hdc1000_trigger_conversion()` and `hdc1000_get_results()` + * functions sequentially. If using the second method, on must wait at least + * `HDC1000_CONVERSION_TIME` between triggering the conversion and reading the + * results. + * + * @note The driver does currently not support using the devices heating + * unit. * * @{ * * @file - * @brief Interface definition for the HDC1000 sensor driver. + * @brief Interface definition for the HDC1000 sensor driver * * @author Johann Fischer + * @author Hauke Petersen */ #ifndef HDC1000_H #define HDC1000_H #include -#include + #include "periph/i2c.h" +#include "hdc1000_regs.h" #ifdef __cplusplus extern "C" { #endif +/** + * @brief Default I2C bus address of HDC1000 devices + */ #ifndef HDC1000_I2C_ADDRESS -#define HDC1000_I2C_ADDRESS 0x43 /**< Default Device Address */ -#endif - -#ifndef HDC1000_CONVERSION_TIME -#define HDC1000_CONVERSION_TIME 26000 /**< Default Conversion Time */ +#define HDC1000_I2C_ADDRESS (0x43) #endif /** - * @brief Device descriptor for HDC1000 sensors. + * @brief Typical conversion time needed to acquire new values [in us] + * + * @note This time value is chosen twice as large as needed for two 14-bit + * conversions (worst case) to allow for timer imprecision: + * (convert temp + convert hum) * 2 -> (6.5ms + 6.5ms) * 2 := 26ms. + */ +#ifndef HDC1000_CONVERSION_TIME +#define HDC1000_CONVERSION_TIME (26000) +#endif + +/** + * @brief HDC1000 specific return values + */ +enum { + HDC1000_OK = 0, /**< everything went as expected */ + HDC1000_NODEV = -1, /**< no HDC1000 device found on the bus */ + HDC1000_NOBUS = -2, /**< errors while initializing the I2C bus */ +}; + +/** + * @brief Possible resolution values + */ +typedef enum { + HDC1000_11BIT = (HDC1000_TRES11 | HDC1000_HRES11), /**< 11-bit conversion */ + HDC1000_14BIT = (HDC1000_TRES14 | HDC1000_HRES14) /**< 14-bit conversion */ +} hdc1000_res_t; + +/** + * @brief Parameters needed for device initialization */ typedef struct { - i2c_t i2c; /**< I2C device the sensor is connected to */ - uint8_t addr; /**< the sensor's slave address on the I2C bus */ - bool initialized; /**< sensor status, true if sensor is initialized */ + i2c_t i2c; /**< bus the device is connected to */ + uint8_t addr; /**< address on that bus */ + hdc1000_res_t res; /**< resolution used for sampling temp and hum */ +} hdc1000_params_t; + +/** + * @brief Device descriptor for HDC1000 sensors + */ +typedef struct { + hdc1000_params_t p; /**< Configuration parameters */ } hdc1000_t; /** - * @brief HDC1000 sensor test. - * This function looks for Manufacturer ID of the HDC1000 sensor. - * - * @param[in] dev device descriptor of sensor - * - * @return 0 on success - * @return -1 on error - */ -int hdc1000_test(hdc1000_t *dev); - -/** - * @brief Initialise the HDC1000 sensor driver. - * 14 bit resolution, heater off, temperature and humidity - * are acquired in sequence. + * @brief Initialize the given HDC1000 device * * @param[out] dev device descriptor of sensor to initialize - * @param[in] i2c I2C bus the sensor is connected to - * @param[in] address sensor's I2C slave address + * @param[in] params configuration parameters * - * @return 0 on success - * @return -1 if initialization of I2C bus failed - * @return -2 if sensor test failed - * @return -3 if sensor configuration failed + * @return HDC1000_OK on success + * @return HDC1000_NOBUS if initialization of I2C bus fails + * @return HDC1000_NODEV if no HDC1000 device found on bus */ -int hdc1000_init(hdc1000_t *dev, i2c_t i2c, uint8_t address); +int hdc1000_init(hdc1000_t *dev, const hdc1000_params_t *params); /** - * @brief Reset the HDC1000 sensor. After that sensor should be reinitialized. + * @brief Trigger a new conversion * - * @param[out] dev device descriptor of sensor to reset - * - * @return 0 on success - * @return -1 on error - */ -int hdc1000_reset(hdc1000_t *dev); - -/** - * @brief Trigger the measurements. - * Conversion Time by 14 bit resolution is 6.50ms. + * After the conversion is triggered, one has to wait + * @ref HDC1000_CONVERSION_TIME us until the results can be read using + * @ref hdc1000_reg_results(). * * @param[in] dev device descriptor of sensor - * - * @return 0 on success - * @return -1 on error */ -int hdc1000_startmeasure(hdc1000_t *dev); +void hdc1000_trigger_conversion(hdc1000_t *dev); /** - * @brief Read sensor's data. + * @brief Read conversion results for temperature and humidity * * @param[in] dev device descriptor of sensor - * @param[out] rawtemp raw temperature value - * @param[out] rawhum raw humidity value - * - * @return 0 on success - * @return -1 on error + * @param[out] temp temperature [in 100 * degree centigrade] + * @param[out] hum humidity [in 100 * percent relative] */ -int hdc1000_read(hdc1000_t *dev, uint16_t *rawtemp, uint16_t *rawhum); +void hdc1000_get_results(hdc1000_t *dev, int16_t *temp, int16_t *hum); /** - * @brief Convert raw sensor values to temperature and humidity. + * @brief Convenience function for reading temperature and humidity * - * @param[in] rawtemp raw temperature value - * @param[in] rawhum raw humidity value - * @param[out] temp converted temperature*100 - * @param[out] hum converted humidity*100 + * This function will trigger a new conversion, wait for the conversion to be + * finished and the get the results from the device. + * + * @param[in] dev device descriptor of sensor + * @param[out] temp temperature [in 100 * degree centigrade] + * @param[out] hum humidity [in 100 * percent relative] */ -void hdc1000_convert(uint16_t rawtemp, uint16_t rawhum, int *temp, int *hum); +void hdc1000_read(hdc1000_t *dev, int16_t *temp, int16_t *hum); #ifdef __cplusplus }