/************************************************************************** * * Copyright (c) 2020-2023 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 Thermistors.c * * @author (last) Dara Navaei * @date (last) 03-Jan-2023 * * @author (original) Dara Navaei * @date (original) 25-Nov-2020 * ***************************************************************************/ #include // For temperature calculations #include "FPGA.h" #include "InternalADC.h" #include "MessageSupport.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "Thermistors.h" #include "TaskGeneral.h" #include "Utilities.h" /** * @addtogroup Thermistors * @{ */ // ********** private definitions ********** #define THERMISTORS_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Thermistors publish data time interval. #define THERMISTORS_ADC_READ_INTERVAL ( MS_PER_SECOND / ( 2 * TASK_GENERAL_INTERVAL ) ) ///< Thermistors ADC read time interval. #define ADC_FPGA_READ_DELAY_COUNT 1.0F ///< FGPA read delay upon startup. #define TWELVE_BIT_RESOLUTION 4096U ///< 12 bit resolution conversion. #define TEN_BIT_RESOLUTION 1024U ///< 10 bit resolution conversion. #define THERMISTOR_REFERENCE_VOLTAGE 3.0F ///< Thermistors source voltage. #define THERMISTOR_FPGA_REF_VOLTAGE 5.0F ///< Onboard NTC, PS2 source voltage (read by FPGA). #define THERMISTOR_REFERENCE_RESISTOR_AT_25 10000.0F ///< Thermistors reference resistor in ohms. #define THERMISTOR_REFERENCE_TEMPERATURE 298.0F ///< Thermistors reference temperature in kelvin. #define THERMISTOR_BETA_VALUE 3380.0F ///< Thermistors beta value. #define CELSIUS_TO_KELVIN_CONVERSION 273.15F ///< Celsius to Kelvin temperature conversion. #define MIN_ALLOWED_TEMPERATURE 0.0F ///< Thermistors/sensors minimum allowed temperature reading. #define MAX_ALLOWED_TEMPERATURE 80.0F ///< Thermistors/sensors maximum allowed temperature reading. #define MAX_ALLOWED_TEMP_OUT_OF_RANGE_PERIOD ( 5 * MS_PER_SECOND ) ///< Thermistors/sensors maximum allowed temperature out of range period. #define DATA_PUBLISH_COUNTER_START_COUNT 2 ///< Data publish counter start count. /// Thermistors exec states typedef enum thermistors_Exec_States { THERMISTORS_EXEC_STATE_START_STATE = 0, ///< Thermistors exec state start state. THERMISTORS_EXEC_STATE_GET_ADC_VALUES_STATE, ///< Thermistors exec state get ADC values state. NUM_OF_THERMISTORS_EXEC_STATES, ///< Number of thermistors exec state. } THERMISTORS_EXEC_STATES_T; /// Thermistor struct typedef struct { S32 rawADCRead; ///< Thermistor raw ADC read. OVERRIDE_F32_T temperatureValue; ///< Thermistor temperature value. F32 voltageReference; ///< Thermistor voltage reference. F32 adcBitResolution; ///< Thermistor ADC Bit resolution } THERMISTOR_T; static THERMISTORS_EXEC_STATES_T thermistorsExecState; ///< Thermistors exec state. static THERMISTOR_T thermistorsStatus[ NUM_OF_THERMISTORS ]; ///< Thermistors array. static OVERRIDE_U32_T thermistorsPublishInterval = { THERMISTORS_DATA_PUBLISH_INTERVAL, THERMISTORS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Thermistors publish time interval override. static U32 dataPublishCounter; ///< Thermistors data publish timer counter. static U32 adcReadCounter; ///< Thermistors ADC read counter. static const F32 THERMISTOR_VOLTAGE_CONV_COEFF = THERMISTOR_REFERENCE_VOLTAGE / (F32)TWELVE_BIT_RESOLUTION; ///< Thermistors ADC to voltage conversion coefficient. static const F32 THERMISTOR_REF_TEMP_INV = 1 / THERMISTOR_REFERENCE_TEMPERATURE; ///< Thermistors reference inverse. // ********** private function prototypes ********** static THERMISTORS_EXEC_STATES_T handleExecStart( void ); static THERMISTORS_EXEC_STATES_T handleExecGetADCValues( void ); static void monitorThermistors( void ); static void convertADC2Temperature( void ); static F32 calculateThermistorTemperature( THERMISTORS_TEMP_SENSORS_T thermistor, U32 adcValue ); static void publishThermistorsData( void ); static void adjustThermistorSensorsRefResistance( void ); /*********************************************************************//** * @brief * The initThermistors function initializes the thermistors module. * @details Inputs: none * @details Outputs: thermistorsExecState, dataPublishCounter * @return none *************************************************************************/ void initThermistors( void ) { // Reset the thermistors values for a run thermistorsExecState = THERMISTORS_EXEC_STATE_START_STATE; dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; // Initialize a persistent alarm for thermistors temeprature out of range initPersistentAlarm( ALARM_ID_DG_THERMISTORS_TEMPERATURE_OUT_OF_RANGE, MAX_ALLOWED_TEMP_OUT_OF_RANGE_PERIOD, MAX_ALLOWED_TEMP_OUT_OF_RANGE_PERIOD ); } /*********************************************************************//** * @brief * The execThermistorsSelfTest function executes the thermistors self test. * @details Inputs: none * @details Outputs: none * @return Status of self test *************************************************************************/ SELF_TEST_STATUS_T execThermistorsSelfTest( void ) { SELF_TEST_STATUS_T status = SELF_TEST_STATUS_IN_PROGRESS; // It returns a pass for now status = SELF_TEST_STATUS_PASSED; return status; } /*********************************************************************//** * @brief * The execThermistors function executes the thermistors exec states. * @details Inputs: thermistorsExecState * @details Outputs: thermistorsExecState * @return none *************************************************************************/ void execThermistors( void ) { // Adjust thermistors based upon hardware configuration adjustThermistorSensorsRefResistance(); switch ( thermistorsExecState ) { case THERMISTORS_EXEC_STATE_START_STATE: thermistorsExecState = handleExecStart(); break; case THERMISTORS_EXEC_STATE_GET_ADC_VALUES_STATE: thermistorsExecState = handleExecGetADCValues(); break; default: // Wrong state was called, raise an alarm SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_THERMISTORS_INVALID_EXEC_STATE, thermistorsExecState ); thermistorsExecState = THERMISTORS_EXEC_STATE_GET_ADC_VALUES_STATE; break; } // First convert the values convertADC2Temperature(); // Monitor the values for a gross range check // Monitor is called in this function because this driver is constantly reading // the thermistor values. Also the internal ADC values are processed with moving average in the internalADC driver // So the thermistors drivers just gets the latest ADC value and converts it to temperature monitorThermistors(); // Check if it is time to publish any data publishThermistorsData(); } /*********************************************************************//** * @brief * The getThermistorTemperatureValue function returns the temperature of * a requested thermistor or temperature sensor. * @details Inputs: thermistorsStatus * @details Outputs: none * @param thermistor index to get its temperature value * @return temperature of a thermistor or temperature sensor *************************************************************************/ F32 getThermistorTemperatureValue( THERMISTORS_TEMP_SENSORS_T thermistor ) { F32 temperature = 0.0; // Check if the thermistor of sensor is in range if ( thermistor < NUM_OF_THERMISTORS ) { if ( OVERRIDE_KEY == thermistorsStatus[ thermistor ].temperatureValue.override ) { temperature = thermistorsStatus[ thermistor ].temperatureValue.ovData; } else { temperature = thermistorsStatus[ thermistor ].temperatureValue.data; } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_THERMISTOR_SELECTED, thermistor ); } return temperature; } /*********************************************************************//** * @brief * The handleExecStart function handles the start state of the exec state * machine. * @details Inputs: adcReadCounter * @details Outputs: adcReadCounter * @return next state of the exec state machine *************************************************************************/ static THERMISTORS_EXEC_STATES_T handleExecStart( void ) { THERMISTORS_EXEC_STATES_T state = THERMISTORS_EXEC_STATE_START_STATE; // Give a short time for FPGA to boot up and start sending the ADC reads if ( ++adcReadCounter > ADC_FPGA_READ_DELAY_COUNT ) { state = THERMISTORS_EXEC_STATE_GET_ADC_VALUES_STATE; adcReadCounter = 0; } return state; } /*********************************************************************//** * @brief * The handleExecGetADCValues function handles the get ADC values state * of the exec state machine. * @details Inputs: adcReadCounter, thermistorsStatus * @details Outputs: adcReadCounter, thermistorsStatus * @return next state of the exec machine *************************************************************************/ static THERMISTORS_EXEC_STATES_T handleExecGetADCValues( void ) { THERMISTORS_EXEC_STATES_T state = THERMISTORS_EXEC_STATE_GET_ADC_VALUES_STATE; // If time has elapsed to read the ADCs, read them all if ( ++adcReadCounter >= THERMISTORS_ADC_READ_INTERVAL ) { #ifndef _RELEASE_ if ( HW_CONFIG_BETA == getHardwareConfigStatus() ) { thermistorsStatus[ THERMISTOR_ONBOARD_NTC ].rawADCRead = getIntADCReading( INT_ADC_BOARD_THERMISTOR ); thermistorsStatus[ THERMISTOR_POWER_SUPPLY_1 ].rawADCRead = getIntADCReading( INT_ADC_POWER_SUPPLY_1_THERMISTOR ); thermistorsStatus[ THERMISTOR_POWER_SUPPLY_2 ].rawADCRead = getIntADCReading( INT_ADC_POWER_SUPPLY_2_THERMISTOR ); } else { thermistorsStatus[ THERMISTOR_ONBOARD_NTC ].rawADCRead = getFPGAOnBoardThermistorCount(); thermistorsStatus[ THERMISTOR_POWER_SUPPLY_1 ].rawADCRead = getIntADCReading( INT_ADC_POWER_SUPPLY_1_THERMISTOR ); thermistorsStatus[ THERMISTOR_POWER_SUPPLY_2 ].rawADCRead = getFPGAPowerSupply2ThermistorCount(); } #endif // Get all the raw readings in ADC // Zero the counter for the next round of reading adcReadCounter = 0; } return state; } /*********************************************************************//** * @brief * The monitorThermistors function monitors the thermistors and sensors * for gross temperature range check. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ static void monitorThermistors( void ) { THERMISTORS_TEMP_SENSORS_T thermistor; THERMISTORS_TEMP_SENSORS_T alarmID; F32 temperature = 0.0F; BOOL isTempOutOfRange = FALSE; F32 alarmTemperature = 0.0F; for ( thermistor = THERMISTOR_ONBOARD_NTC; thermistor < NUM_OF_THERMISTORS; thermistor++ ) { temperature = getThermistorTemperatureValue( thermistor ); // Check if thermistor is out of range if ( ( temperature < MIN_ALLOWED_TEMPERATURE ) || ( temperature > MAX_ALLOWED_TEMPERATURE ) ) { isTempOutOfRange = TRUE; alarmID = thermistor; alarmTemperature = temperature; } } checkPersistentAlarm( ALARM_ID_DG_THERMISTORS_TEMPERATURE_OUT_OF_RANGE, isTempOutOfRange, alarmID, alarmTemperature); } /*********************************************************************//** * @brief * The convertADCtoTemperature function converts the ADC values of different * thermistors and temperature sensors to temperature value. * @details Inputs: none * @details Outputs: thermistorsStatus * @return none *************************************************************************/ static void convertADC2Temperature( void ) { THERMISTORS_TEMP_SENSORS_T thermistor; F32 temperature; S32 rawADC; // Loop through the list and update the temperature values for ( thermistor = THERMISTOR_ONBOARD_NTC; thermistor < NUM_OF_THERMISTORS; thermistor++ ) { rawADC = (S32)thermistorsStatus[ thermistor ].rawADCRead; temperature = calculateThermistorTemperature( thermistor, rawADC ); thermistorsStatus[ thermistor ].temperatureValue.data = temperature; } } /*********************************************************************//** * @brief * The calculateThermistorTemperature function converts the ADC value * of thermistors into temperature in C. Below are the calculation * steps: * voltage = ADC x 3 / 2^12 * voltage = 3 x 10 / ( 10 + R(T) ) * R(T) = 10000 x e^(beta x (1/T - 1/298)) * Solve for T which is temperature in Kelvin * @details Inputs: none * @details Outputs: none * @param ADC value to be converted into temperature in C * @return calculated temperature in C *************************************************************************/ static F32 calculateThermistorTemperature( THERMISTORS_TEMP_SENSORS_T thermistor, U32 adcValue ) { // Voltage = ADC x 3 / 2^12 for 12 bits resolution and a 3V ADC // The value of 3 / 2^12 has been calculated in a const to prevent the division again F32 thermistorVoltage = adcValue * THERMISTOR_VOLTAGE_CONV_COEFF; thermistorVoltage = adcValue * ( thermistorsStatus[ thermistor ].voltageReference / thermistorsStatus[ thermistor ].adcBitResolution ); // Calculate the thermistor resistor by solving: thermistorVoltage = (3 x 10) / (10 + R(T)) F32 thermistorResistor = ( ( THERMISTOR_REFERENCE_RESISTOR_AT_25 * thermistorsStatus[ thermistor ].voltageReference ) - ( THERMISTOR_REFERENCE_RESISTOR_AT_25 * thermistorVoltage ) ) / thermistorVoltage; // 1/T = Ln(thermistorResistor/10000)/3380 + 1/298 F32 invTemperature = ( logf( thermistorResistor / THERMISTOR_REFERENCE_RESISTOR_AT_25 ) / THERMISTOR_BETA_VALUE ) + THERMISTOR_REF_TEMP_INV; // Inverse the value to get the temperature in Kelvin and then convert it to Celsius F32 temperature = ( 1 / invTemperature ) - CELSIUS_TO_KELVIN_CONVERSION; return temperature; } /*********************************************************************//** * @brief * The publishThermistorsData function publishes the thermistors and * temperature sensors data at the specified time interval. * @details Inputs: dataPublishCounter * @details Outputs: dataPublishCounter * @return none *************************************************************************/ static void publishThermistorsData( void ) { if ( ++dataPublishCounter >= getU32OverrideValue( &thermistorsPublishInterval ) ) { THERMISTORS_DATA_T sensorsData; // Get all the sensors/thermistors temperature values for publication sensorsData.onboardThermistor = getThermistorTemperatureValue( THERMISTOR_ONBOARD_NTC ); sensorsData.powerSupply1Thermistor = getThermistorTemperatureValue( THERMISTOR_POWER_SUPPLY_1 ); sensorsData.powerSupply2Thermistor = getThermistorTemperatureValue( THERMISTOR_POWER_SUPPLY_2 ); // Broadcast the thermistors data broadcastData( MSG_ID_DG_THERMISTORS_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&sensorsData, sizeof( THERMISTORS_DATA_T ) ); // Reset the counter dataPublishCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetThermistorPublishIntervalOverride function overrides the * thermistors data publish interval. * @details Inputs: thermistorsPublishInterval * @details Outputs: thermistorsPublishInterval * @param value which is override value for the thermistors data publish * interval * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetThermistorPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_GENERAL_INTERVAL; result = TRUE; thermistorsPublishInterval.ovData = intvl; thermistorsPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetThermistorPublishIntervalOverride function resets the override * of the thermistors publish interval. * @details Inputs: thermistorsPublishInterval * @details Outputs: thermistorsPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetThermistorPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; thermistorsPublishInterval.override = OVERRIDE_RESET; thermistorsPublishInterval.ovData = thermistorsPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetMeasuredThermistorOverride function overrides the value of * a thermistor. * @details Inputs: thermistorsStatus * @details Outputs: thermistorsStatus * @param thermistor which its value is overridden * @param temperature value to override * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredThermistorOverride( U32 thermistor, F32 temperature ) { BOOL result = FALSE; if ( ( thermistor < NUM_OF_THERMISTORS ) && ( TRUE == isTestingActivated() ) ) { result = TRUE; thermistorsStatus[ thermistor ].temperatureValue.ovData = temperature; thermistorsStatus[ thermistor ].temperatureValue.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredThermistorOverride function resets the override * value of a thermistor. * @details Inputs: thermistorsStatus * @details Outputs: thermistorsStatus * @param thermistor which its value is overridden * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredThermistorOverride( U32 thermistor ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { if ( thermistor < NUM_OF_THERMISTORS ) { result = TRUE; thermistorsStatus[ thermistor ].temperatureValue.override = OVERRIDE_RESET; thermistorsStatus[ thermistor ].temperatureValue.ovData = thermistorsStatus[ thermistor ].temperatureValue.ovInitData; } } return result; } /*********************************************************************//** * @brief * The adjustTemperatureSensorsRefResistance function adjusts the temperature * sensors V3 or DVT reference resistance values. * @details Inputs: tempSensors * @details Outputs: tempSensors * @return none *************************************************************************/ static void adjustThermistorSensorsRefResistance( void ) { // Set voltage reference and ADC bit resolution for each thermistor #ifndef _RELEASE_ if ( HW_CONFIG_BETA == getHardwareConfigStatus() ) { thermistorsStatus[ THERMISTOR_ONBOARD_NTC ].voltageReference = THERMISTOR_REFERENCE_VOLTAGE; thermistorsStatus[ THERMISTOR_ONBOARD_NTC ].adcBitResolution = TWELVE_BIT_RESOLUTION; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_1 ].voltageReference = THERMISTOR_REFERENCE_VOLTAGE; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_1 ].adcBitResolution = TWELVE_BIT_RESOLUTION; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_2 ].voltageReference = THERMISTOR_FPGA_REF_VOLTAGE; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_2 ].adcBitResolution = TWELVE_BIT_RESOLUTION; } else #endif { thermistorsStatus[ THERMISTOR_ONBOARD_NTC ].voltageReference = THERMISTOR_FPGA_REF_VOLTAGE; thermistorsStatus[ THERMISTOR_ONBOARD_NTC ].adcBitResolution = TEN_BIT_RESOLUTION; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_1 ].voltageReference = THERMISTOR_REFERENCE_VOLTAGE; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_1 ].adcBitResolution = TWELVE_BIT_RESOLUTION; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_2 ].voltageReference = THERMISTOR_FPGA_REF_VOLTAGE; thermistorsStatus[ THERMISTOR_POWER_SUPPLY_2 ].adcBitResolution = TEN_BIT_RESOLUTION; } } /**@}*/