Library files for ADT74xx, ADT73xx famile of products.

Dependents:  

adt7420.c

Committer:
EndaKilgarriff
Date:
2019-08-28
Revision:
1:a86efb0ef0ad

File content as of revision 1:a86efb0ef0ad:

/***************************************************************************//**
 *   @file   adt7420.c
 *   @brief  Implementation of ADT7420 Driver.
 *   @author DBogdan (dragos.bogdan@analog.com)
********************************************************************************
 * Copyright 2012, 2019(c) Analog Devices, Inc.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *  - Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  - Neither the name of Analog Devices, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *  - The use of this software may or may not infringe the patent rights
 *    of one or more patent holders.  This license does not release you
 *    from the requirement that you obtain separate licenses from these
 *    patent holders to use this software.
 *  - Use of the software either in source or binary form, must be run
 *    on or directly connected to an Analog Devices Inc. component.
 *
 * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <stdint.h>
#include <stdlib.h>
#include "platform_drivers.h"
#include "adt7420.h"

static const struct adt7420_chip_info chip_info[] = {
		[ID_ADT7410] = {
		.resolution = 16,
		.communication = I2C,
	},
		[ID_ADT7420] = {
		.resolution = 16,
		.communication = I2C,
	},
		[ID_ADT7422] = {
		.resolution = 16,
		.communication = I2C,
	},
		[ID_ADT7310] = {
		.resolution = 16,
		.communication = SPI,
	},
		[ID_ADT7320] = {
		.resolution = 16,
		.communication = SPI,
	}
};

/***************************************************************************//**
 * @brief Reads the value of a register SPI/I2C.
 *
 * @param dev              - The device structure.
 * @param register_address - Address of the register.
 *
 * @return register_value  - Value of the register.
*******************************************************************************/
uint16_t get_register_value(struct adt7420_dev *dev,
	                        uint8_t register_address)
{
	uint16_t register_value = 0;

	//remap register map
	if (chip_info[dev->act_device].communication == SPI) {
		switch (register_address) {
			case REG_TEMP:	register_address = ADT7320_REG_TEMP;break;	 // Temperature value
			case REG_STATUS:register_address = ADT7320_REG_STATUS;break; // status info
			case REG_CONFIG:register_address = ADT7320_REG_CONFIG;break; // Configuration
			case REG_T_CRIT:register_address = ADT7320_REG_T_CRIT;break; // Temperature CRIT setpoint (147'C)
			case REG_HIST:	register_address = ADT7320_REG_HIST;break;   // Temperature HYST setpoint (5'C)
			case REG_T_HIGH:register_address = ADT7320_REG_T_HIGH;break; // Temperature HIGH setpoint (64'C)
			case REG_T_LOW: register_address = ADT7320_REG_T_LOW;break;  // Temperature LOW setpoint (10'C)
			case REG_ID:	register_address = ADT7320_REG_ID;break;	 // ID value
		}
		register_value = adt7420_get_register_value(dev, register_address);
	} else {
        switch(register_address) {
		case REG_TEMP: {
			register_value  = adt7420_get_register_value(dev,ADT7420_REG_TEMP_MSB) << 8;
			register_value |= adt7420_get_register_value(dev,ADT7420_REG_TEMP_LSB);
			break; 			
		    }
		case REG_STATUS:  register_value = adt7420_get_register_value(dev, ADT7420_REG_STATUS); break; 			
		case REG_CONFIG:  register_value = adt7420_get_register_value(dev, ADT7420_REG_CONFIG);break; 			
		case REG_T_HIGH: {
			register_value  = adt7420_get_register_value(dev,ADT7420_REG_T_HIGH_MSB) << 8;
			register_value |= adt7420_get_register_value(dev,ADT7420_REG_T_HIGH_LSB);
			break; 			
		    }
		case REG_T_LOW: {
			register_value  = adt7420_get_register_value(dev,ADT7420_REG_T_LOW_MSB) << 8;
			register_value |= adt7420_get_register_value(dev,ADT7420_REG_T_LOW_LSB);
			break; 			
		    }
		case REG_T_CRIT: {
			register_value  = adt7420_get_register_value(dev,ADT7420_REG_T_CRIT_MSB) << 8;
			register_value |= adt7420_get_register_value(dev,ADT7420_REG_T_CRIT_LSB);
			break; 			
		    }
		case REG_HIST:    register_value = adt7420_get_register_value(dev, ADT7420_REG_HIST);break; 			
		case REG_ID:      register_value = adt7420_get_register_value(dev, ADT7420_REG_ID);break; 			
		} 
	}
	return register_value;
}

