Rajiv BadigerArvind Krishnan
Published

Non-contact temperature measurement using a thermopile

This project demonstrates PSoC Analog Coprocessor design to implement a non-contact temperature measurement using thermopile sensor.

IntermediateProtip2 hours2,091
Non-contact temperature measurement using a thermopile

Things used in this project

Hardware components

PSoC Analog Coprocessor Pioneer Kit
Cypress PSoC Analog Coprocessor Pioneer Kit
×1
TE Sensor Solutions - TS305-11C55 Thermopile Sensor
×1

Software apps and online services

PSoC Creator
Cypress PSoC Creator
Cypress - Bridge Control Panel (Installed with PSoC Programmer)

Story

Read more

Schematics

Project Datasheet

Code

PSoC Creator Project

C/C++
PSoC Creator Project File for Thermopile Interface
No preview (download only).

main.c code

C/C++
Main.c code for the thermopile project
/*******************************************************************************
* File Name: main.c
*
* Version: 1.0
*
* Description:
* This is main source code for non-contact temperature measurement using PSoC 
* Analog Coprocessor. 
*
* Owner:
* RJVB
*
* Code Tested With:
* 1. PSoC Creator 3.3 CP3
*
********************************************************************************
* Copyright (2016) , Cypress Semiconductor Corporation.
********************************************************************************
* This software is owned by Cypress Semiconductor Corporation (Cypress) and is
* protected by and subject to worldwide patent protection (United States and
* foreign), United States copyright laws and international treaty provisions.
* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable
* license to copy, use, modify, create derivative works of, and compile the
* Cypress Source Code and derivative works for the sole purpose of creating
* custom software in support of licensee product to be used only in conjunction
* with a Cypress integrated circuit as specified in the applicable agreement.
* Any reproduction, modification, translation, compilation, or representation of
* this software except as specified above is prohibited without the express
* written permission of Cypress.
*
* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH
* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* Cypress reserves the right to make changes without further notice to the
* materials described herein. Cypress does not assume any liability arising out
* of the application or use of any product or circuit described herein. Cypress
* does not authorize its products for use as critical components in life-support
* systems where a malfunction or failure may reasonably be expected to result in
* significant injury to the user. The inclusion of Cypress' product in a life
* support systems application implies that the manufacturer assumes all risk of
* such use and in doing so indemnifies Cypress against all charges. Use may be
* limited by and subject to the applicable Cypress software license agreement.
*******************************************************************************/
#include <project.h>


/**********************#defines *************************************/
/* AMUX Channels */
#define AMUX_DIRECT             (0)
#define AMUX_REVERSE            (1)

/* SAR SEQ Channels */
#define SAR_SEQ_THERMOPILE      (0)
#define SAR_SEQ_REF             (1)
#define SAR_SEQ_RTH             (2)

/* Delay (microsecond) to allow signal to settle after AMUX switching */
#define AMUX_SWITCHING_DELAY    (50)

/* IIR filter coefficients. Sum of the two coefficients should be power of 2. */
#define THERMOPILE_IIR_COEFF_A  (4)
#define THERMOPILE_IIR_COEFF_B  (124)    
#define THERMOPILE_IIR_SHIFT    (7)

#define THERMISTOR_IIR_COEFF_A  (4)
#define THERMISTOR_IIR_COEFF_B  (124)
#define THERMISTOR_IIR_SHIFT    (7)

/* I2C Buffer read write boundary */
#define I2C_RW_BOUNDARY         (0)

/* INDEX for the 2D LUT */
#define ADC_COUNT_COLUMN        (0)
#define SLOPE_COLUMN            (1)

/********************** Function prototypes *************************/

/* Function to initialize the components */
void InitComponents(void);

/* Interrupt handler for the timer */
CY_ISR_PROTO(MyTimerISR);

/* Function to read the voltages using ADC */
void ReadVoltages(void);

/* Function to filter the measurement voltages */
void FilterVoltages(void);

/* Function to get the object temperature */
int16 GetObjectTemperature(int16);

/*********************************************************************/

/********************* Global Variables ******************************/

/* Flag indicating timer interrupt */
uint8 TimerFlag;

/* LUT representing thermopile characteristics in piece wise linear method
* It is arranged in a 2D array in the format {ADCCounts,Slope}. Note that slope value
* is scaled by 10. Each row is for increment of 10degC; first row is for 0degC where its elements 
* are the ADC counts obtained at 0degC and the slope extending upto 10degC. 
* The second row is for 10degC and so on. 
* Object temperature is calcuated by first measuring the thermopile voltage, and then
* identifying the LUT row by looking for the ADC counts in the first element. 
*/

int16 ThermopileData[10][2] = {
    {-153,65},
    {-88,60},
    {-28,67},
    {39,71},
    {110,93},
    {203,87},
    {290,109},
    {399,115},
    {514,115},
    {629,136}
};

