1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers: si70xx: temperature and humidity sensor

This includes the Si7006, Si7013, Si7020 and Si7021 I2C sensors, including a
test application.
This commit is contained in:
Bas Stottelaar 2016-02-09 23:07:51 +01:00 committed by Bas Stottelaar
parent 97ceaadaec
commit 9e50d09b60
6 changed files with 491 additions and 0 deletions

160
drivers/include/si70xx.h Normal file
View File

@ -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_ */
/** @} */

3
drivers/si70xx/Makefile Normal file
View File

@ -0,0 +1,3 @@
MODULE = si70xx
include $(RIOTBASE)/Makefile.base

180
drivers/si70xx/si70xx.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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` &mdash; I2C device to use.
* `TEST_I2C_ADDR` &mdash; The sensor address (usually 0x80 or 0x81).
* `TEST_PIN_EN` &mdash; 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.

114
tests/driver_si70xx/main.c Normal file
View File

@ -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;
}