/***************************************************************************//**
 * @brief Reads the value of a register SPI/I2C interface.
 *
 * @param dev              - The device structure.
 * @param register_address - Address of the register.
 *
 * @return register_value  - Value of the register.
*******************************************************************************/
uint16_t adt7420_get_register_value(struct adt7420_dev *dev,
				                    uint8_t register_address)
{
	uint8_t register_value = 0;
	uint8_t data[2] = {0,0xFF};
	
    if (chip_info[dev->act_device].communication == SPI) {	
        /* Set the SPI command byte. datasheet page 17 */
        data[0] = ADT7320_READ_CMD  | (register_address << 3);
		
   	    return set_shift_reg(dev, register_address,data);
    } else {
        data[0] = register_address;

		i2c_write(dev->i2c_desc,
		      &register_address,
			  1,
			  NO_STOP_BIT); //add a repeat start
		i2c_read(dev->i2c_desc,
			 &register_value,
			 1,
			 STOP_BIT);

        return register_value;
    }	
}

/**************************************************************************//**
 * @brief Write to input shift register SPI interface.
 *
 * @param dev               - The device structure.
 * @param register_address  - Command control bits.
 * @param data              - Data to be written in input register.
 *
 * @return  read_back_data  - value read from register.
******************************************************************************/
uint16_t set_shift_reg(struct adt7420_dev *dev,
	                   uint8_t register_address,
                       uint8_t *data)
{
	uint8_t data_buffer[PKT_LENGTH] = { 0, 0, 0 };
	uint16_t read_back_data = 0;
	uint8_t numBytes;

	switch (register_address) {
	case ADT7320_REG_STATUS:
	case ADT7320_REG_CONFIG:
	case ADT7320_REG_ID:
	case ADT7320_REG_HIST:  numBytes = ONE_BYTE; break;
	default:
		numBytes = TWO_BYTE;
		break;
	}

	if (numBytes == ONE_BYTE) {
        data_buffer[0] = data[0];
		data_buffer[1] = data[1] & ADT7420_LSB_MASK;
		spi_write_and_read(dev->spi_desc, data_buffer, ONE_BYTE);
		read_back_data = (data_buffer[0] & ADT7420_MSB_MASK) | data_buffer[1];
	}
	else {
		data_buffer[0] = data[0];
		data_buffer[1] = data[1] & ADT7420_LSB_MASK;
		data_buffer[2] = (data[2] & ADT7420_LSB_MASK) << 8;
		spi_write_and_read(dev->spi_desc, data_buffer, TWO_BYTE);
		read_back_data = data_buffer[1] << ADT7420_MSB_OFFSET | data_buffer[2];
	}
	return read_back_data;
}