/* Compensation capacitor setting for the PGA resistor array */
#define RES_COMP_CAP (0x0F)

/* Data buffer sent over I2C */
struct I2Cdata
{
    int16 ThermopileADCCounts;
    int16 ObjectTemperature;
    int16 VRrefADCCounts;
    int16 VthADCCounts;
    uint32 ThermistorResistance;
    int16 ambientTemp;
}I2CBuffer;

/* Data buffer to hold ADC results */
struct ADCResult
{
    int16 ThermopileCH0;
    int16 ThermopileCH1;
    int16 VRref;
    int16 Vth;    
}ADCData;

/* Filtered ADC counts */
int16 FilteredThermopile;
int16 FilteredVRref;
int16 FilteredVth;


/*******************************************************************************
* Function Name: MyTimerISR
********************************************************************************
*
* Summary:
*  This function is the interrupt handler for timer. This function sets the TimerFlag
*  variable which is polled in the main loop.
*
* Parameters:
*  None
*
* Return:
*  None.
*
* Side Effects:
*   None
*******************************************************************************/
CY_ISR(MyTimerISR)
{
    /* Set the flag */
    TimerFlag = 1;
    
    /* Clear timer interrupt */
    Timer_ClearInterrupt(Timer_TC_INTERRUPT_MASK);
    
    /* Clear any pending interrupt */
    isr_Timer_ClearPending();
}

int main()
{  
    /* Local variables */
    int16 temperature;
    int16 ambientTemp;
    int16 compThermopileVoltage;
    uint32 thermistorRes;
    uint8 newDataFlag=0;
    
    /* Enable global interrupts */
    CyGlobalIntEnable; 

    /* Initialize all the components */
    InitComponents();
    
    for(;;)
    {
        /* Check if 1ms Timer flag is set */
        if(TimerFlag)
        {
            TimerFlag = 0;
        
            /* Read all the channels of ADC */
            ReadVoltages();
            
            /* Filter the voltages */
            FilterVoltages();
           
            /* Get thermistor resistance */
            thermistorRes = Thermistor_GetResistance(FilteredVRref, FilteredVth);
            
            /* Get ambient temperature */
            ambientTemp = Thermistor_GetTemperature(thermistorRes);
            
            /* Get equivalent thermopile voltage at the ambient temperature */
            compThermopileVoltage = ThermopileData[ambientTemp/1000][0] + (ThermopileData[ambientTemp/1000][1] * ((ambientTemp/100) - (ambientTemp/1000*10))/10);
            
            /* Get the object temperature */
            temperature = GetObjectTemperature(FilteredThermopile + compThermopileVoltage);
            
            /* Set the newDataFlag to update the buffer */
            newDataFlag = 1;
        }
        
        /* Check if new data is available */
        if(newDataFlag)
        {
            /* Check if there is an on going I2C transaction. If no, update the buffer */
        	if((EZI2C_EzI2CGetActivity() & EZI2C_EZI2C_STATUS_BUSY)==0)
        	{
        	    /* Clear flag */
                newDataFlag = 0;
                
                /* Update the I2C buffer */
                I2CBuffer.ThermopileADCCounts = FilteredThermopile;
                I2CBuffer.ObjectTemperature = temperature;
                I2CBuffer.VRrefADCCounts = FilteredVRref;
                I2CBuffer.VthADCCounts = FilteredVth;
                I2CBuffer.ThermistorResistance = thermistorRes;
                I2CBuffer.ambientTemp = ambientTemp;	        		
        	}
        }
    }
}


/*******************************************************************************
* Function Name: void InitComponents(void)
********************************************************************************
*
* Summary:
*  This function configures all the components used in the design.
*
* Parameters:
*  None
*
* Return:
*  None.
*
* Side Effects:
*   None
*******************************************************************************/
void InitComponents(void)
{
    uint32 regupdate;
    
    /* Enable the AMUX component. By default, channel 0 is selected */
    AMux_Start();
    
    /* Start PGAs */
    PGA_1_Start();
    PGA_2_Start();
    
    /* Update compensation capacitors for the PGA resistor array */
	regupdate = CY_GET_REG32(PGA_1_cy_psoc4_abuf__OA_RES_CTRL);	
	regupdate &= ~PGA_1_C_FB_MASK;
	regupdate |= (RES_COMP_CAP << PGA_1_C_FB_SHIFT);	
	CY_SET_REG32(PGA_1_cy_psoc4_abuf__OA_RES_CTRL, regupdate);
	
	regupdate = CY_GET_REG32(PGA_2_cy_psoc4_abuf__OA_RES_CTRL);	
	regupdate &= ~PGA_2_C_FB_MASK;
	regupdate |= (RES_COMP_CAP << PGA_2_C_FB_SHIFT);	
	CY_SET_REG32(PGA_2_cy_psoc4_abuf__OA_RES_CTRL, regupdate);
    
    /* Start ADC */
    ADC_Start();    
    
    /* Enable PVref and buffer */
    PVref_Start();
    Opamp_Buffer_Start();
    
    /* Start the timer and set the interrupt handler */
    Timer_Start();
    isr_Timer_StartEx(MyTimerISR);
    
    /* Enable I2C and configure the buffer */
    EZI2C_EzI2CSetBuffer1(sizeof(I2CBuffer), I2C_RW_BOUNDARY, (uint8 *)&I2CBuffer);
    EZI2C_Start();
}

