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

Merge pull request #2980 from gebart/pr/adt7310-initial

ADT7310 SPI temperature sensor driver
This commit is contained in:
Ludwig Ortmann 2015-07-11 10:46:04 +02:00
commit fe34e7874b
6 changed files with 594 additions and 0 deletions

1
drivers/adt7310/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

240
drivers/adt7310/adt7310.c Normal file
View File

@ -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 <joakim.gebart@eistec.se>
*
* @}
*/
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#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, &reg);
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);
}

169
drivers/include/adt7310.h Normal file
View File

@ -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 <joakim.gebart@eistec.se>
*/
#ifndef ADT7310_H_
#define ADT7310_H_
#include <stdint.h>
#include <stdbool.h>
#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_ */
/** @} */

View File

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

View File

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

152
tests/driver_adt7310/main.c Normal file
View File

@ -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 <joakim.gebart@eistec.se
*
* @}
*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#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;
}