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

drivers/lm75: driver for the lm75 sensor and derivatives

This commit is contained in:
Vitor Batista 2021-07-23 16:38:38 +02:00
parent 0f1e0b6798
commit 9267fa2670
11 changed files with 1042 additions and 0 deletions

View File

@ -90,6 +90,7 @@ rsource "lc709203f/Kconfig"
rsource "lis2dh12/Kconfig"
rsource "lis3dh/Kconfig"
rsource "lis3mdl/Kconfig"
rsource "lm75/Kconfig"
rsource "lpd8808/Kconfig"
rsource "lpsxxx/Kconfig"
rsource "lsm6dsl/Kconfig"

View File

@ -72,6 +72,10 @@ ifneq (,$(filter llcc68,$(USEMODULE)))
USEMODULE += sx126x
endif
ifneq (,$(filter tmp1075 lm75%,$(USEMODULE)))
USEMODULE += lm75
endif
ifneq (,$(filter lps331ap lps2%hb,$(USEMODULE)))
USEMODULE += lpsxxx
endif

246
drivers/include/lm75.h Executable file
View File

@ -0,0 +1,246 @@
/*
* Copyright (C) 2021 ML!PA Consulting GmbH
*
* 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_lm75
*
* @{
* @file
* @brief Driver for the LM75 temperature sensor.
*
* @author Vitor Batista <vitor.batista@ml-pa.com>
*
* @}
*/
/**
* @defgroup drivers_lm75 LM75 Temperature Sensor driver compile configuration
* @ingroup drivers_sensors
* @brief Driver for the lm75 temperature sensors.
*/
#ifndef LM75_H
#define LM75_H
#include "periph/i2c.h"
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name LM75 return values
*/
enum {
LM75_SUCCESS = 0,
LM75_ERROR_I2C,
LM75_ERROR
};
/**
* @brief temperature reading properties/resolutions struct of the LM75 sensors
*/
typedef struct lm75_properties {
uint16_t os_res; /**< resolution of the OS and HYST registers */
uint16_t os_mult; /**< multiplier required for getting the OS and HYST into ºC */
uint16_t temp_res; /**< resolution of the temperature register */
uint16_t temp_mult; /**< multiplier required for getting the temperature into ºC */
uint8_t os_shift; /**< how many bits need to be shifted (2 bytes - any unused bits) */
uint8_t temp_shift; /**< how many bits need to be shifted (2 bytes - any unused bits) */
} lm75_properties_t;
extern lm75_properties_t lm75a_properties; /**< declaration present in lm75.c */
extern lm75_properties_t tmp1075_properties; /**< declaration present in lm75.c */
/**
* @brief params required for initialization
*/
typedef struct lm75_params {
const lm75_properties_t *res; /**< Temperature resolutions */
uint16_t conv_rate; /**< Conversion rate in ms */
gpio_t gpio_alarm; /**< Over-temperature alarm */
i2c_t i2c_bus; /**< I2C Bus used */
uint8_t i2c_addr; /**< i2c address */
uint8_t shutdown_mode; /**< Shutdown mode register */
uint8_t tm_mode; /**< Thermistor Mode */
uint8_t polarity; /**< OS polarity register */
uint8_t fault_q; /**< Fault Queue register */
/* only configurable for the TMP1075 */
uint8_t conv_rate_reg; /**< Device Conversion rate register */
} lm75_params_t;
/**
* @brief lm75 device descriptor
*/
typedef struct lm75 {
lm75_params_t lm75_params; /**< Configuration Parameters */
} lm75_t;
/**
* @brief Initialization of the LM75 sensor
*
* Initializes the sensor according to specific input parameters.
*
* @param[out] dev device structure to initialize
* @param[in] params initialization parameters
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR_I2C, on I2C related error
* @return LM75_ERROR, on initialization related error
*/
int lm75_init(lm75_t *dev, const lm75_params_t *params);
/**
* @brief Temperature values of LM75 sensor
*
* Reads the sensor temperature values from TEMP_REG
* the value is given with the full precision the device is capable of
* If divided by the device's mult property, the result will be the temperature in ºC
* and the remainder of that division will be the decimal part of the temperature,
* at the maximum resolution the device is capable of.
*
* @param[in] dev device structure
* @param[in] temperature buffer where temperature value will be written
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR_I2C, on I2C related error
*/
int lm75_get_temperature_raw(lm75_t *dev, int *temperature);
/**
* @brief Temperature values of LM75 sensor
*
* Gets the device's temperature register with the lm75_get_temperature_raw
* function and then returns the values in mºC, truncating values smaller than this, if available
*
* @param[in] dev device structure
* @param[in] temperature buffer where temperature value will be written in mºC
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR, on error
*/
int lm75_get_temperature(lm75_t *dev, int *temperature);
/**
* @brief Sets the values for Overtemperature shutdown(OS) and Hysteresis temperature(HYST).
* OS gives the temperature's higher bound and HYST the lower bound
* values are rounded to the lowest value that the device supports,
*
* @param[in] dev device structure
* @param[in] temp_os desired OS temperature in mºC
* @param[in] temp_hyst desired HYST temperature in mºC
* @param[in] cb callback that is called from interrupt context
* @param[in] *arg optional arguments for the gpio_init_int function
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR_I2C, on I2C related error
* @return LM75_ERROR, on temperature setting related error
*/
int lm75_set_temp_limits(lm75_t *dev, int temp_hyst, int temp_os, gpio_cb_t cb, void *arg);
/**
* @brief Overshutdown temperature value of LM75 sensor
*
* Reads the sensor OS temperature value from TOS_REG in ºC.
*
* @param[in] dev device structure
* @param[out] temperature buffer where OS temperature value will be written
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR_I2C, on I2C related error
*/
int lm75_get_os_temp(lm75_t *dev, int *temperature);
/**
* @brief Hysteresis temperature value of LM75 sensor
*
* Reads the sensor hysteresis temperature value from THYST_REG in ºC.
*
* @param[in] dev device structure
* @param[out] temperature buffer where HYST temperature value will be written
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR_I2C, on I2C related error
*/
int lm75_get_hyst_temp(lm75_t *dev, int *temperature);
/**
* @brief Read the current state of the OS pin to see if it's active.
*
* Read the configuration register to see the OS pin's polarity and
* then reads its state. Then outputs if the pin is active and whether
* it's in the low and active or high and active.
*
* @param[in] dev device structure
* @param[out] os_pin_state pointer to the state of the OS pin - 0 for inactive and 1 for active
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR, on pin reading error
*/
int lm75_get_os_pin(lm75_t *dev, bool *os_pin_state);
/**
* @brief Activate the LM75 sensor shutdown mode
*
* @param[in] dev device structure to set into shutdown mode
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR, on mode switching related error
*/
int lm75_poweroff(lm75_t *dev);
/**
* @brief Deactivate the LM75 sensor shutdown mode
*
* @param[in] dev device structure to wake up from shutdown mode
*
* @return LM75_SUCCESS, on success
* @return LM75_ERROR, on mode switching related error
*/
int lm75_poweron(lm75_t *dev);
/**
* @brief Activates one shot conversion mode
*
* Wakes from shutdown mode, does a single temperature conversion
* and writes in into the temperature register and then goes back into shutdown
*
* @param[in] dev device structure
*
* @return LM75_ERROR if unsuccessful
* @return LM75_SUCCESS if successful
*/
int tmp1075_one_shot(lm75_t *dev);
/**
* @brief Activates low power mode operation
*
* This function makes the device measure temperatures in a strictly discrete way
* at a user definable rate, as opposed to performing continuous measurements
* at the device's conversion rate.
* It allows the device to stay in shutdown mode for the most part, therefore consuming less power.
* In the tmp1075 and other devices which have the one shot feature this is done automatically.
* In the LM75A sensor nd other sensors which lack the one shot mode feature this is done manually
* by switching the device to and from shutdown mode and staying awake at least long enough
* to perform one ne measurement.
*
* @param[in] dev device structure
* @param[in] interval time interval in ms between measurements
*
* @return LM75_ERROR if unsuccessful
* @return LM75_SUCCESS if successful
*/
int lm75_low_power_mode(lm75_t *dev, uint16_t interval);
#ifdef __cplusplus
}
#endif
#endif /* LM75_H */
/** @} */

