LM77 Temperature sensor with I2C interface. Provides temperature in celsius and fahrenheit. The device also supports temperature alerts.

Dependents:   mbed_HP03SA_LM77

LM77.cpp

Committer:
wim
Date:
2015-01-10
Revision:
0:8e812deb9f66

File content as of revision 0:8e812deb9f66:

/* mbed LM77 Library, for an I2C Temperature sensor
 * Copyright (c) 2015, v01: WH, Initial version
 * 
 * The LM77 is a digital temperature sensor and thermal window comparator with an I2C Serial Bus interface. 
 * The open-drain Interrupt (INT) output becomes active whenever temperature goes outside a programmable window,
 * while a separate Critical Temperature Alarm (T_CRIT_A) output becomes active when the temperature exceeds a 
 * programmable critical limit. The INT output can operate in either a comparator or event mode, while the T_CRIT_A 
 * output operates in comparator mode only. The host can program both the upper and lower limits of the window as 
 * well as the critical temperature limit. Programmable hysterisis as well as a fault queue are available to minimize 
 * false tripping. Two pins (A0, A1) are available for address selection. The sensor powers up with default thresholds 
 * of 2°C THYST, 10°C TLOW, 64°C THIGH, and 80°C TCRIT.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "LM77.h"

/** Create an LM77 device instance
  *
  * @param i2c    I2C Bus 
  * @param char deviceAddress I2C slaveaddress
  */ 
LM77::LM77(I2C *i2c, char deviceAddress) : _i2c(i2c), _slaveAddress(deviceAddress) {

}

/** Get Status
 *
 * @return bool Sensor ready 
 */ 
bool LM77::getStatus(void) {
  char buffer[2];      
  int status;
    
  // Dummy operation to check status
  buffer[0] = 0x00;
  status=_i2c->write(_slaveAddress, buffer, 0);

  return (status==0);           // True when device found   
}


/** Get the current power mode of the LM77
  *
  * @returns The current power mode as a PowerMode enum.
  */
LM77::PowerMode LM77::getPowerMode() {

    //Read the 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Return the status of the SHUTDOWN bit
    if (value & LM77_PWR_MSK) {
        return POWER_SHUTDOWN;
    }    
    else {
        return POWER_NORMAL;
    }    
}


/** Set the power mode of the LM77
  *
  * @param mode The new power mode as a PowerMode enum.
  */
void LM77::setPowerMode(PowerMode mode) {
    
    //Read the current 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Set or clear the SHUTDOWN bit
    if (mode == POWER_SHUTDOWN) {
        value |= LM77_PWR_DWN;
    }    
    else {
        value &= ~LM77_PWR_DWN;
    }    

    //Write the value back out
    _writeReg8(LM77_REG_CONF, value);
}


/** Get the current INT pin mode of the LM77
  *
  * @returns The current INT pin mode as an IntMode enum.
  */
LM77::IntMode LM77::getIntMode() {
    
    //Read the 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Return the status of the INT_MODE bit
    if (value & LM77_INT_MSK) {
        return INT_EVENT;
    }    
    else {
        return INT_COMPARATOR;
    }    
}


/** Set the INT pin mode of the LM77
  *
  * @param mode The new INT pin mode as an IntMode enum.
  */
void LM77::setIntMode(IntMode mode) {

    //Read the current 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Set or clear the OS_COMP_INT bit
    if (mode == INT_EVENT) {
        value |= LM77_INT_EVENT;
    }    
    else {
        // Comparator mode 
        value &= ~LM77_INT_EVENT;
    }    

    //Write the value back out
    _writeReg8(LM77_REG_CONF, value);
}


/** Get the current INT pin polarity of the LM77
  *
  * @returns The current INT pin polarity as an PinPolarity enum.
  */
LM77::PinPolarity LM77::getIntPolarity() {
    
    //Read the 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Return the status of the POL_INT bit
    if (value & LM77_POL_INT_MSK) {
        return ACTIVE_HIGH;
    }    
    else {
        return ACTIVE_LOW;
    }    
}

/** Set the INT pin polarity of the LM77
  *
  * @param polarity The new INT pin polarity as an PinPolarity enum.
  */
void LM77::setIntPolarity(PinPolarity polarity)
{
    //Read the current 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Set or clear the POL_INT bit
    if (polarity == ACTIVE_HIGH) {
        value |= LM77_POL_INT_H;
    }    
    else {
        value &= ~LM77_POL_INT_H;
    }    

    //Write the value back out
    _writeReg8(LM77_REG_CONF, value);
}

/** Get the current T_CRIT_A pin polarity of the LM77
  *
  * @returns The current T_CRIT_A pin polarity as an PinPolarity enum.
  */
