diff --git a/drivers/include/si70xx.h b/drivers/include/si70xx.h new file mode 100644 index 0000000000000000000000000000000000000000..fd372c99bb9c8325322f88f158be1a28868dda79 --- /dev/null +++ b/drivers/include/si70xx.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016 Bas Stottelaar <basstottelaar@gmail.com> + * + * 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. + */ + +/** + * @defgroup driver_si70xx Si70xx + * @ingroup drivers + * @brief Driver for the Si7006/13/20/21 temperature and humidity sensor. + * @{ + * + * @file + * @brief Interface definition of the Si70xx driver. + * + * @author Bas Stottelaar <basstottelaar@gmail.com> + */ + +#ifndef SI70XX_H_ +#define SI70XX_H_ + +#include <stdint.h> + +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Si70xx chip addresses. + */ +#define SI70XX_ADDRESS_SI7006 (0x80) +#define SI70XX_ADDRESS_SI7013 (0x80) +#define SI70XX_ADDRESS_SI7013_ALT (0x81) +#define SI70XX_ADDRESS_SI7020 (0x80) +#define SI70XX_ADDRESS_SI7021 (0x80) + +/** + * @name Si70xx device commands + * @{ + */ +#define SI70XX_MEASURE_RH_HOLD (0xE5) +#define SI70XX_MEASURE_RH (0xF5) +#define SI70XX_MEASURE_TEMP_HOLD (0xE3) +#define SI70XX_MEASURE_TEMP (0xF3) +#define SI70XX_MEASURE_TEMP_PREV (0xE0) +#define SI70XX_RESET (0xFE) +#define SI70XX_WRITE_USER_REG (0xE6) +#define SI70XX_READ_USER_REG (0xE7) +#define SI70XX_WRITE_HEATER_REG (0x51) +#define SI70XX_READ_HEATER_REG (0x11) +#define SI70XX_READ_ID_FIRST_A (0xFA) +#define SI70XX_READ_ID_FIRST_B (0x0F) +#define SI70XX_READ_ID_SECOND_A (0xFC) +#define SI70XX_READ_ID_SECOND_B (0xC9) +#define SI70XX_READ_REVISION_A (0x84) +#define SI70XX_READ_REVISION_B (0xB8) +/** @} */ + +/** + * @name Si70xx register values + * @{ + */ +#define SI70XX_ID_SI7006 (0x06) +#define SI70XX_ID_SI7013 (0x0D) +#define SI70XX_ID_SI7020 (0x14) +#define SI70XX_ID_SI7021 (0x15) + +#define SI70XX_REVISION_1 (0xFF) +#define SI70XX_REVISION_2 (0x20) +/** @} */ + +/** + * @brief Si70xx device descriptor + */ +typedef struct { + i2c_t i2c_dev; /**< I2C bus the sensors is connected to */ + uint8_t address; /**< sensor address */ +} si70xx_t; + +/** + * @brief Test if the device id and revision number are as expected. + * + * @param[in] dev device descriptor + * @return zero on succesful test + * @return non-zero on unsuccesfull test. + */ +int si70xx_test(si70xx_t *dev); + +/** + * @brief Initialize and reset the sensor. + * + * @param[in] dev device descriptor + * @param[in] i2c_dev i2c device to use + * @param[in] address device address (depends on the chip) + * @return zero on succesful initialization. + * @return non-zero on error + */ +int si70xx_init(si70xx_t *dev, i2c_t i2c_dev, uint8_t address); + +/** + * @brief Read the relative humidity from the sensor. Uses clock streching. + * + * @param[in] dev device descriptor + * @return relative humidity in centi-percent (times 100) + */ +uint16_t si70xx_get_relative_humidity(si70xx_t *dev); + +/** + * @brief Read the current temperature from the sensor. Uses clock streching. + * + * @param[in] dev device descriptor + * @return current temperature in centi-degrees Celsius + * (times 100) + */ +int16_t si70xx_get_temperature(si70xx_t *dev); + +/** + * @brief Read the relative humidity and temperature from the sensor. Uses + * clock stretching. + * + * @param[in] dev device descriptor + * @param[out] humidity pointer to relative humidity (in centi-percent) + * @param[out] temperature pointer to temperature (in centi-degrees Celsius) + */ +void si70xx_get_both(si70xx_t *dev, uint16_t *humidity, int16_t *temperature); + +/** + * @brief Read the sensor serial number. + * + * @param[in] dev device descriptor + * @return sensor serial number + */ +uint64_t si70xx_get_serial(si70xx_t *dev); + +/** + * @brief Read the sensor id, to identifier the sensor variant. + * + * @param[in] dev device descriptor + * @return device id + */ +uint8_t si70xx_get_id(si70xx_t *dev); + +/** + * @brief Read the firmware revision of the sensor. + * + * @param[in] dev device descriptor + * @return sensor firmware revision number + */ +uint8_t si70xx_get_revision(si70xx_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* SI70XX_H_ */ +/** @} */ diff --git a/drivers/si70xx/Makefile b/drivers/si70xx/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..37b98330ad63b9ff22d79804728e6361f59b570d --- /dev/null +++ b/drivers/si70xx/Makefile @@ -0,0 +1,3 @@ +MODULE = si70xx + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/si70xx/si70xx.c b/drivers/si70xx/si70xx.c new file mode 100644 index 0000000000000000000000000000000000000000..75aef063ec2fbc08a2b2a90c717fdc0e1ff78012 --- /dev/null +++ b/drivers/si70xx/si70xx.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2016 Bas Stottelaar <basstottelaar@gmail.com> + * + * 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_si70xx + * @{ + * + * @file + * @brief Implementation of Si7006/13/20/21 sensor driver. + * + * @author Bas Stottelaar <basstottelaar@gmail.com> + * + * @} + */ + + +#include "si70xx.h" + +#include "xtimer.h" + +/** + * @brief Utility method to perform and reconstruct a measurement. + */ +static uint32_t si70xx_measure(si70xx_t *dev, uint8_t command) +{ + uint8_t result[2]; + + i2c_acquire(dev->i2c_dev); + i2c_write_byte(dev->i2c_dev, dev->address, command); + i2c_read_bytes(dev->i2c_dev, dev->address, (char *) result, 2); + i2c_release(dev->i2c_dev); + + /* reconstruct raw result */ + return (result[0] << 8) + (result[1] & 0xfc); +} + +int si70xx_test(si70xx_t *dev) +{ + uint8_t revision = si70xx_get_revision(dev); + + if (revision != SI70XX_REVISION_1 && revision != SI70XX_REVISION_2) { + return -1; + } + + uint8_t id = si70xx_get_id(dev); + + if (id != SI70XX_ID_SI7006 && id != SI70XX_ID_SI7013 && + id != SI70XX_ID_SI7020 && id != SI70XX_ID_SI7021) { + return -2; + } + + return 0; +} + +int si70xx_init(si70xx_t *dev, i2c_t i2c_dev, uint8_t address) +{ + dev->i2c_dev = i2c_dev; + dev->address = address; + + /* setup the i2c bus */ + i2c_acquire(dev->i2c_dev); + int result = i2c_init_master(dev->i2c_dev, I2C_SPEED_NORMAL); + + if (result != 0) { + i2c_release(dev->i2c_dev); + return result; + } + + /* initialize the peripheral */ + i2c_write_byte(dev->i2c_dev, dev->address, SI70XX_RESET); + + /* sensor is ready after at most 25 ms */ + xtimer_usleep(25 * MS_IN_USEC); + i2c_release(dev->i2c_dev); + + return 0; +} + +uint16_t si70xx_get_relative_humidity(si70xx_t *dev) +{ + uint32_t raw; + uint16_t humidity; + + /* perform measurement */ + raw = si70xx_measure(dev, SI70XX_MEASURE_RH_HOLD); + + humidity = ((12500 * raw) / 65536) - 600; + + /* according to datasheet, values may exceed bounds, but can be clipped */ + if (humidity < 0) { + return 0; + } + else if (humidity > 10000) { + return 10000; + } + else { + return humidity; + } +} + +int16_t si70xx_get_temperature(si70xx_t *dev) +{ + uint32_t raw; + + /* perform measurement */ + raw = si70xx_measure(dev, SI70XX_MEASURE_TEMP_HOLD); + + return ((17572 * raw) / 65536) - 4685; +} + +void si70xx_get_both(si70xx_t *dev, uint16_t *humidity, int16_t *temperature) +{ + uint32_t raw; + + /* read the humidity the normal way */ + *humidity = si70xx_get_relative_humidity(dev); + + /* read the temperature using the data from the previous measurement */ + raw = si70xx_measure(dev, SI70XX_MEASURE_TEMP_PREV); + + *temperature = ((17572 * raw) / 65536) - 4685; +} + +uint64_t si70xx_get_serial(si70xx_t *dev) +{ + uint8_t out[2]; + uint8_t in_first[8]; + uint8_t in_second[8]; + + /* read the lower bytes */ + out[0] = SI70XX_READ_ID_FIRST_A; + out[1] = SI70XX_READ_ID_FIRST_B; + + i2c_acquire(dev->i2c_dev); + i2c_write_bytes(dev->i2c_dev, dev->address, (char *) out, 2); + i2c_read_bytes(dev->i2c_dev, dev->address, (char *) in_first, 8); + + /* read the higher bytes */ + out[0] = SI70XX_READ_ID_SECOND_A; + out[1] = SI70XX_READ_ID_SECOND_B; + + i2c_write_bytes(dev->i2c_dev, dev->address, (char *) out, 2); + i2c_read_bytes(dev->i2c_dev, dev->address, (char *) in_second, 8); + i2c_release(dev->i2c_dev); + + /* calculate the ID */ + uint32_t id_first = (in_first[0] << 24) + (in_first[2] << 16) + + (in_first[4] << 8) + (in_first[6] << 0); + uint32_t id_second = (in_second[0] << 24) + (in_second[2] << 16) + + (in_second[4] << 8) + (in_second[6] << 0); + + return (((uint64_t) id_first) << 32) + id_second; +} + +uint8_t si70xx_get_id(si70xx_t *dev) +{ + return (si70xx_get_serial(dev) >> 24) & 0xff; +} + +uint8_t si70xx_get_revision(si70xx_t *dev) +{ + uint8_t out[2]; + uint8_t in; + + /* read the revision number */ + out[0] = SI70XX_READ_REVISION_A; + out[1] = SI70XX_READ_REVISION_B; + + i2c_acquire(dev->i2c_dev); + i2c_write_bytes(dev->i2c_dev, dev->address, (char *) out, 2); + i2c_read_byte(dev->i2c_dev, dev->address, (char *) &in); + i2c_release(dev->i2c_dev); + + return in; +} diff --git a/tests/driver_si70xx/Makefile b/tests/driver_si70xx/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..91a7bbb362e73bb179d6ca99c4f3390a68a45417 --- /dev/null +++ b/tests/driver_si70xx/Makefile @@ -0,0 +1,20 @@ +APPLICATION = driver_si70xx +include ../Makefile.tests_common + +FEATURES_REQUIRED += periph_gpio +FEATURES_REQUIRED += periph_i2c + +USEMODULE += si70xx +USEMODULE += xtimer + +# set default device parameters in case they are undefined +TEST_I2C ?= 0 +TEST_I2C_ADDR ?= 128 +TEST_PIN_EN ?= 57 + +# export parameters +CFLAGS += -DTEST_I2C=$(TEST_I2C) +CFLAGS += -DTEST_I2C_ADDR=$(TEST_I2C_ADDR) +CFLAGS += -DTEST_PIN_EN=$(TEST_PIN_EN) + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_si70xx/README.md b/tests/driver_si70xx/README.md new file mode 100644 index 0000000000000000000000000000000000000000..72e737e73f97a601f6cbeda745891f9bb1a4f699 --- /dev/null +++ b/tests/driver_si70xx/README.md @@ -0,0 +1,14 @@ +# Si70xx Driver Test + +## Introduction +This test will test if the Si7006/13/20/21 temperature and humidity sensor is working. + +## Configuration +There are three parameters to configure: + +* `TEST_I2C` — I2C device to use. +* `TEST_I2C_ADDR` — The sensor address (usually 0x80 or 0x81). +* `TEST_PIN_EN` — If required, toggle the enable pin via this GPIO pin (see `GPIO_PIN` macro for your board). + +## Expected result +The sensor should continuously (every 1 sec) output the humidity and temperature. The precision should be two digits. diff --git a/tests/driver_si70xx/main.c b/tests/driver_si70xx/main.c new file mode 100644 index 0000000000000000000000000000000000000000..b954cd871af6871d0229579899ca150cac6c0a2b --- /dev/null +++ b/tests/driver_si70xx/main.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2016 Bas Stottelaar <basstottelaar@gmail.com> + * + * 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 tests + * @{ + * + * @file + * @brief Test application for the Si7006/13/20/21 sensor driver + * + * @author Bas Stottelaar <basstottelaar@gmail.com> + * + * @} + */ + +#ifndef TEST_I2C +#error "TEST_I2C not defined" +#endif + +#ifndef TEST_I2C_ADDR +#error "TEST_I2C_ADDR not defined" +#endif + +#ifndef TEST_PIN_EN +#error "TEST_PIN_EN not defined" +#endif + +#include <stdio.h> + +#include "periph/gpio.h" + +#include "xtimer.h" + +#include "si70xx.h" + +int main(void) +{ + si70xx_t dev; + + puts("SI7021 temperature and humidity sensor test application\n"); + + /* enable the sensor if test pin given */ + if (TEST_PIN_EN != GPIO_UNDEF) { + printf("Toggling enable pin..."); + + if (gpio_init(TEST_PIN_EN, GPIO_DIR_OUT, GPIO_NOPULL) == 0) { + puts("[OK]\n"); + } + else { + puts("[Failed]\n"); + return 1; + } + + gpio_set(TEST_PIN_EN); + } + + /* initialize the sensor */ + printf("Initializing sensor..."); + + if (si70xx_init(&dev, TEST_I2C, TEST_I2C_ADDR) == 0) { + puts("[OK]\n"); + } + else { + puts("[Failed]"); + return 1; + } + + /* run sensor test */ + printf("Testing sensor communication..."); + + if (si70xx_test(&dev) == 0) { + puts("[OK]\n"); + } + else { + puts("[Failed]"); + return 1; + } + + /* print device id */ + printf("Identified sensor as the Si70%02i\n", si70xx_get_id(&dev)); + + /* read temperature and humidity every 1 seconds */ + bool both = false; + + int16_t temperature; + uint16_t humidity; + + while (1) { + /* rotate the way of getting the data */ + if (both) { + si70xx_get_both(&dev, &humidity, &temperature); + } + else { + temperature = si70xx_get_temperature(&dev); + humidity = si70xx_get_relative_humidity(&dev); + } + + both = !both; + + /* display results */ + printf("relative humidity: %d.%d\n", humidity / 100, humidity % 100); + printf("temperature: %d.%d C\n", temperature / 100, temperature % 100); + + /* sleep between measurements */ + xtimer_usleep(1000 * MS_IN_USEC); + } + + return 0; +}