/**********************************************************************//** * * 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 * * @date 7-Apr-2020 * @author Dara Navaei * * @brief DG temperature sensors controller * **************************************************************************/ #include // For temperature calculation #include "TemperatureSensors.h" #include "FPGA.h" #include "SystemCommMessages.h" #include "Timers.h" #include "TaskPriority.h" /** * @addtogroup TemperatureSensors * @{ */ // Private defines #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 ///< Conductivity sensor gain #define COND_SENSORS_TEMP_SENSOR_REF_RESISTANCE 19600U ///< Conductivity sensor reference resistance #define COND_SENSORS_TEMP_SENSOR_0_DEGREE_RESISTANCE 1000U ///< Conductivity sensor 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)) ///< 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 MAX_NUM_OF_RAW_ADC_SAMPLES 32U ///< Number of ADC reads for moving average calculations #define ADC_READ_FIRST_READ_INDEX 0U ///< ADC array first ADC read index #define ADC_READ_NEXT_INDEX_INDEX 0U ///< ADC array next insertion index #define ADC_READ_RUNNING_SUM_INDEX 1U ///< ADC array running sum index #define ADC_READ_GAIN_INDEX 0U ///< ADC array gain index #define ADC_READ_REF_RESISTANCE_INDEX 1U ///< ADC array reference resistances index #define ADC_READ_0_DEG_RESISTANCE_INDEX 2U ///< ADC array zero degree resistance index #define ADC_READ_2_TEMP_CONVERSION_COEFFICIENT 3U ///< ADC array ADC to temperature conversion coefficient (will be used for heaters internal sensors) #define READ_AND_ERROR_PREV_FPGA_COUNT_INDEX 0U ///< Read and error previous FPGA count index #define READ_AND_ERROR_INTERNAL_READ_COUNT_INDEX 1U ///< Read and error internal read count index #define READ_AND_ERROR_INTERNAL_ERROR_COUNT_INDEX 2U ///< Read and error internal error count index #define READ_AND_ERROR_PREV_FPGA_ERROR_INDEX 3U ///< Read and error previous FPGA error index #define NUM_OF_READ_AND_ERROR_ARRAY_COLUMNS 4U ///< Number of read and error array columns #define MAX_ALLOWED_TEMP_DELTA_BETWEEN_SENSORS 2U ///< Maximum allowed temperature delta between sensors #define NUM_OF_RUNNING_SUM_AND_INDEX_ARRAY_COLUMNS 2U ///< Number of columns in running sum and index array #define NUM_OF_TEMP_SENSORS_CONSTANTS_ARRAY_COLUMNS 4U ///< Number of columns in temperature sensors constants #define MAX_ALLOWED_UNCHANGED_ADC_READS 4U ///< Maximum number of times that the read of a sensor cannot change #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 SHIFT_BITS_BY_2 2U ///< Shift bits by 2 #define MIN_WATER_INPUT_TEMPERATURE 10U ///< Minimum water input temperature #define MAX_WATER_INPUT_TEMPERATURE 40U ///< Maximum water input temperature #define ADC_FPGA_READ_DELAY 30U ///< Delay in ms before reading the ADC values from FPGA #define INPUT_WATER_TEMPERATURE_CHECK_TIME_INTERVAL 100U ///< Time interval that is used to check the input water #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 TEMP_SENSORS_DATA_PUBLISH_INTERVAL (5000 / TASK_PRIORITY_INTERVAL) ///< Temperature sensors publish data time interval #define K_THERMOCOUPLE_TEMP_2_MILLI_VOLT_CONVERSION_COEFF 0.041276 ///< K thermocouple temperature to millivolt conversion coefficient #define SHIFT_BITS_BY_5_FOR_AVERAGING 5U ///< Shift the ADCs of the temperature sensors by 5 to average them #define SIZE_OF_THERMOCOUPLE_COEFFICIENTS 10U ///< Size of the thermocouple coefficients /// 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; // Private variables 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 S32 rawADCReads [ NUM_OF_TEMPERATURE_SENSORS ] [ MAX_NUM_OF_RAW_ADC_SAMPLES ]; ///< Raw ADC reads array static S32 runningSumAndIndex [ NUM_OF_TEMPERATURE_SENSORS ] [ NUM_OF_RUNNING_SUM_AND_INDEX_ARRAY_COLUMNS ]; ///< Running sum and next ADC index array static F32 tempSensorsConstants [ NUM_OF_TEMPERATURE_SENSORS ] [ NUM_OF_TEMP_SENSORS_CONSTANTS_ARRAY_COLUMNS ]; ///< Temperature sensors constants array static U32 readAndErrorCounts [ NUM_OF_TEMPERATURE_SENSORS ] [ NUM_OF_READ_AND_ERROR_ARRAY_COLUMNS ]; ///< Read and error counts from FPGA array static OVERRIDE_F32_T temperatureValues [ NUM_OF_TEMPERATURE_SENSORS ]; ///< Temperature values array static U32 elapsedTime; ///< Elapsed time variable static U32 internalHeatersConversionTimer; ///< Conversion timer variable to calculate the heaters internal temperature static OVERRIDE_U32_T tempSensorsPublishInterval = { TEMP_SENSORS_DATA_PUBLISH_INTERVAL, TEMP_SENSORS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Temperature sensors publish time interval override static U32 dataPublicationTimerCounter; ///< Temperature sensors data publish timer counter static F32 tempValuesForPublication [ NUM_OF_TEMPERATURE_SENSORS ]; ///< Temperature sensors data publication array 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 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 }; ///< Thermocouple correction coefficients 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 }; ///< Thermcouple inverse coefficient for positive cold junction temperature // Private functions 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 DATA_GET_PROTOTYPE( U32, getPublishTemperatureSensorsDataInterval ); // Public functions /************************************************************************* * @brief * The initTemperatureSensors function initializes the module * @details * Inputs : none * Outputs : tempSensorsSelfTestResult, tempSensorsSelfTestState, * tempSensorsExecState, sampleCount, elapsedTime, tempSensorsConstants * @param none * @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 */ // Initialize TPi and TPo constants for ( i = TEMPSENSORS_INLET_PRIMARY_HEATER_TEMP_SENSOR; i < TEMPSENSORS_CONDUCTIVITY_SENSOR_1_TEMP_SENSOR; i++ ) { tempSensorsConstants [ i ] [ ADC_READ_GAIN_INDEX ] = PRIMARY_HEATER_EXT_TEMP_SENSORS_GAIN; tempSensorsConstants [ i ] [ ADC_READ_REF_RESISTANCE_INDEX ] = PRIMARY_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE; tempSensorsConstants [ i ] [ ADC_READ_0_DEG_RESISTANCE_INDEX ] = PRIMARY_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensorsConstants [ i ] [ ADC_READ_2_TEMP_CONVERSION_COEFFICIENT ] = 0; } // Initialize TD1 and TD2 constants for ( i = TEMPSENSORS_CONDUCTIVITY_SENSOR_1_TEMP_SENSOR; i < TEMPSENSORS_OUTLET_REDUNDANCY_TEMP_SENSOR; i++ ) { tempSensorsConstants [ i ] [ ADC_READ_GAIN_INDEX ] = COND_SENSORS_TEMP_SENSOR_GAIN; tempSensorsConstants [ i ] [ ADC_READ_REF_RESISTANCE_INDEX ] = COND_SENSORS_TEMP_SENSOR_REF_RESISTANCE; tempSensorsConstants [ i ] [ ADC_READ_0_DEG_RESISTANCE_INDEX ] = COND_SENSORS_TEMP_SENSOR_0_DEGREE_RESISTANCE; tempSensorsConstants [ i ] [ ADC_READ_2_TEMP_CONVERSION_COEFFICIENT ] = 0; } // Initialize TRo and TDi constants for ( i = TEMPSENSORS_OUTLET_REDUNDANCY_TEMP_SENSOR; i < TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE_TEMP_SENSOR; i++ ) { tempSensorsConstants [ i ] [ ADC_READ_GAIN_INDEX ] = TRIMMER_HEATER_EXT_TEMP_SENSORS_GAIN; tempSensorsConstants [ i ] [ ADC_READ_REF_RESISTANCE_INDEX ] = TRIMMER_HEATER_EXT_TEMP_SENSORS_REF_RESISTANCE; tempSensorsConstants [ i ] [ ADC_READ_0_DEG_RESISTANCE_INDEX ] = TRIMMER_HEATER_EXT_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensorsConstants [ i ] [ ADC_READ_2_TEMP_CONVERSION_COEFFICIENT ] = 0; } // Initialize the heaters internal thermocouples constants for ( i = TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE_TEMP_SENSOR; i < TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION_TEMP_SENSOR; i++ ) { tempSensorsConstants [ i ] [ ADC_READ_GAIN_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_REF_RESISTANCE_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_0_DEG_RESISTANCE_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_2_TEMP_CONVERSION_COEFFICIENT ] = HEATERS_INTERNAL_TC_ADC_TO_TEMP_CONVERSION_COEFF; } // Initialize the heaters cold junction constants for ( i = TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION_TEMP_SENSOR; i < TEMPSENSORS_PRIMARY_HEATER_INTERNAL_TEMP; i++ ) { tempSensorsConstants [ i ] [ ADC_READ_GAIN_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_REF_RESISTANCE_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_0_DEG_RESISTANCE_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_2_TEMP_CONVERSION_COEFFICIENT ] = 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 for ( i = TEMPSENSORS_PRIMARY_HEATER_INTERNAL_TEMP; i < NUM_OF_TEMPERATURE_SENSORS; i++ ) { tempSensorsConstants [ i ] [ ADC_READ_GAIN_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_REF_RESISTANCE_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_0_DEG_RESISTANCE_INDEX ] = 0; tempSensorsConstants [ i ] [ ADC_READ_2_TEMP_CONVERSION_COEFFICIENT ] = 0; } } /************************************************************************* * @brief * The execTemperatureSensorsSelfTest function runs the TemperatureSensors * POST during the self test * @details * Inputs : tempSensorsSelfTestState * Outputs : tempSensorsSelfTestState * @param none * @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_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_INVALID_SELF_TEST_STATE, tempSensorsSelfTestState ); tempSensorsSelfTestState = TEMPSENSORS_SELF_TEST_COMPLETE; break; } return tempSensorsSelfTestResult; } /************************************************************************* * @brief * The execTemperatureSensors function runs the TemperatureSensors main * tasks * @details * Inputs : tempSensorsExecState * Outputs : tempSensorsExecState * @param none * @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_SOFTWARE_FAULT, SW_FAULT_ID_TEMPERATURE_SENSORS_EXEC_INVALID_STATE, tempSensorsExecState ); tempSensorsExecState = TEMPSENSORS_EXEC_STATE_GET_ADC_VALUES; break; } } /************************************************************************* * @brief * The getTemperatureValue function gets the enum of the requested * temperature sensor and returns the temperature * @details * Inputs : none * Outputs : none * @param sensor * @return temperature *************************************************************************/ F32 getTemperatureValue ( U32 sensor ) { F32 temperature; if ( temperatureValues [ sensor ].override == OVERRIDE_KEY ) { temperature = temperatureValues [ sensor ].ovData; } else { temperature = temperatureValues [ sensor ].data; } return temperature; } // Private functions /************************************************************************* * @brief * The getADC2TempConversion function calculates the temperature from * ADC read from FPGA * @details * Inputs : none * Outputs : temperatureValues * @param avgADC, gain, refResistance, zeroDegResistance * @return temperature *************************************************************************/ static F32 getADC2TempConversion ( F32 avgADC, U32 gain, U32 refResistance, U32 zeroDegResistance, F32 adcConversionCoeff ) { F32 temperature; if ( adcConversionCoeff == 0.0 ) { //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 internal temperature * of the heaters * @details * Inputs : temperatureValues * Outputs : temperatureValues * @param TCIndex, CJIndex * @return none *************************************************************************/ static void getHeaterInternalTemp ( U32 TCIndex, U32 CJIndex ) { F32 temperature = 0.0; F32 equiVoltage = 0.0; F32 correctedVoltage = 0.0; F32 TCTemp = temperatureValues [ TCIndex ].data; F32 CJTemp = temperatureValues [ CJIndex ].data; // Value in milli-volts F32 rawVoltage = ( TCTemp - CJTemp ) * K_THERMOCOUPLE_TEMP_2_MILLI_VOLT_CONVERSION_COEFF; U08 i; 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) ); } } if ( TCIndex == TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE_TEMP_SENSOR ) { temperatureValues [ TEMPSENSORS_PRIMARY_HEATER_INTERNAL_TEMP ].data = temperature; } else if ( TCIndex == TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE_TEMP_SENSOR ) { temperatureValues [ TEMPSENSORS_TRIMMER_HEATER_INTERNAL_TEMP ].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 : none * @param sensorIndex, adc, fpgaError, fpgaCount * @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 : none * @param sensorIndex, adc, fpgaError, fpgaCount * @return none *************************************************************************/ static void processHtrsTempSnsrsADCRead ( U32 sensorIndex, U32 adc, U32 fpgaError, U32 fpgaCount ) { U16 adcConv; S16 convertedADC; if ( sensorIndex == TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE_TEMP_SENSOR || sensorIndex == TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE_TEMP_SENSOR ) { // 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_TEMP_SENSOR || sensorIndex == TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION_TEMP_SENSOR ) { // 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 error in the FPGA error, it raises and 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 : readAndErrorCounts * Outputs : readAndErrorCounts * @param sensorIndex, fpgaError, fpgaCount * @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 U32 previousReadCount = readAndErrorCounts [ sensorIndex ] [ READ_AND_ERROR_PREV_FPGA_COUNT_INDEX ]; if ( fpgaError != 0 ) { /*if ( error == EXTERNAL_TEMP_SENSORS_ERROR_VALUE ) { // TODO: alarm? }*/ } else if ( fpgaCount == previousReadCount ) { U32 internalErrorCount = readAndErrorCounts [ sensorIndex ] [ READ_AND_ERROR_INTERNAL_ERROR_COUNT_INDEX ]; if ( internalErrorCount > MAX_ALLOWED_UNCHANGED_ADC_READS ) { // TODO alarm } else { internalErrorCount = internalErrorCount + 1; readAndErrorCounts [ sensorIndex ] [ READ_AND_ERROR_PREV_FPGA_COUNT_INDEX ] = fpgaCount; readAndErrorCounts [ sensorIndex ] [ READ_AND_ERROR_INTERNAL_ERROR_COUNT_INDEX ] = internalErrorCount; } } else if ( fpgaCount > previousReadCount ) { readAndErrorCounts [ sensorIndex ] [ READ_AND_ERROR_PREV_FPGA_COUNT_INDEX ] = fpgaCount; readAndErrorCounts [ sensorIndex ] [ READ_AND_ERROR_INTERNAL_ERROR_COUNT_INDEX ] = 0; isADCValid = TRUE; } 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 : runningSumAndIndex, rawADCReads, sampleCount, temperatureValues * Outputs : runningSumAndIndex, rawADCReads, sampleCount, temperatureValues * @param sensorIndex, adc * @return none *************************************************************************/ static void processADCRead ( U32 sensorIndex, S32 adc ) { // Update the values in the folders U32 index = runningSumAndIndex [ sensorIndex ] [ ADC_READ_NEXT_INDEX_INDEX ]; S32 runningSum = runningSumAndIndex [ sensorIndex ] [ ADC_READ_RUNNING_SUM_INDEX ]; S32 indexValue = rawADCReads [ sensorIndex ] [ index ]; U32 nextIndex = INC_WRAP( index, ADC_READ_FIRST_READ_INDEX, MAX_NUM_OF_RAW_ADC_SAMPLES - 1 ); runningSum = runningSum - indexValue + adc; rawADCReads [ sensorIndex ] [ index ] = adc; runningSumAndIndex [ sensorIndex ] [ ADC_READ_NEXT_INDEX_INDEX ] = nextIndex; runningSumAndIndex [ sensorIndex ] [ ADC_READ_RUNNING_SUM_INDEX ] = runningSum; // Calculate the average F32 avgADCReads = runningSum >> SHIFT_BITS_BY_5_FOR_AVERAGING; F32 temperature = getADC2TempConversion ( avgADCReads, (U32)tempSensorsConstants [ sensorIndex ] [ ADC_READ_GAIN_INDEX ], (U32)tempSensorsConstants [ sensorIndex ] [ ADC_READ_REF_RESISTANCE_INDEX ], (U32)tempSensorsConstants [ sensorIndex ] [ ADC_READ_0_DEG_RESISTANCE_INDEX ], tempSensorsConstants [ sensorIndex ] [ ADC_READ_2_TEMP_CONVERSION_COEFFICIENT ] ); temperatureValues [ sensorIndex ].data = temperature; } /************************************************************************* * @brief * The handleSelfTestStart function transitions the self test state to * check ADC * @details * Inputs : none * Outputs : none * @param 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 : none * Outputs : none * @param none * @return state (TEMPSENSORS_SELF_TEST_STATES_T) *************************************************************************/ static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestADCCheck ( void ) { TEMPSENSORS_SELF_TEST_STATES_T state = TEMPSENSORS_SELF_TEST_CONSISTENCY_CHECK; // TODo Remove F32 avgADCReads [ NUM_OF_TEMPERATURE_SENSORS ]; //TODO remove U08 i; for ( i = 0; i < sizeof(avgADCReads); i++ ) { U32 avgADC = avgADCReads [ i ]; if ( avgADC > TEMP_SENSORS_ADC_MAX_COUNT ) { SET_ALARM_WITH_1_U32_DATA ( ALARM_ID_TEMPERATURE_SENSORS_OUT_OF_RANGE, TEMPSENSORS_SELF_TEST_ADC_CHECK ); } } return state; } /************************************************************************* * @brief * The handleSelfTestConsistencyCheck function checks the values of the * sensors to make sure they are within the allowed range from each other * @details * Inputs : none * Outputs : none * @param none * @return state (TEMPSENSORS_SELF_TEST_STATES_T) *************************************************************************/ static TEMPSENSORS_SELF_TEST_STATES_T handleSelfTestConsistencyCheck ( void ) { //TODO Consider edge cases for the consistency check TEMPSENSORS_SELF_TEST_STATES_T state = TEMPSENSORS_SELF_TEST_COMPLETE; U32 largestDelta; U08 i, j; // Check if all the sensors are within a certain degree c // from each other for ( i = 0; i < sizeof(temperatureValues); i++ ) { for ( j = 0; j < sizeof(temperatureValues); j++ ) { if ( i != j ) { largestDelta = MAX( largestDelta, fabs(temperatureValues [ i ].data - temperatureValues [ j ].data) ); } if ( largestDelta > MAX_ALLOWED_TEMP_DELTA_BETWEEN_SENSORS ) { SET_ALARM_WITH_1_U32_DATA ( ALARM_ID_TEMPERATURE_SENSORS_INCONSISTENT, TEMPSENSORS_SELF_TEST_CONSISTENCY_CHECK ); } } } // TODO finalize the results tempSensorsSelfTestResult = SELF_TEST_STATUS_PASSED; return state; } /************************************************************************* * @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 * @param none * @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, checks the temperature range of the * inlet water and calls other functions to calculate the internal temperature * of the heaters * @details * Inputs : none * Outputs : internalHeatersConversionTimer, elapsedTime, temperatureValues * @param none * @return state (TEMPSENSORS_EXEC_STATES_T) *************************************************************************/ static TEMPSENSORS_EXEC_STATES_T handleExecGetADCValues ( void ) { TEMPSENSORS_EXEC_STATES_T state = TEMPSENSORS_EXEC_STATE_GET_ADC_VALUES; // 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_TEMP_SENSOR, getFPGATPiTemp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead ( TEMPSENSORS_OUTLET_PRIMARY_HEATER_TEMP_SENSOR, getFPGATPoTemp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead ( TEMPSENSORS_CONDUCTIVITY_SENSOR_1_TEMP_SENSOR, getFPGACD1Temp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead ( TEMPSENSORS_CONDUCTIVITY_SENSOR_2_TEMP_SENSOR, getFPGACD2Temp(), getFPGARTDErrorCount(), getFPGARTDReadCount() ); processTempSnsrsADCRead ( TEMPSENSORS_OUTLET_REDUNDANCY_TEMP_SENSOR, getFPGATHDoTemp(), getFPGATHDoErrorCount(), getFPGATHDoReadCount() ); processTempSnsrsADCRead ( TEMPSENSORS_INLET_DIALYSATE_TEMP_SENSOR, getFPGATDiTemp(), getFPGATDiErrorCount(), getFPGATDiReadCount() ); processHtrsTempSnsrsADCRead ( TEMPSENSORS_PRIMARY_HEATER_THERMO_COUPLE_TEMP_SENSOR, getFPGAPrimaryHeaterTemp(), getFPGAPrimaryHeaterFlags(), getFPGAPrimaryHeaterReadCount() ); processHtrsTempSnsrsADCRead ( TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE_TEMP_SENSOR, getFPGATrimmerHeaterTemp(), getFPGATrimmerHeaterFlags(), getFPGATrimmerHeaterReadCount() ); processHtrsTempSnsrsADCRead ( TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION_TEMP_SENSOR, getFPGAPRimaryColdJunctionTemp(), getFPGATrimmerHeaterFlags(), getFPGAPrimaryHeaterReadCount() ); processHtrsTempSnsrsADCRead ( TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION_TEMP_SENSOR, getFPGATrimmerColdJunctionTemp(), getFPGATrimmerHeaterFlags(), getFPGATrimmerHeaterReadCount() ); // Check the inlet water is within temperature range at the desired time interval if ( elapsedTime == 0 ) { elapsedTime = getMSTimerCount(); } else if ( didTimeout( elapsedTime, INPUT_WATER_TEMPERATURE_CHECK_TIME_INTERVAL ) && temperatureValues [ TEMPSENSORS_INLET_PRIMARY_HEATER_TEMP_SENSOR ].override != OVERRIDE_KEY ) { elapsedTime = 0; F32 inletTemperature = temperatureValues [ TEMPSENSORS_INLET_PRIMARY_HEATER_TEMP_SENSOR ].data; if ( inletTemperature < MIN_WATER_INPUT_TEMPERATURE || inletTemperature >= MAX_WATER_INPUT_TEMPERATURE ) { SET_ALARM_WITH_1_U32_DATA ( ALARM_ID_INLET_WATER_TEMPERATURE_OUT_OF_RANGE, inletTemperature ); } } // 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_TEMP_SENSOR, TEMPSENSORS_PRIMARY_HEATER_COLD_JUNCTION_TEMP_SENSOR); getHeaterInternalTemp ( TEMPSENSORS_TRIMMER_HEATER_THERMO_COUPLE_TEMP_SENSOR, TEMPSENSORS_TRIMMER_HEATER_COLD_JUNCTION_TEMP_SENSOR); internalHeatersConversionTimer = 0; } else { internalHeatersConversionTimer++; } publishTemperatureSensorsData(); return state; } /************************************************************************* * @brief * The getPublishTemperatureSensorsDataInterval function returns the data * publication interval either from the data or from the override * @details * Inputs : tempSensorsPublishInterval * Outputs : none * @param none * @return result *************************************************************************/ 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 * @param none * @return none *************************************************************************/ static void publishTemperatureSensorsData ( void ) { if ( ++dataPublicationTimerCounter >= getPublishTemperatureSensorsDataInterval() ) { U08 i; for ( i = 0; i < NUM_OF_TEMPERATURE_SENSORS; i++ ) { tempValuesForPublication [ i ] = getTemperatureValue ( i ); } broadcastTemperatureSensorsData ( (U08*)&tempValuesForPublication, sizeof(tempValuesForPublication) ); dataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /************************************************************************* * @brief * The testSetMeasuredTemperatureOverride function sets the override value * for a specific temperature sensor * @details * Inputs : temperatureValues * Outputs : temperatureValues * @param sensor, temperature * @return result *************************************************************************/ BOOL testSetMeasuredTemperatureOverride ( U32 sensor, F32 temperature ) { BOOL result = FALSE; if ( sensor < NUM_OF_TEMPERATURE_SENSORS ) { if ( isTestingActivated() ) { result = TRUE; temperatureValues [ sensor ].ovData = temperature; temperatureValues [ sensor ].override = OVERRIDE_KEY; } } return result; } /************************************************************************* * @brief * The testSetMeasuredTemperatureOverride function resets the override value * of a specified temperature sensor * @details * Inputs : temperatureValues * Outputs : temperatureValues * @param sensor * @return result *************************************************************************/ BOOL testResetMeasuredTemperatureOverride ( U32 sensor ) { BOOL result = FALSE; if ( sensor < NUM_OF_TEMPERATURE_SENSORS ) { if ( isTestingActivated() ) { result = TRUE; temperatureValues [ sensor ].override = OVERRIDE_RESET; temperatureValues [ sensor ].ovData = temperatureValues [ sensor ].ovInitData; } } return result; } /************************************************************************* * @brief * The testSetTemperatureSensorsPublishIntervalOverride function overrides * the temperature sensors publish data interval * @details * Inputs : tempSensorsPublishInterval * Outputs : tempSensorsPublishInterval * @param value * @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 * @param none * @return result *************************************************************************/ BOOL testResetTemperatureSensorsPublishIntervalOverride ( void ) { BOOL result = FALSE; if ( isTestingActivated() ) { result = TRUE; tempSensorsPublishInterval.override = OVERRIDE_RESET; tempSensorsPublishInterval.ovData = tempSensorsPublishInterval.ovInitData; } return result; } /**@}*/