LM77::PinPolarity LM77::getTCritPolarity() {
    
    //Read the 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Return the status of the POL_TCRIT bit
    if (value & LM77_POL_TCRIT_MSK) {
        return ACTIVE_HIGH;
    }    
    else {
        return ACTIVE_LOW;
    }    
}

/** Set the T_CRIT_A pin polarity of the LM77
  *
  * @param polarity The new T_CRIT_A pin polarity as an PinPolarity enum.
  */
void LM77::setTCritPolarity(PinPolarity polarity)
{
    //Read the current 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Set or clear the POL_TCRIT bit
    if (polarity == ACTIVE_HIGH) {
        value |= LM77_POL_TCRIT_H;
    }    
    else {
        value &= ~LM77_POL_TCRIT_H;
    }    

    //Write the value back out
    _writeReg8(LM77_REG_CONF, value);
}


/** Get the current pin and flag fault queue length of the LM77
  *
  * @returns The current pin and flag fault queue length as an FaultQueue enum.
  */
LM77::FaultQueue LM77::getFaultQueue()
{
    //Read the 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Return the status of the FAULT_QUE bit
    if (value & LM77_FQU_MSK) {
        return FAULT_QUEUE_4;
    }    
    else {
        return FAULT_QUEUE_1;
    }    
}


/** Set the pin and flag fault queue length of the LM77
  *
  * @param queue The new pin and flag fault queue length as an FaultQueue enum.
  */
void LM77::setFaultQueue(FaultQueue queue)
{
    //Read the current 8-bit register value
    char value = _readReg8(LM77_REG_CONF);

    //Set the new FAULT_QUE bit
    if (queue == FAULT_QUEUE_4) {
        value |= LM77_FQU_4;
    }    
    else {
        value &= ~LM77_FQU_4;
    }    

    //Write the value back out
    _writeReg8(LM77_REG_CONF, value);
}

/** Get the current Critical alert temperature threshold of the LM77
  * Reset value is 80.0 °C.   
  *
  * @returns The current Critical alert temperature threshold in °C.
  */
float LM77::getCritAlertTemp() {
    
  //Use the 9-bit helper to read the TCRIT register
  return _readTempHelper(LM77_REG_TCRIT);
}

/** Set the critical alert temperature threshold of the LM77
  * Reset value is 80.0 °C.   
  *
  * @param temp The new critical alert temperature threshold in °C.
  */
void LM77::setCritAlertTemp(float temp) {
    
  //Use the 9-bit helper to write to the TCRIT register
  _writeTempHelper(LM77_REG_TCRIT, temp);
}

/** Get the current Low temperature alert threshold of the LM77
  * Reset value is 10.0 °C.   
  *
  * @returns The current Low temperature alert threshold in °C.
  */
float LM77::getLowAlertTemp() {

  //Use the 9-bit helper to read the TLOW register
  return _readTempHelper(LM77_REG_TLOW);
}

/** Set the current Low temperature alert threshold of the LM77
  * Reset value is 10.0 °C.   
  *
  * @param temp The new Low alert temperature threshold in °C.
  */
void LM77::setLowAlertTemp(float temp){
  
  //Use the 9-bit helper to write to the TLOW register  
  _writeTempHelper(LM77_REG_TLOW, temp);
}    

/** Get the current High temperature alert threshold of the LM77
  * Reset value is 64.0 °C.   
  *
  * @returns The current High temperature alert threshold in °C.
  */
float LM77::getHighAlertTemp(){

  //Use the 9-bit helper to read the THIGH register
  return _readTempHelper(LM77_REG_THIGH);
}    
    
/** Set the High temperature alert threshold of the LM77
  * Reset value is 64.0 °C.   
  *
  * @param temp The new High temperature alert threshold in °C.
  */
void LM77::setHighAlertTemp(float temp) {

  //Use the 9-bit helper to write to the THIGH register  
  _writeTempHelper(LM77_REG_THIGH, temp);   
}


/** Get the current alert temperature hysteresis of the LM77
  * Reset value is 2.0 °C.
  *
  * @returns The current alert temperature hysteresis in °C.
  */ 
float LM77::getAlertHyst() {
    
  //Use the 9-bit helper to read the THYST register
  return _readTempHelper(LM77_REG_THYST);
}

/** Set the alert temperature hysteresis of the LM77
  * Reset value is 2.0 °C.
  *
  * @param temp The new alert temperature hysteresis in °C.
  */
void LM77::setAlertHyst(float temp) {
    
  //Use the 9-bit helper to write to the THYST register
  _writeTempHelper(LM77_REG_THYST, temp);
}


/** Get Temperature as int in °Celsius x 10
  *
  * @return int Temperature in °Celsius x 10
  */ 
