/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file TemperatureSensors.c * * @author (last) Quang Nguyen * @date (last) 27-Aug-2020 * * @author (original) Dara Navaei * @date (original) 08-Apr-2020 * ***************************************************************************/ #include // For temperature calculation #include "FPGA.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "TemperatureSensors.h" #include "Timers.h" #include "TaskPriority.h" #include "TaskGeneral.h" #include "Utilities.h" /** * @addtogroup TemperatureSensors * @{ */ // ********** private definitions ********** #define PRIMARY_HEATER_EXT_TEMP_SENSORS_GAIN 16U ///< Primary heater external temperature sensors gain. #define PRIMARY_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE 19600U ///< Primary heater external temperature sensors reference resistance. #define PRIMARY_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE 1000U ///< Primary heater external temperature sensors zero degree resistance. #define COND_SENSORS_TEMP_SENSOR_GAIN 16U ///< Temperature sensor for conductivity gain. #define COND_SENSORS_TEMP_SENSOR_REF_RESISTANCE 19600U ///< Temperature sensor for conductivity reference resistance. #define COND_SENSORS_TEMP_SENSOR_0_DEGREE_RESISTANCE 1000U ///< Temperature sensor for conductivity zero degree resistance. #define TRIMMER_HEATER_EXT_TEMP_SENSORS_GAIN 32U ///< Trimmer heater external temperature sensors gain. #define TRIMMER_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE 5110U ///< Trimmer heater external temperature sensors reference resistance. #define TRIMMER_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE 100U ///< Trimmer heater external temperature sensors zero degree resistance. #define TEMP_SENSORS_ADC_BITS 24U ///< External temperature sensors ADC bits. #define TEMP_SENSORS_ADC_MAX_COUNT ( pow(2,TEMP_SENSORS_ADC_BITS) - 1 ) ///< Temperature sensors max ADC count. #define TEMP_EQUATION_COEFF_A (3.9083 * pow(10,-3)) ///< ADC to temperature conversion coefficient A. #define TEMP_EQUATION_COEFF_B (-5.775 * pow(10,-7)) ///< ADC to temperature conversion coefficient B. #define ADC_FPGA_READ_DELAY 30U ///< Delay in ms before reading the ADC values from FPGA. #define MAX_NUM_OF_RAW_ADC_SAMPLES 32U ///< Number of ADC reads for moving average calculations. #define MAX_ALLOWED_TEMP_DELTA_BETWEEN_SENSORS 2U ///< Maximum allowed temperature delta between sensors. #define MAX_ALLOWED_UNCHANGED_ADC_READS 4U ///< Maximum number of times that the read of a sensor cannot change. #define SHIFT_BITS_BY_2 2U ///< Shift bits by 2. #define SHIFT_BITS_BY_5_FOR_AVERAGING 5U ///< Shift the ADCs of the temperature sensors by 5 to average them. #define INLET_WATER_TEMPERATURE_PERSISTENCE_COUNT (5 * MS_PER_SECOND / TASK_GENERAL_INTERVAL) ///< Number of persistence count for temperature sensors out of range error. #define MIN_WATER_INPUT_TEMPERATURE 10U ///< Minimum water input temperature. #define MAX_WATER_INPUT_TEMPERATURE 35U ///< Maximum water input temperature. #define HEATERS_INTERNAL_TEMPERTURE_CALCULATION_INTERVAL 20U ///< Time interval that is used to calculate the heaters internal temperature. #define HEATERS_INTERNAL_TC_ADC_TO_TEMP_CONVERSION_COEFF 0.25 ///< Heaters internal temperature sensors ADC to temperature conversion coefficient. #define HEATERS_COLD_JUNCTION_ADC_TO_TEMP_CONVERSION_COEFF 0.0625 ///< Heaters cold junction temperature sensors ADC to temperature conversion coefficient. #define K_THERMOCOUPLE_TEMP_2_MILLI_VOLT_CONVERSION_COEFF 0.041276 ///< K thermocouple temperature to millivolt conversion coefficient. #define SIZE_OF_THERMOCOUPLE_COEFFICIENTS 10U ///< Size of the thermocouple coefficients. #define EXTERNAL_TEMP_SENSORS_ERROR_VALUE 0x80 ///< External temperature sensors error value. #define HEATERS_INTERNAL_TEMP_SENSOR_FAULT 0x01 ///< Heaters internal temperature sensor fault. #define TEMP_SENSORS_DATA_PUBLISH_INTERVAL (MS_PER_SECOND / TASK_PRIORITY_INTERVAL) ///< Temperature sensors publish data time interval. #define MAX_TEMPERATURE_SENSOR_FAILURES 10 ///< Maximum number of temperature sensor errors within window period before alarm. #define MAX_TEMPERATURE_SENSOR_FAILURE_WINDOW_MS (10 * MS_PER_SECOND) ///< Temperature sensor error window. /// Temperature sensor self test states. typedef enum tempSensors_Self_Test_States { TEMPSENSORS_SELF_TEST_START = 0, ///< Temperature sensors self test start TEMPSENSORS_SELF_TEST_ADC_CHECK, ///< Temperature sensors self ADC check TEMPSENSORS_SELF_TEST_CONSISTENCY_CHECK, ///< Temperature sensors self test consistency check TEMPSENSORS_SELF_TEST_COMPLETE, ///< Temperature sensors self test complete NUM_OF_TEMPSENSORS_SELF_TEST_STATES ///< Total number of self test states } TEMPSENSORS_SELF_TEST_STATES_T; /// Temperature sensor exec states. typedef enum tempSensors_Exec_States { TEMPSENSORS_EXEC_STATE_START = 0, ///< Temperature sensors exec start TEMPSENSORS_EXEC_STATE_GET_ADC_VALUES, ///< Temperature sensors exec get ADC values NUM_OF_TEMPSENSORS_EXEC_STATES, ///< Total number of exec states } TEMPSENSORS_EXEC_STATES_T; /// Temperature sensor struct. typedef struct { F32 gain; ///< ADC gain F32 refResistance; ///< ADC reference resistance F32 conversionCoef; ///< ADC conversion coefficient F32 zeroDegreeResistance; ///< ADC zero degree resistance S32 rawADCReads[ MAX_NUM_OF_RAW_ADC_SAMPLES ]; ///< Raw ADC reads array S32 adcNextIndex; ///< Next ADC read index S32 adcRunningSum; ///< ADC running sum U32 readCount; ///< Read counts from FPGA U32 internalErrorCount; ///< Internal error counts OVERRIDE_F32_T temperatureValues; ///< Temperature values with override } TEMP_SENSOR_T; // ********** private data ********** static SELF_TEST_STATUS_T tempSensorsSelfTestResult; ///< Self test result of the TemperatureSensors module. static TEMPSENSORS_SELF_TEST_STATES_T tempSensorsSelfTestState; ///< TemperatureSensor self test state. static TEMPSENSORS_EXEC_STATES_T tempSensorsExecState; ///< TemperatureSensor exec state. static TEMP_SENSOR_T tempSensors [ NUM_OF_TEMPERATURE_SENSORS ]; ///< Temperature sensors' data structure. static U32 elapsedTime; ///< Elapsed time variable. static U32 internalHeatersConversionTimer; ///< Conversion timer variable to calculate the heaters internal temperature. static F32 tempValuesForPublication [ NUM_OF_TEMPERATURE_SENSORS ]; ///< Temperature sensors data publication array. static U32 dataPublicationTimerCounter; ///< Temperature sensors data publish timer counter. static OVERRIDE_U32_T tempSensorsPublishInterval = { TEMP_SENSORS_DATA_PUBLISH_INTERVAL, TEMP_SENSORS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Temperature sensors publish time interval override. static const F32 positiveTCExpA0 = 0.118597600000E0; ///< K TC positive temperature exponent coefficient A0. static const F32 positiveTCExpA1 = -0.118343200000E-3; ///< K TC positive temperature exponent coefficient A1. static const F32 positiveTCExpA2 = 0.126968600000E3; ///< K TC positive temperature exponent coefficient A2. ///< Thermocouple correction coefficients for positive cold junction temperature. static const F32 positiveTCCoeffs [ SIZE_OF_THERMOCOUPLE_COEFFICIENTS ] = { -0.176004136860E-1, 0.389212049750E-1, 0.185587700320E-4, -0.994575928740E-7, 0.318409457190E-9, -0.560728448890E-12, 0.560750590590E-15,-0.320207200030E-18, 0.971511471520E-22,-0.121047212750E-25 }; ///< Thermcouple inverse coefficient for positive cold junction temperature. static const F32 positiveTCInverserCoeffs [ SIZE_OF_THERMOCOUPLE_COEFFICIENTS ] = { 0.0, 2.508355E1, 7.860106E-2, -2.503131E-1, 8.315270E-2, -1.228034E-2, 9.804036E-4, -4.413030E-5, 1.057734E-6, -1.052755E-8 }; // ********** private function prototypes ********** static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestStart( void ); static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestADCCheck( void ); static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestConsistencyCheck( void ); static TEMPSENSORS_EXEC_STATES_T handleExecStart( void ); static TEMPSENSORS_EXEC_STATES_T handleExecGetADCValues( void ); static F32 getADC2TempConversion( F32 avgADC, U32 gain, U32 refResistance, U32 zeroDegResistance, F32 adcConversionCoeff ); static void getHeaterInternalTemp( U32 TCIndex, U32 CJIndex ); static void processTempSnsrsADCRead( U32 sensorIndex, U32 adc, U32 fpgaError, U32 fpgaCount ); static void processHtrsTempSnsrsADCRead( U32 sensorIndex, U32 adc, U32 fpgaError, U32 fpgaCount ); static BOOL isADCReadValid( U32 sensorIndex, U32 fpgaError, U32 fpgaCount ); static void processADCRead( U32 sensorIndex, S32 adc ); static void publishTemperatureSensorsData( void ); static U32 getPublishTemperatureSensorsDataInterval( void ); /*********************************************************************//** * @brief * The initTemperatureSensors function initializes the module * @details * Inputs : none * Outputs : TemperatureSensors module initialized * @return none *************************************************************************/ void initTemperatureSensors( void ) { U08 i; tempSensorsSelfTestState = TEMPSENSORS_SELF_TEST_START; tempSensorsExecState = TEMPSENSORS_EXEC_STATE_START; elapsedTime = 0; internalHeatersConversionTimer = 0; dataPublicationTimerCounter = 0; /* NOTE: The temperature sensors do not have conversion coefficient. * The conversion coefficients are used for the heaters internal temperature sensors and * the temperature will be calculated using a quadratic equation * The internal thermocouple has 0.25 conversion coefficient * The heaters cold junction sensor has 0.0625 conversion coefficient * The conversion coefficient will be set to 0 for the temperature sensors */ for ( i = 0; i < NUM_OF_TEMPERATURE_SENSORS; ++i ) { memset( &tempSensors[ i ], 0x0, sizeof( TEMP_SENSOR_T ) ); } // Initialize TPi and TPo constants tempSensors[ TEMPSENSORS_INLET_PRIMARY_HEATER ].gain = PRIMARY_HEATER_EXT_TEMP_SENSORS_GAIN; tempSensors[ TEMPSENSORS_INLET_PRIMARY_HEATER ].refResistance = PRIMARY_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ TEMPSENSORS_INLET_PRIMARY_HEATER ].zeroDegreeResistance = PRIMARY_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ TEMPSENSORS_OUTLET_PRIMARY_HEATER ].gain = PRIMARY_HEATER_EXT_TEMP_SENSORS_GAIN; tempSensors[ TEMPSENSORS_OUTLET_PRIMARY_HEATER ].refResistance = PRIMARY_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ TEMPSENSORS_OUTLET_PRIMARY_HEATER ].zeroDegreeResistance = PRIMARY_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE; // Initialize TD1 and TD2 constants tempSensors[ TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ].gain = COND_SENSORS_TEMP_SENSOR_GAIN; tempSensors[ TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ].refResistance = COND_SENSORS_TEMP_SENSOR_REF_RESISTANCE; tempSensors[ TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ].zeroDegreeResistance = COND_SENSORS_TEMP_SENSOR_0_DEGREE_RESISTANCE; tempSensors[ TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ].gain = COND_SENSORS_TEMP_SENSOR_GAIN; tempSensors[ TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ].refResistance = COND_SENSORS_TEMP_SENSOR_REF_RESISTANCE; tempSensors[ TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ].zeroDegreeResistance = COND_SENSORS_TEMP_SENSOR_0_DEGREE_RESISTANCE; // Initialize TRo and TDi constants tempSensors[ TEMPSENSORS_OUTLET_REDUNDANCY ].gain = TRIMMER_HEATER_EXT_TEMP_SENSORS_GAIN; tempSensors[ TEMPSENSORS_OUTLET_REDUNDANCY ].refResistance = TRIMMER_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ TEMPSENSORS_OUTLET_REDUNDANCY ].zeroDegreeResistance = TRIMMER_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ TEMPSENSORS_INLET_DIALYSATE ].gain = TRIMMER_HEATER_EXT_TEMP_SENSORS_GAIN; tempSensors[ TEMPSENSORS_INLET_DIALYSATE ].refResistance = TRIMMER_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ TEMPSENSORS_INLET_DIALYSATE ].zeroDegreeResistance = TRIMMER_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE; // Initialize the heaters internal thermocouples constants tempSensors[ TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE ].conversionCoef = HEATERS_INTERNAL_TC_ADC_TO_TEMP_CONVERSION_COEFF; tempSensors[ TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE ].conversionCoef = HEATERS_INTERNAL_TC_ADC_TO_TEMP_CONVERSION_COEFF; // Initialize the heaters cold junction constants tempSensors[ TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION ].conversionCoef = HEATERS_COLD_JUNCTION_ADC_TO_TEMP_CONVERSION_COEFF; tempSensors[ TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION ].conversionCoef = HEATERS_COLD_JUNCTION_ADC_TO_TEMP_CONVERSION_COEFF; // Initialize the heaters calculated internal temperature sensors. The constants are zero since they will not be used for conversion // Windowed time count for FPGA temperature sensor error initTimeWindowedCount( TIME_WINDOWED_COUNT_FPGA_TEMPERATURE_SENSOR_ERROR, MAX_TEMPERATURE_SENSOR_FAILURES, MAX_TEMPERATURE_SENSOR_FAILURE_WINDOW_MS ); // Persistent alarms for inlet water high/low temperature initPersistentAlarm( PERSISTENT_ALARM_INLET_WATER_HIGH_TEMPERATURE, ALARM_ID_INLET_WATER_HIGH_TEMPERATURE, TRUE, INLET_WATER_TEMPERATURE_PERSISTENCE_COUNT, INLET_WATER_TEMPERATURE_PERSISTENCE_COUNT ); initPersistentAlarm( PERSISTENT_ALARM_INLET_WATER_LOW_TEMPERATURE, ALARM_ID_INLET_WATER_LOW_TEMPERATURE, TRUE, INLET_WATER_TEMPERATURE_PERSISTENCE_COUNT, INLET_WATER_TEMPERATURE_PERSISTENCE_COUNT ); } /*********************************************************************//** * @brief * The execTemperatureSensorsSelfTest function runs the TemperatureSensors * POST during the self test * @details * Inputs : tempSensorsSelfTestState * Outputs : tempSensorsSelfTestState * @return tempSensorsSelfTestState *************************************************************************/ SELF_TEST_STATUS_T execTemperatureSensorsSelfTest( void ) { switch ( tempSensorsSelfTestState ) { case TEMPSENSORS_SELF_TEST_START: tempSensorsSelfTestState = handleSelfTestStart(); break; case TEMPSENSORS_SELF_TEST_ADC_CHECK: tempSensorsSelfTestState = handleSelfTestADCCheck(); break; case TEMPSENSORS_SELF_TEST_CONSISTENCY_CHECK: tempSensorsSelfTestState = handleSelfTestConsistencyCheck(); break; case TEMPSENSORS_SELF_TEST_COMPLETE: // Done with self test, do nothing break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_INVALID_SELF_TEST_STATE, tempSensorsSelfTestState ); tempSensorsSelfTestState = TEMPSENSORS_SELF_TEST_COMPLETE; break; } return tempSensorsSelfTestResult; } /*********************************************************************//** * @brief * The execTemperatureSensors function executes the temperature sensors' state machine. * @details * Inputs : tempSensorsExecState * Outputs : tempSensorsExecState * @return none *************************************************************************/ void execTemperatureSensors( void ) { // Read the sensors all the time switch ( tempSensorsExecState ) { case TEMPSENSORS_SELF_TEST_START: tempSensorsExecState = handleExecStart(); break; case TEMPSENSORS_EXEC_STATE_GET_ADC_VALUES: tempSensorsExecState = handleExecGetADCValues(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_EXEC_INVALID_STATE, tempSensorsExecState ); tempSensorsExecState = TEMPSENSORS_EXEC_STATE_GET_ADC_VALUES; break; } } /*********************************************************************//** * @brief * The checkInletWaterTemperature checks inlet water temperature value * and triggers an alarm when temperature value is out of allowed range. * @details * Inputs : Inlet water temperature value * Outputs : Trigger alarms when temperature is out of allowed range * @return none *************************************************************************/ void checkInletWaterTemperature( void ) { F32 const temperature = getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ); BOOL const isWaterTempTooHigh = temperature > MAX_WATER_INPUT_TEMPERATURE; BOOL const isWaterTempTooLow = temperature < MIN_WATER_INPUT_TEMPERATURE; checkPersistentAlarm( PERSISTENT_ALARM_INLET_WATER_HIGH_TEMPERATURE, isWaterTempTooHigh, temperature ); checkPersistentAlarm( PERSISTENT_ALARM_INLET_WATER_LOW_TEMPERATURE, isWaterTempTooLow, temperature ); } /*********************************************************************//** * @brief * The getTemperatureValue function gets the temperature of the requested sensor. * @details * Inputs : none * Outputs : none * @param sensor temperature sensor index * @return temperature *************************************************************************/ F32 getTemperatureValue( U32 sensorIndex ) { F32 temperature = 0.0; if ( sensorIndex < NUM_OF_TEMPERATURE_SENSORS ) { if ( tempSensors[ sensorIndex ].temperatureValues.override == OVERRIDE_KEY ) { temperature = tempSensors[ sensorIndex ].temperatureValues.ovData; } else { temperature = tempSensors[ sensorIndex ].temperatureValues.data; } } return temperature; } /*********************************************************************//** * @brief * The getADC2TempConversion function calculates the temperature from ADC read from FPGA. * @details * Inputs : none * Outputs : none * @param avgADC Running average ADC * @param gain ADC gain * @param refResistance ADC reference resistance * @param zeroDegResistance ADC zero degree resistance * @param adcConversionCoeff ADC conversion coefficient * @return temperature *************************************************************************/ static F32 getADC2TempConversion( F32 avgADC, U32 gain, U32 refResistance, U32 zeroDegResistance, F32 adcConversionCoeff ) { F32 temperature; if ( fabs( adcConversionCoeff ) < NEARLY_ZERO ) { // R(RTD) = R(ref) * ( adc – 2^N - 1 ) / ( G * 2^N - 1 ); F32 resistance = ( refResistance * ( avgADC - pow( 2,(TEMP_SENSORS_ADC_BITS - 1 ) ) ) ) / ( gain * pow( 2, ( TEMP_SENSORS_ADC_BITS - 1 ) ) ); // T = (-A + √( A^2 - 4B * ( 1 - R_T / R_0 ) ) ) / 2B F32 secondSqrtPart = 4 * TEMP_EQUATION_COEFF_B * (1 - ( resistance / zeroDegResistance ) ); temperature = ( -TEMP_EQUATION_COEFF_A + sqrt( pow( TEMP_EQUATION_COEFF_A, 2 ) - secondSqrtPart ) ) / ( 2 * TEMP_EQUATION_COEFF_B ); } else { temperature = avgADC * adcConversionCoeff; } return temperature; } /*********************************************************************//** * @brief * The getHeaterInternalTemp function calculates the heaters' internal temperature. * @details * Inputs : temperatureValues * Outputs : temperatureValues * @param TCIndex thermocouple index * @param CJIndex cold junction index * @return none *************************************************************************/ static void getHeaterInternalTemp( U32 TCIndex, U32 CJIndex ) { F32 temperature = 0.0; F32 equiVoltage = 0.0; F32 correctedVoltage = 0.0; F32 TCTemp = tempSensors[ TCIndex ].temperatureValues.data; F32 CJTemp = tempSensors[ CJIndex ].temperatureValues.data; // Value in mV F32 rawVoltage = ( TCTemp - CJTemp ) * K_THERMOCOUPLE_TEMP_2_MILLI_VOLT_CONVERSION_COEFF; U08 i; // Check if cold junction is positive if ( CJTemp > 0 ) { for ( i = 0; i < SIZE_OF_THERMOCOUPLE_COEFFICIENTS; i++ ) { equiVoltage = equiVoltage + ( positiveTCCoeffs[ i ] * pow( CJTemp, i ) ); } equiVoltage = equiVoltage + ( positiveTCExpA0 * ( exp( positiveTCExpA1 * pow( ( CJTemp - positiveTCExpA2 ), 2 ) ) ) ); correctedVoltage = rawVoltage + equiVoltage; for ( i = 0; i < SIZE_OF_THERMOCOUPLE_COEFFICIENTS; i++ ) { temperature = temperature + ( positiveTCInverserCoeffs[ i ] * pow( correctedVoltage, i ) ); } } else { // TODO Alarm temperature = -1.0; } if ( TCIndex == TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE ) { tempSensors[ TEMPSENSORS_PRIMARY_HEATER_INTERNAL ].temperatureValues.data = temperature; } else { tempSensors[ TEMPSENSORS_TRIMMER_HEATER_INTERNAL ].temperatureValues.data = temperature; } } /*********************************************************************//** * @brief * The processTemperatureSensorsADCRead function masks the MSB of the ADC * read from FPGA and converts it to an S32. Then it calls another function * to check if the read ADC is valid or not and if it is, it calls another * function to process the ADC value and covert it to temperature. * @details * Inputs : none * Outputs : Processed valid ADC reading * @param sensorIndex ID of temperature sensor to process * @param adc ADC value for the temperature sensor * @param fpgaError reported FPGA error status * @param fpgaCount reported FPGA read count * @return none *************************************************************************/ static void processTempSnsrsADCRead( U32 sensorIndex, U32 adc, U32 fpgaError, U32 fpgaCount ) { S32 convertedADC = (S32)( adc & MASK_OFF_U32_MSB ); if ( isADCReadValid( sensorIndex, fpgaError, fpgaCount ) ) { processADCRead( sensorIndex, convertedADC ); } } /*********************************************************************//** * @brief * The processHeatersInternalSensorsADCRead function checks whether the provided * sensor is the heaters thermocouple or cold junction sensors and performs * different bit shifts on them accordingly. Then it call another function to * check if the read ADC is valid and if it is, the function calls another function * process the ADC and convert it to temperature. * @details * Inputs : none * Outputs : Processed heater ADC reading * @param sensorIndex ID of temperature sensor to process * @param adc reported ADC value for temperature sensor * @param fpgaError reported error status by FPGA * @param fpgaCount reported read count by FPGA * @return none *************************************************************************/ static void processHtrsTempSnsrsADCRead( U32 sensorIndex, U32 adc, U32 fpgaError, U32 fpgaCount ) { U16 adcConv; S16 convertedADC; if ( ( sensorIndex == TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE ) || ( sensorIndex == TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE ) ) { // Cast the adc from U32 to U16 and shit it to left by 2 adcConv = ( (U16)adc ) << SHIFT_BITS_BY_2; // Cast from U16 to S16 and shift the bits to right by 2 // so if the sign bit is 1, the sign bit is extended convertedADC = ( (S16)adcConv ) >> SHIFT_BITS_BY_2; } else if ( ( sensorIndex == TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION ) || (sensorIndex == TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION ) ) { // Cast the adc from U32 to U16 and shift it by 4 adcConv = ( (U16)adc ) << SHIFT_BITS_BY_4; // Cast from U16 to S16 and shift the bits to right by 4 // so if the sign bit is 1, the sign bit is extended convertedADC = ( (S16)adcConv ) >> SHIFT_BITS_BY_4; } if ( isADCReadValid( sensorIndex, fpgaError, fpgaCount ) ) { processADCRead( sensorIndex, convertedADC ); } } /*********************************************************************//** * @brief * The isADCReadValid function checks if there is an FPGA error and FPGA * count. If there is any FPGA, it raises an alarm. If the count has changed * and the ADC value is not the same as the previous ADC read, it returns a * TRUE, signaling that the ADC is valid to be processed. * @details * Inputs : readCount * Outputs : readCount, internalErrorCount * @param sensorIndex Temperature sensor index * @param fpgaError FPGA error count * @param fpgaCount FPGA read count * @return isADCValid (BOOL) *************************************************************************/ static BOOL isADCReadValid( U32 sensorIndex, U32 fpgaError, U32 fpgaCount ) { BOOL isADCValid = FALSE; #ifndef _VECTORCAST_ isADCValid = TRUE; // TODO remove this line. Temporary set to true until FPGA error count is fixed #endif if ( fpgaError == 0 ) { if ( tempSensors[ sensorIndex ].readCount != fpgaCount ) { tempSensors[ sensorIndex ].readCount = fpgaCount; tempSensors[ sensorIndex ].internalErrorCount = 0; isADCValid = TRUE; } else { ++tempSensors[ sensorIndex ].internalErrorCount; if ( tempSensors[ sensorIndex ].internalErrorCount > MAX_ALLOWED_UNCHANGED_ADC_READS ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TEMPERATURE_SENSORS_FAULT, sensorIndex ); } } } else { if ( TRUE == incTimeWindowedCount( TIME_WINDOWED_COUNT_FPGA_TEMPERATURE_SENSOR_ERROR ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TEMPERATURE_SENSORS_FAULT, sensorIndex ); } } return isADCValid; } /*********************************************************************//** * @brief * The processADCRead function receives the ADC value and the sensor * index and calculates the running sum and the moving average of the ADCs * The temperatureSensorsADCRead and tempSensorsAvgADCValues are updated. * @details * Inputs : adcNextIndex, rawADCReads, adcRunningSum * Outputs : adcNextIndex, rawADCReads, adcRunningSum, temperatureValues * @param sensorIndex Temperature sensor index * @param adc adc reading from fpga * @return none *************************************************************************/ static void processADCRead( U32 sensorIndex, S32 adc ) { U32 const index = tempSensors[ sensorIndex ].adcNextIndex; S32 const indexValue = tempSensors[ sensorIndex ].rawADCReads [ index ]; tempSensors[ sensorIndex ].rawADCReads[ index ] = adc; tempSensors[ sensorIndex ].adcNextIndex = INC_WRAP( index, 0, MAX_NUM_OF_RAW_ADC_SAMPLES - 1 ); tempSensors[ sensorIndex ].adcRunningSum = tempSensors[ sensorIndex ].adcRunningSum - indexValue + adc; // Calculate the average F32 const avgADCReads = tempSensors[ sensorIndex ].adcRunningSum >> SHIFT_BITS_BY_5_FOR_AVERAGING; F32 const temperature = getADC2TempConversion( avgADCReads, (U32)tempSensors [ sensorIndex ].gain, (U32)tempSensors [ sensorIndex ].refResistance, (U32)tempSensors [ sensorIndex ].zeroDegreeResistance, tempSensors [ sensorIndex ].conversionCoef ); tempSensors[ sensorIndex ].temperatureValues.data = temperature; } /*********************************************************************//** * @brief * The handleSelfTestStart function transitions the self test state to check ADC. * @details * Inputs : tempSensorsSelfTestResult * Outputs : none * @return state (TEMPSENSORS_SELF_TEST_STATES_T) *************************************************************************/ static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestStart( void ) { tempSensorsSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; return TEMPSENSORS_SELF_TEST_ADC_CHECK; } /*********************************************************************//** * @brief * The handleSelfTestADCCheck function checks whether the ADC reads. If the * reads are above the maximum 24bit ADC count, it will throw an alarm and * switches to the next state. * @details * Inputs : TPi ADC reading from FPGA * Outputs : none * @return TEMPSENSORS_SELF_TEST_CONSISTENCY_CHECK (TEMPSENSORS_SELF_TEST_STATES_T) *************************************************************************/ static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestADCCheck( void ) { S32 const tpiADC = (S32)getFPGATPiTemp(); BOOL const isLessThanZero = tpiADC <= 0; BOOL const isGreaterThanFullScale = tpiADC >= TEMP_SENSORS_ADC_MAX_COUNT; if ( isLessThanZero || isGreaterThanFullScale ) { tempSensorsSelfTestResult = SELF_TEST_STATUS_FAILED; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TEMPERATURE_SENSORS_FAULT, TEMPSENSORS_SELF_TEST_ADC_CHECK ); } return TEMPSENSORS_SELF_TEST_CONSISTENCY_CHECK; } /*********************************************************************//** * @brief * The handleSelfTestConsistencyCheck function checks the values of the * sensors to make sure they are within the allowed range from each other. * @details * Inputs : TPi and TPo ADC reading from FPGA * Outputs : none * @return TEMPSENSORS_SELF_TEST_COMPLETE (TEMPSENSORS_SELF_TEST_STATES_T) *************************************************************************/ static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestConsistencyCheck( void ) { S32 const tpiConvertedADC = ( (S32)getFPGATPiTemp() & MASK_OFF_U32_MSB ); F32 const tpiTemperature = getADC2TempConversion( tpiConvertedADC, (U32) tempSensors[ TEMPSENSORS_INLET_PRIMARY_HEATER ].gain, (U32) tempSensors[ TEMPSENSORS_INLET_PRIMARY_HEATER ].refResistance, (U32) tempSensors[ TEMPSENSORS_INLET_PRIMARY_HEATER ].zeroDegreeResistance, tempSensors[ TEMPSENSORS_INLET_PRIMARY_HEATER ].conversionCoef ); S32 const tpoConvertedADC = ( (S32)getFPGATPoTemp() & MASK_OFF_U32_MSB ); F32 const tpoTemperature = getADC2TempConversion( tpoConvertedADC, (U32) tempSensors[ TEMPSENSORS_OUTLET_PRIMARY_HEATER ].gain, (U32) tempSensors[ TEMPSENSORS_OUTLET_PRIMARY_HEATER ].refResistance, (U32) tempSensors[ TEMPSENSORS_OUTLET_PRIMARY_HEATER ].zeroDegreeResistance, tempSensors[ TEMPSENSORS_OUTLET_PRIMARY_HEATER ].conversionCoef ); F32 const tempDiff = fabs( tpiTemperature - tpoTemperature ); if ( tempDiff > MAX_ALLOWED_TEMP_DELTA_BETWEEN_SENSORS ) { tempSensorsSelfTestResult = SELF_TEST_STATUS_FAILED; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TEMPERATURE_SENSORS_INCONSISTENT, TEMPSENSORS_SELF_TEST_CONSISTENCY_CHECK ); } else { tempSensorsSelfTestResult = SELF_TEST_STATUS_PASSED; } return TEMPSENSORS_SELF_TEST_COMPLETE; } /*********************************************************************//** * @brief * The handleExecStart function waits for a period of time and switches to * the state that reads the ADC values from FPGA. * @details * Inputs : none * Outputs : elapsedTime * @return state (TEMPSENSORS_EXEC_STATES_T) *************************************************************************/ static TEMPSENSORS_EXEC_STATES_T handleExecStart( void ) { TEMPSENSORS_EXEC_STATES_T state = TEMPSENSORS_EXEC_STATE_START; if ( elapsedTime == 0 ) { elapsedTime = getMSTimerCount(); } else if ( didTimeout( elapsedTime, ADC_FPGA_READ_DELAY ) ) { elapsedTime = 0; state = TEMPSENSORS_EXEC_STATE_GET_ADC_VALUES; } return state; } /*********************************************************************//** * @brief * The handleExecGetADCValues function reads the ADC values from FPGA and * at the specified time intervals and calls other functions to calculate * the internal temperature of the heaters. * @details * Inputs : none * Outputs : internalHeatersConversionTimer, elapsedTime, temperatureValues * @return state (TEMPSENSORS_EXEC_STATES_T) *************************************************************************/ static TEMPSENSORS_EXEC_STATES_T handleExecGetADCValues( void ) { // Look at the error counter and the specific error flag to make sure the error is a temp sensor // Add a byte array to have bits for each sensor to find out exactly what sensor failed processTempSnsrsADCRead( TEMPSENSORS_INLET_PRIMARY_HEATER, getFPGATPiTemp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead( TEMPSENSORS_OUTLET_PRIMARY_HEATER, getFPGATPoTemp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead( TEMPSENSORS_CONDUCTIVITY_SENSOR_1, getFPGACD1Temp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead( TEMPSENSORS_CONDUCTIVITY_SENSOR_2, getFPGACD2Temp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead( TEMPSENSORS_OUTLET_REDUNDANCY, getFPGATHDoTemp(), getFPGATHDoErrorCount(), getFPGATHDoReadCount() ); processTempSnsrsADCRead( TEMPSENSORS_INLET_DIALYSATE, getFPGATDiTemp(), getFPGATDiErrorCount(), getFPGATDiReadCount() ); processHtrsTempSnsrsADCRead( TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE, getFPGAPrimaryHeaterTemp(), getFPGAPrimaryHeaterFlags(), getFPGAPrimaryHeaterReadCount() ); processHtrsTempSnsrsADCRead( TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE, getFPGATrimmerHeaterTemp(), getFPGATrimmerHeaterFlags(), getFPGATrimmerHeaterReadCount() ); processHtrsTempSnsrsADCRead( TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION, getFPGAPrimaryColdJunctionTemp(), getFPGATrimmerHeaterFlags(), getFPGAPrimaryHeaterReadCount() ); processHtrsTempSnsrsADCRead( TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION, getFPGATrimmerColdJunctionTemp(), getFPGATrimmerHeaterFlags(), getFPGATrimmerHeaterReadCount() ); // Check if time has elapsed to calculate the internal temperature of the heaters if ( ++internalHeatersConversionTimer >= HEATERS_INTERNAL_TEMPERTURE_CALCULATION_INTERVAL ) { getHeaterInternalTemp( TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE, TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION ); getHeaterInternalTemp( TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE, TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION ); internalHeatersConversionTimer = 0; } publishTemperatureSensorsData(); return TEMPSENSORS_EXEC_STATE_GET_ADC_VALUES; } /*********************************************************************//** * @brief * The getPublishTemperatureSensorsDataInterval function returns the data * publication interval either from the data or from the override. * @details * Inputs : tempSensorsPublishInterval * Outputs : none * @return result *************************************************************************/ static U32 getPublishTemperatureSensorsDataInterval( void ) { U32 result = tempSensorsPublishInterval.data; if ( tempSensorsPublishInterval.override == OVERRIDE_KEY ) { result = tempSensorsPublishInterval.ovData; } return result; } /*********************************************************************//** * @brief * The publishTemperatureSensorsData function broadcasts the temperature * sensors data at the publication interval * @details * Inputs : dataPublicationTimerCounter, tempValuesForPublication * Outputs : dataPublicationTimerCounter, tempValuesForPublication * @return none *************************************************************************/ static void publishTemperatureSensorsData( void ) { if ( ++dataPublicationTimerCounter >= getPublishTemperatureSensorsDataInterval() ) { U32 i; for ( i = 0; i < NUM_OF_TEMPERATURE_SENSORS; i++ ) { tempValuesForPublication[ i ] = getTemperatureValue ( i ); } broadcastTemperatureSensorsData( (U08*)(&tempValuesForPublication), NUM_OF_TEMPERATURE_SENSORS * sizeof(F32) ); dataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetMeasuredTemperatureOverride function sets the override value * for a specific temperature sensor. * @details * Inputs : temperatureValues * Outputs : temperatureValues * @param sensorIndex temperature sensor index * @param temperature temperature value to override if testing activated * @return result *************************************************************************/ BOOL testSetMeasuredTemperatureOverride( U32 sensorIndex, F32 temperature ) { BOOL result = FALSE; if ( sensorIndex < NUM_OF_TEMPERATURE_SENSORS ) { if ( isTestingActivated() ) { result = TRUE; tempSensors[ sensorIndex ].temperatureValues.ovData = temperature; tempSensors[ sensorIndex ].temperatureValues.override = OVERRIDE_KEY; } } return result; } /*********************************************************************//** * @brief * The testSetMeasuredTemperatureOverride function resets the override value * of a specified temperature sensor. * @details * Inputs : temperatureValues * Outputs : temperatureValues * @param sensorIndex temperature sensor index * @return result *************************************************************************/ BOOL testResetMeasuredTemperatureOverride( U32 sensorIndex ) { BOOL result = FALSE; if ( sensorIndex < NUM_OF_TEMPERATURE_SENSORS ) { if ( isTestingActivated() ) { result = TRUE; tempSensors[ sensorIndex ].temperatureValues.override = OVERRIDE_RESET; tempSensors[ sensorIndex ].temperatureValues.ovData = tempSensors[ sensorIndex ].temperatureValues.ovInitData; } } return result; } /*********************************************************************//** * @brief * The testSetTemperatureSensorsPublishIntervalOverride function overrides * the temperature sensors publish data interval. * @details * Inputs : tempSensorsPublishInterval * Outputs : tempSensorsPublishInterval * @param value temperature sensor data broadcast interval (in ms) to override to * @return result *************************************************************************/ BOOL testSetTemperatureSensorsPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( isTestingActivated() ) { U32 interval = value / TASK_PRIORITY_INTERVAL; result = TRUE; tempSensorsPublishInterval.ovData = interval; tempSensorsPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetTemperatureSensorsPublishIntervalOverride function resets * the override value of temperature sensors publish data interval. * @details * Inputs : tempSensorsPublishInterval * Outputs : tempSensorsPublishInterval * @return result *************************************************************************/ BOOL testResetTemperatureSensorsPublishIntervalOverride( void ) { BOOL result = FALSE; if ( isTestingActivated() ) { result = TRUE; tempSensorsPublishInterval.override = OVERRIDE_RESET; tempSensorsPublishInterval.ovData = tempSensorsPublishInterval.ovInitData; } return result; } /**@}*/