/************************************************************************** * * Copyright (c) 2024-2024 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) Sean Nash * @date (last) 09-Nov-2024 * * @author (original) Sean Nash * @date (original) 09-Nov-2024 * ***************************************************************************/ #include // For temperature calculation #include // For memset() #include "Conductivity.h" #include "FpgaRO.h" #include "MessageSupport.h" #include "Messaging.h" //#include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "Temperature.h" #include "Timers.h" #include "Utilities.h" /** * @addtogroup TemperatureSensors * @{ */ // ********** private definitions ********** #define USE_PT_100_SENSORS 1 ///< Flag indicates whether we have PT100 or PT1000 temperature sensors. #define PRIMARY_HEATER_TEMP_SENSORS_GAIN 8U ///< Primary heater temperature sensors gain. #define PRIMARY_HEATER_TEMP_SENSORS_REF_RESISTANCE 20000 ///< Primary heater temperature sensors reference resistance. #define PRIMARY_HEATER_TEMP_SENSORS_0_DEGREE_RESISTANCE 1000U ///< Primary heater temperature sensors zero degree resistance. #define PT100_TEMP_SENSORS_GAIN 8U ///< PT100 temperature sensors gain. #define PT100_TEMP_SENSORS_REF_RESISTANCE 4700 ///< PT100 temperature sensors reference resistance. #define PT100_TEMP_SENSORS_0_DEGREE_RESISTANCE 100U ///< PT100 temperature sensors zero degree resistance. #define TEMP_SENSORS_ADC_BITS 24U ///< External temperature sensors ADC bits. #define MAX_NUM_OF_RAW_ADC_SAMPLES 4U ///< Number of ADC reads for moving average calculations. #define SHIFT_BITS_BY_2 2U ///< Shift bits by 2 to create a 4 for averaging 4 samples. #define SHIFT_BITS_BY_2_FOR_AVERAGING 2U ///< Shift the ADCs of the temperature sensors by 2 to average them. #define CELSIUS_TO_KELVIN_CONVERSION 273.15F ///< Celsius to Kelvin temperature conversion. #define ADC_BOARD_TEMP_SENSORS_CONVERSION_CONST 272.5F ///< ADC board temperature sensors conversion constant. #define ADC_BOARD_TEMP_SENSORS_CONST 0x800000 ///< ADC board temperature sensors constant. #define TEMP_SENSORS_FPGA_ERROR_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< Temperature sensors FPGA error timeout in milliseconds. #define TEMP_SENSORS_MIN_ALLOWED_DEGREE_C 0.0F ///< Temperature sensors minimum allowed temperature in C. #define TEMP_SENSORS_MAX_ALLOWED_DEGREE_C 120.0F ///< Temperature sensors maximum allowed temperature in C. #define HEATERS_INTERNAL_TEMP_SENSORS_MAX_ALLOWED_DEGREE_C 200.0F ///< Heaters' internal temperature sensors maximum allowed temperature in C. #define NON_FLUID_PATH_TEMP_SENSORS_MAX_ALLOWED_DEGREE_C 80.0F ///< Non fluid temperature sensors path maximum allowed temperature in C. #define TEMP_SENSORS_OUT_OF_RANGE_TIME_OUT_MS ( 5 * MS_PER_SECOND ) ///< Temperature sensor out of range persistent period in milliseconds. #define DATA_PUBLISH_COUNTER_START_COUNT 30 ///< Data publish counter start count. #define TEMP_READ_COUNTER_MAX_VALUE 255 ///< FPGA temperature sensor read counter max value. static const U32 TEMP_EQUATION_RESISTOR_CALC = 1 << ( TEMP_SENSORS_ADC_BITS - 1 ); ///< Temperature sensors resistor calculation (2^(24 - 1)). static const F32 TEMP_EQUATION_COEFF_A = 3.9083E-3; ///< ADC to temperature conversion coefficient A. static const F32 TEMP_EQUATION_COEFF_B = -5.775E-7; ///< ADC to temperature conversion coefficient B. static const U32 TWO_TO_POWER_OF_8 = ( 1 << 8 ); ///< 2^8. static const U32 TWO_TO_POWER_OF_23 = ( 1 << 23 ); ///< 2^23. /// Temperature sensor struct. typedef struct { F32 gain; ///< ADC gain F32 refResistance; ///< ADC reference resistance F32 conversionCoeff; ///< 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 F32 maxAllowedTemp; ///< Maximum allowed temperature of the sensor } TEMP_SENSOR_T; // ********** private data ********** static TEMP_SENSOR_T tempSensors[ NUM_OF_TEMPERATURE_SENSORS ]; ///< Temperature sensors' data structure. static OVERRIDE_F32_T temperatureValue[ NUM_OF_TEMPERATURE_SENSORS ]; ///< Temperature values with override static OVERRIDE_U32_T lastTemperatureReadCounter; ///< Temperature sensors read count from FPGA. //static RO_TEMP_SENSORS_CAL_RECORD_T tempSensorCalRecord; ///< Temperature sensors calibration record. // ********** private function prototypes ********** static F32 getADC2TempConversion( F32 avgADC, U32 gain, U32 refResistance, U32 zeroDegResistance, F32 adcConversionCoeff ); static void processTempSnsrsADCRead( U32 sensorIndex, U32 adc ); static void processADCRead( U32 sensorIndex, S32 adc ); //static void checkTemperatureSensors( void ); //static void getCalibrationAppliedTemperatureValue( U32 sesnorIndex, F32* temperature ); /*********************************************************************//** * @brief * The initTemperatureSensors function initializes the temperature sensors unit. * @details \b Inputs: none * @details \b Outputs: unit variables initialized * @return none *************************************************************************/ void initTemperatureSensors( void ) { U08 i; F32 conversionCoeff = 1.0F / 13584.0F; for ( i = 0; i < NUM_OF_TEMPERATURE_SENSORS; ++i ) { memset( &tempSensors[ i ], 0x0, sizeof( TEMP_SENSOR_T ) ); //benignPolynomialCalRecord( &tempSensorCalRecord.tempSensors[ i ] ); } // Initialize P23 and P22 temperature sensors #ifdef USE_PT_100_SENSORS tempSensors[ P23_TEMP ].gain = PT100_TEMP_SENSORS_GAIN; tempSensors[ P23_TEMP ].refResistance = PT100_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ P23_TEMP ].zeroDegreeResistance = PT100_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ P23_TEMP ].maxAllowedTemp = TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; tempSensors[ P22_TEMP ].gain = PT100_TEMP_SENSORS_GAIN; tempSensors[ P22_TEMP ].refResistance = PT100_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ P22_TEMP ].zeroDegreeResistance = PT100_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ P22_TEMP ].maxAllowedTemp = TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; #else tempSensors[ P23_TEMP ].gain = PRIMARY_HEATER_TEMP_SENSORS_GAIN; tempSensors[ P23_TEMP ].refResistance = PRIMARY_HEATER_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ P23_TEMP ].zeroDegreeResistance = PRIMARY_HEATER_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ P23_TEMP ].maxAllowedTemp = TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; tempSensors[ P22_TEMP ].gain = PRIMARY_HEATER_TEMP_SENSORS_GAIN; tempSensors[ P22_TEMP ].refResistance = PRIMARY_HEATER_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ P22_TEMP ].zeroDegreeResistance = PRIMARY_HEATER_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ P22_TEMP ].maxAllowedTemp = TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; #endif // Board temperature sensors conversion coefficient // tempSensors[ TEMPSENSORS_BOARD_TEMPERATURE ].conversionCoeff = conversionCoeff; // tempSensors[ TEMPSENSORS_BOARD_TEMPERATURE ].maxAllowedTemp = NON_FLUID_PATH_TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; // Persistent alarm for the temperature sensors range check // initPersistentAlarm( ALARM_ID_DD_TEMPERATURE_SENSOR_OUT_OF_RANGE, TEMP_SENSORS_OUT_OF_RANGE_TIME_OUT_MS, TEMP_SENSORS_OUT_OF_RANGE_TIME_OUT_MS ); // Initialize the FPGA persistent alarms // initFPGAPersistentAlarm( FPGA_PERS_ERROR_RTD_ADC_TEMP_SENSORS, ALARM_ID_DD_RTD_SENSORS_FPGA_FAULT, TEMP_SENSORS_FPGA_ERROR_TIMEOUT_MS, TEMP_SENSORS_FPGA_ERROR_TIMEOUT_MS); } /*********************************************************************//** * @brief * The getTemperatureValue function gets the temperature of a given sensor. * @details \b Inputs: tempSensors[] * @details \b Outputs: none * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid temperature * sensor is seen. * @param sensorId ID of temperature sensor to get temperature from * @return temperature reading of the given sensor *************************************************************************/ F32 getTemperatureValue( TEMPERATURE_SENSOR_T sensorId ) { F32 temperature = 0.0F; // if valid sensor, return its temperature reading if ( sensorId < NUM_OF_TEMPERATURE_SENSORS ) { temperature = getF32OverrideValue( &temperatureValue[ sensorId ] ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_RO_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED1, (U32)sensorId ); } return temperature; } /*********************************************************************//** * @brief * The readTemperatureSensors function reads the temperature sensor * value from FPGA. * @details \b Inputs: FPGA * @details \b Outputs: lastTemperatureReadCounter * @return none *************************************************************************/ void readTemperatureSensors( void ) { // Temperature sensor read count from FPGA // lastTemperatureReadCounter.data = (U32)getFPGARTDReadCount(); //Read temperature sensors processTempSnsrsADCRead( P23_TEMP, getFPGAP23Temperature() ); processTempSnsrsADCRead( P22_TEMP, getFPGAP22Temperature() ); // //TODO: Read Board temperature // //processTempSnsrsADCRead( TEMPSENSORS_INTERNAL_COND_TEMP_SENSOR, getFPGACondSnsrInternalTemp() ); } /*********************************************************************//** * @brief * The getTemperatureSensorsReadCount function gets the current temperature sensor * read count for the RTD temperature sensors. * @details \b Inputs: lastTemperatureReadCounter * @details \b Outputs: none * @return The RTD temperature sensor read count. *************************************************************************/ //U32 getTemperatureSensorsReadCount( void ) //{ // U32 result = lastTemperatureReadCounter.data; // // if ( OVERRIDE_KEY == lastTemperatureReadCounter.override ) // { // result = lastTemperatureReadCounter.ovData; // } // // return result; //} /*********************************************************************//** * @brief * The checkTemperatureSensors function checks the temperature sensor * freshness and see if there is any read failures from FPGA. * @details \b Inputs: Temperature sensors reading from FPGA * @details \b Outputs: none * @details \b Alarms: ALARM_ID_DD_RTD_SENSORS_FPGA_FAULT when temperature sensor * read count not updated periodically * @return none *************************************************************************/ //static void checkTemperatureSensors( void ) //{ // checkFPGAPersistentAlarms( FPGA_PERS_ERROR_RTD_ADC_TEMP_SENSORS, getTemperatureSensorsReadCount() ); //} /*********************************************************************//** * @brief * The getADC2TempConversion function calculates the temperature from the * moving average ADC samples. * @details \b Inputs: tempEquationCoeffA, tempEquationCoeffB * @details \b Outputs: none * @param avgADC moving average ADC * @param gain ADC gain * @param refResistance ADC reference resistance * @param zeroDegResistance ADC zero degree resistance * @param adcConversionCoeff ADC conversion coefficient * @return calculated temperature *************************************************************************/ static F32 getADC2TempConversion( F32 avgADC, U32 gain, U32 refResistance, U32 zeroDegResistance, F32 adcConversionCoeff ) { F32 temperature = 0.0F; if ( fabs( adcConversionCoeff ) <= NEARLY_ZERO ) { // R(RTD) = R(ref) * ( adc – 2^(N - 1) ) / ( G * 2^(N - 1) ); F32 resistance = ( refResistance * ( avgADC - TEMP_EQUATION_RESISTOR_CALC ) ) / ( gain * TEMP_EQUATION_RESISTOR_CALC ); // 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 processTemperatureSensorsADCRead function masks the MSB of the ADC * read from FPGA and converts it to an S32. Then it calls the routine * that checks if the read ADC is valid or not and if it is, to process * the ADC value and covert it to temperature. * @details \b Inputs: none * @details \b Outputs: none * @param sensorId ID of temperature sensor to process * @param adc ADC value from the temperature sensor * @return none *************************************************************************/ static void processTempSnsrsADCRead( U32 sensorId, U32 adc ) { S32 convertedADC = (S32)( adc & MASK_OFF_U32_MSB ); // Make sure the error bit is not true before processADCRead( sensorId, convertedADC ); } /*********************************************************************//** * @brief * The processADCRead function updates the ADC count moving average for a * given temperature sensor. * @details \b Inputs: tempSensors * @details \b Outputs: tempSensors * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid temperature * sensor is seen. * @param sensorIndex Temperature sensor index * @param adc adc reading from FPGA * @return none *************************************************************************/ static void processADCRead( U32 sensorIndex, S32 adc ) { F32 temperature; F32 avgADCReads; U32 index = tempSensors[ sensorIndex ].adcNextIndex; S32 indexValue = tempSensors[ sensorIndex ].rawADCReads[ index ]; // Update the temperature sensors' structure 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; avgADCReads = tempSensors[ sensorIndex ].adcRunningSum >> SHIFT_BITS_BY_2_FOR_AVERAGING; // Calculate the average // Different sensors have different ADC to temperature conversion methods switch( sensorIndex ) { case P23_TEMP: case P22_TEMP: temperature = getADC2TempConversion( avgADCReads, (U32)tempSensors [ sensorIndex ].gain, (U32)tempSensors [ sensorIndex ].refResistance, (U32)tempSensors [ sensorIndex ].zeroDegreeResistance, tempSensors [ sensorIndex ].conversionCoeff ); break; case P10_TEMP: temperature = getConductivityTemperatureValue( P9_COND ); break; case P19_TEMP: temperature = getConductivityTemperatureValue( P18_COND ); break; // case TEMPSENSORS_BOARD_TEMPERATURE: // //TODO : Need details on calculations. // break; default: // invalid sensor ID, raise an alarm SET_ALARM_WITH_2_U32_DATA( ALARM_ID_RO_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED2, sensorIndex ); // Wrong sensor, return temperature to be -1 temperature = -1.0F; break; } // Calibrate temperature sensor reading // getCalibrationAppliedTemperatureValue( sensorIndex, &temperature ); // Update the temperature temperatureValue[ sensorIndex ].data = temperature; } /*********************************************************************//** * @brief * The monitorTemperatureSensors function monitors the temperature sensors' * temperature value and raises an alarm if any of them are out of range * for more than the specified time. * @details \b Inputs: tempSensors * @details \b Outputs: tempSensors * @details \b Alarms: ALARM_ID_DD_TEMPERATURE_SENSOR_OUT_OF_RANGE when the * measured temperature exceeds the maximum limit temperature. * @return none *************************************************************************/ void monitorTemperatureSenors( void ) { // TEMPERATURE_SENSOR_T sensorId; // TEMPERATURE_SENSOR_T sensorInAlarm = TEMPSENSORS_FIRST; // F32 temperature = 0.0F; // BOOL isTemperatureOutOfRange = FALSE; // F32 alarmTemperature = 0.0F; // // for ( sensorId = TEMPSENSORS_FIRST; sensorId < NUM_OF_TEMPERATURE_SENSORS; sensorId++ ) // { // // Get temperature value. // temperature = getTemperatureValue( sensorId ); // // Check both temperature and to be in range // if ( ( ( temperature < TEMP_SENSORS_MIN_ALLOWED_DEGREE_C ) || ( temperature > tempSensors[ sensorId ].maxAllowedTemp ) ) && // ( getCurrentOperationMode() != DD_MODE_INIT ) ) // { // isTemperatureOutOfRange |= TRUE; // sensorInAlarm = sensorId; // alarmTemperature = temperature; // } // } // // checkPersistentAlarm( ALARM_ID_DD_TEMPERATURE_SENSOR_OUT_OF_RANGE, isTemperatureOutOfRange, sensorInAlarm, alarmTemperature ); // // //check freshness of temperature read // checkTemperatureSensors(); } /*********************************************************************//** * @brief * The getCalibrationAppliedTemperatureValue function applies the calibration * values to the provided temperature value * @details \b Inputs: tempSensorCalRecord * @details \b Outputs: none * @param sensorIndex Temperature sensor index * @param temperature pointer to the calculated temperature value * @return none *************************************************************************/ //static void getCalibrationAppliedTemperatureValue( U32 sesnorIndex, F32* temperature ) //{ // //CAL_DATA_RO_TEMP_SENSORS_T calId; // F32 tempTemperature = *temperature; // // switch( sesnorIndex ) // { // case P23_TEMP: // //calId = CAL_DATA_P23_TEMP; // break; // // case P22_TEMP: // //calId = CAL_DATA_P22_TEMP; // break; // // default: // // Set the calibration temperature value as num of meaning calibration is not needed for the provided sensor // //calId = NUM_OF_CAL_DATA_TEMP_SENSORS; // break; // } // // if ( calId != NUM_OF_CAL_DATA_TEMP_SENSORS ) // { // *temperature = pow( tempTemperature, 4 ) * tempSensorCalRecord.tempSensors[ calId ].fourthOrderCoeff + // pow( tempTemperature, 3 ) * tempSensorCalRecord.tempSensors[ calId ].thirdOrderCoeff + // pow( tempTemperature, 2 ) * tempSensorCalRecord.tempSensors[ calId ].secondOrderCoeff + // tempTemperature * tempSensorCalRecord.tempSensors[ calId ].gain + // tempSensorCalRecord.tempSensors[ calId ].offset; // } //} /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetMeasuredTemperatureOverride function sets the override value * for a specific temperature sensor. * @details Inputs: tempSensors * @details Outputs: tempSensors * @param message Override message from Dialin which includes an ID of * the sensor to override and the state to override the sensor to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testMeasuredTemperatureOverride( MESSAGE_T *message ) { BOOL result = f32ArrayOverride( message, &temperatureValue[0], NUM_OF_TEMPERATURE_SENSORS - 1 ); return result; } /*********************************************************************//** * @brief * The testTemperatureReadCounterOverride function sets the override value * of the temperature read counter. * @details Inputs: lastTemperatureReadCounter * @details Outputs: lastTemperatureReadCounter * @param message Override message from Dialin which includes the read * counter value to override for the set of temperature sensors. * @return TRUE if override successful, FALSE if not *************************************************************************/ //BOOL testTemperatureReadCounterOverride( MESSAGE_T *message ) //{ // BOOL result = u32Override( message, &lastTemperatureReadCounter, 0, TEMP_READ_COUNTER_MAX_VALUE ); // // return result; //} /**@}*/