±0.5°C Maximum Accuracy Digital Temperature Sensor

Dependents:   mbed-os5-F303-18650-Manager-tp4056

MCP9808.cpp

Committer:
mcm
Date:
2019-04-25
Revision:
2:da266f1b2273
Parent:
0:6a0fe4376e0f
Child:
3:54515d036e93

File content as of revision 2:da266f1b2273:

/**
 * @brief       MCP9808.cpp
 * @details     ±0.5°C Maximum Accuracy Digital Temperature Sensor.
 *              Function file.
 *
 *
 * @return      N/A
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019    The ORIGIN
 * @pre         N/A.
 * @warning     N/A
 * @pre         This code belongs to AqueronteBlog ( http://unbarquero.blogspot.com ). All rights reserved.
 */

#include "MCP9808.h"


MCP9808::MCP9808 ( PinName sda, PinName scl, uint32_t addr, uint32_t freq )
    : _i2c           ( sda, scl )
    , _MCP9808_Addr ( addr )
{
    _i2c.frequency( freq );
}


MCP9808::~MCP9808()
{
}



/**
 * @brief       MCP9808_GetCONFIG ( MCP9808_config_reg_t* )
 *
 * @details     It gets CONFIG register value.
 *
 * @param[in]    N/A.
 *
 * @param[out]   myCONFIG: CONFIG register value.
 *
 *
 * @return       Status of MCP9808_GetCONFIG.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         N/A
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_GetCONFIG ( MCP9808_config_reg_t* myCONFIG )
{
  char      cmd[2]       = { 0U };
  uint16_t  myConfigAux  = 0U;
  uint32_t  aux;

  /* Read the register   */
  cmd[0]   =   MCP9808_CONFIG;
  aux      =   _i2c.write ( _MCP9808_Addr, &cmd[0], 1U, true );
  aux      =   _i2c.read  ( _MCP9808_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ) );

  /* Mask it and update it with the new value  */
  myConfigAux   =   cmd[0];
  myConfigAux <<=   8U;
  myConfigAux  |=   cmd[1];

  myCONFIG->t_hyst       =   (MCP9808_config_thyst_t)( myConfigAux & CONFIG_T_HYST_MASK );
  myCONFIG->shdn         =   (MCP9808_config_shdn_t)( myConfigAux & CONFIG_SHDN_MASK );
  myCONFIG->t_crit       =   (MCP9808_config_crit_lock_t)( myConfigAux & CONFIG_CRIT_LOCK_MASK );
  myCONFIG->t_win_lock   =   (MCP9808_config_win_lock_t)( myConfigAux & CONFIG_WIN_LOCK_MASK );
  myCONFIG->int_clear    =   (MCP9808_conf_int_clear_t)( myConfigAux & CONFIG_INT_CLEAR_MASK );
  myCONFIG->alert_stat   =   (MCP9808_config_alert_stat_t)( myConfigAux & CONFIG_ALERT_STAT_MASK );
  myCONFIG->alert_cnt    =   (MCP9808_config_alert_cnt_t)( myConfigAux & CONFIG_ALERT_CNT_MASK );
  myCONFIG->alert_sel    =   (MCP9808_config_alert_sel_t)( myConfigAux & CONFIG_ALERT_SEL_MASK );
  myCONFIG->alert_pol    =   (MCP9808_config_alert_pol_t)( myConfigAux & CONFIG_ALERT_POL_MASK );
  myCONFIG->alert_mod    =   (MCP9808_config_alert_mod_t)( myConfigAux & CONFIG_ALERT_MOD_MASK );



  if ( aux == I2C_SUCCESS )
  {
    return   MCP9808_SUCCESS;
  }
  else
  {
    return   MCP9808_FAILURE;
  }
}



/**
 * @brief       MCP9808_SetCONFIG ( MCP9808_config_reg_t )
 *
 * @details     It sets CONFIG register value.
 *
 * @param[in]    myCONFIG: CONFIG register value.
 *
 * @param[out]   N/A.
 *
 *
 * @return       Status of MCP9808_SetCONFIG.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         MCP9808_GetCONFIG function should be called first to mask the bits.
 * @warning     Be aware that some commands can NOT be written depending on Crit.Lock and/or Win.Lock values.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_SetCONFIG ( MCP9808_config_reg_t myCONFIG )
{
  char      cmd[3]       = { 0U };
  uint16_t  myConfigAux  = 0U;
  uint32_t  aux;

  /* Update CONFI register  */
  myConfigAux   =   ( myCONFIG.t_hyst | myCONFIG.shdn | myCONFIG.t_crit | myCONFIG.t_win_lock | myCONFIG.int_clear |
                      myCONFIG.alert_stat | myCONFIG.alert_cnt | myCONFIG.alert_sel | myCONFIG.alert_pol | myCONFIG.alert_mod );
  
  cmd[0]   =   MCP9808_CONFIG;
  cmd[1]   =   (char)( myConfigAux >> 8U );
  cmd[2]   =   (char)( myConfigAux & 0xFF );
  aux      =   _i2c.write ( _MCP9808_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false );




  if ( aux == I2C_SUCCESS )
  {
    return   MCP9808_SUCCESS;
  }
  else
  {
    return   MCP9808_FAILURE;
  }
}