/*******************************************************************************
* Function Name: void ReadVoltages(void)
********************************************************************************
*
* Summary:
*  This function reads all the ADC channels - two channels of thermopile (via AMUX 
*  component) and two channels of thermistor (via ADC SAR Sequencer). 
*
* Parameters:
*  None
*
* Return:
*  None.
*
* Side Effects:
*   None
*******************************************************************************/
void ReadVoltages(void)
{   
    /* Read Thermopile Voltages */
    
    /* Set the channel */
    AMux_Select(AMUX_DIRECT);

    /* Wait for the signal chain to settle */
    CyDelayUs(AMUX_SWITCHING_DELAY);
    
    /* Enable ADC to sample */
    ADC_StartConvert();
    
    /* Wait and get the ADC result */
    ADC_IsEndConversion(ADC_WAIT_FOR_RESULT);
    ADCData.ThermopileCH0 = ADC_GetResult16(SAR_SEQ_THERMOPILE);
    
    /* Set the channel */
    AMux_Select(AMUX_REVERSE);
    
    /* Wait for the signal chain to settle */
    CyDelayUs(AMUX_SWITCHING_DELAY);
    
    /* Enable ADC to sample */
    ADC_StartConvert();
    
    /* Wait and get the ADC result */
    ADC_IsEndConversion(ADC_WAIT_FOR_RESULT);
    ADCData.ThermopileCH1 = ADC_GetResult16(SAR_SEQ_THERMOPILE);            
     
    /* ADC has already scanned the other two of its channels. No need to start the conversion again */
    /* Read reference resistor voltage */
    ADCData.VRref = ADC_GetResult16(SAR_SEQ_REF);
    
    /* Read thermistor voltage */
    ADCData.Vth = ADC_GetResult16(SAR_SEQ_RTH);
}

/*******************************************************************************
* Function Name: void FilterVoltages(void)
********************************************************************************
*
* Summary:
*  This function implements IIR filter for thermopile and thermistor readings.
*
* Parameters:
*  None
*
* Return:
*  None.
*
* Side Effects:
*   None
*******************************************************************************/
void FilterVoltages(void)
{
    int16 tempVar;
    
    tempVar = (ADCData.ThermopileCH0 - ADCData.ThermopileCH1);
    FilteredThermopile = (int32)(tempVar * THERMOPILE_IIR_COEFF_A + FilteredThermopile*THERMOPILE_IIR_COEFF_B)>>THERMOPILE_IIR_SHIFT; 
    
    FilteredVRref = (int32)(ADCData.VRref * THERMISTOR_IIR_COEFF_A + FilteredVRref * THERMISTOR_IIR_COEFF_B)>>THERMISTOR_IIR_SHIFT;
    FilteredVth = (int32)(ADCData.Vth * THERMISTOR_IIR_COEFF_A + FilteredVth * THERMISTOR_IIR_COEFF_B)>>THERMISTOR_IIR_SHIFT;
    
}

/*******************************************************************************
* Function Name: int16 GetObjectTemperature(int16)
********************************************************************************
*
* Summary:
*  This function calculates the object temperature using piece wise linear approximation. 
*  Thermistor characteristics are described in LUT - ThermopileData.
*
* Parameters:
*  Temperature compensated thermopile ADC counts
*
* Return:
*  Object temperature in degC. Note that the value is scaled up by 10. 
*
* Side Effects:
*   None
*******************************************************************************/
int16 GetObjectTemperature(int16 ADCCounts)
{
    uint8 index;
    
    /* Scroll through the LUT to get to the required row */
    for(index=0;index<9;index++)
    {
        if((ADCCounts >= ThermopileData[index][ADC_COUNT_COLUMN]) && (ADCCounts < ThermopileData[index+1][ADC_COUNT_COLUMN]))
        {
            return((100*(ADCCounts - ThermopileData[index][ADC_COUNT_COLUMN]))/ThermopileData[index][SLOPE_COLUMN] + index*100);
        }
    }
    
    return((100*(ADCCounts - ThermopileData[9][ADC_COUNT_COLUMN]))/ThermopileData[9][SLOPE_COLUMN] + index*100);
}


/* [] END OF FILE */

Credits

Rajiv Badiger

Rajiv Badiger

2 projects • 4 followers
Like to do something new, in a different way
Arvind Krishnan

Arvind Krishnan

2 projects • 6 followers
Tinker-smith who likes writing embedded programs when not making robots or flying quadcopters

Comments