/************************************************************************** * * Copyright (c) 2024-2025 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) Vinayakam Mani * @date (last) 18-Sep-2025 * * @author (original) Vinayakam Mani * @date (original) 25-Sep-2024 * ***************************************************************************/ #include // For temperature calculation #include // For memset() #include "FpgaDD.h" #include "MessageSupport.h" #include "Messaging.h" //#include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "TemperatureSensors.h" #include "Timers.h" #include "Utilities.h" /** * @addtogroup TemperatureSensors * @{ */ // ********** private definitions ********** #define USE_PT_100 1 ///< flag to check PT100 use in calculations #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 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 DD_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 TH1 (primary heater), TH2(outlet Heat Exchanger), TAUX ( Inlet Heat exchanger), // TH3 ( Trim Heater) constants. tempSensors[ D1_TEMP ].gain = PRIMARY_HEATER_TEMP_SENSORS_GAIN; tempSensors[ D1_TEMP ].refResistance = PRIMARY_HEATER_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ D1_TEMP ].zeroDegreeResistance = PRIMARY_HEATER_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ D1_TEMP ].maxAllowedTemp = TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; tempSensors[ D78_TEMP ].gain = PRIMARY_HEATER_TEMP_SENSORS_GAIN; tempSensors[ D78_TEMP ].refResistance = PRIMARY_HEATER_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ D78_TEMP ].zeroDegreeResistance = PRIMARY_HEATER_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ D78_TEMP ].maxAllowedTemp = TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; tempSensors[ D4_TEMP ].gain = PRIMARY_HEATER_TEMP_SENSORS_GAIN; tempSensors[ D4_TEMP ].refResistance = PRIMARY_HEATER_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ D4_TEMP ].zeroDegreeResistance = PRIMARY_HEATER_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ D4_TEMP ].maxAllowedTemp = HEATERS_INTERNAL_TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; tempSensors[ D50_TEMP ].gain = PRIMARY_HEATER_TEMP_SENSORS_GAIN; tempSensors[ D50_TEMP ].refResistance = PRIMARY_HEATER_TEMP_SENSORS_REF_RESISTANCE; tempSensors[ D50_TEMP ].zeroDegreeResistance = PRIMARY_HEATER_TEMP_SENSORS_0_DEGREE_RESISTANCE; tempSensors[ D50_TEMP ].maxAllowedTemp = TEMP_SENSORS_MAX_ALLOWED_DEGREE_C; // Board temperature sensors conversion coefficient tempSensors[ BRD_TEMP ].conversionCoeff = conversionCoeff; tempSensors[ BRD_TEMP ].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 the requested * sensor. * @details \b Inputs: tempSensors * @details \b Outputs: none * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT when invalid temperature * sensor is seen. * @param sensorIndex which is the temperature sensor index * @return temperature of the requested sensor *************************************************************************/ F32 getTemperatureValue( U32 sensorIndex ) { F32 temperature = 0.0F; if ( sensorIndex < NUM_OF_TEMPERATURE_SENSORS ) { temperature = getF32OverrideValue( &temperatureValue[ sensorIndex ] ); } else { // Wrong sensor was called, raise an alarm SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED1, sensorIndex ); } 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( D1_TEMP, getFPGAD1Temp() ); processTempSnsrsADCRead( D78_TEMP, getFPGAD78Temp() ); processTempSnsrsADCRead( D4_TEMP, getFPGAD4Temp() ); processTempSnsrsADCRead( D50_TEMP, getFPGAD50Temp() ); //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 sensorIndex ID of temperature sensor to process * @param adc ADC value for the temperature sensor * @return none *************************************************************************/ static void processTempSnsrsADCRead( U32 sensorIndex, U32 adc ) { S32 convertedADC = (S32)( adc & MASK_OFF_U32_MSB ); // Make sure the error bit is not true before processADCRead( sensorIndex, 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 D1_TEMP: case D78_TEMP: case D4_TEMP: case D50_TEMP: temperature = getADC2TempConversion( avgADCReads, (U32)tempSensors [ sensorIndex ].gain, (U32)tempSensors [ sensorIndex ].refResistance, (U32)tempSensors [ sensorIndex ].zeroDegreeResistance, tempSensors [ sensorIndex ].conversionCoeff ); break; case BRD_TEMP: //TODO : Need details on calculations. break; default: // Wrong sensor was called, raise an alarm SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_TEMPERATURE_SENSOR_SELECTED2, sensorIndex ); // Wrong sensor, return temperature to be -1 temperature = -1.0F; break; } 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_SENSORS_T sensorId; TEMPERATURE_SENSORS_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_DD_TEMP_SENSORS_T calId; F32 tempTemperature = *temperature; switch( sesnorIndex ) { case D1_TEMP: //calId = CAL_DATA_INLET_HEAT_EXCHANGER_TEMP; break; case D78_TEMP: //calId = CAL_DATA_OUTLET_HEAT_EXCHANGER_TEMP; break; case D4_TEMP: //calId = CAL_DATA_HYD_PRIMARY_HEATER_TEMP; break; case D50_TEMP: //calId = CAL_DATA_TRIM_HEATER_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; } //TODO : Enable the code later when calibration data available. // 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; } /**@}*/