/**
 * @brief       MCP9808_GetRawTA ( MCP9808_data_t* )
 *
 * @details     It gets ambient temperature register ( raw value ).
 *
 * @param[in]    N/A
 *
 * @param[out]   myRawTA: Raw T_A data.
 *
 *
 * @return       Status of MCP9808_GetRawTA.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         N/A
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_GetRawTA ( MCP9808_data_t* myRawTA )
{
  char     cmd[2]  = { 0U };
  uint32_t aux;

  /* Read the register   */
  cmd[0]   =   MCP9808_TA;
  aux      =   _i2c.write ( _MCP9808_Addr, &cmd[0], 1U, true );
  aux      =   _i2c.read  ( _MCP9808_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ) );
  
  /* Mask it and update it with the new value  */
  myRawTA->t_a_raw   =   cmd[0];
  myRawTA->t_a_raw <<=   8U;
  myRawTA->t_a_raw  |=   cmd[1];




  if ( aux == I2C_SUCCESS )
  {
    return   MCP9808_SUCCESS;
  }
  else
  {
    return   MCP9808_FAILURE;
  }
}



/**
 * @brief       MCP9808_GetTA ( MCP9808_data_t* )
 *
 * @details     It gets ambient temperature register ( Celsius degrees ).
 *
 * @param[in]    N/A
 *
 * @param[out]   myTA: T_A value and flags.
 *
 *
 * @return       Status of MCP9808_GetTA.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         This function also updates the comparison flags.
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_GetTA ( MCP9808_data_t* myTA )
{
  char          myUpperByte   =   0U;
  char          myLowerByte   =   0U;
  MCP9808::MCP9808_status_t aux;

  /* Read the register   */
  aux  =   MCP9808::MCP9808_GetRawTA ( &(*myTA) );
  
  /* Mask it and update it with the new value  */
  /* Mask the flags  */
  myTA->ta_vs_tcrit  =   (MCP9808_t_a_ta_vs_tcrit_t)( myTA->t_a_raw & T_A_TA_VS_TCRIT_MASK );
  myTA->ta_vs_tupper =   (MCP9808_t_a_ta_vs_tupper_t)( myTA->t_a_raw & T_A_TA_VS_TUPPER_MASK );
  myTA->t_lower      =   (MCP9808_t_a_ta_vs_tlower_t)( myTA->t_a_raw & T_A_TA_VS_TLOWER_MASK );
  myTA->t_a_sign     =   (MCP9808_t_a_sign_t)( myTA->t_a_raw & T_A_TA_SIGN_MASK );

  /* Mask the ambient temperature value  */
  myUpperByte  =   (char)( myTA->t_a_raw >> 8U );
  myLowerByte  =   (char)( myTA->t_a_raw & 0xFF );

  /* Clean the flags   */
  myUpperByte &=  ~(char)( ( T_A_TA_VS_TCRIT_MASK | T_A_TA_VS_TUPPER_MASK | T_A_TA_VS_TLOWER_MASK ) >> 8U );
  
  /* Check if T_A is negative/positive   */
  if ( myTA->t_a_sign == T_A_TA_SIGN_NEGATIVE )
  {
    /* Ambient temperature is NEGATIVE   */
    myUpperByte &=  ~(char)( T_A_TA_SIGN_MASK >> 8U );                                     // Clear the SIGN flag
    myTA->t_a    =   256.0f - (float)( ( myUpperByte * 16.0f ) + ( myLowerByte / 16.0f ) );   // Ambient temperature value
  }
  else
  {
    /* Ambient temperature is POSITIVE   */
    myTA->t_a    =   (float)( ( myUpperByte * 16.0f ) + ( myLowerByte / 16.0f ) );            // Ambient temperature value
  }


  return aux;
}



