From b10e33dfc0d540837278a40a6dfa7ba2e8c2bc14 Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Thu, 14 May 2015 15:38:39 +0200 Subject: [PATCH 1/2] drivers/adt7310: Add ADT7310 temperature sensor driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a driver for Analog Devices ADT7310 +/-0.5°C Accurate, 16-Bit Digital SPI Temperature Sensor. Only basic reading is supported. Interrupt and compare are not implemented yet. See: http://www.analog.com/en/products/analog-to-digital-converters/integrated-special-purpose-converters/integrated-temperature-sensors/adt7310.html --- drivers/adt7310/Makefile | 1 + drivers/adt7310/adt7310.c | 240 ++++++++++++++++++++++++++++++++++++++ drivers/include/adt7310.h | 169 +++++++++++++++++++++++++++ 3 files changed, 410 insertions(+) create mode 100644 drivers/adt7310/Makefile create mode 100644 drivers/adt7310/adt7310.c create mode 100644 drivers/include/adt7310.h diff --git a/drivers/adt7310/Makefile b/drivers/adt7310/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/adt7310/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/adt7310/adt7310.c b/drivers/adt7310/adt7310.c new file mode 100644 index 0000000000..a9111e3883 --- /dev/null +++ b/drivers/adt7310/adt7310.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2015 Eistec AB + * + * 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_adt7310 + * @{ + * + * @file + * @brief Driver for the ADT7310 ±0.5°C Accurate, 16-Bit Digital SPI + * Temperature Sensor from Analog Devices + * + * @author Joakim Gebart + * + * @} + */ + +#include +#include +#include +#include "adt7310.h" +#include "byteorder.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* SPI command byte parameters */ +#define ADT7310_CMD_READ (0x40) +#define ADT7310_CMD_WRITE (0x00) +#define ADT7310_CMD_ADDR_SHIFT (3) +#define ADT7310_CMD_CONTINUOUS (0x04) + +/* ****************** * + * Register addresses * + * ****************** */ +#define ADT7310_REG_STATUS (0x00) +#define ADT7310_REG_CONFIG (0x01) +#define ADT7310_REG_VALUE (0x02) +#define ADT7310_REG_ID (0x03) +#define ADT7310_REG_TCRIT (0x04) +#define ADT7310_REG_THYST (0x05) +#define ADT7310_REG_THIGH (0x06) +#define ADT7310_REG_TLOW (0x07) + +/* ************** * + * Register sizes * + * ************** */ +#define ADT7310_REG_SIZE_STATUS (1) +#define ADT7310_REG_SIZE_CONFIG (1) +#define ADT7310_REG_SIZE_VALUE (2) +#define ADT7310_REG_SIZE_ID (1) +#define ADT7310_REG_SIZE_TCRIT (2) +#define ADT7310_REG_SIZE_THYST (1) +#define ADT7310_REG_SIZE_THIGH (2) +#define ADT7310_REG_SIZE_TLOW (2) + +/* Register bit masks */ +/** @brief Manufacturer ID */ +#define ADT7310_REG_ID_MASK_MANUFACTURER_ID (0xF8) +/** @brief Silicon version */ +#define ADT7310_REG_ID_MASK_SILICON_VERSION (0x07) + +/** @brief Expected manufacturer ID */ +#define ADT7310_EXPECTED_MANUF_ID (0b11000000) + +/** @brief 13 bit temperature mask */ +#define ADT7310_REG_VALUE_MASK_13BIT (0xF8) + +/** @brief Number of fractional bits in the raw readings */ +#define ADT7310_VALUE_FRAC_BITS (7) + +/** @brief Scale factor for converting raw temperature readings to degrees + * Celsius, floating point number */ +#define ADT7310_TEMPERATURE_LSB_FLOAT (1.f/((float)((int)1 << ADT7310_VALUE_FRAC_BITS))) + +/** + * @brief Read a register from the sensor + * + * @param[in] dev device descriptor + * @param[in] addr register address + * @param[in] len register size + * @param[out] buf destination buffer + * + * @return 0 on success + * @return -1 on communication errors + */ +static int adt7310_read_reg(const adt7310_t *dev, const uint8_t addr, const uint8_t len, + uint8_t *buf) +{ + int status = 0; + uint8_t command = ADT7310_CMD_READ | (addr << ADT7310_CMD_ADDR_SHIFT); + /* Acquire exclusive access to the bus. */ + spi_acquire(dev->spi); + /* Perform the transaction */ + gpio_clear(dev->cs); + + if (spi_transfer_regs(dev->spi, command, NULL, (char *)buf, len) < len) { + status = -1; + } + + gpio_set(dev->cs); + /* Release the bus for other threads. */ + spi_release(dev->spi); + + return status; +} + +/** + * @brief Write a register value to the sensor + * + * @param[in] dev device descriptor + * @param[in] addr register address + * @param[in] len register size + * @param[in] buf source buffer + * + * @return 0 on success + * @return -1 on communication errors + */ +static int adt7310_write_reg(const adt7310_t *dev, const uint8_t addr, + const uint8_t len, uint8_t *buf) +{ + int status = 0; + uint8_t command = ADT7310_CMD_WRITE | (addr << ADT7310_CMD_ADDR_SHIFT); + /* Acquire exclusive access to the bus. */ + spi_acquire(dev->spi); + /* Perform the transaction */ + gpio_clear(dev->cs); + + if (spi_transfer_regs(dev->spi, command, (char *)buf, NULL, len) < len) { + status = -1; + } + + gpio_set(dev->cs); + /* Release the bus for other threads. */ + spi_release(dev->spi); + + return status; +} + +int adt7310_init(adt7310_t *dev, spi_t spi, gpio_t cs) +{ + int status; + uint8_t reg = 0; + /* write device descriptor */ + dev->spi = spi; + dev->cs = cs; + dev->initialized = false; + dev->high_res = false; + + /* CS */ + gpio_init(dev->cs, GPIO_DIR_OUT, GPIO_NOPULL); + gpio_set(dev->cs); + +#if ENABLE_DEBUG + for (int i = 0; i < 8; ++i) { + uint16_t dbg_reg = 0; + status = adt7310_read_reg(dev, i, sizeof(dbg_reg), (uint8_t *)&dbg_reg); + if (status != 0) { + printf("Error reading address 0x%02x", i); + } + dbg_reg = HTONS(dbg_reg); + printf("%02x: %04" PRIx16 "\n", i, dbg_reg); + } +#endif + + /* Read ID register from device */ + status = adt7310_read_reg(dev, ADT7310_REG_ID, ADT7310_REG_SIZE_ID, ®); + if (status != 0) { + /* SPI bus error */ + return -1; + } + if ((reg & ADT7310_REG_ID_MASK_MANUFACTURER_ID) != ADT7310_EXPECTED_MANUF_ID) { + /* Wrong part ID */ + return -2; + } + + /* Set a configuration, go to shut down mode to save power until the sensor is needed. */ + if (adt7310_set_config(dev, ADT7310_MODE_SHUTDOWN) != 0) { + /* communication error */ + return -3; + } + + dev->initialized = true; + return 0; +} + +int adt7310_set_config(adt7310_t *dev, uint8_t config) +{ + if (config & ADT7310_CONF_RESOLUTION_MASK) { + dev->high_res = true; + } + return adt7310_write_reg(dev, ADT7310_REG_CONFIG, ADT7310_REG_SIZE_CONFIG, &config); +} + +int16_t adt7310_read_raw(adt7310_t *dev) +{ + int status; + int16_t raw; + + /* Read the temperature value register */ + status = adt7310_read_reg(dev, ADT7310_REG_VALUE, ADT7310_REG_SIZE_VALUE, (uint8_t*)&raw); + if (status < 0) { + /* communication error */ + return INT16_MIN; + } + /* The temperature value is sent big endian (network byte order) */ + raw = (int16_t)NTOHS((uint16_t)raw); + return raw; +} + +int32_t adt7310_read(adt7310_t *dev) +{ + int16_t raw = adt7310_read_raw(dev); + if (raw == INT16_MIN) { + return INT32_MIN; + } + if (!dev->high_res) { + /* filter out the flag bits */ + raw &= ADT7310_REG_VALUE_MASK_13BIT; + } + return ((((int32_t)raw) * 1000) >> ADT7310_VALUE_FRAC_BITS); +} + +float adt7310_read_float(adt7310_t *dev) +{ + int16_t raw = adt7310_read_raw(dev); + if (raw == INT16_MIN) { + return (0.0f / 0.0f); /* return NaN */ + } + if (!dev->high_res) { + /* filter out the flag bits */ + raw &= ADT7310_REG_VALUE_MASK_13BIT; + } + return (((float) raw) * ADT7310_TEMPERATURE_LSB_FLOAT); +} diff --git a/drivers/include/adt7310.h b/drivers/include/adt7310.h new file mode 100644 index 0000000000..91ecb51dce --- /dev/null +++ b/drivers/include/adt7310.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 Eistec AB + * + * 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 drivers_adt7310 ADT7310 SPI temperature sensor + * @ingroup drivers + * @brief Driver for the Analog Devices ADT7310 temperature sensor. + * + * ## Description + * + * The ADT7310 is a high accuracy digital temperature sensor + * in a narrow SOIC package. It contains a band gap temperature + * reference and a 13-bit ADC to monitor and digitize the + * temperature to a 0.0625°C resolution. The ADC resolution, + * by default, is set to 13 bits (0.0625 °C). This can be changed + * to 16 bits (0.0078 °C) by setting Bit 7 in the configuration + * register (Register Address 0x01). + * The ADT7310 is guaranteed to operate over supply voltages from + * 2.7 V to 5.5 V. Operating at 3.3 V, the average supply current is + * typically 210 μA. The ADT7310 has a shutdown mode that + * powers down the device and offers a shutdown current of + * typically 2 μA. The ADT7310 is rated for operation over the + * −55°C to +150°C temperature range. + * + * ## Usage + * + * See `tests/driver_adt7310` for an example application using this driver. + * + * ## Caveats + * + * This driver is currently missing support for a number of hardware features: + * + * - Interrupt and compare pins are not handled + * - There is no public API for setting the temperature alarm levels + * - Device SPI reset is not implemented (drive MISO high from the master while clocking SCK) + * + * @{ + * + * @file + * @brief Interface definition for the ADT7310 sensor driver. + * + * @author Joakim Gebart + */ + +#ifndef ADT7310_H_ +#define ADT7310_H_ + +#include +#include +#include "periph/spi.h" +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief Device descriptor for ADT7310 sensors. + */ +typedef struct { + spi_t spi; /**< SPI bus the sensor is connected to */ + gpio_t cs; /**< CS pin GPIO handle */ + bool initialized; /**< sensor status, true if sensor is initialized */ + bool high_res; /**< Sensor resolution, true if configured to 16 bit resolution */ +} adt7310_t; + +/** @name ADT7310 configuration bits */ +/** @{ */ +#define ADT7310_CONF_FAULT_QUEUE_MASK (0x03) +#define ADT7310_CONF_FAULT_QUEUE_SHIFT (0) +#define ADT7310_CONF_FAULT_QUEUE(x) (((x) << ADT7310_CONF_FAULT_QUEUE_SHIFT) & ADT7310_CONF_FAULT_QUEUE_MASK) +#define ADT7310_CONF_CT_POL_MASK (0x04) +#define ADT7310_CONF_CT_POL_SHIFT (2) +#define ADT7310_CONF_CT_POL(x) (((x) << ADT7310_CONF_CT_POL_SHIFT) & ADT7310_CONF_CT_POL_MASK) +#define ADT7310_CONF_INT_POL_MASK (0x08) +#define ADT7310_CONF_INT_POL_SHIFT (3) +#define ADT7310_CONF_INT_POL(x) (((x) << ADT7310_CONF_INT_POL_SHIFT) & ADT7310_CONF_INT_POL_MASK) +#define ADT7310_CONF_INTCT_MODE_MASK (0x10) +#define ADT7310_CONF_INTCT_MODE_SHIFT (4) +#define ADT7310_CONF_INTCT_MODE(x) (((x) << ADT7310_CONF_INTCT_MODE_SHIFT) & ADT7310_CONF_INTCT_MODE_MASK) +#define ADT7310_CONF_OPERATION_MODE_MASK (0x60) +#define ADT7310_CONF_OPERATION_MODE_SHIFT (5) +#define ADT7310_CONF_OPERATION_MODE(x) (((x) << ADT7310_CONF_OPERATION_MODE_SHIFT) & ADT7310_CONF_OPERATION_MODE_MASK) +#define ADT7310_CONF_RESOLUTION_MASK (0x80) +#define ADT7310_CONF_RESOLUTION_SHIFT (7) +#define ADT7310_CONF_RESOLUTION(x) (((x) << ADT7310_CONF_RESOLUTION_SHIFT) & ADT7310_CONF_RESOLUTION_MASK) + +/** @brief Continuous operation mode */ +#define ADT7310_MODE_CONTINUOUS (ADT7310_CONF_OPERATION_MODE(0)) +/** @brief One shot */ +#define ADT7310_MODE_ONE_SHOT (ADT7310_CONF_OPERATION_MODE(1)) +/** @brief 1 sample per second */ +#define ADT7310_MODE_1SPS (ADT7310_CONF_OPERATION_MODE(2)) +/** @brief Shut down (powersave) */ +#define ADT7310_MODE_SHUTDOWN (ADT7310_CONF_OPERATION_MODE(3)) +/** @} */ + +/** + * @brief Set configuration register of an ADT7310 sensor + * + * @param[in] dev pointer to sensor device descriptor + * @param[in] config configuration byte, see macros in adt7310.h + * + * @return 0 on success + * @return -1 on error + */ +int adt7310_set_config(adt7310_t *dev, uint8_t config); + +/** + * @brief Initialize the ADT7310 sensor driver. + * + * @note The SPI bus is expected to have been initialized when adt7310_init is called. + * + * @param[in] dev pointer to sensor device descriptor + * @param[in] spi SPI bus the sensor is connected to + * @param[in] cs GPIO pin the chip select signal is connected to + * + * @return 0 on success + * @return <0 on error + */ +int adt7310_init(adt7310_t *dev, spi_t spi, gpio_t cs); + +/** + * @brief Read raw temperature register value + * + * @note The three least-significant bits of the value register are used for + * flags if the sensor is configured for 13 bit mode. + * + * @param[in] dev pointer to sensor device descriptor + * + * @return raw sensor value on success + * @return INT16_MIN on error + */ +int16_t adt7310_read_raw(adt7310_t *dev); + +/** + * @brief Read temperature value from sensor and convert to milli-degrees Celsius. + * + * Divide the returned value by 1000 to get integer degrees. + * + * @param[in] dev pointer to sensor device descriptor + * + * @return temperature in milli-degrees Celsius + * @return INT32_MIN on errors + */ +int32_t adt7310_read(adt7310_t *dev); + +/** + * @brief Read temperature value from sensor and convert to degrees Celsius. + * + * @param[in] dev pointer to sensor device descriptor + * + * @return floating point representation of temperature in degrees Celsius + * @return NaN on errors + */ +float adt7310_read_float(adt7310_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* ADT7310_H_ */ +/** @} */ From fb90e6786356de7fb105124305af3c2f2abb17b9 Mon Sep 17 00:00:00 2001 From: Joakim Gebart Date: Thu, 14 May 2015 15:37:56 +0200 Subject: [PATCH 2/2] drivers/adt7310: Add test application --- tests/driver_adt7310/Makefile | 22 +++++ tests/driver_adt7310/README.md | 10 +++ tests/driver_adt7310/main.c | 152 +++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 tests/driver_adt7310/Makefile create mode 100644 tests/driver_adt7310/README.md create mode 100644 tests/driver_adt7310/main.c diff --git a/tests/driver_adt7310/Makefile b/tests/driver_adt7310/Makefile new file mode 100644 index 0000000000..6e43958f96 --- /dev/null +++ b/tests/driver_adt7310/Makefile @@ -0,0 +1,22 @@ +APPLICATION = driver_adt7310 +include ../Makefile.tests_common + +FEATURES_REQUIRED = periph_spi periph_gpio + +USEMODULE += adt7310 +USEMODULE += vtimer + +ifneq (,$(TEST_ADT7310_SPI)) + CFLAGS += -DTEST_ADT7310_SPI=$(TEST_ADT7310_SPI) +else + # set arbitrary default + CFLAGS += -DTEST_ADT7310_SPI=SPI_0 +endif +ifneq (,$(TEST_ADT7310_CS)) + CFLAGS += -DTEST_ADT7310_CS=$(TEST_ADT7310_CS) +else + # set arbitrary default + CFLAGS += -DTEST_ADT7310_CS=GPIO\(0,0\) +endif + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_adt7310/README.md b/tests/driver_adt7310/README.md new file mode 100644 index 0000000000..18f02bc5a6 --- /dev/null +++ b/tests/driver_adt7310/README.md @@ -0,0 +1,10 @@ +# About +This is a manual test application for the ADT7310 temperature sensor driver. + +# Usage +This test application will initialize the sensor with the following parameters: + - Mode: 1 SPS + - Resolution: 16 bit + +After initialization, the sensor reads the acceleration values every second +and prints them to the STDOUT. diff --git a/tests/driver_adt7310/main.c b/tests/driver_adt7310/main.c new file mode 100644 index 0000000000..5ca63ea4af --- /dev/null +++ b/tests/driver_adt7310/main.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2015 Eistec AB + * + * 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 ADT7310 accelerometer driver + * + * @author Joakim Gebart +#include +#include + +#include "board.h" +#include "vtimer.h" +#include "periph/spi.h" +#include "adt7310.h" + +/* Check for definition of hardware settings */ +#ifndef TEST_ADT7310_SPI +#error "TEST_ADT7310_SPI not defined" +#endif +#ifndef TEST_ADT7310_CS +#error "TEST_ADT7310_CS not defined" +#endif + +#define SPI_CONF (SPI_CONF_SECOND_FALLING) +#define SPI_SPEED (SPI_SPEED_10MHZ) + +#define SLEEP_CONT (100 * 1000U) +#define SLEEP_1SPS (1000 * 1000U) +#define READINGS_CONT (200) +#define READINGS_1SPS (20) + +int test_adt7310_sample_print(adt7310_t *dev) +{ + int16_t raw; + float celsius_float; + int32_t millicelsius; + + raw = adt7310_read_raw(dev); + + if (raw == INT16_MIN) { + puts("Reading temperature data (adt7310_read_raw)... "); + puts("[Failed]\n"); + return 1; + } + + millicelsius = adt7310_read(dev); + + if (millicelsius == INT32_MIN) { + puts("Reading temperature data (adt7310_read)... "); + puts("[Failed]\n"); + return 1; + }; + + celsius_float = adt7310_read_float(dev); + + if (isnan(celsius_float)) { + puts("Reading temperature data (adt7310_read_float)... "); + puts("[Failed]\n"); + return 1; + }; + + /* Several platforms usually build with nano.specs, (without float printf) */ + /* Split value into two integer parts for printing. */ + float integral = 0; + float fractional; + fractional = modff(celsius_float, &integral); + + printf("0x%04" PRIx16 " %7" PRId32 " mC %4d.%07u C)\n", raw, millicelsius, + (int)integral, abs(fractional * 10000000.f)); + return 0; +} + +int main(void) +{ + adt7310_t dev; + + puts("ADT7310 temperature driver test application\n"); + printf("Initializing SPI_%i... ", TEST_ADT7310_SPI); + if (spi_init_master(TEST_ADT7310_SPI, SPI_CONF, SPI_SPEED) == 0) { + puts("[OK]"); + } + else { + puts("[Failed]\n"); + return 1; + } + + puts("Initializing ADT7310 sensor... "); + if (adt7310_init(&dev, TEST_ADT7310_SPI, TEST_ADT7310_CS) == 0) { + puts("[OK]"); + } + else { + puts("[Failed]\n"); + return 1; + } + + puts("ADT7310 init done.\n"); + + while (1) { + puts("Set mode to continuous, 16 bit... "); + if (adt7310_set_config(&dev, ADT7310_MODE_CONTINUOUS | ADT7310_CONF_RESOLUTION(1)) == 0) { + puts("[OK]"); + } + else { + puts("[Failed]\n"); + return 1; + } + + for (int i = 0; i < READINGS_CONT; ++i) + { + printf("%4d: ", i); + if (test_adt7310_sample_print(&dev) != 0) + { + return 1; + } + vtimer_usleep(SLEEP_CONT); + } + puts("Set mode to 1SPS, 13 bit... "); + if (adt7310_set_config(&dev, ADT7310_MODE_1SPS) == 0) { + puts("[OK]"); + } + else { + puts("[Failed]\n"); + return 1; + } + + for (int i = 0; i < READINGS_1SPS; ++i) + { + printf("%4d: ", i); + if (test_adt7310_sample_print(&dev) != 0) + { + return 1; + } + vtimer_usleep(SLEEP_1SPS); + } + } + + return 0; +}