Index: firmware/App/Controllers/LoadCell.c =================================================================== diff -u -r64b37cc2955d04bbc77ea41940b1b1b30c06651b -re56a0b7c6fcae8ee61d247b3d89ea1874782eb0d --- firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 64b37cc2955d04bbc77ea41940b1b1b30c06651b) +++ firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision e56a0b7c6fcae8ee61d247b3d89ea1874782eb0d) @@ -15,8 +15,12 @@ * ***************************************************************************/ +#include // For load cells calibration calculations + #include "FPGA.h" #include "LoadCell.h" +#include "NVDataMgmt.h" +#include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "TaskPriority.h" @@ -27,46 +31,59 @@ // ********** private definitions ********** -#define LOAD_CELL_REPORT_PERIOD (100 / TASK_PRIORITY_INTERVAL) ///< Broadcast load cell values message every 100 ms. -#define LOAD_CELL_SAMPLES_TO_AVERAGE (100 / TASK_PRIORITY_INTERVAL) ///< Averaging load cell data over the reporting interval. -#define LOAD_CELL_AVERAGE_MULTIPLIER (1.0 / (F32)LOAD_CELL_SAMPLES_TO_AVERAGE) ///< Optimization - multiplying is faster than dividing. -// TODO - gain and offset for load cells should be read from NV Data calibration record. -#define ADC2GRAM (0.0894 * 1.1338) ///< Conversion factor from ADC counts to grams. -#define LOAD_CELL_ZERO_OFFSET -1215.0 ///< Zero offset (in grams). TODO - right now, this is empty reservoir weight. +// TODO check the maximum weight on the load cells in tare. There was 1500 grams limit +// but it has been removed. Check the load cells data sheet. -#define SIZE_OF_SMALL_LOAD_CELL_AVG 10 ///< Small load cell moving average has 10 samples. -#define SIZE_OF_LARGE_LOAD_CELL_AVG 40 ///< Large load cell moving average has 40 samples. +#define LOAD_CELL_REPORT_PERIOD (100 / TASK_PRIORITY_INTERVAL) ///< Broadcast load cell values message every 100 ms. +/// Conversion factor from ADC counts to grams. +static const F32 ADC2GRAM = (0.0894 * 1.1338); +#define SIZE_OF_SMALL_LOAD_CELL_AVG 100 ///< Small load cell moving average has 100 raw samples @ 10ms intervals (1-second). +#define SIZE_OF_LARGE_LOAD_CELL_AVG 40 ///< Large load cell moving average has 40 samples from small filter @ 100ms intervals (4-second). + +#define LOAD_CELL_ADC_ERROR_PERSISTENCE 500 ///< Alarm persistence period (in ms) for load cell ADC errors. +#define LOAD_CELL_MIN_ALLOWED_WEIGHT_GRAMS 0.0 ///< Load cell minimum allowed weight in grams. +#define LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS 4500.0 ///< Load cell maximum allowed weight in grams. +#define LOAD_CELL_MIN_ALLOWED_WEIGHT_BEFORE_TARE_GRAMS 1600.0 ///< Load cell minimum allowed weight before tare in grams. +#define LOAD_CELL_WEIGHT_OUT_RANGE_PERSISTENT_PERIOD_MS (5 * MS_PER_SECOND) ///< Load cell weight out of range persistent period in milliseconds. +#define LOAD_CELL_PRIMARY_BACKUP_MAX_DRIFT_PERSISTENT_PERIOD_MS (5 * MS_PER_SECOND) ///< Load cell primary and backup maximum allowed weight drift persistent period in milliseconds. +#define EMPTY_RESERVOIR_WEIGHT_GRAMS 1600 ///< Reservoirs empty weight in grams. +#define MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_FIRST_TARE_GRAMS 300 ///< Max allowed extra weight before first tare in grams. +#define MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS 60 ///< Max allowed extra weight before tare in grams. + /// Load cell data structure. typedef struct { - U32 measuredReadingSum; ///< Raw load cell sums for averaging - OVERRIDE_F32_T filteredWeight; ///< Latest filtered load cell weights - F32 autoCalOffset; ///< Load cell auto-calibration offset + U32 rawReading; ///< Latest raw load cell reading + OVERRIDE_F32_T weight; ///< Latest load cell weight + F32 autoCalOffset; ///< Load cell auto-calibration offset + F32 loadCellVelocity_g_min; ///< Velocity (in g/min) of load cell. - F32 smallFilterReadings[ SIZE_OF_SMALL_LOAD_CELL_AVG ]; ///< Load cell samples for small load cell moving average - F32 smallFilterTotal; ///< Small filter rolling total - used to calc small load cell moving average - F32 smallFilteredWeight; ///< Load cell small filtered (8 sample) weight + F32 smallFilterReadings[ SIZE_OF_SMALL_LOAD_CELL_AVG ]; ///< Load cell samples for small load cell moving average. + F64 smallFilterTotal; ///< Small filter rolling total - used to calc small load cell moving average. + F32 smallFilteredWeight; ///< Load cell small filtered (100 100Hz raw sample) weight. - F32 largeFilterReadings[ SIZE_OF_LARGE_LOAD_CELL_AVG ]; ///< Load cell samples for large load cell moving average - F32 largeFilterTotal; ///< Large filter rolling total - used to calc small load cell moving average - F32 largeFilteredWeight; ///< Load cell large filtered (32 sample) weight + F32 largeFilterReadings[ SIZE_OF_LARGE_LOAD_CELL_AVG ]; ///< Load cell samples for large load cell moving average. + F64 largeFilterTotal; ///< Large filter rolling total - used to calc small load cell moving average. + F32 largeFilteredWeight; ///< Load cell large filtered (40 10Hz filtered sample) weight. } LOADCELL_T; // ********** private data ********** static OVERRIDE_U32_T loadCellDataPublishInterval = { LOAD_CELL_REPORT_PERIOD, - LOAD_CELL_REPORT_PERIOD, 0, 0 }; ///< Broadcast load cell data publish interval. -static LOADCELL_T loadcells[ NUM_OF_LOAD_CELLS ]; ///< Load cell data structures. -static U32 loadCellFilterTimerCount = 0; ///< Load cell filtering timer count. -static U32 loadCellDataPublicationTimerCounter = 0; ///< Load cell data publication timer counter to CAN bus. + LOAD_CELL_REPORT_PERIOD, 0, 0 }; ///< Broadcast load cell data publish interval. +static LOADCELL_T loadcells[ NUM_OF_LOAD_CELLS ]; ///< Load cell data structures. +static U32 loadCellFilterTimerCount = 0; ///< Load cell filtering timer count. +static U32 loadCellDataPublicationTimerCounter = 0; ///< Load cell data publication timer counter to CAN bus. -static U32 smallReadingsIdx; ///< Index for next sample in load cell small rolling average sample array. -static U32 largeReadingsIdx; ///< Index for next sample in load cell large rolling average sample array. +static U32 smallReadingsIdx; ///< Index for next sample in load cell small rolling average sample array. +static U32 largeReadingsIdx; ///< Index for next sample in load cell large rolling average sample array. +static DG_LOAD_CELLS_CAL_RECORD_T loadCellsCalRecord; ///< Load cells calibration record. // ********** private function prototypes ********** -static U32 getLoadCellDataPublishInterval( void ); +static BOOL processCalibrationData( void ); +static void monitorLoadCellsWeightOutOfRange( LOAD_CELL_ID_T loadCell ); /*********************************************************************//** * @brief @@ -77,6 +94,7 @@ *************************************************************************/ void initLoadCell( void ) { + U32 cell; U32 i; U32 j; @@ -85,12 +103,13 @@ for ( i = 0; i < NUM_OF_LOAD_CELLS; i++ ) { - loadcells[ i ].filteredWeight.data = 0.0; - loadcells[ i ].filteredWeight.ovData = 0.0; - loadcells[ i ].filteredWeight.ovInitData = 0.0; - loadcells[ i ].filteredWeight.override = OVERRIDE_RESET; + loadcells[ i ].rawReading = 0; - loadcells[ i ].measuredReadingSum = 0; + loadcells[ i ].weight.data = 0.0; + loadcells[ i ].weight.ovData = 0.0; + loadcells[ i ].weight.ovInitData = 0.0; + loadcells[ i ].weight.override = OVERRIDE_RESET; + loadcells[ i ].autoCalOffset = 0.0; loadcells[ i ].largeFilterTotal = 0.0; @@ -108,7 +127,22 @@ { loadcells[ i ].largeFilterReadings[ j ] = 0.0; } + + loadcells[ i ].loadCellVelocity_g_min = 0.0; } + + for ( cell = CAL_DATA_LOAD_CELL_A1; cell < NUM_OF_CAL_DATA_LOAD_CELLS; cell++ ) + { + // Reset the calibration variables + loadCellsCalRecord.loadCells[ cell ].fourthOrderCoeff = 0.0; + loadCellsCalRecord.loadCells[ cell ].thirdOrderCoeff = 0.0; + loadCellsCalRecord.loadCells[ cell ].secondOrderCoeff = 0.0; + loadCellsCalRecord.loadCells[ cell ].gain = 1.0; + loadCellsCalRecord.loadCells[ cell ].offset = 0.0; + } + + // Initialize persistent alarm(s) + initPersistentAlarm( ALARM_ID_DG_LOAD_CELL_ADC_ERROR, 0, LOAD_CELL_ADC_ERROR_PERSISTENCE ); } /*********************************************************************//** @@ -122,51 +156,131 @@ void execLoadCell( void ) { U32 ii; + U32 a1 = getFPGALoadCellA1(); + U32 a2 = getFPGALoadCellA2(); + U32 b1 = getFPGALoadCellB1(); + U32 b2 = getFPGALoadCellB2(); // update sums for load cell average calculations - loadcells[ LOAD_CELL_RESERVOIR_1_PRIMARY ].measuredReadingSum += getFPGALoadCellA1(); - loadcells[ LOAD_CELL_RESERVOIR_1_BACKUP ].measuredReadingSum += getFPGALoadCellA2(); - loadcells[ LOAD_CELL_RESERVOIR_2_PRIMARY ].measuredReadingSum += getFPGALoadCellB1(); - loadcells[ LOAD_CELL_RESERVOIR_2_BACKUP ].measuredReadingSum += getFPGALoadCellB2(); + loadcells[ LOAD_CELL_RESERVOIR_1_PRIMARY ].rawReading = a1 & MASK_OFF_U32_MSB; + loadcells[ LOAD_CELL_RESERVOIR_1_BACKUP ].rawReading = a2 & MASK_OFF_U32_MSB; + loadcells[ LOAD_CELL_RESERVOIR_2_PRIMARY ].rawReading = b1 & MASK_OFF_U32_MSB; + loadcells[ LOAD_CELL_RESERVOIR_2_BACKUP ].rawReading = b2 & MASK_OFF_U32_MSB; - // filter every 100ms - if ( ++loadCellFilterTimerCount >= LOAD_CELL_SAMPLES_TO_AVERAGE ) + // Check error bits from new readings + a1 = ( a1 >> 31 ) << SHIFT_24_BITS; + a2 = ( a2 >> 31 ) << SHIFT_16_BITS_FOR_WORD_SHIFT; + b1 = ( b1 >> 31 ) << SHIFT_8_BITS_FOR_BYTE_SHIFT; + b2 = ( b2 >> 31 ); + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_DG_LOAD_CELL_ADC_ERROR, ( ( a1 > 0 ) || ( a2 > 0 ) || ( b1 > 0 ) || ( b2 > 0 ) ) ) ) { - for ( ii = 0; ii < NUM_OF_LOAD_CELLS; ++ii ) - { - // calculate load cell average weights - loadcells[ ii ].filteredWeight.data = (F32)loadcells[ ii ].measuredReadingSum * LOAD_CELL_AVERAGE_MULTIPLIER * - ADC2GRAM + LOAD_CELL_ZERO_OFFSET - loadcells[ ii ].autoCalOffset; + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_LOAD_CELL_ADC_ERROR, ( a1 | a2 | b1 | b2 ) ) + } - // reset sums for next averaging - loadcells[ ii ].measuredReadingSum = 0; + // Check if a new calibration is available + if ( TRUE == isNewCalibrationRecordAvailable() ) + { + // Get the new calibration data and check its validity + processCalibrationData(); - // Update filtered with new filtered weight sample and moving averages - loadcells[ ii ].smallFilterTotal -= loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ]; - loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ] = loadcells[ ii ].filteredWeight.data; - loadcells[ ii ].smallFilterTotal += loadcells[ ii ].filteredWeight.data; - loadcells[ ii ].smallFilteredWeight = loadcells[ ii ].smallFilterTotal / (F32)SIZE_OF_SMALL_LOAD_CELL_AVG; + // Zero the current tare values when new calibration data is available + loadcells[ LOAD_CELL_RESERVOIR_1_PRIMARY ].autoCalOffset = 0.0; + loadcells[ LOAD_CELL_RESERVOIR_1_BACKUP ].autoCalOffset = 0.0; + loadcells[ LOAD_CELL_RESERVOIR_2_PRIMARY ].autoCalOffset = 0.0; + loadcells[ LOAD_CELL_RESERVOIR_2_BACKUP ].autoCalOffset = 0.0; + } + // Rolling average of last 100 raw samples in small filter + for ( ii = 0; ii < NUM_OF_LOAD_CELLS; ++ii ) + { + F32 loadCell = (F32)loadcells[ ii ].rawReading * ADC2GRAM; + + // Apply the calibration factors to the data. + // load_cell_weight = fourth_order_coeff * (load_cell^4) + third_order_coeff * (load_cell^3) + second_order_coeff * (load_cell^2) + gain * load_cell + offset + loadcells[ ii ].weight.data = pow(loadCell, 4) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].fourthOrderCoeff + + pow(loadCell, 3) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].thirdOrderCoeff + + pow(loadCell, 2) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].secondOrderCoeff + + loadCell * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].gain + + loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)ii ].offset; + + loadcells[ ii ].loadCellVelocity_g_min = ( getLoadCellWeight( (LOAD_CELL_ID_T)ii ) - + loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ] ) * (F32)SEC_PER_MIN; + + // Update small filter with new weight sample + loadcells[ ii ].smallFilterTotal -= loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ]; + loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ] = getLoadCellWeight( (LOAD_CELL_ID_T)ii ); + loadcells[ ii ].smallFilterTotal += getLoadCellWeight( (LOAD_CELL_ID_T)ii ); + + // Calculate the load cell value before applying calibration to it + loadcells[ ii ].smallFilteredWeight = (F32)( loadcells[ ii ].smallFilterTotal / (F64)SIZE_OF_SMALL_LOAD_CELL_AVG ); + + // Monitor the load cells weight + monitorLoadCellsWeightOutOfRange( (LOAD_CELL_ID_T)ii ); + + // Apply the tare offset. NOTE: tare must be applied after checking the weight out of range. + loadcells[ ii ].smallFilteredWeight -= loadcells[ ii ].autoCalOffset; + } + + smallReadingsIdx = INC_WRAP( smallReadingsIdx, 0, SIZE_OF_SMALL_LOAD_CELL_AVG - 1 ); + + // filter every 100ms + if ( ++loadCellFilterTimerCount >= LOAD_CELL_REPORT_PERIOD ) + { + for ( ii = 0; ii < NUM_OF_LOAD_CELLS; ++ii ) + { + // Update large filter with new small filter weight sample loadcells[ ii ].largeFilterTotal -= loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ]; - loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ] = loadcells[ ii ].filteredWeight.data; - loadcells[ ii ].largeFilterTotal += loadcells[ ii ].filteredWeight.data; - loadcells[ ii ].largeFilteredWeight = loadcells[ ii ].largeFilterTotal / (F32)SIZE_OF_LARGE_LOAD_CELL_AVG; + loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ] = loadcells[ ii ].smallFilteredWeight; + loadcells[ ii ].largeFilterTotal += loadcells[ ii ].smallFilteredWeight; + loadcells[ ii ].largeFilteredWeight = (F32)( loadcells[ ii ].largeFilterTotal / (F64)SIZE_OF_LARGE_LOAD_CELL_AVG ); } loadCellFilterTimerCount = 0; - smallReadingsIdx = INC_WRAP( smallReadingsIdx, 0, SIZE_OF_SMALL_LOAD_CELL_AVG - 1 ); largeReadingsIdx = INC_WRAP( largeReadingsIdx, 0, SIZE_OF_LARGE_LOAD_CELL_AVG - 1 ); } // broadcast load cell data if we are at scheduled interval. - if ( ++loadCellDataPublicationTimerCounter >= getLoadCellDataPublishInterval() ) + if ( ++loadCellDataPublicationTimerCounter >= getU32OverrideValue( &loadCellDataPublishInterval ) ) { + LOAD_CELL_DATA_T loadCellData; + + loadCellData.loadCellA1inGram = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + loadCellData.loadCellA2inGram = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_BACKUP ); + loadCellData.loadCellB1inGram = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + loadCellData.loadCellB2inGram = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_BACKUP ); + + // Broadcast small filtered load cell data + broadcastLoadCellData( &loadCellData ); + loadCellDataPublicationTimerCounter = 0; + } +} - // broadcast load cell data - broadcastLoadCellData( getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ), getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_1_BACKUP ), - getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ), getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_2_BACKUP ) ); +/*********************************************************************//** + * @brief + * The execLoadCellsSelfTest function executes the load cell self test. + * It gets the calibration record from NVDataMgmt and checks whether the + * values have a calibration date. + * @details Inputs: none + * @details Outputs: + * @return result of the load cell self test + *************************************************************************/ +SELF_TEST_STATUS_T execLoadCellsSelfTest ( void ) +{ + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + + BOOL calStatus = processCalibrationData(); + + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; } + else + { + result = SELF_TEST_STATUS_FAILED; + } + + return result; } /*********************************************************************//** @@ -179,8 +293,32 @@ *************************************************************************/ void tareLoadCell( LOAD_CELL_ID_T loadCellID ) { - // Add old auto calibration offset to get back to actual weight value - loadcells[ loadCellID ].autoCalOffset = ( loadcells[ loadCellID ].filteredWeight.data + loadcells[ loadCellID ].autoCalOffset ); + BOOL isWeightOutOfRange = FALSE; + + F32 weight = getLoadCellSmallFilteredWeight( loadCellID ); + + // Check if the load cell is being tared for the first time + if ( fabs(loadcells[ loadCellID ].autoCalOffset ) < NEARLY_ZERO ) + { + // For the first tare, the weight of the reservoir should be considered + // The current weight of the load cell should not be greater than the weight of the reservoir + the extra weight + F32 deltaWeight = fabs( weight - EMPTY_RESERVOIR_WEIGHT_GRAMS ); + isWeightOutOfRange = ( deltaWeight > MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_FIRST_TARE_GRAMS ? TRUE : FALSE ); + } + else + { + isWeightOutOfRange = ( fabs(weight) > MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS ? TRUE : FALSE ); + } + + if ( FALSE == isWeightOutOfRange ) + { + // Add old auto calibration offset to get back to actual weight value + loadcells[ loadCellID ].autoCalOffset = ( loadcells[ loadCellID ].smallFilteredWeight + loadcells[ loadCellID ].autoCalOffset ); + } + else + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DG_LOAD_CELLS_TARE_WEIGHT_OUT_OF_RANGE, weight ) + } } /*********************************************************************//** @@ -198,56 +336,58 @@ /*********************************************************************//** * @brief - * The getLoadCellFilteredWeight function gets the measured filtered load cell - * weight for a given load cell ID. - * @details Inputs: load cell filtered weight + * The getLoadCellWeight function gets the measured load cell weight for + * a given load cell ID. + * @details Inputs: load cell weight * @details Outputs: none - * @param loadCellID ID of load cell to get filtered weight for - * @return the filtered load cell weight for the given load cell ID. + * @param loadCellID ID of load cell to get weight for + * @return the load cell weight for the given load cell ID. *************************************************************************/ -F32 getLoadCellFilteredWeight( LOAD_CELL_ID_T loadCellID ) +F32 getLoadCellWeight( LOAD_CELL_ID_T loadCellID ) { - F32 result = 0; + F32 result = loadcells[ loadCellID ].weight.data; if ( loadCellID < NUM_OF_LOAD_CELLS ) { - if ( OVERRIDE_KEY == loadcells[ loadCellID ].filteredWeight.override ) + if ( OVERRIDE_KEY == loadcells[ loadCellID ].weight.override ) { - result = loadcells[ loadCellID ].filteredWeight.ovData; + result = loadcells[ loadCellID ].weight.ovData; } - else - { - result = loadcells[ loadCellID ].filteredWeight.data; - } } else { - activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_LOAD_CELL_ID, (U32)loadCellID ) } return result; } /*********************************************************************//** * @brief - * The getLoadCellSmallFilteredWeight function gets the small filtered load cell - * weight for a given load cell ID. + * The getLoadCellSmallFilteredWeight function gets the small filtered + * load cell weight for a given load cell ID. * @details Inputs: load cell filtered weight * @details Outputs: none - * @param loadCellID ID of load cell to get small filtered weight for + * @param loadCellID ID of load cell to get large filtered weight * @return the small filtered load cell weight for the given load cell ID. *************************************************************************/ -F32 getLoadCellSmallFilteredWeight( LOAD_CELL_ID_T loadCellID ) +F32 getLoadCellSmallFilteredWeight( LOAD_CELL_ID_T loadCellID ) { - F32 result = 0; + F32 result = 0.0; if ( loadCellID < NUM_OF_LOAD_CELLS ) { result = loadcells[ loadCellID ].smallFilteredWeight; + + // If the load cell is in override mode, return the override weight + if ( OVERRIDE_KEY == loadcells[ loadCellID ].weight.override ) + { + result = loadcells[ loadCellID ].weight.ovData; + } } else { - activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_LOAD_CELL_ID, (U32)loadCellID ) } return result; @@ -259,45 +399,109 @@ * weight for a given load cell ID. * @details Inputs: load cell filtered weight * @details Outputs: none - * @param loadCellID ID of load cell to get large filtered weight for + * @param loadCellID ID of load cell to get large filtered weight * @return the large filtered load cell weight for the given load cell ID. *************************************************************************/ -F32 getLoadCellLargeFilteredWeight( LOAD_CELL_ID_T loadCellID ) +F32 getLoadCellLargeFilteredWeight( LOAD_CELL_ID_T loadCellID ) { - F32 result = 0; + F32 result = 0.0; if ( loadCellID < NUM_OF_LOAD_CELLS ) { result = loadcells[ loadCellID ].largeFilteredWeight; } else { - activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_LOAD_CELL_ID, (U32)loadCellID ) } return result; } /*********************************************************************//** * @brief - * The getLoadCellDataPublishInterval function gets the load cell data publish interval. - * @details Inputs: loadCellDataPublishInterval + * The getLoadCellVelocity function gets the current velocity (in g/min) + * for the given load cell. + * @details Inputs: loadcells[] * @details Outputs: none - * @return the current load cell data publication interval (in ms/task interval). + * @param loadCellID ID of load cell to get velocity + * @return the velocity (in g/min) for the given load cell ID. *************************************************************************/ -static U32 getLoadCellDataPublishInterval( void ) +F32 getLoadCellVelocity( LOAD_CELL_ID_T loadCellID ) { - U32 result = loadCellDataPublishInterval.data; + F32 result = 0.0; - if ( OVERRIDE_KEY == loadCellDataPublishInterval.override ) + if ( loadCellID < NUM_OF_LOAD_CELLS ) { - result = loadCellDataPublishInterval.ovData; + result = loadcells[ loadCellID ].loadCellVelocity_g_min; } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_LOAD_CELL_ID, (U32)loadCellID ) + } return result; } +/*********************************************************************//** + * @brief + * The processCalibrationData function gets the calibration data and makes + * sure it is valid by checking the calibration date. The calibration date + * should not be 0. + * @details Inputs: none + * @details Outputs: loadCellsCalRecord + * @return TRUE if the calibration record is valid, otherwise FALSE + *************************************************************************/ +static BOOL processCalibrationData( void ) +{ + BOOL status = TRUE; + U32 cell; + // Get the calibration record from NVDataMgmt + DG_LOAD_CELLS_CAL_RECORD_T calData = getDGLoadCellsCalibrationRecord(); + + for ( cell = 0; cell < NUM_OF_CAL_DATA_LOAD_CELLS; cell++ ) + { +#ifndef SKIP_CAL_CHECK + // Check if the calibration data that was received from NVDataMgmt is legitimate + // The calibration date item should not be zero. If the calibration date is 0, + // then the load cells data is not stored in the NV memory or it was corrupted. + if ( 0 == calData.loadCells[ cell ].calibrationTime ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_LOAD_CELLS_INVALID_CALIBRATION, (U32)cell ); + status = FALSE; + } +#endif + + // The calibration data was valid, update the local copy + loadCellsCalRecord.loadCells[ cell ].fourthOrderCoeff = calData.loadCells[ cell ].fourthOrderCoeff; + loadCellsCalRecord.loadCells[ cell ].thirdOrderCoeff = calData.loadCells[ cell ].thirdOrderCoeff; + loadCellsCalRecord.loadCells[ cell ].secondOrderCoeff = calData.loadCells[ cell ].secondOrderCoeff; + loadCellsCalRecord.loadCells[ cell ].gain = calData.loadCells[ cell ].gain; + loadCellsCalRecord.loadCells[ cell ].offset = calData.loadCells[ cell ].offset; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The monitorLoadCellsWeightOutOfRange function monitors the weight of the + * load cells and if they are not in range, it raises an alarm. + * @details Inputs: none + * @details Outputs: none + * @param loadCell which is the load cell ID that its range is checked + * @return none + *************************************************************************/ +static void monitorLoadCellsWeightOutOfRange( LOAD_CELL_ID_T loadCell ) +{ + F32 weight = getLoadCellSmallFilteredWeight( loadCell ); + BOOL isWeightOutOfRange = ( weight < LOAD_CELL_MIN_ALLOWED_WEIGHT_GRAMS ) || ( weight > LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ); + + checkPersistentAlarm( ALARM_ID_DG_LOAD_CELL_WEIGHT_OUT_OF_RANGE, isWeightOutOfRange, weight, LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ); +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ @@ -321,8 +525,9 @@ if ( TRUE == isTestingActivated() ) { result = TRUE; - loadcells[ loadCellID ].filteredWeight.ovData = value; - loadcells[ loadCellID ].filteredWeight.override = OVERRIDE_KEY; + // Add tare to given value so reported load will be what is requested when tare is subtracted later + loadcells[ loadCellID ].weight.ovData = value + loadcells[ loadCellID ].autoCalOffset; + loadcells[ loadCellID ].weight.override = OVERRIDE_KEY; } } @@ -346,8 +551,8 @@ if ( TRUE == isTestingActivated() ) { result = TRUE; - loadcells[ loadCellID ].filteredWeight.override = OVERRIDE_RESET; - loadcells[ loadCellID ].filteredWeight.ovData = loadcells[ loadCellID ].filteredWeight.ovInitData; + loadcells[ loadCellID ].weight.override = OVERRIDE_RESET; + loadcells[ loadCellID ].weight.ovData = loadcells[ loadCellID ].weight.ovInitData; } }