diff --git a/boards/common/msb-430/Makefile b/boards/common/msb-430/Makefile index baa55d873151f46154b83eaf906cf985093e8d3b..4695a0f76122f2071ad699558c56d5db8c4cc60a 100644 --- a/boards/common/msb-430/Makefile +++ b/boards/common/msb-430/Makefile @@ -1,5 +1,3 @@ MODULE = boards_common_msb-430 -DIRS = drivers - include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msb-430/Makefile.include b/boards/common/msb-430/Makefile.include index 3125ba07156dde89ca3ac1024abab859dc231b86..5c054c4c66a56b51993fd9b50f34c582d588156a 100644 --- a/boards/common/msb-430/Makefile.include +++ b/boards/common/msb-430/Makefile.include @@ -25,6 +25,3 @@ export DEBUGGER_FLAGS = --tui --ex="target remote localhost:2000" --ex "monitor # export common msb-430 includes export INCLUDES += -I$(RIOTBOARD)/common/msb-430/include -export INCLUDES += -I$(RIOTBOARD)/common/msb-430/drivers/include - -USEMODULE += boards_common_msb-430-drivers diff --git a/boards/common/msb-430/drivers/Makefile b/boards/common/msb-430/drivers/Makefile deleted file mode 100644 index ed11f0ae340a4b762ad012b7380fdc0f9e5aaecf..0000000000000000000000000000000000000000 --- a/boards/common/msb-430/drivers/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -MODULE = boards_common_msb-430-drivers - -include $(RIOTBOARD)/$(BOARD)/Makefile.include - -include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msb-430/drivers/include/sht1x-board.h b/boards/common/msb-430/drivers/include/sht1x-board.h deleted file mode 100644 index 873504d47c92fa511c7eb611de1f8a4b0800941d..0000000000000000000000000000000000000000 --- a/boards/common/msb-430/drivers/include/sht1x-board.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2013, Freie Universitaet Berlin (FUB). All rights reserved. - * - * 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. - */ - -#ifndef SHT1X_BOARD_H -#define SHT1X_BOARD_H - -/** - * @ingroup boards_common_msb-430 - * @{ - */ - -/** - * @file - * @brief SHT11 Device Driver Configuration For MSB-430 Platform - * - * @author Freie Universität Berlin, Computer Systems & Telematics, RIOT - * - */ -#include <msp430x16x.h> -#include "bitarithm.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* SCK = P3B5 - * DATA = P3B4 - */ - -#define SHT1X_SCK_LOW P3OUT &= ~(BIT5); /**< serial clock line low */ -#define SHT1X_SCK_HIGH P3OUT |= BIT5; /**< serial clock line high */ -#define SHT1X_DATA (P3IN & BIT5) /**< read serial I/O */ -#define SHT1X_DATA_LOW P3OUT &= ~(BIT5); /**< serial I/O line low */ -#define SHT1X_DATA_HIGH P3OUT |= BIT5; /**< serial I/O line high */ -#define SHT1X_DATA_IN P3DIR &= ~(BIT5); /**< serial I/O as input */ -#define SHT1X_DATA_OUT P3DIR |= BIT5; /**< serial I/O as output */ -#define SHT1X_INIT P3DIR |= BIT5; /* FIO1DIR |= BIT25; PINSEL3 &= ~(BIT14|BIT15 | BIT16|BIT17); */ - -#ifdef __cplusplus -} -#endif - -/** @} */ -#endif /* SHT1X_BOARD_H */ diff --git a/boards/common/msba2/Makefile b/boards/common/msba2/Makefile index a8c0997289fe79230595a9ce38d8fe672455deb8..ccf58a08443576884bb286094d185f5f49707ff5 100644 --- a/boards/common/msba2/Makefile +++ b/boards/common/msba2/Makefile @@ -1,5 +1,3 @@ MODULE = boards_common_msba2 -DIRS = drivers - include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msba2/Makefile.include b/boards/common/msba2/Makefile.include index 5ba3b3d01cf3a541777712afb7d7229d2298ef75..00260c06232bfe698b32669e1ecf753e1c35a622 100644 --- a/boards/common/msba2/Makefile.include +++ b/boards/common/msba2/Makefile.include @@ -27,8 +27,5 @@ endif export FFLAGS = $(PORT) $(HEXFILE) INCLUDES += -I$(RIOTBOARD)/common/msba2/include -INCLUDES += -I$(RIOTBOARD)/common/msba2/drivers/include export UNDEF += $(BINDIR)/cpu/startup.o - -USEMODULE += boards_common_msba2-drivers diff --git a/boards/common/msba2/drivers/Makefile b/boards/common/msba2/drivers/Makefile deleted file mode 100644 index 26dd1f4f5da09469500439c62212c5d5f9aa905b..0000000000000000000000000000000000000000 --- a/boards/common/msba2/drivers/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -MODULE = boards_common_msba2-drivers - -include $(RIOTBASE)/Makefile.base diff --git a/boards/common/msba2/drivers/include/sht1x-board.h b/boards/common/msba2/drivers/include/sht1x-board.h deleted file mode 100644 index a70589b9ab7c2e80caf32a48de00315e30c9cdeb..0000000000000000000000000000000000000000 --- a/boards/common/msba2/drivers/include/sht1x-board.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2009, Freie Universitaet Berlin (FUB). All rights reserved. - * - * 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. - */ - -#ifndef SHT1X_BOARD_H -#define SHT1X_BOARD_H - -/** - * @ingroup boards_common_msba2 - * @{ - */ - -/** - * @file - * @brief LPC2387 SHT11 Device Driver - * - * @author Freie Universität Berlin, Computer Systems & Telematics, FeuerWhere project - * @version $Revision$ - * - * @note $Id$ - */ - -#include "cpu.h" -#include "board.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* serial clock line low */ -#define SHT1X_SCK_LOW FIO1CLR = BIT25; -/* serial clock line high */ -#define SHT1X_SCK_HIGH FIO1SET = BIT25; -/* read serial I/O */ -#define SHT1X_DATA ((FIO1PIN & BIT26) != 0) -/* serial I/O line low */ -#define SHT1X_DATA_LOW (FIO1CLR = BIT26); -/* serial I/O line high */ -#define SHT1X_DATA_HIGH (FIO1SET = BIT26); -/* serial I/O as input */ -#define SHT1X_DATA_IN (FIO1DIR &= ~BIT26) -/* serial I/O as output */ -#define SHT1X_DATA_OUT (FIO1DIR |= BIT26) - -#define SHT1X_INIT FIO1DIR |= BIT25; PINSEL3 &= ~(BIT14|BIT15 | BIT16|BIT17); - -#ifdef __cplusplus -} -#endif - -/** @} */ -#endif /* SHT1X_BOARD_H */ diff --git a/boards/msb-430/Makefile.dep b/boards/msb-430/Makefile.dep new file mode 100644 index 0000000000000000000000000000000000000000..f5124575543f30d05365553fb31a71cf64b4ab33 --- /dev/null +++ b/boards/msb-430/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += sht11 +endif diff --git a/boards/msb-430/include/board.h b/boards/msb-430/include/board.h index a053c327f338d41cdb072833b4845f9a31e44820..7baa71f1aa0a16d481f5c8df2cc139a2c1a06732 100644 --- a/boards/msb-430/include/board.h +++ b/boards/msb-430/include/board.h @@ -59,6 +59,14 @@ extern "C" { #define MSP430_HAS_EXTERNAL_CRYSTAL 0 /** @} */ +/** + * @name Configure on-board SHT11 device + * @{ + */ +#define SHT1X_PARAM_CLK (GPIO_PIN(3, 5)) +#define SHT1X_PARAM_DATA (GPIO_PIN(3, 4)) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/msb-430h/Makefile.dep b/boards/msb-430h/Makefile.dep new file mode 100644 index 0000000000000000000000000000000000000000..f5124575543f30d05365553fb31a71cf64b4ab33 --- /dev/null +++ b/boards/msb-430h/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += sht11 +endif diff --git a/boards/msb-430h/include/board.h b/boards/msb-430h/include/board.h index 8fc284faa1f3e9e1463c13ccc82d6f55edd99058..ae744f1b5f0432a32c9a636daea553f06a5ba5d3 100644 --- a/boards/msb-430h/include/board.h +++ b/boards/msb-430h/include/board.h @@ -47,6 +47,14 @@ extern "C" { #define MSP430_HAS_EXTERNAL_CRYSTAL 1 /** @} */ +/** + * @name Configure on-board SHT11 device + * @{ + */ +#define SHT1X_PARAM_CLK (GPIO_PIN(3, 5)) +#define SHT1X_PARAM_DATA (GPIO_PIN(3, 4)) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/msba2/Makefile.dep b/boards/msba2/Makefile.dep index e5d94f2d25850c062976f10d5a38ff3fca618243..d7230f86c2db4c22efab874deb9b8f70a1c0c7f2 100644 --- a/boards/msba2/Makefile.dep +++ b/boards/msba2/Makefile.dep @@ -3,3 +3,7 @@ include $(RIOTBOARD)/common/msba2/Makefile.dep ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) USEMODULE += cc110x endif + +ifneq (,$(filter saul_default,$(USEMODULE))) + USEMODULE += sht11 +endif diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 4a95122c303a9035400815ed9c3eb62feb76981b..75e0bdb4d4fd4fc4076b4f3cd00cd577759ec578 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -304,6 +304,7 @@ endif ifneq (,$(filter sht1%,$(USEMODULE))) USEMODULE += sht1x + FEATURES_REQUIRED += periph_gpio USEMODULE += xtimer endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 8c7a1c44826fa5fa58ee8c033687976a779dcd45..3832016dd3104ff888186d2d841d36be70f4a92c 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -50,6 +50,10 @@ ifneq (,$(filter dht,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/dht/include endif +ifneq (,$(filter sht1x,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sht1x/include +endif + ifneq (,$(filter ds1307,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ds1307/include endif diff --git a/drivers/include/sht1x.h b/drivers/include/sht1x.h index 447aa74a1f48b67610b2d94bf3683bf028d974ec..b8c7bd87197323e29e0f5728f1d48490e744e6e2 100644 --- a/drivers/include/sht1x.h +++ b/drivers/include/sht1x.h @@ -1,5 +1,6 @@ /* - * Copyright 2009, Freie Universitaet Berlin (FUB). All rights reserved. + * Copyright 2009 Freie Universitaet Berlin (FUB) + * 2018 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -16,90 +17,168 @@ * @file * @brief SHT10/SHT11/SHT15 Device Driver * - * @author Freie Universität Berlin, Computer Systems & Telematics + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> */ #ifndef SHT1X_H #define SHT1X_H #include <stdint.h> +#include <periph/gpio.h> #ifdef __cplusplus extern "C" { #endif -#define SHT1X_NO_ACK (0) /**< don't ack read in `read_byte` */ -#define SHT1X_ACK (1) /**< do acknowledge read in `read_byte` */ -/* adr command r/w */ -#define SHT1X_STATUS_REG_W (0x06) /**< will write to status register */ -#define SHT1X_STATUS_REG_R (0x07) /**< will read from status register */ -#define SHT1X_MEASURE_TEMP (0x03) /**< tell sensor to measure temperature */ -#define SHT1X_MEASURE_HUMI (0x05) /**< tell sensor to measure humidity */ -#define SHT1X_RESET (0x1E) /**< reset the sensor */ +/** + * @brief Possible configuration (=status byte) values of the SHT10/11/15 + * + * These values can be or'ed together to get the configuration. + */ +typedef enum { + /** Use 8/12 bit resolution instead of 12/14 bit for temp/hum */ + SHT1X_CONF_LOW_RESOLUTION = 0x01, + /** Don't upload calibration data to register to safe 10 millisec */ + SHT1X_CONF_SKIP_CALIBRATION = 0x02, + /** Waste 8mA at 5V to increase the sensor temperature up to 10°C */ + SHT1X_CONF_ENABLE_HEATER = 0x04, + /** Skip the CRC check (and reading the CRC byte) to safe time */ + SHT1X_CONF_SKIP_CRC = 0x08, +} sht1x_conf_t; -/** time to wait after toggling the data line */ -#define SHT1X_DATA_WAIT (1) -/** time to wait after toggling the clock line */ -#define SHT1X_CLK_WAIT (1) +/** + * @brief Possible values for Vdd (measured temperature depends on it) + */ +typedef enum { + SHT1X_VDD_5_0V = 0, + SHT1X_VDD_4_0V = 1, + SHT1X_VDD_3_5V = 2, + SHT1X_VDD_3_0V = 3, + SHT1X_VDD_2_5V = 4, +} sht1x_vdd_t; -/** set measurement timeout to 1 second */ -#define SHT1X_MEASURE_TIMEOUT (1000) +/** + * @brief SHT10/11/15 temperature humidity sensor + */ +typedef struct { + gpio_t clk; /**< GPIO connected to the clock pin of the SHT1X */ + gpio_t data; /**< GPIO connected to the data pin of the SHT1X */ + int16_t temp_off; /**< Offset to add to the measured temperature */ + int16_t hum_off; /**< Offset to add to the measured humidity */ + uint8_t conf; /**< Status byte (containing configuration) of the SHT1X */ + uint8_t vdd; /**< Supply voltage of the SHT1X (as sht1x_vdd_t) */ +} sht1x_dev_t; /** - * @brief sht11 measureable data + * @brief Parameters required to set up the SHT10/11/15 device driver */ typedef struct { - float temperature; /**< temperature value */ - float relhum; /**< linear relative humidity */ - float relhum_temp; /**< temperature compensated relative humidity */ -} sht1x_val_t; + gpio_t clk; /**< GPIO connected to the clock pin of the SHT1X */ + gpio_t data; /**< GPIO connected to the data pin of the SHT1X */ + sht1x_vdd_t vdd; /**< The supply voltage of the SHT1X */ +} sht1x_params_t; /** - * @brief SHT11 modes that can be measured + * @brief Initialize the SHT10/11/15 sensor + * + * @param dev SHT1X sensor to initialize + * @param params Information on how the SHT1X is connected to the board + * + * @retval 0 Success + * @retval -EIO IO failure (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge reset command */ -typedef enum { - TEMPERATURE = 1, - HUMIDITY = 2 -} sht1x_mode_t; +int sht1x_init(sht1x_dev_t *dev, const sht1x_params_t *params); /** - * @brief Initialize SHT11 ports + * @brief Calculate the temperature from the raw input + * @note This internal function is exposed for unit tests + * + * @param dev Device from which the raw value was received + * @param raw The raw (unprocessed) temperature value + * + * @return The correct temperature in E-02 °C + * @retval INT16_MIN Passed `NULL` for parameter `dev` or `dev->vdd` + */ +int16_t sht1x_temperature(const sht1x_dev_t *dev, uint16_t raw); + +/** + * @brief Calculate the relative humidity from the raw input + * @note This internal function is exposed for unit tests + * + * @param dev Device from which the raw value was received + * @param raw The raw (unprocessed) temperature value + * @param temp The temperature at which the humidity was measure in + * E-02 °C + * + * @return The correct temperature in E-02 % + * @retval -1 Passed `NULL` for parameter `dev` */ -void sht1x_init(void); +int16_t sht1x_humidity(const sht1x_dev_t *dev, uint16_t raw, int16_t temp); /** - * @brief Read sensor + * @brief Read the current temperature + * + * @param dev SHT1X sensor to read + * @param temp Store the measured temperature in E-02 °C here + * @param hum Store the measured relative humidity in E-02 % here + * + * @retval 0 Success + * @retval -EIO IO failure (`gpio_init()` failed) + * @retval -EBADMSG CRC-8 checksum didn't match (--> Retry) + * @retval -EINVAL Passed `NULL` for dev or for both `temp` and `hum` + * @retval -EBADMSG CRC checksum didn't match + * @retval -ECANCELED Measurement timed out + * @retval -EPROTO Sensor did not acknowledge command * - * @param value The struct to be filled with measured values - * @param mode Specifies type of data to be read + * For either `temp` or `hum` `NULL` can be passed, if only one value is of + * interest. Passing `NULL` for `hum` speeds up the communication, but + * passing `NULL` for `temp` does not. The temperature value is required to + * calculate the relative humidity from the raw input. So the temperature is + * measured in any case, it is just not returned if `temp` is `NULL`. + */ +int sht1x_read(const sht1x_dev_t *dev, int16_t *temp, int16_t *hum); + +/** + * @brief Apply the given configuration (= status byte) to * - * @return 1 on success, 0 otherwise + * @param dev SHT1X device to configure + * @param conf Configuration to apply * - * Example: - * \code sht1x_val sht11; - * sht1x_read_sensor(&sht11, HUMIDITY|TEMPERATURE); - * printf("%-6.2f °C %5.2f %% %5.2f %%\n", sht11.temperature, sht11.relhum, sht11.relhum_temp); \endcode + * @retval 0 Configuration applied + * @retval -EINVAL Called with `dev == NULL` + * @retval -EIO I/O error (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge command + * @retval -ECANCELED Sensor did not apply configuration + * @retval -EBADMSG CRC checksum error while verifying uploaded configuration */ -uint8_t sht1x_read_sensor(sht1x_val_t *value, sht1x_mode_t mode); +int sht1x_configure(sht1x_dev_t *dev, sht1x_conf_t conf); /** - * @brief Write status register + * @brief Read the status byte of an SHT1X sensor * - * @param p_value The value to write + * @param dev SHT1X device to receive the status from + * @param status Store the received status byte here * - * @return 1 on success, 0 otherwise + * @retval 0 Configuration applied + * @retval -EINVAL Called with `dev == NULL` + * @retval -EIO I/O error (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge command + * @retval -EBADMSG CRC checksum didn't match */ -uint8_t sht1x_write_status(uint8_t *p_value); +int sht1x_read_status(sht1x_dev_t *dev, uint8_t *status); /** - * @brief Read status register with checksum + * @brief Reset the sensor's configuration to default values * - * @param p_value The read value - * @param p_checksum The received checksum + * @param dev SHT1X device to reset * - * return 1 on success, 0 otherwise + * @retval 0 Reset successful + * @retval -EINVAL Called with `dev == NULL` + * @retval -EIO I/O error (`gpio_init()` failed) + * @retval -EPROTO Sensor did not acknowledge reset command */ -uint8_t sht1x_read_status(uint8_t *p_value, uint8_t *p_checksum); +int sht1x_reset(sht1x_dev_t *dev); #ifdef __cplusplus } diff --git a/drivers/sht1x/include/sht1x_defines.h b/drivers/sht1x/include/sht1x_defines.h new file mode 100644 index 0000000000000000000000000000000000000000..0327387ab08d78cedf0c80f87eb46ea52b5c8514 --- /dev/null +++ b/drivers/sht1x/include/sht1x_defines.h @@ -0,0 +1,72 @@ +/* + * Copyright 2009 Freie Universitaet Berlin (FUB) + * 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_sht1x + * @{ + * + * @file + * @brief Internal defines required by the SHT10/SHT11/SHT15 driver + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + */ + +#ifndef SHT1X_DEFINES_H +#define SHT1X_DEFINES_H + +#include <stdint.h> +#include <periph/gpio.h> +#include <mutex.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Possible values to pass as `ack` parameter to `write_byte` + * @see write_byte + * @{ + */ +#define SHT1X_NO_ACK (1) /**< don't ack read in `read_byte` */ +#define SHT1X_ACK (0) /**< do acknowledge read in `read_byte` */ +/** @} */ + +/** + * @name Commands that can be sent to the SHT1X driver + * @{ + */ +#define SHT1X_STATUS_REG_W (0x06) /**< will write to status register */ +#define SHT1X_STATUS_REG_R (0x07) /**< will read from status register */ +#define SHT1X_MEASURE_TEMP (0x03) /**< tell sensor to measure temperature */ +#define SHT1X_MEASURE_HUM (0x05) /**< tell sensor to measure humidity */ +#define SHT1X_RESET (0x1E) /**< reset the sensor */ +/** @} */ + +/** + * @name Timing parameters for the SHT10/SHT1X/SHT15 + * @{ + */ +#define SHT1X_HALF_CLOCK (1) /**< Half clock length in µsec */ +#define SHT1X_MEASURE_TIMEOUT (1000) /**< Timeout for the SHT1x to complete + the measurement (in millisec) */ +#define SHT1X_RESET_WAIT (11000) /**< Wait 11ms after soft reset */ +/** @} */ + +#define SHT1X_CONF_MASK (0x07) /**< Bitmask to get writable bits of the + status byte */ +#define SHT1X_SAUL_RETRIES (3) /**< How often reading the sensor should + be retried in case of communication + failures */ + +#ifdef __cplusplus +} +#endif + +#endif /* SHT1X_DEFINES_H */ +/** @} */ diff --git a/drivers/sht1x/include/sht1x_params.h b/drivers/sht1x/include/sht1x_params.h new file mode 100644 index 0000000000000000000000000000000000000000..f7cd54f8a71f989753957d51e5b625001d0f8436 --- /dev/null +++ b/drivers/sht1x/include/sht1x_params.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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_sht1x + * + * @{ + * @file + * @brief Default configuration for SHT10/SHT11/SHT15 devices + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + */ + +#ifndef SHT1X_PARAMS_H +#define SHT1X_PARAMS_H + +#include "board.h" +#include "sht1x.h" +#include "saul_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Set default configuration parameters for the SHT1X devices + * @{ + */ +#ifndef SHT1X_PARAM_CLK +#define SHT1X_PARAM_CLK (GPIO_PIN(1, 25)) +#endif +#ifndef SHT1X_PARAM_DATA +#define SHT1X_PARAM_DATA (GPIO_PIN(1, 26)) +#endif +#ifndef SHT1X_PARAM_VDD +#define SHT1X_PARAM_VDD (SHT1X_VDD_3_5V) +#endif +#ifndef SHT1X_PARAMS +#define SHT1X_PARAMS { .clk = SHT1X_PARAM_CLK, \ + .data = SHT1X_PARAM_DATA, \ + .vdd = SHT1X_PARAM_VDD } +#endif +/**@}*/ + +/** + * @brief Configure SHT1X devices + */ +static const sht1x_params_t sht1x_params[] = +{ + SHT1X_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SHT1X_PARAMS_H */ +/** @} */ diff --git a/drivers/sht1x/sht1x.c b/drivers/sht1x/sht1x.c index c48d03a177e6c832c1832a0ef5162a1398b2b6f2..f2a55cc0139e168001c18a47e374fca56ad69558 100644 --- a/drivers/sht1x/sht1x.c +++ b/drivers/sht1x/sht1x.c @@ -1,5 +1,6 @@ /* - * Copyright 2009, Freie Universitaet Berlin (FUB). All rights reserved. + * Copyright 2009 Freie Universitaet Berlin (FUB) + * 2018 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -8,343 +9,686 @@ /** * @ingroup drivers_sht1x - * @brief Driver for the Sensirion SHT11 humidity and temperature sensor + * @brief Driver for the Sensirion SHT10/SHT11/SHT15 humidity and + * temperature sensor * @{ * * @file - * @brief SHT11 Device Driver + * @brief SHT10/SHT11/SHT15 Device Driver + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> * - * @version $Revision: 2396 $ - * - * @note $Id: sht1x.c 2396 2010-07-06 15:12:35Z ziegert $ * @} */ - -#include <stdio.h> +#include <errno.h> #include <stdint.h> #include "xtimer.h" -#include "mutex.h" #include "sht1x.h" -#include "sht1x-board.h" +#include "sht1x_defines.h" #include "bitarithm.h" -float sht1x_temperature_offset; +#define ENABLE_DEBUG (0) +#include "debug.h" /** - * @brief Perform measurement + * @brief Perform measurement * - * @param p_value Measured value (14 or 12 bit -> 2 bytes) - * @param p_checksum Checksum of measurement - * @param mode The requestested measurement mode: temperature or humidity + * @param dev SHT1X device to use + * @param value Measured value + * @param mode The requested measurement mode: temperature or humidity * - * @return 1 on success, 0 otherwise + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) + * @retval -EBADMSG CRC-8 checksum didn't match + * @retval -EPROTO SHT1x did not acknowledge command + * @retval -ECANCELED Measurement timed out */ -static uint8_t measure(uint8_t *p_value, uint8_t *p_checksum, uint8_t mode); +static int measure(const sht1x_dev_t *dev, uint16_t *value, uint8_t mode); /** - * @brief Write one byte + * @brief Write one byte * - * @param value The value to write + * @param dev SHT1X device to send the byte to + * @param value The value to write * - * @return 1 for acknowledged write, 0 otherwise + * @retval 1 Write was acknowledged + * @retval 0 Write was *NOT* acknowledged (communication failure) + * @retval -EIO I/O failure (`gpio_init()` failed) */ -static uint8_t write_byte(uint8_t value); +static int write_byte(const sht1x_dev_t *dev, uint8_t value); /** - * @brief Read ony byte + * @brief Read one byte * - * @param ack Set if the data read should be acknowledged + * @param dev SHT1X device to receive the byte from + * @param dest Store the received byte here + * @param ack `SHT1X_ACK` to acknowledge byte, `SHT1X_NO_ACK` otherwise * - * @return The read byte + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) */ -static uint8_t read_byte(uint8_t ack); +static int read_byte(const sht1x_dev_t *dev, uint8_t *dest, int ack); /** - * @brief Communication reset + * @brief Communication reset + * + * @param dev SHT1X device to reset the connection to + * + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) */ -static void connection_reset(void); +static int connection_reset(const sht1x_dev_t *dev); /** - * @brief Send start of transmision sequence + * @brief Send start of transmission sequence + * + * @param dev SHT1X device to send the transmission start sequence to + * + * @retval 0 Success + * @retval -EIO I/O failure (`gpio_init()` failed) */ -static void transmission_start(void); +static int transmission_start(const sht1x_dev_t *dev); /** - * @brief Toggle the clock line + * @brief Toggle the clock line + * + * @param dev SHT1X device to send one clock signal to */ -static inline void clk_signal(void); +static inline void clk_signal(const sht1x_dev_t *dev); -/* mutex for exclusive measurement operation */ -mutex_t sht1x_mutex = MUTEX_INIT; +/** + * @brief Calculate the initial value of the CRC-8 checksum + * + * @param status The current sensor status + * + * @return The initial value of the CRC-8 checksum + */ +static inline uint8_t crc_initial_value(uint8_t status); + +/** + * @brief Reverse the order of bits in a byte (needed for CRC) + * + * @param value The byte to reverse the bits of + * + * @return The reversed input + */ +static inline uint8_t reverse_byte(uint8_t value); + +/** + * @brief Look up table required for CRC-8 calculation + * + * Values taken from the Application Note PDF of Sensirion (December 2011) + */ +static const uint8_t crc_lookup_table[] = { + 0x00, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, + 0xb9, 0x88, 0xdb, 0xea, 0x7d, 0x4c, 0x1f, 0x2e, + 0x43, 0x72, 0x21, 0x10, 0x87, 0xb6, 0xe5, 0xd4, + 0xfa, 0xcb, 0x98, 0xa9, 0x3e, 0x0f, 0x5c, 0x6d, + 0x86, 0xb7, 0xe4, 0xd5, 0x42, 0x73, 0x20, 0x11, + 0x3f, 0x0e, 0x5d, 0x6c, 0xfb, 0xca, 0x99, 0xa8, + 0xc5, 0xf4, 0xa7, 0x96, 0x01, 0x30, 0x63, 0x52, + 0x7c, 0x4d, 0x1e, 0x2f, 0xb8, 0x89, 0xda, 0xeb, + 0x3d, 0x0c, 0x5f, 0x6e, 0xf9, 0xc8, 0x9b, 0xaa, + 0x84, 0xb5, 0xe6, 0xd7, 0x40, 0x71, 0x22, 0x13, + 0x7e, 0x4f, 0x1c, 0x2d, 0xba, 0x8b, 0xd8, 0xe9, + 0xc7, 0xf6, 0xa5, 0x94, 0x03, 0x32, 0x61, 0x50, + 0xbb, 0x8a, 0xd9, 0xe8, 0x7f, 0x4e, 0x1d, 0x2c, + 0x02, 0x33, 0x60, 0x51, 0xc6, 0xf7, 0xa4, 0x95, + 0xf8, 0xc9, 0x9a, 0xab, 0x3c, 0x0d, 0x5e, 0x6f, + 0x41, 0x70, 0x23, 0x12, 0x85, 0xb4, 0xe7, 0xd6, + 0x7a, 0x4b, 0x18, 0x29, 0xbe, 0x8f, 0xdc, 0xed, + 0xc3, 0xf2, 0xa1, 0x90, 0x07, 0x36, 0x65, 0x54, + 0x39, 0x08, 0x5b, 0x6a, 0xfd, 0xcc, 0x9f, 0xae, + 0x80, 0xb1, 0xe2, 0xd3, 0x44, 0x75, 0x26, 0x17, + 0xfc, 0xcd, 0x9e, 0xaf, 0x38, 0x09, 0x5a, 0x6b, + 0x45, 0x74, 0x27, 0x16, 0x81, 0xb0, 0xe3, 0xd2, + 0xbf, 0x8e, 0xdd, 0xec, 0x7b, 0x4a, 0x19, 0x28, + 0x06, 0x37, 0x64, 0x55, 0xc2, 0xf3, 0xa0, 0x91, + 0x47, 0x76, 0x25, 0x14, 0x83, 0xb2, 0xe1, 0xd0, + 0xfe, 0xcf, 0x9c, 0xad, 0x3a, 0x0b, 0x58, 0x69, + 0x04, 0x35, 0x66, 0x57, 0xc0, 0xf1, 0xa2, 0x93, + 0xbd, 0x8c, 0xdf, 0xee, 0x79, 0x48, 0x1b, 0x2a, + 0xc1, 0xf0, 0xa3, 0x92, 0x05, 0x34, 0x67, 0x56, + 0x78, 0x49, 0x1a, 0x2b, 0xbc, 0x8d, 0xde, 0xef, + 0x82, 0xb3, 0xe0, 0xd1, 0x46, 0x77, 0x24, 0x15, + 0x3b, 0x0a, 0x59, 0x68, 0xff, 0xce, 0x9d, 0xac, +}; + +/** @brief Lookuptable for d1 parameter depending on supply voltage */ +static const int16_t sht1x_d1[] = { -4010, -3980, -3970, -3960, -3940 }; /*---------------------------------------------------------------------------*/ -static inline void clk_signal(void) +static inline void clk_signal(const sht1x_dev_t *dev) { - SHT1X_SCK_HIGH; - xtimer_usleep(SHT1X_CLK_WAIT); - SHT1X_SCK_LOW; - xtimer_usleep(SHT1X_CLK_WAIT); + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); } /*---------------------------------------------------------------------------*/ -static uint8_t write_byte(uint8_t value) +static int write_byte(const sht1x_dev_t *dev, uint8_t value) { - uint8_t i; - uint8_t ack; + int ack; - SHT1X_DATA_OUT; + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; + } - /* send value bit by bit to sht11 */ - for (i = 0; i < 8; i++) { + /* send value bit by bit to sht1x */ + for (int i = 0; i < 8; i++) { if (value & BIT7) { - SHT1X_DATA_HIGH; - xtimer_usleep(SHT1X_DATA_WAIT); + gpio_set(dev->data); } else { - SHT1X_DATA_LOW; - xtimer_usleep(SHT1X_DATA_WAIT); + gpio_clear(dev->data); } + xtimer_usleep(SHT1X_HALF_CLOCK); /* trigger clock signal */ - clk_signal(); + clk_signal(dev); /* shift value to write next bit */ - value = value << 1; + value <<= 1; } /* wait for ack */ - SHT1X_DATA_IN; - xtimer_usleep(SHT1X_CLK_WAIT); - ack = SHT1X_DATA; + if (gpio_init(dev->data, GPIO_IN) == -1) { + return -EIO; + } + xtimer_usleep(SHT1X_HALF_CLOCK); + ack = gpio_read(dev->data); - clk_signal(); + clk_signal(dev); return ack; } + /*---------------------------------------------------------------------------*/ -static uint8_t read_byte(uint8_t ack) +static int read_byte(const sht1x_dev_t *dev, uint8_t *dest, int ack) { - uint8_t i; uint8_t value = 0; - SHT1X_DATA_IN; - xtimer_usleep(SHT1X_DATA_WAIT); + xtimer_usleep(SHT1X_HALF_CLOCK); /* read value bit by bit */ - for (i = 0; i < 8; i++) { - value = value << 1; - SHT1X_SCK_HIGH; - xtimer_usleep(SHT1X_CLK_WAIT); - - if (SHT1X_DATA) { - /* increase data by one when DATA is high */ - value++; + for (int i = 0; i < 8; i++) { + value <<= 1; + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + if (gpio_read(dev->data)) { + /* set bit when DATA is high */ + value |= 0x01; } - SHT1X_SCK_LOW; - xtimer_usleep(SHT1X_CLK_WAIT); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); } /* send ack if necessary */ - SHT1X_DATA_OUT; - - if (ack) { - SHT1X_DATA_LOW; - xtimer_usleep(SHT1X_DATA_WAIT); - } - else { - SHT1X_DATA_HIGH; - xtimer_usleep(SHT1X_DATA_WAIT); + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; } - clk_signal(); + gpio_write(dev->data, ack); + xtimer_usleep(SHT1X_HALF_CLOCK); + + clk_signal(dev); /* release data line */ - SHT1X_DATA_IN; + if (gpio_init(dev->data, GPIO_IN) == -1) { + return -EIO; + } + + *dest = value; - return value; + return 0; } + /*---------------------------------------------------------------------------*/ -static void transmission_start(void) +static int transmission_start(const sht1x_dev_t *dev) { /* _____ ________ DATA: |_______| ___ ___ SCK : ___| |___| |______ - */ - SHT1X_DATA_OUT; + */ + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; + } /* set initial state */ - SHT1X_DATA_HIGH; - xtimer_usleep(SHT1X_DATA_WAIT); - SHT1X_SCK_LOW; - xtimer_usleep(SHT1X_CLK_WAIT); + gpio_set(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); + + gpio_clear(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); - SHT1X_SCK_HIGH; - xtimer_usleep(SHT1X_CLK_WAIT); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); - SHT1X_DATA_LOW; - xtimer_usleep(SHT1X_DATA_WAIT); + gpio_set(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); - SHT1X_SCK_LOW; - xtimer_usleep(SHT1X_CLK_WAIT); + gpio_set(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); - SHT1X_SCK_HIGH; - xtimer_usleep(SHT1X_CLK_WAIT); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); - SHT1X_DATA_HIGH; - xtimer_usleep(SHT1X_DATA_WAIT); + if (gpio_init(dev->data, GPIO_IN) == -1) { + return -EIO; + } - SHT1X_SCK_LOW; - xtimer_usleep(SHT1X_CLK_WAIT); + return 0; } + /*---------------------------------------------------------------------------*/ -static void connection_reset(void) +static int connection_reset(const sht1x_dev_t *dev) { /* _____________________________________________________ ____ DATA: |_______| _ _ _ _ _ _ _ _ _ ___ ___ SCK : __| |__| |__| |__| |__| |__| |__| |__| |__| |______| |___| |__ - */ - uint8_t i; - SHT1X_DATA_HIGH; - xtimer_usleep(SHT1X_DATA_WAIT); - SHT1X_SCK_LOW; - xtimer_usleep(SHT1X_CLK_WAIT); + */ + if (gpio_init(dev->data, GPIO_OUT) == -1) { + return -EIO; + } + + gpio_set(dev->data); + xtimer_usleep(SHT1X_HALF_CLOCK); + gpio_clear(dev->clk); + xtimer_usleep(SHT1X_HALF_CLOCK); - for (i = 0; i < 9; i++) { - clk_signal(); + for (int i = 0; i < 9; i++) { + clk_signal(dev); } - transmission_start(); + return transmission_start(dev); } + /*---------------------------------------------------------------------------*/ -static uint8_t measure(uint8_t *p_value, uint8_t *p_checksum, uint8_t mode) +static inline uint8_t crc_initial_value(uint8_t status) { - uint8_t error = 0; - uint8_t ack = 1; - uint16_t i; + status &= 0x07; - transmission_start(); - error = write_byte(mode); + return ( + ((0x01 & status) << 7) | + ((0x02 & status) << 5) | + ((0x04 & status) << 3) + ); +} - xtimer_usleep(1000); +/*---------------------------------------------------------------------------*/ +static inline uint8_t reverse_byte(uint8_t value) +{ + uint8_t result = (value & 0x01) << 7; - /* wait untile sensor has finished measurement or timeout */ - for (i = 0; (i < SHT1X_MEASURE_TIMEOUT) && (!error); i++) { - ack = SHT1X_DATA; + result |= (value & 0x02) << 5; + result |= (value & 0x04) << 3; + result |= (value & 0x08) << 1; + result |= (value & 0x10) >> 1; + result |= (value & 0x20) >> 3; + result |= (value & 0x40) >> 5; + result |= (value & 0x80) >> 7; - if (!ack) { + return result; +} + +/*---------------------------------------------------------------------------*/ +static int measure(const sht1x_dev_t *dev, uint16_t *value, uint8_t mode) +{ + uint8_t data[2] = { 0, 0 }; + int retval; + + retval = transmission_start(dev); + if (retval != 0) { + return retval; + } + + switch (write_byte(dev, mode)) { + case -EIO: + return -EIO; + case 0: break; + default: + case 1: + return -EPROTO; + } + + /* wait until sensor has finished measurement or timeout */ + { + int ack = 1; + for (int i = 0; ack != 0; i++) { + if (i > SHT1X_MEASURE_TIMEOUT) { + return -ECANCELED; + } + + xtimer_usleep(1000); + ack = gpio_read(dev->data); } + } - xtimer_usleep(1000); + /* read MSB */ + retval = read_byte(dev, &data[0], SHT1X_ACK); + if (retval != 0) { + return retval; } - error += ack; + /* read LSB, send ACK only if CRC checking is enabled */ + retval = (dev->conf & SHT1X_CONF_SKIP_CRC) ? SHT1X_NO_ACK : SHT1X_ACK; + retval = read_byte(dev, &data[1], retval); + if (retval != 0) { + return retval; + } - /* read MSB */ - *(p_value + 1) = read_byte(SHT1X_ACK); - /* read LSB */ - *(p_value) = read_byte(SHT1X_ACK); - /* read checksum */ - *p_checksum = read_byte(SHT1X_NO_ACK); + if (!(dev->conf & SHT1X_CONF_SKIP_CRC)) { + uint8_t crc; + uint8_t expected; + + retval = read_byte(dev, &crc, SHT1X_NO_ACK); + if (retval != 0) { + return retval; + } + + expected = crc_initial_value(dev->conf); + expected = crc_lookup_table[expected ^ mode]; + expected = crc_lookup_table[expected ^ data[0]]; + expected = crc_lookup_table[expected ^ data[1]]; + expected = reverse_byte(expected); + if (expected != crc) { + DEBUG("[sht1x] CRC expected: 0x%02x, got: 0x%02x\n" + " CRC0: 0x%02x, CMD: 0x%02x, data: {0x%02x, 0x%02x}\n", + (int)expected, (int)crc, + (int)crc_initial_value(dev->conf), mode, + (int)data[0], (int)data[1]); + return -EBADMSG; + } + } - return (!error); + *value = (((uint16_t)data[0]) << 8) | (uint16_t)data[1]; + return 0; } + /*---------------------------------------------------------------------------*/ -void sht1x_init(void) +int sht1x_init(sht1x_dev_t *dev, const sht1x_params_t *params) { - sht1x_temperature_offset = 0; - SHT1X_INIT; - xtimer_usleep(11 * 1000); + if ( + !dev || + !params || + (((uint8_t)params->vdd) >= sizeof(sht1x_d1) / sizeof(sht1x_d1[0])) + ) { + return -EINVAL; + } + + dev->clk = params->clk; + dev->data = params->data; + if (gpio_init(dev->clk, GPIO_OUT) || gpio_init(dev->data, GPIO_IN)) { + return -EIO; + } + + dev->temp_off = 0; + dev->hum_off = 0; + dev->conf = 0; + dev->vdd = (uint8_t)params->vdd; + return sht1x_reset(dev); } + /*---------------------------------------------------------------------------*/ -uint8_t sht1x_read_status(uint8_t *p_value, uint8_t *p_checksum) +int16_t sht1x_temperature(const sht1x_dev_t *dev, uint16_t raw) { - uint8_t error = 0; + if (!dev || (dev->vdd >= sizeof(sht1x_d1) / sizeof(sht1x_d1[0]))) { + return INT16_MIN; + } - transmission_start(); - error |= write_byte(SHT1X_STATUS_REG_R); - *p_value = read_byte(SHT1X_ACK); - *p_checksum = read_byte(SHT1X_NO_ACK); - return (!error); + int16_t d1 = sht1x_d1[dev->vdd]; + int16_t d2 = (dev->conf & SHT1X_CONF_LOW_RESOLUTION) ? 4 : 1; + return d1 + d2 * ((int16_t)raw); } + /*---------------------------------------------------------------------------*/ -uint8_t sht1x_write_status(uint8_t *p_value) +int16_t sht1x_humidity(const sht1x_dev_t *dev, uint16_t raw, int16_t temp) { - uint8_t error = 0; + if (!dev) { + return -1; + } + + static const int32_t c1 = -20468; + static const int32_t t1 = 1; + int32_t c2, c3, c4, t2; + if (dev->conf & SHT1X_CONF_LOW_RESOLUTION) { + c2 = 5872; + c3 = 494801; + c4 = 1000000; + t2 = 781; + } + else { + c2 = 367; + c3 = 791684; + c4 = 100000; + t2 = 12500; + } + + /* + * Calculate linear humidity, but slightly different. Original formula: + * + * hum_lin = c1 + c2 * raw + c3 * (raw * raw) + * + * But we use: + * + * hum_lin = c1 + c2 * raw - (c4 * raw / c3') * (c4 * raw / c3') + * + * where: c3' = 1 / (sqrt(-c3) / c4) + * + * (This better fits for integer calculation) + */ + + int32_t res = ((int32_t)raw * c4) / c3; + res = c1 + c2 * (int32_t)raw - (res * res); + + /* + * Perform temperature compensation, again slightly different. + * Original formula: + * + * hum_true = (temp - 25) * (t1 + t2 * raw) + hum_lin + * + * But we use: + * + * hum_true = (temp - 25) * t1 + (temp - 25) * raw / t2') + hum_lin + * + * where t2' = 1/t2 + */ + int32_t temp_diff = temp - 2500; + res = temp_diff * t1 + (temp_diff * (int32_t)raw * 100) / t2 + res; + return (int16_t)(res / 100); +} + + +/*---------------------------------------------------------------------------*/ +int sht1x_read(const sht1x_dev_t *dev, int16_t *temp, int16_t *rel_hum) +{ + uint16_t temp_raw; + int16_t t; + uint16_t hum_raw; + int retval; + + if ( + !dev || + (dev->vdd >= sizeof(sht1x_d1) / sizeof(sht1x_d1[0])) || + (!temp && !rel_hum) + ) { + return -EINVAL; + } + + retval = measure(dev, &temp_raw, SHT1X_MEASURE_TEMP); + if (retval != 0) { + connection_reset(dev); + return retval; + } + + t = sht1x_temperature(dev, temp_raw) + dev->temp_off; + + if (temp != NULL) { + *temp = t; + } + + if (rel_hum != NULL) { + retval = measure(dev, &hum_raw, SHT1X_MEASURE_HUM); + if (retval != 0) { + connection_reset(dev); + return retval; + } - transmission_start(); - error += write_byte(SHT1X_STATUS_REG_W); - error += write_byte(*p_value); - return (!error); + *rel_hum = sht1x_humidity(dev, hum_raw, t) + dev->hum_off; + } + + return 0; } + /*---------------------------------------------------------------------------*/ -uint8_t sht1x_read_sensor(sht1x_val_t *value, sht1x_mode_t mode) +int sht1x_configure(sht1x_dev_t *dev, sht1x_conf_t conf) { - uint8_t error = 0; - uint8_t checksum; - uint16_t humi_int, temp_int; + if (!dev) { + return -EINVAL; + } - /* Temperature arithmetic where S0(T) is read value - * T = D1 + D2 * S0(T) */ - const float D1 = -39.6; - const float D2 = 0.01; + /* Apply config that is not stored on the sensor */ + dev->conf &= SHT1X_CONF_MASK; + dev->conf |= conf & (~(SHT1X_CONF_MASK)); - /* Arithmetic for linear humdity where S0(RH) is read value - * HL = C1 + C2 * S0(RH) + C3 * SO(RH)^2 */ - const float C1 = -4.0; - const float C2 = +0.0405; - const float C3 = -0.0000028; + /* Send new status byte to sensor, if on-device config was changed */ + if ((conf & SHT1X_CONF_MASK) != (dev->conf & SHT1X_CONF_MASK)) { + int retval = transmission_start(dev); + if (retval != 0) { + return retval; + } + + switch (write_byte(dev, SHT1X_STATUS_REG_W)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } - /* Arithmetic for temperature compesated relative humdity - * HT = (T-25) * ( T1 + T2 * SO(RH) ) + HL */ - const float T1 = +0.01; - const float T2 = +0.00008; + switch (write_byte(dev, conf & SHT1X_CONF_MASK)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } - /* check for valid buffer */ - if (value == NULL) { - return 0; + /* Read back uploaded configuration to verify that sensor applied it */ + uint8_t status; + retval = sht1x_read_status(dev, &status); + if (retval != 0) { + return retval; + } + if (dev->conf != conf) { + /* Configuration was not applied by sensor */ + return -ECANCELED; + } } - value->temperature = 0; - value->relhum = 0; - value->relhum_temp = 0; + return 0; +} - mutex_lock(&sht1x_mutex); - connection_reset(); +/*---------------------------------------------------------------------------*/ +int sht1x_read_status(sht1x_dev_t *dev, uint8_t *status) +{ + int retval; - /* measure humidity */ - if (mode & HUMIDITY) { - error += (!measure((uint8_t *) &humi_int, &checksum, SHT1X_MEASURE_HUMI)); + if (!dev || !status) { + return -EINVAL; } - /* measure temperature */ - if (mode & TEMPERATURE) { - error += (!measure((uint8_t *) &temp_int, &checksum, SHT1X_MEASURE_TEMP)); + retval = transmission_start(dev); + if (retval != 0) { + return retval; } - /* break on error */ - if (error != 0) { - connection_reset(); - mutex_unlock(&sht1x_mutex); - return 0; + switch (write_byte(dev, SHT1X_STATUS_REG_R)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; } - if (mode & TEMPERATURE) { - value->temperature = D1 + (D2 * ((float) temp_int)) + sht1x_temperature_offset; + retval = read_byte(dev, status, SHT1X_ACK); + if (retval != 0) { + return retval; } - if (mode & HUMIDITY) { - value->relhum = C1 + (C2 * ((float) humi_int)) + (C3 * ((float) humi_int) * ((float) humi_int)); + { + uint8_t crc; + uint8_t expected; + /* read checksum */ + retval = read_byte(dev, &crc, SHT1X_NO_ACK); + if (retval != 0) { + return retval; + } - if (mode & TEMPERATURE) { - value->relhum_temp = (value->temperature - 25) * (T1 + (T2 * (float) humi_int)) + value->relhum; + expected = crc_initial_value(*status); + expected = crc_lookup_table[expected ^ SHT1X_STATUS_REG_R]; + expected = crc_lookup_table[expected ^ *status]; + expected = reverse_byte(expected); + if (expected != crc) { + DEBUG("[sht1x] CRC expected: 0x%02x, got: 0x%02x\n" + " CRC0: 0x%02x, CMD: 0x%02x, data: {0x%02x}\n", + (int)expected, (int)crc, + (int)crc_initial_value(*status), SHT1X_STATUS_REG_R, + (int)*status); + return -EBADMSG; } } - mutex_unlock(&sht1x_mutex); - return 1; + /* Extract config from status and store it after CRC check passed */ + dev->conf &= ~(SHT1X_CONF_MASK); + dev->conf |= *status & SHT1X_CONF_MASK; + + return 0; +} + +/*---------------------------------------------------------------------------*/ +int sht1x_reset(sht1x_dev_t *dev) +{ + int retval; + + if (!dev) { + return -EINVAL; + } + + retval = transmission_start(dev); + if (retval != 0) { + return retval; + } + + switch (write_byte(dev, SHT1X_RESET)) { + case -EIO: + return -EIO; + case 0: + break; + default: + case 1: + return -EPROTO; + } + + dev->conf = 0; + xtimer_usleep(SHT1X_RESET_WAIT); + + return 0; } diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 617a0735235962e8a7fb5a165ff40cc3dbbcfb35..bdfda5229b5e2bb849cf60231556600b83f51d96 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -20,10 +20,6 @@ #include "auto_init.h" -#ifdef MODULE_SHT1X -#include "sht1x.h" -#endif - #ifdef MODULE_MCI #include "diskio.h" #endif @@ -101,10 +97,6 @@ void auto_init(void) DEBUG("Auto init xtimer module.\n"); xtimer_init(); #endif -#ifdef MODULE_SHT1X - DEBUG("Auto init SHT1X module.\n"); - sht1x_init(); -#endif #ifdef MODULE_MCI DEBUG("Auto init mci module.\n"); mci_initialize(); @@ -275,6 +267,12 @@ void auto_init(void) #endif /* initialize sensors and actuators */ +#ifdef MODULE_SHT1X + DEBUG("Auto init SHT1X module (SHT10/SHT11/SHT15 sensor driver).\n"); + extern void auto_init_sht1x(void); + auto_init_sht1x(); +#endif + #ifdef MODULE_AUTO_INIT_SAUL DEBUG("auto_init SAUL\n"); diff --git a/sys/auto_init/auto_init_sht1x.c b/sys/auto_init/auto_init_sht1x.c new file mode 100644 index 0000000000000000000000000000000000000000..49d9e6f5c9c0d1341e138d1aaf576039d3806fd7 --- /dev/null +++ b/sys/auto_init/auto_init_sht1x.c @@ -0,0 +1,74 @@ +/* + * Copyright 2018 Otto-von-Guericke-Universität Magdeburg + * + * 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 sys_auto_init + * @{ + * + * @file + * @brief Auto initialization for SHT1X temperature/humidity sensors + * + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> + * + * @} + */ + +#ifdef MODULE_SHT1X + +#include "log.h" +#include "sht1x_params.h" +#include "sht1x.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief Define the number of configured sensors + */ +#define SHT1X_NUM (sizeof(sht1x_params) / sizeof(sht1x_params[0])) + +/** + * @brief Allocate memory for the device descriptors + */ +sht1x_dev_t sht1x_devs[SHT1X_NUM]; + +static void sht1x_error(unsigned int num, const char *reason) +{ + LOG_ERROR("[auto_init] error initializing SHT10/SHT11/SHT15 sensor " + "#%u: %s\n", num, reason); +} + +void auto_init_sht1x(void) +{ + for (unsigned int i = 0; i < SHT1X_NUM; i++) { + DEBUG("[auto_init_sht1x] Initializing SHT1X sensor #%u\n", i); + + switch (sht1x_init(&sht1x_devs[i], &sht1x_params[i])) { + case 0: + break; + case -EIO: + sht1x_error(i, "Failed to initialize GPIOs"); + continue; + case -EINVAL: + sht1x_error(i, "Invalid configuration for VDD"); + continue; + case -EPROTO: + sht1x_error(i, "Reset command not acknowledged"); + continue; + default: + /* Should not happen, but better safe than sorry */ + sht1x_error(i, "?"); + continue; + } + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_SHT1X */ diff --git a/sys/shell/commands/sc_sht1x.c b/sys/shell/commands/sc_sht1x.c index 2a379def5cce0afc2addf3ac1c2fbdd40fe4a10e..c814ccff484d7afb4a89ece2bfaa535377657b53 100644 --- a/sys/shell/commands/sc_sht1x.c +++ b/sys/shell/commands/sc_sht1x.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2013 INRIA. + * Copyright (C) 2013 INRIA + * 2018 Otto-von-Guericke-Universität Magdeburg * * 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 @@ -11,102 +12,367 @@ * @{ * * @file - * @brief Provides shell commands to poll sht11 sensor + * @brief Provides shell commands to access SHT10/SHT11/SHT15 sensors * * @author Oliver Hahm <oliver.hahm@inria.fr> + * @author Marian Buschsieweke <marian.buschsieweke@ovgu.de> * * @} */ +#ifdef MODULE_SHT1X + #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include "sht1x.h" +#include "sht1x_params.h" -#ifdef MODULE_SHT1X +#define SHT1X_NUM (sizeof(sht1x_params) / sizeof(sht1x_params[0])) -extern float sht1x_temperature_offset; +extern sht1x_dev_t sht1x_devs[SHT1X_NUM]; -int _get_humidity_handler(int argc, char **argv) +static sht1x_dev_t *get_dev(int argc, char **argv) { - (void) argc; - (void) argv; + switch (argc) { + case 1: + return &sht1x_devs[0]; + case 2: + { + int pos = atoi(argv[1]); + if ((pos < 0) || (pos >= (int)SHT1X_NUM)) { + printf("No SHT10/SHT11/SHT15 device with number %i\n", pos); + return NULL; + } + return &sht1x_devs[pos]; + } + default: + break; + } + + printf("Usage: %s [DEVICE_NUMBER]\n", argv[0]); + return NULL; +} - uint8_t success; - sht1x_val_t sht1x_val; - success = sht1x_read_sensor(&sht1x_val, HUMIDITY | TEMPERATURE); +static void error_msg(const char *msg) +{ + printf("[sht1x] Operation failed: %s\n", msg); +} - if (!success) { - printf("Error reading SHT11\n"); +static int read_sensor(int16_t *temp, int16_t *hum, int argc, char **argv) +{ + const sht1x_dev_t *dev = get_dev(argc, argv); - return 1; + if (!dev) { + return -1; } - else { - printf("Relative humidity: %5.2f%% / Temperature compensated humidity; %5.2f%%\n", - (double) sht1x_val.relhum, (double) sht1x_val.relhum_temp); - return 0; + switch (sht1x_read(dev, temp, hum)) { + case 0: + break; + case -EIO: + error_msg("gpio_init() failed"); + return -1; + case -EBADMSG: + error_msg("CRC checksum error"); + return -1; + case -ECANCELED: + error_msg("Measurement timed out"); + return -1; + case -EPROTO: + error_msg("Sensor did not acknowledge command"); + return -1; + default: + /* Should never happen, but better safe the sorry */ + error_msg("Unknown error"); + return -1; } + + return 0; } -int _get_temperature_handler(int argc, char **argv) +int _get_humidity_handler(int argc, char **argv) { - (void) argc; - (void) argv; + int16_t hum; - uint8_t success; - sht1x_val_t sht1x_val; - success = sht1x_read_sensor(&sht1x_val, TEMPERATURE); + if (read_sensor(NULL, &hum, argc, argv)) { + return -1; + } - if (!success) { - printf("Error reading SHT11\n"); + printf("Relative humidity: %i.%02i%%\n", (int)hum / 100, (int)hum % 100); + return 0; +} - return 1; - } - else { - printf("Temperature: %-6.2f°C\n", (double) sht1x_val.temperature); +int _get_temperature_handler(int argc, char **argv) +{ + int16_t temp; - return 0; + if (read_sensor(&temp, NULL, argc, argv)) { + return -1; } + + printf("Temperature: %i.%02i°C\n", (int)temp / 100, (int)temp % 100); + return 0; } int _get_weather_handler(int argc, char **argv) { - (void) argc; - (void) argv; + int16_t hum; + int16_t temp; + + if (read_sensor(&temp, &hum, argc, argv)) { + return -1; + } + + printf("Relative humidity: %i.%02i%%\n", (int)hum / 100, (int)hum % 100); + printf("Temperature: %i.%02i°C\n", (int)temp / 100, (int)temp % 100); + return 0; +} + +static void print_config(const sht1x_dev_t *dev) +{ + const char *vdds[] = { "5.0", "4.0", "3.5", "3.0", "2.5" }; - uint8_t success; - sht1x_val_t sht1x_val; - success = sht1x_read_sensor(&sht1x_val, HUMIDITY | TEMPERATURE); + printf("Sensor VDD = %s\n", vdds[dev->vdd]); + printf("Temperature offset [-t]: %i\n", (int)dev->temp_off); + printf("Humidity offset [-h]: %i\n", (int)dev->hum_off); + printf("Resolution [-r]: %s\n", + (dev->conf & SHT1X_CONF_LOW_RESOLUTION) ? "low" : "high"); + printf("Skip calibration (faster reading) [-c]: %s\n", + (dev->conf & SHT1X_CONF_SKIP_CALIBRATION) ? "yes" : "no"); + printf("Heater [-H]: %s\n", + (dev->conf & SHT1X_CONF_ENABLE_HEATER) ? "on" : "off"); + printf("CRC checking [-C]: %s\n", + (dev->conf & SHT1X_CONF_SKIP_CRC) ? "off" : "on"); +} - if (!success) { - printf("Error reading SHT11\n"); +static void unknown_parameter(int index, char **argv) +{ + printf("Unknown parameter \"%s\"\n" + "Usage: \"%s [PARAMS]\", run \"%s --help\" for help\n", + argv[index], argv[0], argv[0]); +} - return 1; - } - else { - printf("Relative humidity: %5.2f%% / Temperature compensated humidity; %5.2f%% ", - (double) sht1x_val.relhum, (double) sht1x_val.relhum_temp); - printf("Temperature: %-6.2f°C\n", (double) sht1x_val.temperature); +static void missing_argument(int index, char **argv) +{ + printf("Missing argument for parameter \"%s\"\n" + "Usage: \"%s [%s <ARG>][PARAMS]\", run \"%s --help\" for help\n", + argv[index], argv[0], argv[index], argv[0]); +} - return 0; - } +static void invalid_argument(int index, char **argv, const char *valid) +{ + printf("Invalid argument \"%s\" for parameter \"%s\"\n" + "Valid arguments are: \"%s\", run \"%s --help\" for help\n", + argv[index + 1], argv[index], valid, argv[0]); } -int _set_offset_handler(int argc, char **argv) +int _sht_config_handler(int argc, char **argv) { - if (argc != 2) { - printf("Usage: %s <OFFSET>\n", argv[0]); + uint8_t set_conf = 0; + uint8_t unset_conf = 0; + int16_t temp_off = INT16_MAX; + int16_t hum_off = INT16_MAX; + int dev_num = 0; - return 1; + if ((argc == 2) && (strcmp("--help", argv[1]) == 0)) { + printf("Usage: \"%s [PARMS]\n" + "\n" + "Supported parameters:\n" + " -d <NUM>\n" + " Use SHT10/11/15 sensor number <NUM>. Default: 0\n" + "\n" + " -t <OFFSET>\n" + " Add <OFFSET> (in e-2°C) to all temperature measurements\n" + "\n" + " -h <OFFSET>\n" + " Add <OFFSET> (in e-2%%) to all humidity measurements\n" + "\n" + " -r l/h\n" + " Set resolution to low/high. Low resolution trades " + "presicion for speed\n" + "\n" + " -H y/n\n" + " Turns heater on/off. Can increase temperature by up to " + "10°C\n" + "\n" + " -C y/n\n" + " Turns on/off CRC checking. No checking trades robustness " + "for speed\n", + argv[0]); + return 0; } - else { - sht1x_temperature_offset = atoi(argv[1]); - printf("Temperature offset set to %f\n", (double) sht1x_temperature_offset); - return 0; + for (int i = 1; i < argc; i++) { + if ((argv[i][0] != '-') || (!argv[i][1]) || (argv[i][2])) { + unknown_parameter(i, argv); + return -1; + } + + switch (argv[i][1]) { + case 'd': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + dev_num = atoi(argv[i]); + if ((dev_num < 0) || (dev_num >= (int)SHT1X_NUM)) { + printf("No SHT10/11/15 sensor with number %i\n", dev_num); + return -1; + } + break; + case 't': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + temp_off = (int16_t)atoi(argv[i]); + break; + case 'h': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + hum_off = (int16_t)atoi(argv[i]); + break; + case 'r': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "l, h"); + return -1; + } + switch (argv[i][0]) { + case 'l': + set_conf |= SHT1X_CONF_LOW_RESOLUTION; + break; + case 'h': + unset_conf |= SHT1X_CONF_LOW_RESOLUTION; + break; + default: + invalid_argument(i - 1, argv, "l, h"); + return -1; + } + break; + case 'c': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + switch (argv[i][0]) { + case 'y': + set_conf |= SHT1X_CONF_SKIP_CALIBRATION; + break; + case 'n': + unset_conf |= SHT1X_CONF_SKIP_CALIBRATION; + break; + default: + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + break; + case 'H': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + switch (argv[i][0]) { + case 'y': + set_conf |= SHT1X_CONF_ENABLE_HEATER; + break; + case 'n': + unset_conf |= SHT1X_CONF_ENABLE_HEATER; + break; + default: + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + break; + case 'C': + if (++i >= argc) { + missing_argument(i - 1, argv); + return -1; + } + if ((!argv[i][0]) || (argv[i][1])) { + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + switch (argv[i][0]) { + case 'y': + unset_conf |= SHT1X_CONF_SKIP_CRC; + break; + case 'n': + set_conf |= SHT1X_CONF_SKIP_CRC; + break; + default: + invalid_argument(i - 1, argv, "y, n"); + return -1; + } + break; + default: + unknown_parameter(i, argv); + return -1; + } + } + + if ((set_conf) || (unset_conf)) { + /* Apply new configuration */ + uint8_t new_conf = sht1x_devs[dev_num].conf; + new_conf &= ~(unset_conf); + new_conf |= set_conf; + switch (sht1x_configure(&sht1x_devs[dev_num], new_conf)) { + case 0: + break; + case -EIO: + error_msg("gpio_init() failed"); + return -1; + case -EBADMSG: + error_msg("CRC checksum error"); + return -1; + case -ECANCELED: + error_msg("Sensor did not apply configuration"); + return -1; + case -EPROTO: + error_msg("Sensor did not acknowledge command"); + return -1; + default: + /* Should never happen, but better safe the sorry */ + error_msg("Unknown error"); + return -1; + } } + + if (temp_off != INT16_MAX) { + if ((temp_off > 2000) || (temp_off < -2000)) { + printf("A temperature offset of %i.%02i°C is unreasonable\n", + (int)temp_off / 100, (int)temp_off % 100); + return -1; + } + sht1x_devs[dev_num].temp_off = temp_off; + } + + if (hum_off != INT16_MAX) { + if ((hum_off > 1000) || (hum_off < -1000)) { + printf("A humidity offset of %i.%02i%% is unreasonable\n", + (int)hum_off / 100, (int)hum_off % 100); + return -1; + } + sht1x_devs[dev_num].hum_off = hum_off; + } + + print_config(&sht1x_devs[dev_num]); + return 0; } #endif diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index ce73861323c392790bcfdc716cac1a4010ec570a..a9871517d3e64da7b5cdbd5642db37827d1f3e27 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -41,7 +41,7 @@ extern int _ps_handler(int argc, char **argv); extern int _get_temperature_handler(int argc, char **argv); extern int _get_humidity_handler(int argc, char **argv); extern int _get_weather_handler(int argc, char **argv); -extern int _set_offset_handler(int argc, char **argv); +extern int _sht_config_handler(int argc, char **argv); #endif #ifdef MODULE_LTC4150 @@ -157,7 +157,7 @@ const shell_command_t _shell_command_list[] = { {"temp", "Prints measured temperature.", _get_temperature_handler}, {"hum", "Prints measured humidity.", _get_humidity_handler}, {"weather", "Prints measured humidity and temperature.", _get_weather_handler}, - {"offset", "Set temperature offset.", _set_offset_handler}, + {"sht-config", "Get/set SHT10/11/15 sensor configuration.", _sht_config_handler}, #endif #ifdef MODULE_LTC4150 {"cur", "Prints current and average power consumption.", _get_current_handler},