140
drivers/lm75/Kconfig Executable file
View File

@ -0,0 +1,140 @@
choice
bool "LM75A/TMP1075 temperature sensors"
optional
depends on HAS_PERIPH_I2C
help
Only the LM75A and TMP1075 temperature sensors are supported at the time.
config MODULE_LM75A
bool "LM75A temperature sensor"
select MODULE_LM75
config MODULE_TMP1075
bool "TMP1075 extended driver"
select MODULE_LM75
endchoice
config MODULE_LM75
bool
depends on HAS_PERIPH_I2C
menuconfig KCONFIG_USEMODULE_LM75
bool "Configure LM75 driver"
depends on USEMODULE_LM75
help
Configure the LM75 driver using Kconfig.
if KCONFIG_USEMODULE_LM75
config M75_PARAMS_I2C_ADDR
hex "Default I2C Address"
range 0x48 0x4F
default 0x48
help
The LM75A and TMP1075 allow for up to 8 and 32 devices, respectively, on a single bus.
The address value depends on the state of the A0, A1 and A2 pins.
Default value (0x48) corresponds to A0, A1 and A2 pins all connected to GND.
For more information refer to the 'Slaves Address' section in the datasheet.
choice
bool "Operation Mode"
default NORMAL_MODE
help
Whether the device operates in normal or shutdown mode.
config NORMAL_MODE
bool "Normal mode"
config SHUTDOWN_MODE
bool "Shutdown mode"
endchoice
choice
bool "Thermostat mode"
default COMPARATOR_MODE
help
Defines whether the device operates is comparator or interrupt mode.
The main difference between the two modes is that in comparator mode, the OS output becomes active
when Temp has exceeded T_OS and reset when Temp has dropped below T_hyst, reading a register or
putting the device into shutdown mode does not change the state of the OS output; while in interrupt mode,
once it has been activated either by exceeding T_OS or dropping below T_hyst the OS output will remain active
indefinitely until reading a register, then the OS output is reset.
For more information please refer to the datasheet.
config COMPARATOR_MODE
bool "Comparator mode"
config INTERRUPT_MODE
bool "Interrupt mode"
endchoice
choice
bool "OS pin polarity"
default OS_ACTIVE_LOW
help
Define the polarity of the overtemperature shutdown(OS) pin.
config OS_ACTIVE_LOW
bool "os active on low voltage"
config OS_ACTIVE_HIGH
bool "os active on high voltage"
endchoice
choice
bool "Fault Queue configuration"
default FAULT_1
help
Define the number of consecutive faults that must occur for the OS pin to become active
config FAULT_1
bool "1 fault"
config FAULT_2
bool "2 faults"
config FAULT_3
bool "3 faults - only available in the TMP1075 sensor"
config FAULT_4
bool "4 faults"
config FAULT_6
bool "6 faults - only available in the LM75A sensor"
endchoice
if MODULE_TMP1075
choice
bool "Conversion rate"
default TMP1075_CONV_RATE_REG_27H
help
Defines the frequency through which temperature conversions are performed and the temperature register is updated
config TMP1075_CONV_RATE_REG_27H
bool "27.5 ms conversion rate"
config TMP1075_CONV_RATE_REG_55
bool "55 ms conversion rate"
config TMP1075_CONV_RATE_REG_110
bool "110 ms conversion rate"
config TMP1075_CONV_RATE_REG_220
bool "220 ms conversion rate"
endchoice
endif # MODULE_TMP1075
endif # KCONFIG_USEMODULE_LM75

