Index: firmware/App/Controllers/LoadCell.c =================================================================== diff -u -r22f1a58ac8e419353ec004b04e7c765c1d59df2b -r922516483829939a2a387d4a2fddeccdb4c454d4 --- firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 22f1a58ac8e419353ec004b04e7c765c1d59df2b) +++ firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 922516483829939a2a387d4a2fddeccdb4c454d4) @@ -34,54 +34,58 @@ // 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 LOAD_CELL_REPORT_PERIOD (100 / TASK_PRIORITY_INTERVAL) ///< Broadcast load cell values message every 100 ms. +#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 LOAD_CELL_FILTER_ALPHA 0.05 ///< Alpha factor for the alpha filter used on load cell readings. +static const F32 ADC2GRAM = (0.0894 * 1.1338); +#define LOAD_CELL_FILTER_ALPHA 0.05 ///< Alpha factor for the alpha filter used on load cell readings. -#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 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 1600 ///< Load cell minimum allowed weight in grams. -#define LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS 4500 ///< Load cell maximum allowed weight in grams. -#define MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS 300 ///< Max allowed extra 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_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 MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS 300.0 ///< Max allowed extra 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_ALLOWED_DRIFT_GRAMS 60.0 ///< Load cell primary and backup maximum allowed weight drift in grams. +#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. /// Load cell data structure. typedef struct { - 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. + 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. - 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 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. - 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. + 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 DG_LOAD_CELLS_CAL_RECORD_T loadCellsCalRecord; ///< Load cells calibration record. +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( void ); +static void monitorLoadCellsWeightOutOfRange( LOAD_CELL_ID_T loadCell ); +static void monitorLoadCellsPrimaryBackupDriftOutOfRange( void ); /*********************************************************************//** * @brief @@ -92,7 +96,6 @@ *************************************************************************/ void initLoadCell( void ) { - U32 cell; U32 i; U32 j; @@ -134,6 +137,9 @@ initPersistentAlarm( ALARM_ID_DG_LOAD_CELL_WEIGHT_OUT_OF_RANGE, LOAD_CELL_WEIGHT_OUT_RANGE_PERSISTENT_PERIOD_MS, LOAD_CELL_WEIGHT_OUT_RANGE_PERSISTENT_PERIOD_MS ); + + initPersistentAlarm( ALARM_ID_DG_LOAD_CELL_PRIMARY_BACKUP_DRIFT_OUT_OF_RANGE, LOAD_CELL_PRIMARY_BACKUP_MAX_DRIFT_PERSISTENT_PERIOD_MS, + LOAD_CELL_PRIMARY_BACKUP_MAX_DRIFT_PERSISTENT_PERIOD_MS ); } /*********************************************************************//** @@ -186,8 +192,6 @@ { loadcells[ ii ].weight.data = (F32)loadcells[ ii ].rawReading * ADC2GRAM; - loadcells[ ii ].weight.data = loadcells[ ii ].weight.data - loadcells[ ii ].autoCalOffset; - loadcells[ ii ].loadCellVelocity_g_min = ( getLoadCellWeight( (LOAD_CELL_ID_T)ii ) - loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ] ) * (F32)SEC_PER_MIN; @@ -205,6 +209,12 @@ 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; + + // 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 ].weight.data = loadcells[ ii ].weight.data - loadcells[ ii ].autoCalOffset; } smallReadingsIdx = INC_WRAP( smallReadingsIdx, 0, SIZE_OF_SMALL_LOAD_CELL_AVG - 1 ); @@ -241,8 +251,8 @@ loadCellDataPublicationTimerCounter = 0; } - // Monitor the weight of the load cells - monitorLoadCellsWeightOutOfRange(); + // Monitor the load cells drift + monitorLoadCellsPrimaryBackupDriftOutOfRange(); } /*********************************************************************//** @@ -291,7 +301,7 @@ { // 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 - LOAD_CELL_MIN_ALLOWED_WEIGHT_GRAMS ); + F32 deltaWeight = fabs( weight - LOAD_CELL_MIN_ALLOWED_WEIGHT_BEFORE_TARE_GRAMS ); isWeightOutOfRange = ( deltaWeight > MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS ? TRUE : FALSE ); } else @@ -496,27 +506,52 @@ * @brief * The monitorLoadCellsWeightOutOfRange function monitors the weight of the * load cells and if they are not in range, it raises an alarm. - * @details Inputs: loadcells + * @details Inputs: none * @details Outputs: none + * @param loadCell which is the load cell ID that its range is checked * @return none *************************************************************************/ -static void monitorLoadCellsWeightOutOfRange( void ) +static void monitorLoadCellsWeightOutOfRange( LOAD_CELL_ID_T loadCell ) { - LOAD_CELL_ID_T loadCell; - BOOL isWeightOutOfRange; - F32 weight; + F32 weight = getLoadCellSmallFilteredWeight( loadCell ); + BOOL isWeightOutOfRange = ( weight < LOAD_CELL_MIN_ALLOWED_WEIGHT_GRAMS ) || ( weight > LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ); - for ( loadCell = LOAD_CELL_RESERVOIR_1_PRIMARY; loadCell < NUM_OF_LOAD_CELLS; loadCell++ ) - { - // The load cells must have been tared before checking for the weight range - if ( loadcells[ loadCell ].autoCalOffset != 0 ) - { - weight = getLoadCellSmallFilteredWeight( loadCell ); - 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 ); +} - checkPersistentAlarm( ALARM_ID_DG_LOAD_CELL_WEIGHT_OUT_OF_RANGE, isWeightOutOfRange, weight, LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ); - } +/*********************************************************************//** + * @brief + * The monitorLoadCellsPrimaryBackupDriftOutOfRange function monitors the + * load cells' primary and backup drift. + * @details Inputs: none + * @details Outputs: none + * @return none + *************************************************************************/ +static void monitorLoadCellsPrimaryBackupDriftOutOfRange( void ) +{ + F32 drift; + + F32 loadCellADrift = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ) - + getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_BACKUP ) ); + + F32 loadCellBDrift = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ) - + getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_BACKUP ) ); + + BOOL isDriftOutOfRange = ( loadCellADrift > LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS ) || + ( loadCellBDrift > LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS ); + + if ( TRUE == isDriftOutOfRange ) + { + drift = ( loadCellADrift > LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS ? loadCellADrift : loadCellBDrift ); } + else + { + // Pick the biggest drift in between the two load cells when none of the is above range + drift = ( loadCellADrift > loadCellBDrift ? loadCellADrift : loadCellBDrift ); + } + + checkPersistentAlarm( ALARM_ID_DG_LOAD_CELL_PRIMARY_BACKUP_DRIFT_OUT_OF_RANGE, isDriftOutOfRange, drift, + LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS ); }