/**
 * @brief       MCP9808_GetManufacturerID ( MCP9808_data_t* )
 *
 * @details     It gets manufacturer ID.
 *
 * @param[in]    N/A.
 *
 * @param[out]   myManufacturerID:  Manufacturer ID code
 *
 *
 * @return       Status of MCP9808_GetManufacturerID.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         It should be 0x0054 ( hexadecimal ).
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_GetManufacturerID ( MCP9808_data_t* myManufacturerID )
{
  char     cmd[2]  = { 0U };
  uint32_t aux;

  /* Read the register   */
  cmd[0]   =   MCP9808_MANUFACTURER_ID;
  aux      =   _i2c.write ( _MCP9808_Addr, &cmd[0], 1U, true );
  aux      =   _i2c.read  ( _MCP9808_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ) );
  
  /* Mask it and update it with the new value  */
  myManufacturerID->manufacturerID   =   cmd[0];
  myManufacturerID->manufacturerID <<=   8U;
  myManufacturerID->manufacturerID  |=   cmd[1];



  if ( aux == I2C_SUCCESS )
  {
    return   MCP9808_SUCCESS;
  }
  else
  {
    return   MCP9808_FAILURE;
  }
}



/**
 * @brief       MCP9808_GetDeviceID ( MCP9808_data_t* )
 *
 * @details     It gets device ID and device revision.
 *
 * @param[in]    N/A
 *
 * @param[out]   myDeviceID: Both device ID and device revision 
 *
 *
 * @return       Status of MCP9808_GetDeviceID.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         Device ID should be 0x04 ( hexadecimal ).
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_GetDeviceID ( MCP9808_data_t* myDeviceID )
{
  char     cmd[2]  = { 0U };
  uint32_t aux;

  /* Read the register   */
  cmd[0]   =   MCP9808_DEVICE_ID;
  aux      =   _i2c.write ( _MCP9808_Addr, &cmd[0], 1U, true );
  aux      =   _i2c.read  ( _MCP9808_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ) );
  
  /* Mask it and update it with the new value  */
  myDeviceID->deviceID         =   cmd[0];
  myDeviceID->deviceRevision   =   cmd[1];



  if ( aux == I2C_SUCCESS )
  {
    return   MCP9808_SUCCESS;
  }
  else
  {
    return   MCP9808_FAILURE;
  }
}



/**
 * @brief       MCP9808_SetResolution ( MCP9808_data_t )
 *
 * @details     It sets the sensor resolution.
 *
 * @param[in]    myResolution: Device resolution.
 *
 * @param[out]   N/A 
 *
 *
 * @return       Status of MCP9808_SetResolution.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         Resolution vs timing conversion:
 *                · Resolution: +0.5°C    ( t_CONV = 30ms typical )
 *                · Resolution: +0.25°C   ( t_CONV = 65ms typical )
 *                · Resolution: +0.125°C  ( t_CONV = 130ms typical )
 *                · Resolution: +0.0625°C ( power-up default, t_CONV = 250ms typical )
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_SetResolution ( MCP9808_data_t myResolution )
{
  char     cmd[2]  = { 0U };
  uint32_t aux;

  /* Read the register   */
  cmd[0]   =   MCP9808_RESOLUTION;
  cmd[1]   =   myResolution.resolution;
  aux      =   _i2c.write ( _MCP9808_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false );



  if ( aux == I2C_SUCCESS )
  {
    return   MCP9808_SUCCESS;
  }
  else
  {
    return   MCP9808_FAILURE;
  }
}



/**
 * @brief       MCP9808_GetResolution ( MCP9808_data_t* )
 *
 * @details     It gets the sensor resolution.
 *
 * @param[in]    N/A.
 *
 * @param[out]   myResolution: Device resolution. 
 *
 *
 * @return       Status of MCP9808_GetResolution.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         Resolution vs timing conversion:
 *                · Resolution: +0.5°C    ( t_CONV = 30ms typical )
 *                · Resolution: +0.25°C   ( t_CONV = 65ms typical )
 *                · Resolution: +0.125°C  ( t_CONV = 130ms typical )
 *                · Resolution: +0.0625°C ( power-up default, t_CONV = 250ms typical )
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_GetResolution ( MCP9808_data_t* myResolution )
{
  char     cmd  = 0U;
  uint32_t aux;

  /* Read the register   */
  cmd  =   MCP9808_RESOLUTION;
  aux  =   _i2c.write ( _MCP9808_Addr, &cmd, 1U, true );
  aux  =   _i2c.read  ( _MCP9808_Addr, &cmd, 1U );
  
  /* Mask it and update it with the new value  */
  myResolution->resolution     =   (MCP9808_resolution_t)cmd;



  if ( aux == I2C_SUCCESS )
  {
    return   MCP9808_SUCCESS;
  }
  else
  {
    return   MCP9808_FAILURE;
  }
}