int LM77::getTemperatureInt(void) {
  
    //Signed return value
    int16_t value;

    //Read the 9-bit raw temperature value in 0.5 degree resolution
    value = _readReg16(LM77_REG_TEMP) >> 3;

    //Sign extend negative numbers
    if (value & (1 << 9))
        value |= 0xFE00;

    //Return the temperature in °C x 10
    return (int) value * 5;    // x 10 x 0.5
}

/** Get the Alert flags of the LM77
  *
  * @returns The current Alert flags as int.
  */
int LM77::getAlertFlags() {

    //Signed return value
    int16_t value;

    //Read the 9-bit raw temperature value and the flags
    value = _readReg16(LM77_REG_TEMP);
    
    //Return the flag bits
    // LM77_FLAG_LOW    0x01
    // LM77_FLAG_HIGH   0x02
    // LM77_FLAG_CRIT   0x04
    // LM77_FLAG_MSK    0x07   
    return (value & LM77_FLAG_MSK);
}


/** Get Temperature as float in °Celsius
  *
  * @return float Temperature in °Celsius
  */ 
float LM77::getTemperature(void) {
  
  return _readTempHelper(LM77_REG_TEMP);
}

#ifdef MBED_OPERATORS
LM77::operator float() {

  //Return the current temperature reading
  return getTemperature();
}
#endif

/** Convert Temperature from °Celsius into °Fahrenheit
  *
  * @param  float celsius in °Celsius  
  * @return float temperature in °Fahrenheit
  */ 
float LM77::celsiusToFahrenheit(float celsius) {
  
  return ((celsius * 9.0f) / 5.0f) + 32.0f; // Convert to Fahrenheit
}


/** Read 8 bit value from register
  *
  * @param reg Index of register
  * @return data value from register
  */     
char LM77::_readReg8(char reg) {
    
    //Select the register
    _i2c->write(_slaveAddress, &reg, 1, true);

    //Read the 8-bit register
    _i2c->read(_slaveAddress, &reg, 1);

    //Return the byte
    return reg;
}

/** Write 8 bit value to register
  *
  * @param reg Index of register
  * @param data value to write
  */     
void LM77::_writeReg8(char reg, char data) {
    
    //Create a temporary buffer
    char buf[2];

    //Load the register address and 8-bit data
    buf[0] = reg;
    buf[1] = data;

    //Write the data
    _i2c->write(_slaveAddress, buf, 2);
}

/** Read 16 bit value from register
  * Used for Critical temp threshold, Low and High temp threshold window and Hysteresis
  *
  * @param reg Index of register
  * @return data value from register  
  */     
int16_t LM77::_readReg16(char reg) {
    
    //Create a temporary buffer
    char buf[2];
    
    //Select the register
    _i2c->write(_slaveAddress, &reg, 1, true);

    //Read the 16-bit register
    _i2c->read(_slaveAddress, buf, 2);

    //Return the combined 16-bit value
    return (buf[0] << 8) | buf[1];
}

/** Write 16 bit value to register
  * Used for Critical temp threshold, Low and High temp threshold window and Hysteresis
  *
  * @param reg Index of register
  * @param data value to write
  */     
void LM77::_writeReg16(char reg, int16_t data) {
    
    //Create a temporary buffer
    char buf[3];

    //Load the register address and 16-bit data
    buf[0] = reg;
    buf[1] = data >> 8;
    buf[2] = data;

    //Write the data
    _i2c->write(_slaveAddress, buf, 3);    
}

/** Get Temperature as float in °Celsius
  * Used for Critical temp threshold, Low and High temp threshold window and Hysteresis
  *
  * @param reg Index of register to read temp value
  * @return float Temperature in °Celsius
  */     
float LM77::_readTempHelper(char reg) {

    //Signed return value
    int16_t value;
       
#if(1)
    //Read the 9-bit raw temperature value in 0.5 degree resolution
    value = _readReg16(reg) >> 3;
#else
    int16_t readreg;
    
    //Test neg temps
    readreg   = 0xFE70;  //-25 degree
    value = readreg >> 3;
#endif   
    
    //Sign extend negative numbers
    if (value & (1 << 9))
        value |= 0xFE00;

    //Return the temperature in °C
    return (float) value * 0.5f; 
}


/** Set Temperature as float in °Celsius
 * Used for Critical temp threshold, Low and High temp threshold window and Hysteresis
 *
 * @param reg Index of register to write temp value
 * @param temp float Temperature value in °Celsius
 */        
void LM77::_writeTempHelper(char reg, float temp) {
    //Signed value
    int16_t value;    

    //Range limit temp
    if (temp < -55.0)
        temp = -55.0;
    else if (temp > 125.0)
        temp = 125.0;

    //Extract and shift the signed integer
    value =  temp * 2.0f;
    value <<= 3;

    //Send the new value
    _writeReg16(reg, value);
}