diff --git a/drivers/Kconfig b/drivers/Kconfig index f86c928127..6412a3b5fe 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -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" diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 2c9c47ab66..2277628649 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -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 diff --git a/drivers/include/lm75.h b/drivers/include/lm75.h new file mode 100755 index 0000000000..3fb571953c --- /dev/null +++ b/drivers/include/lm75.h @@ -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 + * + * @} + */ + +/** + * @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 */ +/** @} */ diff --git a/drivers/lm75/Kconfig b/drivers/lm75/Kconfig new file mode 100755 index 0000000000..2b11f2b6ed --- /dev/null +++ b/drivers/lm75/Kconfig @@ -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 diff --git a/drivers/lm75/Makefile b/drivers/lm75/Makefile new file mode 100755 index 0000000000..48422e909a --- /dev/null +++ b/drivers/lm75/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/lm75/Makefile.dep b/drivers/lm75/Makefile.dep new file mode 100755 index 0000000000..a51c7989e4 --- /dev/null +++ b/drivers/lm75/Makefile.dep @@ -0,0 +1,3 @@ +FEATURES_REQUIRED += periph_gpio periph_gpio_irq +FEATURES_REQUIRED += periph_i2c +USEMODULE += xtimer diff --git a/drivers/lm75/Makefile.include b/drivers/lm75/Makefile.include new file mode 100755 index 0000000000..4fa3aa14e0 --- /dev/null +++ b/drivers/lm75/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_lm75 := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_lm75) diff --git a/drivers/lm75/include/lm75_params.h b/drivers/lm75/include/lm75_params.h new file mode 100755 index 0000000000..60a2769d57 --- /dev/null +++ b/drivers/lm75/include/lm75_params.h @@ -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 + * + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/lm75/include/lm75_regs.h b/drivers/lm75/include/lm75_regs.h new file mode 100755 index 0000000000..82b8a49a9a --- /dev/null +++ b/drivers/lm75/include/lm75_regs.h @@ -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 + * + * @} + */ + +#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 */ +/** @} */ diff --git a/drivers/lm75/lm75.c b/drivers/lm75/lm75.c new file mode 100755 index 0000000000..2a8d1d08c1 --- /dev/null +++ b/drivers/lm75/lm75.c @@ -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 + * + * @} + */ + +#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 +#include +#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; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index b95c836451..681ceee0ce 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -236,6 +236,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% diff --git a/tests/driver_lm75/Makefile b/tests/driver_lm75/Makefile new file mode 100755 index 0000000000..e49b958cf4 --- /dev/null +++ b/tests/driver_lm75/Makefile @@ -0,0 +1,8 @@ +include ../Makefile.tests_common + +DRIVER ?= tmp1075 + +USEMODULE += $(DRIVER) +USEMODULE += xtimer + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_lm75/main.c b/tests/driver_lm75/main.c new file mode 100755 index 0000000000..c7ab466cd0 --- /dev/null +++ b/tests/driver_lm75/main.c @@ -0,0 +1,152 @@ +/* + * 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 Test program for the driver for the LM75 temperature sensor. + * + * A test setup for the driver for the lm75 temperature sensor + * including support for the lm75a and tmp1075 sensors as well. + * + * @author Vitor Batista + * + * @} + */ + +#include "board.h" +#include "lm75.h" +#include "lm75_regs.h" +#include "lm75_params.h" +#include "xtimer.h" +#include "log.h" +#include +#include + +/* prints currently set OS and HYST temperatures */ +int lm75_check_set_temperature_limits(lm75_t *dev) { + + int t_os, t_hyst; + /* get already set OS and HYST values */ + lm75_get_os_temp(dev, &t_os); + printf("Set OS temp is %d.%dºC\n", t_os / dev->lm75_params.res->os_mult, \ + t_os % dev->lm75_params.res->os_mult); + + lm75_get_hyst_temp(dev, &t_hyst); + printf("Set HYST temp is %d.%dºC\n", t_hyst / dev->lm75_params.res->os_mult, \ + t_hyst % dev->lm75_params.res->os_mult); + return 0; +} + +/* This function prints the current temperature with maximum precision */ +int lm75_print_temperature_raw(lm75_t *dev) { + + int temp; + if (lm75_get_temperature_raw(dev, &temp) != 0) { + return -1; + } + + printf("%d.%04dºC\n", temp / dev->lm75_params.res->temp_mult, \ + temp % dev->lm75_params.res->temp_mult); + + return 0; +} + +/* This function prints the current temperature with maximum precision */ +int lm75_print_temperature(lm75_t *dev) { + + int temp; + if (lm75_get_temperature(dev, &temp) != 0) { + return -1; + } + printf("%d.%dºC\n", temp / 1000, temp % 1000); + + return 0; +} + +/* This function is implemented to make an explicit call + * for the comparison functionality of the sensor */ +int lm75_check_alarm_state(lm75_t *dev) { + + int temp, t_os, t_hyst; + + /* read the current temperature on the device's register */ + lm75_get_temperature_raw(dev, &temp); + lm75_get_os_temp(dev, &t_os); + lm75_get_hyst_temp(dev, &t_hyst); + + /* this is required not to discard the leading zero and + * output a false result in the case the temperature is x.0625 */ + + if ((temp / dev->lm75_params.res->temp_mult > t_os / dev->lm75_params.res->os_mult) || \ + ((temp / dev->lm75_params.res->temp_mult == t_os / dev->lm75_params.res->os_mult) && \ + (temp % dev->lm75_params.res->temp_mult > t_os % dev->lm75_params.res->os_mult))) { + LOG_INFO("OS Limit exceeded\n"); + } + + else if ((temp / dev->lm75_params.res->temp_mult < t_hyst / dev->lm75_params.res->os_mult) || \ + ((temp / dev->lm75_params.res->temp_mult == t_hyst / dev->lm75_params.res->os_mult) && \ + (temp % dev->lm75_params.res->temp_mult < t_hyst % dev->lm75_params.res->os_mult))) { + LOG_INFO("HYST Limit exceeded\n"); + } + + return 0; +} + +static void cb(void *arg); + +int main(void) { + + lm75_t descriptor; + lm75_t *dev = &descriptor; + bool alert_state; + puts("Sensor test..."); + /* LM75 Sensor initialization */ + puts("Initialization..."); + if (lm75_init(dev, lm75_params) != LM75_SUCCESS) { + puts("Initialization failed"); + return -1; + } + else { + puts("Initialization succeeded"); + } + + /* Set the hysteresis and overtemperature shutdown */ + if (lm75_set_temp_limits(dev, 24500, 29000, cb, NULL) != LM75_SUCCESS) { + puts("error setting Hyst and/or OS temps"); + return -1; + } + + lm75_check_set_temperature_limits(dev); + + /* Check already set values */ + while (1) { + + /* lm75_print_temperature_raw(dev); prints raw temp */ + lm75_print_temperature(dev); /* prints temp in mºC */ + lm75_check_set_temperature_limits(dev); + lm75_low_power_mode(dev, 3000); /* testing in low power mode */ + /* xtimer_msleep(1000); testing in continuous mode */ + /* lm75_check_alarm_state(dev); checking if temp is within limits implicitly */ + if (lm75_get_os_pin(dev, &alert_state) != 0) { + LOG_ERROR("Error reading OS pin state\n"); + } + else if (alert_state == 1) { + puts("OS pin is active"); + } + } + + return 0; +} + +static void cb(void *arg) { + (void)arg; + puts("INTERRUPT"); +}