1
drivers/lm75/Makefile Executable file
View File

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

3
drivers/lm75/Makefile.dep Executable file
View File

@ -0,0 +1,3 @@
FEATURES_REQUIRED += periph_gpio periph_gpio_irq
FEATURES_REQUIRED += periph_i2c
USEMODULE += xtimer

2
drivers/lm75/Makefile.include Executable file
View File

@ -0,0 +1,2 @@
USEMODULE_INCLUDES_lm75 := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_lm75)

View File

@ -0,0 +1,184 @@
/*
* Copyright (C) 2021 ML!PA Consulting GmbH
*
* 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_lm75
*
* @{
* @file
* @brief Default configuration parameters for the lm75 sensors.
*
*
* @author Vitor Batista <vitor.batista@ml-pa.com>
*
* @}
*/
#ifndef LM75_PARAMS_H
#define LM75_PARAMS_H
#include "board.h"
#include "lm75.h"
#include "lm75_regs.h"
#include "kernel_defines.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LM75_PARAMS_I2C
#define LM75_PARAMS_I2C I2C_DEV(0) /**< I2C BUS used */
#endif
/** 7-bit I2C slave address: 1-0-0-1-A2-A1-A0, where
the last three bits A2, A1, A0 are defined
by the voltage level on the ADDR pin */
#ifndef CONFIG_I2C_ADDR
#define CONFIG_I2C_ADDR (0x48) /**< Default I2C address */
#endif
/* Device operation mode configuration - normal or shutdown */
#if IS_ACTIVE(CONFIG_NORMAL_MODE)
#define CONFIG_OPERATION_MODE NORMAL_MODE
#elif IS_ACTIVE(CONFIG_SHUTDOWN_MODE)
#define CONFIG_OPERATION_MODE SHUTDOWN_MODE
#endif
#ifndef CONFIG_OPERATION_MODE
#define CONFIG_OPERATION_MODE NORMAL_MODE /**< Normal Mode is the default */
#endif
/* Device Overtemperature Shutdown operation mode configuration - comparator or interrupt */
#if IS_ACTIVE(CONFIG_COMPARATOR_MODE)
#define CONFIG_THERMOSTAT_MODE COMPARATOR_MODE
#elif IS_ACTIVE(CONFIG_INTERRUPT_MODE)
#define CONFIG_THERMOSTAT_MODE INTERRUPT_MODE
#endif
#ifndef CONFIG_THERMOSTAT_MODE
#define CONFIG_THERMOSTAT_MODE COMPARATOR_MODE /**< Comparator Mode is the default */
#endif
/* Device Overtemperature Shutdown polarity configuration - OS active low or high */
#if IS_ACTIVE(CONFIG_OS_ACTIVE_LOW)
#define CONFIG_OS_POLARITY OS_ACTIVE_LOW
#elif IS_ACTIVE(CONFIG_OS_ACTIVE_HIGH)
#define CONFIG_OS_POLARITY OS_ACTIVE_HIGH
#endif
#ifndef CONFIG_OS_POLARITY
#define CONFIG_OS_POLARITY OS_ACTIVE_LOW /**< OS pin active on low is the default */
#endif
/* Device Overtemperatue Shutdown fault queue configuration -
* number of faults that must occur consecutively until OS goes active */
#if IS_ACTIVE(CONFIG_FAULT_1)
#define CONFIG_FAULT_QUEUE FAULT_1
#elif IS_ACTIVE(CONFIG_FAULT_2)
#define CONFIG_FAULT_QUEUE FAULT_2
#elif (IS_ACTIVE(CONFIG_FAULT_3) && IS_USED(MODULE_TMP1075))
#define CONFIG_FAULT_QUEUE FAULT_3
#elif (IS_ACTIVE(CONFIG_FAULT_4) && IS_USED(MODULE_LM75A))
#define CONFIG_FAULT_QUEUE FAULT_4
#elif (IS_ACTIVE(CONFIG_FAULT_4) && IS_USED(MODULE_TMP1075))
#define CONFIG_FAULT_QUEUE FAULT_4_TMP1075
#elif (IS_ACTIVE(CONFIG_FAULT_6) && IS_USED(MODULE_LM75A))
#define CONFIG_FAULT_QUEUE FAULT_6
#endif
#ifndef CONFIG_FAULT_QUEUE
#define CONFIG_FAULT_QUEUE FAULT_1 /**< One Fault is the default */
#endif
#ifndef LM75_PARAM_INT
#define LM75_PARAM_INT GPIO_UNDEF /**< Pin used for Interrupts defined by the board */
#endif
#define LM75A_CONV_RATE (100) /**< temperature register updated every 100ms */
#define LM75A_OS_RES (5) /**< resolution in 0.5ºC */
#define LM75A_OS_MULT (10) /**< Must multiply by 10 to get temp in ºC */
#define LM75A_OS_SHIFT (7) /**< Only the 9 most significant bits are needed */
#define LM75A_TEMP_RES (125) /**< resolution in 0.125ºC */
#define LM75A_TEMP_MULT (1000) /**< Must multiply by 1000 to get temp in ºC */
#define LM75A_TEMP_SHIFT (5) /**< Only the 11 most significant bits are needed */
#define TMP1075_OS_RES (625) /**< resolution in 0.0625ºC */
#define TMP1075_OS_MULT (10000) /**< Must multiply by 10000 to get temp in ºC */
#define TMP1075_OS_SHIFT (4) /**< Only the 12 most significant bits are needed */
#define TMP1075_TEMP_RES (625) /**< resolution in 0.0625ºC */
#define TMP1075_TEMP_MULT (10000) /**< Must multiply by 10000 to get temp in ºC */
#define TMP1075_TEMP_SHIFT (4) /**< Only the 12 most significant bits are needed */
/* Device conversion rate configuration - only available in the TMP1075 sensor */
#if IS_ACTIVE(CONFIG_TMP1075_CONV_RATE_REG_27H)
#define CONFIG_TMP1075_CONV_RATE_REG TMP1075_CONV_RATE_REG_27H
#define TMP1075_CONV_RATE (28)
#elif IS_ACTIVE(CONFIG_TMP1075_CONV_RATE_REG_55)
#define CONFIG_TMP1075_CONV_RATE_REG TMP1075_CONV_RATE_REG_55
#define TMP1075_CONV_RATE (55)
#elif IS_ACTIVE(CONFIG_TMP1075_CONV_RATE_REG_110)
#define CONFIG_TMP1075_CONV_RATE_REG TMP1075_CONV_RATE_REG_110
#define TMP1075_CONV_RATE (110)
#elif IS_ACTIVE(CONFIG_TMP1075_CONV_RATE_REG_220)
#define CONFIG_TMP1075_CONV_RATE_REG TMP1075_CONV_RATE_REG_220
#define TMP1075_CONV_RATE (220)
#endif
#ifndef CONFIG_TMP1075_CONV_RATE_REG
#define CONFIG_TMP1075_CONV_RATE_REG TMP1075_CONV_RATE_REG_27H /**< Default conv rate is 27.5ms */
#define TMP1075_CONV_RATE (28) /**< Default conversion rate is 27.5 ms */
/* this was rounded up to 28ms to retain usage of integers and to keep all times in ms */
#endif
#ifndef LM75_PARAMS
#if IS_USED(MODULE_LM75A)
#define LM75_PARAMS { .res = &lm75a_properties, \
.gpio_alarm = LM75_PARAM_INT, \
.conv_rate = LM75A_CONV_RATE, \
.i2c_bus = LM75_PARAMS_I2C, \
.i2c_addr = CONFIG_I2C_ADDR, \
.shutdown_mode = CONFIG_OPERATION_MODE, \
.tm_mode = CONFIG_THERMOSTAT_MODE, \
.polarity = CONFIG_OS_POLARITY, \
.fault_q = CONFIG_FAULT_QUEUE }
#endif
#if IS_USED(MODULE_TMP1075)
#define LM75_PARAMS { .res = &tmp1075_properties, \
.gpio_alarm = LM75_PARAM_INT, \
.conv_rate = TMP1075_CONV_RATE, \
.i2c_bus = LM75_PARAMS_I2C, \
.i2c_addr = CONFIG_I2C_ADDR, \
.shutdown_mode = CONFIG_OPERATION_MODE, \
.tm_mode = CONFIG_THERMOSTAT_MODE, \
.polarity = CONFIG_OS_POLARITY, \
.fault_q = CONFIG_FAULT_QUEUE, \
.conv_rate_reg = CONFIG_TMP1075_CONV_RATE_REG }
#endif
#endif /* LM75_PARAMS */
/**
* @brief LM75 power-up configuration
*/
static const lm75_params_t lm75_params[] =
{
LM75_PARAMS
};
#ifdef __cplusplus
}
#endif
#endif /* LM75_PARAMS_H */
/** @} */

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2021 ML!PA Consulting GmbH
*
* 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_lm75
*
* @{
* @file
* @brief Registers for the lm75 and derived (lm75a and tmp1075) temperature sensors.
*
* @author Vitor Batista <vitor.batista@ml-pa.com>
*
* @}
*/
#ifndef LM75_REGS_H
#define LM75_REGS_H
#ifdef __cplusplus
extern "C" {
#endif
/* LM75 register list */
#define LM75_TEMP_REG (0x00) /**< Temperature register pointer */
#define LM75_CONF_REG (0x01) /**< Configuration register pointer */
#define LM75_THYST_REG (0x02) /**< Hysteresis register pointer */
#define LM75_TOS_REG (0x03) /**< Overtemperature shutdown register pointer */
/* Device Operation mode */
#define NORMAL_MODE 0 /**< Continuous conversion mode */
#define SHUTDOWN_MODE 1 /**< Shutdown mode ON */
/* Device Thermostat operation mode */
#define COMPARATOR_MODE 0 /**< OS operation in comparator mode */
#define INTERRUPT_MODE 1 /**< OS operation in interrupt mode */
/* OS polarity */
#define OS_ACTIVE_LOW 0 /**< OS pin active on Low voltage */
#define OS_ACTIVE_HIGH 1 /**< OS pin active on positive voltage */
/* Consecutive fault measurements to trigger the alert function */
#define FAULT_1 0 /**< OS/ALERT active after 1 fault */
#define FAULT_2 1 /**< OS/ALERT active after 2 faults */
/* LM75A exclusive registers */
#define FAULT_4 2 /**< OS active after 4 faults */
#define FAULT_6 3 /**< OS active after 6 faults */
/* TMP1075 exclusive registers */
/* Device ID register - only available in the TMP1075 sensor */
#define TMP1075_DEVICE_ID_REG (0x0F) /**< ID register pointer */
/* fault queue values exclusive to the TMP1075 sensor */
#define FAULT_3 2 /**< ALERT active after 3 faults */
#define FAULT_4_TMP1075 3 /**< ALERT active after 4 faults */
/* Conversion rate setting when device is in continuous conversion mode
* Only configurable in the TMP1075 sensor */
#define TMP1075_CONV_RATE_REG_27H 0 /**< 27.5ms conversion rate */
#define TMP1075_CONV_RATE_REG_55 1 /**< 55ms conversion rate */
#define TMP1075_CONV_RATE_REG_110 2 /**< 110ms conversion rate */
#define TMP1075_CONV_RATE_REG_220 3 /**< 220ms conversion rate */
#ifdef __cplusplus
}
#endif
#endif /* LM75_REGS_H */
/** @} */