/***************************************************************************//**
 * @brief Sets the value of a register SPI/I2C.
 *
 * @param dev              - The device structure.
 * @param register_address - Address of the register.
 * @param num_data_bytes   - Number of bytes.
 * @param data             - Data to be written in input register.
 *
 * @return None.
*******************************************************************************/
void set_register_value(struct adt7420_dev *dev,
                        uint8_t register_address,
                        uint8_t num_data_bytes,
                        uint8_t *data)
{

	uint8_t data_buffer[PKT_LENGTH] = {0, 0, 0};

	
	data_buffer[1] = data[0];
	data_buffer[2] = data[1];

	if (chip_info[dev->act_device].communication == SPI) {
		//simple address re-map for SPI devices
        switch (register_address) {
		    case REG_TEMP:	register_address = ADT7320_REG_TEMP;    break; // Temperature value
			case REG_STATUS:register_address = ADT7320_REG_STATUS;  break; // status info
			case REG_CONFIG:register_address = ADT7320_REG_CONFIG;  break; // Configuration
			case REG_T_CRIT:register_address = ADT7320_REG_T_CRIT;  break; // Temperature CRIT setpoint (147'C)
			case REG_HIST:	register_address = ADT7320_REG_HIST;    break; // Temperature HYST setpoint (5'C)
			case REG_T_HIGH:register_address = ADT7320_REG_T_HIGH;  break; // Temperature HIGH setpoint (64'C)
			case REG_T_LOW: register_address = ADT7320_REG_T_LOW;   break; // Temperature LOW setpoint (10'C)
			case REG_ID:	register_address = ADT7320_REG_ID;      break; // ID value
		}
        /* Set the SPI command byte. datasheet page 17 */
        data_buffer[0] = (register_address << 3) & ADT7320_WRITE_MASK_CMD;
        spi_write_and_read(dev->spi_desc, data_buffer, num_data_bytes);     
	} else {  
         //simple address re-map for I2Cdevices
        switch (register_address) {
		  	case REG_TEMP:	 register_address = ADT7420_REG_T_HIGH_MSB; break; // Temperature value
            case REG_STATUS: register_address = ADT7420_REG_STATUS;     break; // status info
			case REG_CONFIG: register_address = ADT7420_REG_CONFIG;     break; // Configuration
            case REG_T_CRIT: register_address = ADT7420_REG_T_CRIT_MSB;	break; // Temperature CRIT setpoint (147'C)
			case REG_HIST:   register_address = ADT7420_REG_HIST;	    break; // Temperature HYST setpoint (5'C)
			case REG_T_HIGH: register_address = ADT7420_REG_T_HIGH_MSB;	break; // Temperature HIGH setpoint (64'C)
			case REG_T_LOW:  register_address = ADT7420_REG_T_LOW_MSB;	break; // Temperature LOW setpoint (10'C)
            case REG_ID:     register_address = ADT7420_REG_ID;         break; // ID value
        }
        
        data_buffer[0] = register_address;

	    i2c_write(dev->i2c_desc,
		    data_buffer,
		    num_data_bytes,
		    STOP_BIT); //no repeat start
	}
}

/***************************************************************************//**
 * @brief Initializes the communication peripheral and checks if the device is
 *        present.
 *
 * @param device     - The device structure.
 * @param init_param - The structure that contains the device initial
 * 		       parameters.
 *
 * @return status - The result of the initialization procedure.
 *                  Example: -1 - I2C peripheral was not initialized or the
 *                                device is not present.
 *                            0 - I2C peripheral was initialized and the
 *                                device is present.
*******************************************************************************/
int32_t adt7420_init(struct adt7420_dev **device,
		             struct adt7420_init_param init_param)
{
	struct adt7420_dev *dev;
	int32_t status;
	uint8_t device_connected_check = 0;

	dev = (struct adt7420_dev *)malloc(sizeof(*dev));
	if (!dev)
		return -1;

	dev->act_device = init_param.act_device;

	
	if (chip_info[dev->act_device].communication == SPI) {
		 status = spi_init(&dev->spi_desc, &init_param.spi_init);
	} else {
		 status = i2c_init(&dev->i2c_desc, &init_param.i2c_init);
	}

	/* Device Settings */
	dev->resolution_setting = init_param.resolution_setting;

	/* Reset device to default values to ensure all internal circuitry is properly initialised*/
    adt7420_reset_interface(dev);
    
    /*Register read to ensure that next read will be valid - acts as 200us delay while 
      device resets*/
    get_register_value(dev, REG_STATUS);
        

    device_connected_check = get_register_value(dev, REG_ID);
    device_connected_check >>= GET_MANUFACTURER_ID;
 
	if (device_connected_check != ADT7XXX_ID_CHECK)
		status = FAILURE;
	else
		status = SUCCESS;

	*device = dev;

	return status;
}

