/************************************************************************** * * Copyright (c) 2019-2022 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 Temperatures.c * * @author (last) Sean Nash * @date (last) 12-Nov-2021 * * @author (original) Dara Navaei * @date (original) 01-Aug-2021 * ***************************************************************************/ #include // For temperature calculations #include "FPGA.h" #include "InternalADC.h" #include "NVDataMgmt.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "Temperatures.h" #include "TaskGeneral.h" /** * @addtogroup Temperatures * @{ */ // ********** private definitions ********** #define TEMPERATURES_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Temperatures publish data time interval. #define TEMPERATURES_ADC_READ_INTERVAL ( MS_PER_SECOND / ( 2 * TASK_GENERAL_INTERVAL ) ) ///< Temperatures ADC read time interval. #define ADC_FPGA_READ_DELAY_COUNT 1.0 ///< FGPA read delay upon startup. #define TWELVE_BIT_RESOLUTION 4096U ///< 12 bit resolution conversion. #define THERMISTOR_REFERENCE_VOLTAGE 3.0 ///< Thermistors source voltage. #define THERMISTOR_REFERENCE_RESISTOR_AT_25 10000.0 ///< Thermistors reference resistor in ohms. #define THERMISTOR_REFERENCE_TEMPERATURE 298.0 ///< Thermistors reference temperature in kelvin. #define THERMISTOR_BETA_VALUE 3380.0 ///< Thermistor beta value. #define CELSIUS_TO_KELVIN_CONVERSION 273.15 ///< Celsius to Kelvin temperature conversion. #define ADC_BOARD_TEMP_SENSOR_CONVERSION_CONST_1 272.5 ///< ADC board temperature sensor conversion constant 1. #define ADC_BOARD_TEMP_SENSOR_CONVERSION_CONST_2 0x800000 ///< ADC board temperature sensor conversion constant 2. #define MIN_ALLOWED_TEMPERATURE 0.0 ///< Thermistors/sensors minimum allowed temperature reading. #define MAX_ALLOWED_TEMPERATURE 80.0 ///< 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 6 ///< Data publish counter start count. /// Temperatures exec states typedef enum thermistors_Exec_States { TEMPERATURES_EXEC_STATE_START_STATE = 0, ///< Temperatures exec state start state. TEMPERATURES_EXEC_STATE_GET_ADC_VALUES_STATE, ///< Temperatures exec state get ADC values state. NUM_OF_TEMPERATURES_EXEC_STATES, ///< Number of temperatures exec state. } TEMPERATURES_EXEC_STATES_T; /// Temperatures structure typedef struct { U32 rawADCRead; ///< Temperatures raw ADC read. OVERRIDE_F32_T temperatureValue; ///< Temperatures value. } TEMPERATURE_SENSORS_T; // ********** private data ********** static TEMPERATURES_EXEC_STATES_T temperaturesExecState; ///< Temperatures exec state. static TEMPERATURE_SENSORS_T temperaturesStatus[ NUM_OF_TEMPERATURES ]; ///< Temperature sensors status. static OVERRIDE_U32_T temperaturesPublishInterval = { TEMPERATURES_DATA_PUBLISH_INTERVAL, TEMPERATURES_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Temperatures publish time interval override. static U32 dataPublishCounter; ///< Temperatures data publish timer counter. static U32 adcReadCounter; ///< Temperatures ADC read counter. static HD_TEMP_SENSORS_CAL_RECORD_T temperaturesCalRecord; ///< Temperatures calibration record. static const F32 THERMISTOR_VOLTAGE_CONV_COEFF = THERMISTOR_REFERENCE_VOLTAGE / (F32)TWELVE_BIT_RESOLUTION; ///< On board thermistor ADC to voltage conversion coefficient. static const F32 ON_BOARD_THERMISTOR_REF_TEMP_INV = 1.0 / THERMISTOR_REFERENCE_TEMPERATURE; ///< On board thermistor reference inverse. static const F32 FGPA_BOARD_TEMP_CONVERSION_COEFF = 503.975 / (F32)TWELVE_BIT_RESOLUTION; ///< FPGA board temperature conversion coefficient. static const F32 VENOUS_PRESS_SENSOR_TEMP_CONVERSION_COEFF = 200.0 / 2047.0; ///< Venous pressure sensor temperature conversion coefficient. static const F32 VENOUS_PRESS_SENSOR_TEMP_CONVERSION_CONSTANT = 50.0; ///< Venous pressure sensor temperature conversion constant. static const F32 ADC_BOARD_TEMP_SENSOR_CONVERSION_COEFF = 1.0 / 13584.0; ///< ADC board temperatures sensor conversion coefficient. // ********** private function prototypes ********** static TEMPERATURES_EXEC_STATES_T handleExecStart( void ); static TEMPERATURES_EXEC_STATES_T handleExecGetADCValues( void ); static void monitorTemperatures( void ); static void convertADC2Temperature( void ); static F32 calculateThemristorTemperature( S32 adcValue ); static void publishTemperaturesData( void ); static U32 getPublishTemperaturesDataInterval( void ); /*********************************************************************//** * @brief * The initTemperatures function initializes the temperatures module. * @details Inputs: none * @details Outputs: temperaturesExecState, dataPublishCounter, temperaturesStatus * @return none *************************************************************************/ void initTemperatures( void ) { U08 i; temperaturesExecState = TEMPERATURES_EXEC_STATE_START_STATE; dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; for ( i = 0; i < NUM_OF_TEMPERATURES; i++ ) { memset( &temperaturesStatus[ i ], 0x0, sizeof( TEMPERATURE_SENSORS_T ) ); } // Initialize a persistent alarm for temperatures temperature out of range initPersistentAlarm( ALARM_ID_HD_TEMPERATURES_OUT_OF_RANGE, MAX_ALLOWED_TEMP_OUT_OF_RANGE_PERIOD, MAX_ALLOWED_TEMP_OUT_OF_RANGE_PERIOD ); } /*********************************************************************//** * @brief * The execTemperaturesSelfTest function runs the temperatures POST during * the self-test. * @details Inputs: none * @details Outputs: none * @return execTemperaturesSelfTest which is the status of the self test *************************************************************************/ SELF_TEST_STATUS_T execTemperaturesSelfTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; BOOL calStatus = getNVRecord2Driver( GET_CAL_TEMPERATURE_SESNORS, (U08*)&temperaturesCalRecord, sizeof( HD_TEMP_SENSORS_CAL_RECORD_T ), NUM_OF_CAL_DATA_HD_TEMP_SENSORS, ALARM_ID_NO_ALARM ); if ( TRUE == calStatus ) { result = SELF_TEST_STATUS_PASSED; } else { result = SELF_TEST_STATUS_FAILED; } return result; } /*********************************************************************//** * @brief * The execTemperatures function executes the temperature sensors' state machine. * @details Inputs: temperaturesExecState * @details Outputs: temperaturesExecState * @return none *************************************************************************/ void execTemperatures( void ) { // Check if a new calibration is available if ( TRUE == isNewCalibrationRecordAvailable() ) { getNVRecord2Driver( GET_CAL_TEMPERATURE_SESNORS, (U08*)&temperaturesCalRecord, sizeof( HD_TEMP_SENSORS_CAL_RECORD_T ), NUM_OF_CAL_DATA_HD_TEMP_SENSORS, ALARM_ID_NO_ALARM ); } // Read the sensors all the time switch ( temperaturesExecState ) { case TEMPERATURES_EXEC_STATE_START_STATE: temperaturesExecState = handleExecStart(); break; case TEMPERATURES_EXEC_STATE_GET_ADC_VALUES_STATE: temperaturesExecState = handleExecGetADCValues(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_TEMPERATURES_INALID_EXEC_STATE, temperaturesExecState ); temperaturesExecState = TEMPERATURES_EXEC_STATE_GET_ADC_VALUES_STATE; break; } // Convert the ADC values to temperature convertADC2Temperature(); // Monitor the values for a gross range check // Monitor is called in this function because this driver is constantly reading // the temperatures values. Also the internal ADC values are processed with moving average in the internalADC driver // So the temperatures drivers just gets the latest ADC value and converts it to temperature monitorTemperatures(); // Publish the data publishTemperaturesData(); } /*********************************************************************//** * @brief * The getThermistorTemperatureValue function returns the temperature of * a requested thermistor or temperature sensor. * @details Inputs: thermistorsStatus * @details Outputs: none * @param sensorID index to get its temperature value * @return temperature of a thermistor or temperature sensor *************************************************************************/ F32 getTemperatureValue( TEMPERATURES_T sensorID ) { F32 temperature = 0.0; // Check if the temperature sensor is in range if ( sensorID < NUM_OF_TEMPERATURES ) { if ( OVERRIDE_KEY == temperaturesStatus[ sensorID ].temperatureValue.override ) { temperature = temperaturesStatus[ sensorID ].temperatureValue.ovData; } else { temperature = temperaturesStatus[ sensorID ].temperatureValue.data; } } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_TEMPERATURE_SENSOR_SELECTED, sensorID ); } 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 TEMPERATURES_EXEC_STATES_T handleExecStart( void ) { TEMPERATURES_EXEC_STATES_T state = TEMPERATURES_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 = TEMPERATURES_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 * @details Outputs: adcReadCounter, thermistorsStatus * @return next state of the exec machine *************************************************************************/ static TEMPERATURES_EXEC_STATES_T handleExecGetADCValues( void ) { TEMPERATURES_EXEC_STATES_T state = TEMPERATURES_EXEC_STATE_GET_ADC_VALUES_STATE; // If time has elapsed to read the ADCs, read them all if ( ++adcReadCounter >= TEMPERATURES_ADC_READ_INTERVAL ) { // Get all the raw readings in ADC temperaturesStatus[ THERMISTOR_ONBOARD_NTC ].rawADCRead = getIntADCReading( INT_ADC_BOARD_THERMISTOR ); temperaturesStatus[ THERMISTOR_POWER_SUPPLY_1 ].rawADCRead = getIntADCReading( INT_ADC_PS_THERMISTOR ); temperaturesStatus[ TEMPSENSOR_FPGA_BOARD_SENSOR ].rawADCRead = getFPGABoardTemperature(); temperaturesStatus[ TEMPSENSOR_VENOUS_PRESSURE_SENSOR ].rawADCRead = getFPGAVenousPressureTemperature(); temperaturesStatus[ TEMPSENSOR_PBA_ADC_SENSOR ].rawADCRead = getFPGAPBAADCTemperature(); // Zero the counter for the next round of reading adcReadCounter = 0; } return state; } /*********************************************************************//** * @brief * The monitorTemperatures function monitors the temperature sensors for * gross temperature range check. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ static void monitorTemperatures( void ) { TEMPERATURES_T sensor; BOOL isTempOutOfRange = FALSE; F32 temperature = 0.0; U32 lastFaultSensor = 0; F32 faultSensorTemp = 0.0; for ( sensor = THERMISTOR_ONBOARD_NTC; sensor < NUM_OF_TEMPERATURES; sensor++ ) { temperature = getTemperatureValue( sensor ); if ( ( temperature > MAX_ALLOWED_TEMPERATURE ) || ( temperature < MIN_ALLOWED_TEMPERATURE ) ) { isTempOutOfRange = TRUE; lastFaultSensor = sensor; faultSensorTemp = temperature; } } if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_TEMPERATURES_OUT_OF_RANGE, isTempOutOfRange ) ) { //SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_TEMPERATURES_OUT_OF_RANGE, lastFaultSensor, faultSensorTemp ); } } /*********************************************************************//** * @brief * The convertADCtoTemperature function converts the ADC values of different * thermistors and temperature sensors to temperature value. * @details Inputs: none * @details Outputs: temperaturesStatus * @return none *************************************************************************/ static void convertADC2Temperature( void ) { TEMPERATURES_T sensor; F32 temperature; S32 rawADC; // Loop through the list and update the temperatures values for ( sensor = THERMISTOR_ONBOARD_NTC; sensor < NUM_OF_TEMPERATURES; sensor++ ) { rawADC = (S32)( temperaturesStatus[ sensor ].rawADCRead & MASK_OFF_U32_MSB ); switch ( sensor ) { case THERMISTOR_ONBOARD_NTC: case THERMISTOR_POWER_SUPPLY_1: temperature = calculateThemristorTemperature( rawADC ); break; case TEMPSENSOR_FPGA_BOARD_SENSOR: // Temperature(C) = ((ADC x 503.975) / 4096) - 273.15 // The value of 503.975/4096 has been calculated and stored in the conversion coefficient variable of the structure temperature = ( rawADC * FGPA_BOARD_TEMP_CONVERSION_COEFF ) - CELSIUS_TO_KELVIN_CONVERSION; break; case TEMPSENSOR_VENOUS_PRESSURE_SENSOR: // Temperature (C) = ((ADC / 2047) * 200) - 50 temperature = ( rawADC * VENOUS_PRESS_SENSOR_TEMP_CONVERSION_COEFF ) - VENOUS_PRESS_SENSOR_TEMP_CONVERSION_CONSTANT; break; case TEMPSENSOR_PBA_ADC_SENSOR: // Temperature (C) = ((ADC - 0x800000) / 13584) - 272.5 temperature = ( ( rawADC - ADC_BOARD_TEMP_SENSOR_CONVERSION_CONST_2 ) * ADC_BOARD_TEMP_SENSOR_CONVERSION_COEFF ) - ADC_BOARD_TEMP_SENSOR_CONVERSION_CONST_1; break; default: // Wrong sensor was called, raise an alarm SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_INVALID_TEMPERATURE_SENSOR_SELECTED, sensor ); // Wrong sensor, return temperature to be -1 temperature = -1.0; break; } // Apply the calibration record the temperature values prior to updating the structures temperaturesStatus[ sensor ].temperatureValue.data = pow(temperature, 4) * temperaturesCalRecord.hdTemperatureSensors[ (CAL_DATA_HD_TEMEPERATURE_SENSORS_T)sensor ].fourthOrderCoeff + pow(temperature, 3) * temperaturesCalRecord.hdTemperatureSensors[ (CAL_DATA_HD_TEMEPERATURE_SENSORS_T)sensor ].thirdOrderCoeff + pow(temperature, 3) * temperaturesCalRecord.hdTemperatureSensors[ (CAL_DATA_HD_TEMEPERATURE_SENSORS_T)sensor ].secondOrderCoeff + temperature * temperaturesCalRecord.hdTemperatureSensors[ (CAL_DATA_HD_TEMEPERATURE_SENSORS_T)sensor ].gain + temperaturesCalRecord.hdTemperatureSensors[ (CAL_DATA_HD_TEMEPERATURE_SENSORS_T)sensor ].offset; } } /*********************************************************************//** * @brief * The calculateThemristorTemperature 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 10000 / ( 10000 + 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 adcValue which is the ADC value to be converted into temperature in C * @return calculated temperature in C *************************************************************************/ static F32 calculateThemristorTemperature( S32 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; // Calculate the thermistor resistor by solving: thermistorVoltage = (3 x 10) / (10 + R(T)) F32 thermistorResistor = ( ( THERMISTOR_REFERENCE_RESISTOR_AT_25 * THERMISTOR_REFERENCE_VOLTAGE ) - ( 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 ) + ON_BOARD_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 getPublishTemperaturesDataInterval function gets the temperatures * data publish interval. * @details Inputs: temperaturesPublishInterval * @details Outputs: none * @return data publish time interval in counts *************************************************************************/ static U32 getPublishTemperaturesDataInterval( void ) { U32 result = temperaturesPublishInterval.data; if ( OVERRIDE_KEY == temperaturesPublishInterval.override ) { result = temperaturesPublishInterval.ovData; } return result; } /*********************************************************************//** * @brief * The publishTemperaturesData function publishes the temperatures sensors * data at the specified time interval. * @details Inputs: dataPublishCounter * @details Outputs: dataPublishCounter * @return none *************************************************************************/ static void publishTemperaturesData( void ) { if ( ++dataPublishCounter > getPublishTemperaturesDataInterval() ) { TEMPERATURES_DATA_T sensorsData; // Get all the sensors/thermistors temperature values for publication sensorsData.onboardThermistor = getTemperatureValue( THERMISTOR_ONBOARD_NTC ); sensorsData.powerSupply1Thermistor = getTemperatureValue( THERMISTOR_POWER_SUPPLY_1 ); sensorsData.venousPressSensorTemp = getTemperatureValue( TEMPSENSOR_VENOUS_PRESSURE_SENSOR ); sensorsData.fpgaBoardTempSensor = getTemperatureValue( TEMPSENSOR_FPGA_BOARD_SENSOR ); sensorsData.pbaADCTempSensor = getTemperatureValue( TEMPSENSOR_PBA_ADC_SENSOR ); // Broadcast the temperatures data broadcastData( MSG_ID_HD_TEMPERATURES_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&sensorsData, sizeof( TEMPERATURES_DATA_T ) ); // Reset the counter dataPublishCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetMeasuredTemperatureOverride function overrides the value of * a temperature sensor/thermistor. * @details Inputs: none * @details Outputs: temperaturesStatus * @param sensorID the sensor its value is overridden * @param temperature value to override * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetMeasuredTemperatureOverride( U32 sensorID, F32 temperature ) { BOOL result = FALSE; if ( ( sensorID < NUM_OF_TEMPERATURES ) && ( TRUE == isTestingActivated() ) ) { result = TRUE; temperaturesStatus[ sensorID ].temperatureValue.ovData = temperature; temperaturesStatus[ sensorID ].temperatureValue.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetMeasuredTemperatureOverride function resets the override * value of a temperature sensors/thermistor. * @details Inputs: none * @details Outputs: temperaturesStatus * @param sensorID which its value is reset * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetMeasuredTemperatureOverride( U32 sensorID ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { if ( sensorID < NUM_OF_TEMPERATURES ) { result = TRUE; temperaturesStatus[ sensorID ].temperatureValue.override = OVERRIDE_RESET; temperaturesStatus[ sensorID ].temperatureValue.ovData = temperaturesStatus[ sensorID ].temperatureValue.ovInitData; } } return result; } /*********************************************************************//** * @brief * The testSetTemperturesPublishIntervalOverride function overrides the * temperatures data publish interval. * @details Inputs: none * @details Outputs: temperaturesPublishInterval * @param value which is override value for the temperatures data publish * interval * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetTemperaturesPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_GENERAL_INTERVAL; result = TRUE; temperaturesPublishInterval.ovData = intvl; temperaturesPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetTemperaturesPublishIntervalOverride function resets the override * of the temperatures publish interval. * @details Inputs: none * @details Outputs: temperaturesPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetTemperaturesPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; temperaturesPublishInterval.override = OVERRIDE_RESET; temperaturesPublishInterval.ovData = temperaturesPublishInterval.ovInitData; } return result; } /**@}*/