381
drivers/lm75/lm75.c Executable file
View File

@ -0,0 +1,381 @@
/*
* Copyright (C) 2021 ML!PA Consulting GmbH
*
* 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_lm75
*
* @{
* @file
* @brief Driver for the LM75 temperature sensor.
*
* A general driver for the lm75 temperature sensor including support for the
* lm75a and tmp1075 sensors as well.
*
* @author Vitor Batista <vitor.batista@ml-pa.com>
*
* @}
*/
#include "board.h"
#include "lm75.h"
#include "lm75_regs.h"
#include "lm75_params.h"
#include "log.h"
#include "kernel_defines.h"
#include "periph/gpio.h"
#include <stdint.h>
#include <byteorder.h>
#include "xtimer.h"
#include "debug.h"
#define I2C_BUS dev->lm75_params.i2c_bus
#define I2C_ADDR dev->lm75_params.i2c_addr
#define LM75_CONFIG_SHUTDOWN_MODE 0x01
#define TMP1075_CONFIG_ONE_SHOT_MODE 0x81 /* also sets the shutdown register to 1 */
#if IS_ACTIVE(MODULE_LM75A)
lm75_properties_t lm75a_properties = {
.os_res = LM75A_OS_RES,
.os_mult = LM75A_OS_MULT,
.temp_res = LM75A_TEMP_RES,
.temp_mult = LM75A_TEMP_MULT,
.os_shift = LM75A_OS_SHIFT,
.temp_shift = LM75A_TEMP_SHIFT,
};
#elif IS_ACTIVE(MODULE_TMP1075)
lm75_properties_t tmp1075_properties = {
.os_res = TMP1075_OS_RES,
.os_mult = TMP1075_OS_MULT,
.temp_res = TMP1075_TEMP_RES,
.temp_mult = TMP1075_TEMP_MULT,
.os_shift = TMP1075_OS_SHIFT,
.temp_shift = TMP1075_TEMP_SHIFT,
};
#endif
int lm75_init(lm75_t *dev, const lm75_params_t *params) {
dev->lm75_params = *params;
uint8_t config = (params->shutdown_mode) | (params->tm_mode << 1) \
| (params->polarity << 2) | (params->fault_q << 3);
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
/* read the device ID register of the TMP1075 sensor to confirm it is a TMP1075 */
if (IS_USED(MODULE_TMP1075) && (dev->lm75_params.res == &tmp1075_properties)) {
uint16_t deid = 0;
if (i2c_read_regs(I2C_BUS, I2C_ADDR, TMP1075_DEVICE_ID_REG, &deid, 2, 0) != 0) {
LOG_ERROR("Error reading device ID\n");
}
deid = (uint16_t)ntohs(deid);
/* checks if the device ID corresponds to the TMP1075 sensor
* and extends the parameter configuration if so */
if (deid == 0x7500) {
DEBUG("Device is a TMP1075\n");
config |= (params->conv_rate_reg << 5);
}
else {
LOG_ERROR("Device ID Register doesnt match");
i2c_release(I2C_BUS);
return LM75_ERROR;
}
}
else if (IS_USED(MODULE_LM75A) && (dev->lm75_params.res == &lm75a_properties)) {
DEBUG("Device is an LM75A\n");
}
else {
LOG_ERROR("Device not supported\n");
i2c_release(I2C_BUS);
return LM75_ERROR;
}
/* write the config byte into the configuration register */
if (i2c_write_reg(I2C_BUS, I2C_ADDR, LM75_CONF_REG, config, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR_I2C;
}
i2c_release(I2C_BUS);
return LM75_SUCCESS;
}
int lm75_get_temperature_raw(lm75_t *dev, int *temperature) {
int16_t temp;
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
/* read the temperature register */
if (i2c_read_regs(I2C_BUS, I2C_ADDR, LM75_TEMP_REG, &temp, 2, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR_I2C;
}
i2c_release(I2C_BUS);
/* since the value is read in big endian, it must be converted to little endian
* then it must be multiplied by its resolution
* and then shifted so only the correct number of bits is used */
*temperature = (int16_t)ntohs(temp) * dev->lm75_params.res->temp_res \
>> dev->lm75_params.res->temp_shift;
return LM75_SUCCESS;
}
int lm75_get_temperature(lm75_t *dev, int *temperature) {
if (lm75_get_temperature_raw(dev, temperature) != 0) {
return LM75_ERROR;
}
/* if the device's resolution is lower than mºC convert the temp to mºC */
if (dev->lm75_params.res->temp_mult < 1000) {
*temperature *= 1000 / dev->lm75_params.res->temp_mult;
}
/* if the device's resolution is greater than mºC
* truncates the device's values lower than the mºC range */
else if (dev->lm75_params.res->temp_mult > 1000) {
*temperature /= dev->lm75_params.res->temp_mult / 1000;
}
return LM75_SUCCESS;
}
int lm75_set_temp_limits(lm75_t *dev, int temp_hyst, int temp_os, gpio_cb_t cb, void *arg) {
/* Check if the OS alert pin is valid */
if (!gpio_is_valid(dev->lm75_params.gpio_alarm)) {
return LM75_ERROR;
}
/* Enable OS interrupt */
if (gpio_init_int(dev->lm75_params.gpio_alarm, GPIO_IN, \
dev->lm75_params.polarity ? GPIO_FALLING : GPIO_RISING, cb, arg) != 0) {
return LM75_ERROR;
}
if (temp_hyst >= temp_os) {
LOG_ERROR("THYST must be lower than TOS\n");
return LM75_ERROR;
}
int16_t temp_hyst_short, temp_os_short;
/* getting into the correct precision value in units of 10 */
temp_hyst = (temp_hyst * dev->lm75_params.res->os_mult) / 1000;
temp_os = (temp_os * dev->lm75_params.res->os_mult) / 1000;
/* temp must first be converted to 16 bit format, and sampled to its resolution
* then shifted by the number of unused bits and finally reversed
* into little endian for writing into the register.
* NOTE: values smaller than the resolution steps are truncated */
temp_hyst_short = (int16_t) (temp_hyst / dev->lm75_params.res->os_res);
temp_hyst_short = temp_hyst_short << dev->lm75_params.res->os_shift;
temp_hyst_short = ntohs(temp_hyst_short);
temp_os_short = (int16_t) (temp_os / dev->lm75_params.res->os_res);
temp_os_short = temp_os_short << dev->lm75_params.res->os_shift;
temp_os_short = ntohs(temp_os_short);
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
if (i2c_write_regs(I2C_BUS, I2C_ADDR, LM75_THYST_REG, &temp_hyst_short, 2, 0) != 0) {
i2c_release(I2C_BUS);
LOG_ERROR("ERROR wrtiting Hyst temp\n");
return LM75_ERROR_I2C;
}
if (i2c_write_regs(I2C_BUS, I2C_ADDR, LM75_TOS_REG, &temp_os_short, 2, 0) != 0) {
i2c_release(I2C_BUS);
LOG_ERROR("ERROR writing OS temp\n");
return LM75_ERROR_I2C;
}
i2c_release(I2C_BUS);
return LM75_SUCCESS;
}
int lm75_get_os_temp(lm75_t *dev, int *temperature) {
int16_t temp;
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
/* read the temperature register */
if (i2c_read_regs(I2C_BUS, I2C_ADDR, LM75_TOS_REG, &temp, 2, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR_I2C;
}
i2c_release(I2C_BUS);
/* since the value is read in big endian, it must be converted into little endian
* then it must be multiplied by its resolution
* and then shifted by the number of unused bits that must be discarded */
*temperature = (int16_t)ntohs(temp) * dev->lm75_params.res->os_res \
>> dev->lm75_params.res->os_shift;
return LM75_SUCCESS;
}
int lm75_get_hyst_temp(lm75_t *dev, int *temperature) {
int16_t temp;
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
/* read the temperature register */
if (i2c_read_regs(I2C_BUS, I2C_ADDR, LM75_THYST_REG, &temp, 2, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR_I2C;
}
i2c_release(I2C_BUS);
*temperature = (int16_t)ntohs(temp) * dev->lm75_params.res->os_res \
>> dev->lm75_params.res->os_shift;
return LM75_SUCCESS;
}
int lm75_get_os_pin(lm75_t *dev, bool *os_pin_state) {
if (!gpio_is_valid(dev->lm75_params.gpio_alarm)) {
LOG_ERROR("OS alert pin not connected or defined\n");
return LM75_ERROR;
}
*os_pin_state = !!gpio_read(dev->lm75_params.gpio_alarm) == dev->lm75_params.polarity;
return LM75_SUCCESS;
}
int lm75_poweroff(lm75_t *dev) {
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
uint8_t config;
if (i2c_read_reg(I2C_BUS, I2C_ADDR, LM75_CONF_REG, &config, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR;
}
/* sets every register to 0 except the shutdown reg and sees if it is active */
if ((config & LM75_CONFIG_SHUTDOWN_MODE) != 0) {
LOG_ERROR("device already in shutdown mode\n");
i2c_release(I2C_BUS);
return LM75_SUCCESS;
}
/* set the shutdown register to 1 (shutdown mode) and keeps every other intact */
config |= LM75_CONFIG_SHUTDOWN_MODE;
if (i2c_write_reg(I2C_BUS, I2C_ADDR, LM75_CONF_REG, config, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR;
}
i2c_release(I2C_BUS);
return LM75_SUCCESS;
}
int lm75_poweron(lm75_t *dev) {
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
uint8_t config;
if (i2c_read_reg(I2C_BUS, I2C_ADDR, LM75_CONF_REG, &config, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR;
}
/* sets every reg to 0 except the shutdown register and sees if it is active */
if ((config & LM75_CONFIG_SHUTDOWN_MODE) == 0) {
LOG_INFO("device is already awake\n");
i2c_release(I2C_BUS);
return LM75_SUCCESS;
}
/* set the shutdown bit to 0 (continuous conversion mode) and keep every other reg intact */
config &= ~LM75_CONFIG_SHUTDOWN_MODE;
if (i2c_write_reg(I2C_BUS, I2C_ADDR, LM75_CONF_REG, config, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR;
}
i2c_release(I2C_BUS);
return LM75_SUCCESS;
}
/* Performs a single temperature conversion from shutdown mode and goes back into shutdown */
int tmp1075_one_shot(lm75_t *dev) {
if (!IS_USED(MODULE_TMP1075) && (dev->lm75_params.res != &tmp1075_properties)) {
LOG_ERROR("Device incompatible with the one shot conversion function\n");
return LM75_ERROR;
}
else {
if (i2c_acquire(I2C_BUS) != 0) {
return LM75_ERROR_I2C;
}
uint8_t config;
if (i2c_read_reg(I2C_BUS, I2C_ADDR, LM75_CONF_REG, &config, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR;
}
/* set the shutdown and one shot mode bits to 1 and keep every other register intact */
config |= TMP1075_CONFIG_ONE_SHOT_MODE;
if (i2c_write_reg(I2C_BUS, I2C_ADDR, LM75_CONF_REG, config, 0) != 0) {
i2c_release(I2C_BUS);
return LM75_ERROR;
}
i2c_release(I2C_BUS);
}
return LM75_SUCCESS;
}
int lm75_low_power_mode(lm75_t *dev, uint16_t interval) {
if (IS_USED(MODULE_TMP1075) && (dev->lm75_params.res == &tmp1075_properties)) {
if (tmp1075_one_shot(dev) != 0) {
return LM75_ERROR;
}
xtimer_msleep(interval);
}
else {
if (lm75_poweron(dev) != 0) {
return LM75_ERROR;
}
/* this is required to ensure the temp register updates for followup readings
* otherwise the temperature register will have outdated and possibly bogus values */
if (interval < dev->lm75_params.conv_rate) {
xtimer_msleep(dev->lm75_params.conv_rate);
}
else {
xtimer_msleep(interval);
}
if (lm75_poweroff(dev) != 0) {
return LM75_ERROR;
}
}
return LM75_SUCCESS;
}

View File

@ -232,6 +232,10 @@ PSEUDOMODULES += mpu9250
PSEUDOMODULES += ina219
PSEUDOMODULES += ina220
# include vairants of lm75 drivers as pseudo modules
PSEUDOMODULES += lm75a
PSEUDOMODULES += tmp1075
# include variants of mrf24j40 drivers as pseudo modules
PSEUDOMODULES += mrf24j40m%