mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
300 lines
7.7 KiB
C
300 lines
7.7 KiB
C
/*
|
|
* Copyright (C) Daniel Krebs
|
|
*
|
|
* 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_at30tse75x
|
|
* @file
|
|
* @brief Driver for the AT30TSE75x temperature sensor with serial EEPROM
|
|
*
|
|
* @author Daniel Krebs <github@daniel-krebs.net>
|
|
*/
|
|
|
|
#include "periph/i2c.h"
|
|
#include "ztimer.h"
|
|
|
|
#include "at30tse75x.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
static inline float temperature_to_float(uint16_t temp)
|
|
{
|
|
/* Integer part is 8-bit signed */
|
|
int8_t temp_int = (temp & AT30TSE75X_INTEGER_MASK) >>
|
|
AT30TSE75X_INTEGER_SHIFT;
|
|
/* Fractional part is multiple of 0.0625 */
|
|
uint8_t frac_multiplier = (temp & AT30TSE75X_FRACTIONAL_MASK) >>
|
|
AT30TSE75X_FRACTIONAL_SHIFT;
|
|
|
|
return temp_int + (frac_multiplier * AT30TSE75X_FRACTIONAL_BASE);
|
|
}
|
|
|
|
static int at30tse75x_get_register(const at30tse75x_t *dev, uint8_t reg, uint16_t* data)
|
|
{
|
|
i2c_acquire(dev->i2c);
|
|
ztimer_spin(ZTIMER_USEC, AT30TSE75X_BUS_FREE_TIME_US);
|
|
if (i2c_read_regs(dev->i2c, dev->addr, reg, data, 2, 0) < 0) {
|
|
DEBUG("[at30tse75x] Can't read register 0x%x\n", reg);
|
|
i2c_release(dev->i2c);
|
|
return -1;
|
|
}
|
|
i2c_release(dev->i2c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at30tse75x_set_register(const at30tse75x_t *dev, uint8_t reg, uint16_t *data)
|
|
{
|
|
i2c_acquire(dev->i2c);
|
|
ztimer_spin(ZTIMER_USEC, AT30TSE75X_BUS_FREE_TIME_US);
|
|
if (i2c_write_regs(dev->i2c, dev->addr, reg, data, 2, 0) < 0) {
|
|
DEBUG("[at30tse75x] Can't write to register 0x%x\n", reg);
|
|
i2c_release(dev->i2c);
|
|
return -1;
|
|
}
|
|
i2c_release(dev->i2c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int at30tse75x_reset(const at30tse75x_t *dev)
|
|
{
|
|
i2c_acquire(dev->i2c);
|
|
ztimer_spin(ZTIMER_USEC, AT30TSE75X_BUS_FREE_TIME_US);
|
|
if (i2c_write_byte(dev->i2c, 0x00, AT30TSE75X_CMD__GENERAL_CALL_RESET, 0) < 0) {
|
|
i2c_release(dev->i2c);
|
|
return -1;
|
|
}
|
|
i2c_release(dev->i2c);
|
|
/* Wait for reset to complete */
|
|
ztimer_sleep(ZTIMER_USEC, 500);
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_get_config(const at30tse75x_t *dev, uint8_t *data)
|
|
{
|
|
i2c_acquire(dev->i2c);
|
|
ztimer_spin(ZTIMER_USEC, AT30TSE75X_BUS_FREE_TIME_US);
|
|
if (i2c_read_reg(dev->i2c, dev->addr, AT30TSE75X_REG__CONFIG, data, 0) < 0) {
|
|
DEBUG("[at30tse75x] Can't read CONFIG register\n");
|
|
i2c_release(dev->i2c);
|
|
return -1;
|
|
}
|
|
i2c_release(dev->i2c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_set_config(const at30tse75x_t *dev, uint8_t data)
|
|
{
|
|
i2c_acquire(dev->i2c);
|
|
ztimer_spin(ZTIMER_USEC, AT30TSE75X_BUS_FREE_TIME_US);
|
|
if (i2c_write_reg(dev->i2c, dev->addr, AT30TSE75X_REG__CONFIG, data, 0) < 0) {
|
|
DEBUG("[at30tse75x] Can't write to CONFIG register\n");
|
|
i2c_release(dev->i2c);
|
|
return -1;
|
|
}
|
|
i2c_release(dev->i2c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_set_resolution(const at30tse75x_t *dev, at30tse75x_resolution_t resolution)
|
|
{
|
|
uint8_t config;
|
|
|
|
if (at30tse75x_get_config(dev, &config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
config &= ~(AT30TSE75X_CONFIG__RESOLUTION_MASK);
|
|
config |= resolution << AT30TSE75X_CONFIG__RESOLUTION_SHIFT;
|
|
|
|
if (at30tse75x_set_config(dev, config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_set_mode(const at30tse75x_t *dev, at30tse75x_mode_t mode)
|
|
{
|
|
uint8_t config;
|
|
if (at30tse75x_get_config(dev, &config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
switch (mode) {
|
|
case AT30TSE75X_MODE_ONE_SHOT:
|
|
config |= AT30TSE75X_CONFIG__SHUTDOWN_BIT;
|
|
/* Don't touch alarm mode */
|
|
break;
|
|
case AT30TSE75X_MODE_COMPARATOR:
|
|
config &= ~(AT30TSE75X_CONFIG__SHUTDOWN_BIT);
|
|
config &= ~(AT30TSE75X_CONFIG__ALARM_MODE_BIT);
|
|
break;
|
|
case AT30TSE75X_MODE_INTERRUPT:
|
|
config &= ~(AT30TSE75X_CONFIG__SHUTDOWN_BIT);
|
|
config |= AT30TSE75X_CONFIG__ALARM_MODE_BIT;
|
|
break;
|
|
default:
|
|
return -2;
|
|
}
|
|
|
|
if (at30tse75x_set_config(dev, config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_set_alarm_polarity(const at30tse75x_t *dev, at30tse75x_alarm_polatity_t polarity)
|
|
{
|
|
uint8_t config;
|
|
if (at30tse75x_get_config(dev, &config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
switch (polarity) {
|
|
case AT30TSE75X_ALARM_ACTIVE_HIGH:
|
|
config |= AT30TSE75X_CONFIG__ALERT_POL_BIT;
|
|
break;
|
|
case AT30TSE75X_ALARM_ACTIVE_LOW:
|
|
config &= ~(AT30TSE75X_CONFIG__ALERT_POL_BIT);
|
|
break;
|
|
default:
|
|
return -2;
|
|
}
|
|
|
|
if (at30tse75x_set_config(dev, config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_set_fault_tolerance(const at30tse75x_t *dev, at30tse75x_fault_tolerance_t tolerance)
|
|
{
|
|
uint8_t config;
|
|
if (at30tse75x_get_config(dev, &config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
config &= ~(AT30TSE75X_CONFIG__FTQ_MASK);
|
|
config |= tolerance << AT30TSE75X_CONFIG__FTQ_SHIFT;
|
|
|
|
if (at30tse75x_set_config(dev, config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_set_limit_low(const at30tse75x_t *dev, int8_t t_low)
|
|
{
|
|
uint16_t tmp = (t_low << 8) | (0x00);
|
|
return at30tse75x_set_register(dev, AT30TSE75X_REG__LIMIT_LOW, &tmp);
|
|
}
|
|
|
|
int at30tse75x_set_limit_high(const at30tse75x_t *dev, int8_t t_high)
|
|
{
|
|
uint16_t tmp = (t_high << 8) | (0x00);
|
|
return at30tse75x_set_register(dev, AT30TSE75X_REG__LIMIT_HIGH, &tmp);
|
|
}
|
|
|
|
int at30tse75x_save_config(const at30tse75x_t *dev)
|
|
{
|
|
i2c_acquire(dev->i2c);
|
|
ztimer_spin(ZTIMER_USEC, AT30TSE75X_BUS_FREE_TIME_US);
|
|
if (i2c_write_byte(dev->i2c, dev->addr, AT30TSE75X_CMD__SAVE_TO_NVRAM, 0) < 0) {
|
|
i2c_release(dev->i2c);
|
|
return -1;
|
|
}
|
|
i2c_release(dev->i2c);
|
|
/* Wait for copy to complete */
|
|
ztimer_sleep(ZTIMER_USEC, 5000);
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_restore_config(const at30tse75x_t *dev)
|
|
{
|
|
i2c_acquire(dev->i2c);
|
|
ztimer_spin(ZTIMER_USEC, AT30TSE75X_BUS_FREE_TIME_US);
|
|
if (i2c_write_byte(dev->i2c, dev->addr, AT30TSE75X_CMD__RESTORE_FROM_NVRAM, 0) < 0) {
|
|
i2c_release(dev->i2c);
|
|
return -1;
|
|
}
|
|
i2c_release(dev->i2c);
|
|
/* Wait for copy to complete */
|
|
ztimer_sleep(ZTIMER_USEC, 200);
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_get_temperature(const at30tse75x_t *dev, float *temperature)
|
|
{
|
|
uint16_t tmp;
|
|
uint8_t config;
|
|
|
|
if (at30tse75x_get_config(dev, &config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* If sensor is shutdown trigger One-Shot mode*/
|
|
if (config & AT30TSE75X_CONFIG__SHUTDOWN_BIT) {
|
|
|
|
config |= AT30TSE75X_CONFIG__OS_BIT;
|
|
if (at30tse75x_set_config(dev, config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Use resolution to calculate conversion time */
|
|
uint8_t resolution = (config & AT30TSE75X_CONFIG__RESOLUTION_MASK) >>
|
|
AT30TSE75X_CONFIG__RESOLUTION_SHIFT;
|
|
|
|
/* Wait until conversion is finished */
|
|
ztimer_sleep(ZTIMER_USEC, (uint32_t)(25000 << resolution));
|
|
}
|
|
|
|
/* Read temperature */
|
|
if (at30tse75x_get_register(dev, AT30TSE75X_REG__TEMPERATURE, &tmp) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Convert fixed point to float */
|
|
*temperature = temperature_to_float(tmp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int at30tse75x_init(at30tse75x_t *dev, i2c_t i2c, uint8_t addr)
|
|
{
|
|
uint8_t config;
|
|
|
|
dev->i2c = i2c;
|
|
|
|
if ( (addr < 0x48) || (addr > 0x4f) ) {
|
|
DEBUG("[at30tse75x] Invalid address\n");
|
|
return -2;
|
|
}
|
|
dev->addr = addr;
|
|
|
|
/* Reset the device */
|
|
if (at30tse75x_reset(dev) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* Poll the device, fail if unavailable */
|
|
if (at30tse75x_get_config(dev, &config) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("[at30tse75x] Config: 0x%x\n", config);
|
|
|
|
return 0;
|
|
}
|