/**
 * @brief       MCP9808_SetT_Limit ( MCP9808_registers_t, MCP9808_data_t )
 *
 * @details     It sets temperature limit for: T_UPPER, T_LOWER or T_CRIT.
 *
 * @param[in]    myTLimit:        Temperature limit register: T_UPPER, T_LOWER or T_CRIT.
 * @param[in]    myTValue_Limit:  Temperature limit value.
 *
 * @param[out]   N/A. 
 *
 *
 * @return       Status of MCP9808_SetT_Limit.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_SetT_Limit ( MCP9808_registers_t myTLimit, MCP9808_data_t myTValue_Limit )
{
  char     cmd[2]     = { 0U };
  int8_t   myDecimal  = 0U;
  char     myIntegral = 0U;
  uint32_t aux;
  
  /* Only temperature limit registers can keep going   */
  if ( ( myTLimit != MCP9808_TUPPER ) || ( myTLimit != MCP9808_TLOWER ) || ( myTLimit != MCP9808_TCRIT ) )
  {
    return  MCP9808_FAILURE;
  }
  else
  {
    /* Parse the data  */
    myIntegral =   (int8_t)myTValue_Limit.t_upper;
    myDecimal  =   (char)( ( myTValue_Limit.t_upper - myIntegral ) * 100.0f );
  
    /* Check the decimal part is correct; Valid decimal values: 0.00, 0.25, 0.50 or 0.75   */
    myIntegral <<=  4U;
    myDecimal  <<=  2U;
    if( ( myDecimal != TEMPERATURE_LIMIT_DECIMAL_PART_0_00C ) || ( myDecimal != TEMPERATURE_LIMIT_DECIMAL_PART_0_25C ) || 
        ( myDecimal != TEMPERATURE_LIMIT_DECIMAL_PART_0_50C ) || ( myDecimal != TEMPERATURE_LIMIT_DECIMAL_PART_0_50C ) )
    {
      return  MCP9808_FAILURE;
    }
    else
    {
      /* Update the register with the new value  */
      cmd[0]  =   myTLimit;
      cmd[1]  =   ( myIntegral | myDecimal );
      aux     =   _i2c.write ( _MCP9808_Addr, &cmd[0], sizeof( cmd )/sizeof( cmd[0] ), false );
  

      if ( aux == I2C_SUCCESS )
      {
        return   MCP9808_SUCCESS;
      }
      else
      {
        return   MCP9808_FAILURE;
      }
    }
  }
}



/**
 * @brief       MCP9808_GetT_Limit ( MCP9808_registers_t, MCP9808_data_t* )
 *
 * @details     It gets temperature limit for: T_UPPER, T_LOWER or T_CRIT.
 *
 * @param[in]    myTLimit:        Temperature limit register: T_UPPER, T_LOWER or T_CRIT.
 *
 * @param[out]   myTValue_Limit:  Temperature limit value for the chosen register. 
 *
 *
 * @return       Status of MCP9808_GetT_Limit.
 *
 *
 * @author      Manuel Caballero
 * @date        25/April/2019
 * @version     25/April/2019     The ORIGIN
 * @pre         N/A.
 * @warning     N/A.
 */
MCP9808::MCP9808_status_t MCP9808::MCP9808_GetT_Limit ( MCP9808_registers_t myTLimit, MCP9808_data_t* myTValue_Limit )
{
  char     cmd        = 0U;
  char     myDecimal  = 0U;
  float    myAuxValue = 0U;
  uint32_t aux;
  
  /* Only temperature limit registers can keep going   */
  if ( ( myTLimit != MCP9808_TUPPER ) || ( myTLimit != MCP9808_TLOWER ) || ( myTLimit != MCP9808_TCRIT ) )
  {
    return  MCP9808_FAILURE;
  }
  else
  {
    /* Read the register   */
    cmd  =   myTLimit;
    aux  =   _i2c.write ( _MCP9808_Addr, &cmd, 1U, true );
    aux  =   _i2c.read  ( _MCP9808_Addr, &cmd, 1U );
    
    /* Process the data  */
    myAuxValue =   (float)( ( cmd & TEMPERATURE_LIMIT_INTEGRAL_PART_MASK ) >> 4U );
    myDecimal  =   ( ( cmd & TEMPERATURE_LIMIT_DECIMAL_PART_MASK ) >> 2U );
    
    /* Process the decimal part  */
    for( cmd = ( TEMPERATURE_LIMIT_DECIMAL_PART_0_00C >> 2U ); cmd < myDecimal; cmd++ )
    {
      myAuxValue  +=   0.25f;
    }

    /* Parse the data   */
    switch( myTLimit )
    {
      case MCP9808_TUPPER:
        myTValue_Limit->t_upper  =   (float)myAuxValue;
        break;
      
      case MCP9808_TLOWER:
        myTValue_Limit->t_lower  =   (float)myAuxValue;
        break;

      case MCP9808_TCRIT:
        myTValue_Limit->t_crit  =   (float)myAuxValue;
        break;

      default:
        return  MCP9808_FAILURE;
        break;
    }

  

    if ( aux == I2C_SUCCESS )
    {
      return   MCP9808_SUCCESS;
    }
    else
    {
      return   MCP9808_FAILURE;
    }
  }
}