/***************************************************************************//**
 * @brief Free the resources allocated by adt7420_init().
 *
 * @param dev - The device structure.
 *
 * @return ret - The result of the remove procedure.
*******************************************************************************/
int32_t adt7420_remove(struct adt7420_dev *dev)
{
	int32_t ret;

	ret = i2c_remove(dev->i2c_desc);
	free(dev);

	return ret;
}

/***************************************************************************//**
 * @brief Sets the operational mode for ADT7420/ADT7320.
 *
 * @param dev  - The device structure.
 * @param mode - Operation mode.
 *               Example: ADT7420_OP_MODE_CONT_CONV - continuous conversion;
 *                        ADT7420_OP_MODE_ONE_SHOT  - one shot;
 *                        ADT7420_OP_MODE_1_SPS     - 1 SPS mode;
 *                        ADT7420_OP_MODE_SHUTDOWN  - shutdown.
 *
 * @return None.
*******************************************************************************/
void adt7420_set_operation_mode(struct adt7420_dev *dev,
				                uint8_t mode)
{
	uint8_t register_value [ONE_BYTE] = { 0, 0 };

    register_value[0]  = get_register_value(dev, REG_CONFIG);

	register_value[0] &= ~ADT7420_CONFIG_OP_MODE(ADT7420_OP_MODE_SHUTDOWN);
	register_value[0] |= ADT7420_CONFIG_OP_MODE(mode);

    set_register_value(dev,REG_CONFIG, ONE_BYTE, register_value);
}

/***************************************************************************//**
 * @brief Sets the resolution for ADT7420/ADT7320.
 *
 * @param dev        - The device structure.
 * @param resolution - Resolution.
 *                     Example: 0 - 13-bit resolution;
 *                              1 - 16-bit resolution.
 *
 * @return None.
*******************************************************************************/
void adt7420_set_resolution(struct adt7420_dev *dev,
			                uint8_t resolution)
{
	uint8_t register_value[1] = { 0 };

    register_value[0]  = get_register_value(dev, REG_CONFIG);

	register_value[0] &= ~ADT7420_CONFIG_RESOLUTION;
	register_value[0] |= (resolution * ADT7420_CONFIG_RESOLUTION);
	
	
    set_register_value(dev,REG_CONFIG, ONE_BYTE, register_value);
    dev->resolution_setting = resolution;
}

/***************************************************************************//**
 * @brief Resets the SPI or I2C inteface for the ADT7420/ADT7320
 *
 * @param dev        - The device structure.
 *
 * @return None.
*******************************************************************************/
void adt7420_reset_interface(struct adt7420_dev *dev)
{
	uint8_t data_buffer[] = { 0xFF, 0xFF, 0xFF, 0xFF };

	if (chip_info[dev->act_device].communication == SPI)
		spi_write_and_read(dev->spi_desc, data_buffer, FOUR_BYTES);
	else {
		uint8_t register_address = ADT7420_REG_RESET;
    	i2c_write(dev->i2c_desc,
	    	  &register_address,
		      1,
		    STOP_BIT);//no repeat start
	    dev->resolution_setting = 0;
	}
}

/***************************************************************************//**
 * @brief Reads the temperature data and converts it to Celsius degrees.
 *
 * @param dev - The device structure.
 *
 * @return temperature - Temperature in degrees Celsius.
*******************************************************************************/
float adt7420_get_temperature(struct adt7420_dev *dev)
{
	uint16_t temp = 0;
	float temp_c = 0;

	temp = get_register_value(dev, REG_TEMP);

	if(dev->resolution_setting) {
		if(temp & 0x8000)
			/*! Negative temperature */
			temp_c = (float)((int32_t)temp - 65536) / 128;
		else
			/*! Positive temperature */
			temp_c = (float)temp / 128;
	} else {
		temp >>= 3;
		if(temp & 0x1000)
			/*! Negative temperature */
			temp_c = (float)((int32_t)temp - 8192) / 16;
		else
			/*! Positive temperature */
			temp_c = (float)temp / 16;
	}

	return temp_c;
}

/**************************************************************************//**
 * @brief Write to a setpoint register.
 *
 * @param dev            - The device structure.
 * @param register_value - Command control bits.
 * @param data           - Data to be written in input register.
 *
 * @return  read_back_data - value read from register.
******************************************************************************/
uint8_t adt7420_wr_setpoint_reg(struct adt7420_dev *dev,
                                uint8_t register_value,
                                uint16_t data)
{
	uint16_t read_back_data = 0;
	uint8_t address, num_bytes;
	uint8_t data_buffer[PKT_LENGTH] = { 0, 0, 0 };

	switch (register_value) {
	    case REG_T_CRIT:
        case REG_T_HIGH:
        case REG_T_LOW: {
		    num_bytes = TWO_BYTE;
		 	data_buffer[0] = ((data & ADT7420_MSB_MASK) >> ADT7420_MSB_OFFSET);
		 	data_buffer[1] = data & ADT7420_LSB_MASK;
			break;
		}
		case REG_HIST: {
			num_bytes = ONE_BYTE;
			data_buffer[0] = data & ADT7420_LSB_MASK;
			break;
		}
	}
    
    set_register_value(dev, register_value, num_bytes, data_buffer);
	read_back_data  = get_register_value(dev, register_value);
    
    if (register_value == REG_HIST) {
			data &= 0x000F; //msbits are all low for HIST register
			read_back_data &= 0x000F; //msbits are all low for HIST register
	}

	return read_back_data == data ? SUCCESS : FAILURE;
}

/***************************************************************************//**
 * @brief Sets the Fault Queue option for ADT7420/ADT7320.
 *
 * @param dev        - The device structure.
 * @param mode       - Fault Queue selection.
 *                     Example: 1 - 1 fault (default).
 *                            	2 - 2 faults.
 *			                    3 - 3 faults.
 *			                    4 - 4 faults.  
 *
 * @return None.
*******************************************************************************/
void adt7420_set_fault_queue(struct adt7420_dev *dev,
                             uint8_t mode)
{
	uint8_t register_value[1] = { 0 };
    
    register_value[0]  = get_register_value(dev, REG_CONFIG);

	register_value[0] &= ~ADT7420_CONFIG_FAULT_QUEUE(ADT7420_FAULT_QUEUE_4_FAULTS);
	register_value[0] |= ADT7420_CONFIG_FAULT_QUEUE(mode);

	set_register_value(dev,REG_CONFIG, ONE_BYTE, register_value);
}

/***************************************************************************//**
 * @brief Sets comparator/interrupt (CT/INT) mode for ADT7420/ADT7320.
 *
 * @param dev        - The device structure.
 * @param setting    - Mode selection.
 *                     Example: 0 - Interrupt (default).
 *                            	1 - Comparator.
 *			                     
 *
 * @return None.
*******************************************************************************/
void adt7420_set_ct_int_mode(struct adt7420_dev *dev,
                             uint8_t setting)
{
    uint8_t register_value[1] = { 0 };

    register_value[0]  = get_register_value(dev, REG_CONFIG);
    
    register_value[0] &= ~ADT7420_CONFIG_INT_CT_MODE;
    register_value[0] |= (setting * ADT7420_CONFIG_INT_CT_MODE);

    set_register_value(dev,REG_CONFIG, ONE_BYTE, register_value);   
 }

/***************************************************************************//**
 * @brief Sets output polarity for the pins CT/INT (Critical Temp - Over/Under Temp).
 *
 * @param dev        - The device structure.
 * @param polarity   - Polarity selection.
 *                     Example: 0 - Active Low (default).
 *                            	1 - Active High.
 *			                     
 *
 * @return None.
*******************************************************************************/
void adt7420_set_ct_int_polarity(struct adt7420_dev *dev,
                                 uint8_t polarity)
{
    uint8_t register_value[1] = { 0 };

    register_value[0]  = get_register_value(dev, REG_CONFIG);
    
    register_value[0] &= ~ADT7420_CONFIG_CT_POL;
    register_value[0] &= ~ADT7420_CONFIG_INT_POL; 
    register_value[0] |= (polarity * ADT7420_CONFIG_CT_POL);
    register_value[0] |= (polarity * ADT7420_CONFIG_INT_POL);
    
    set_register_value(dev,REG_CONFIG, ONE_BYTE, register_value);
}