Index: firmware/App/Controllers/LoadCell.c =================================================================== diff -u -r6499ea25921fcf67826fa0c35bb03caf411ba542 -r6bf297b685487355fd581f221c59cf9d5a4fa53e --- firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 6499ea25921fcf67826fa0c35bb03caf411ba542) +++ firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision 6bf297b685487355fd581f221c59cf9d5a4fa53e) @@ -7,8 +7,8 @@ * * @file LoadCell.c * -* @author (last) Bill Bracken -* @date (last) 07-Dec-2022 +* @author (last) Sean Nash +* @date (last) 05-Jan-2023 * * @author (original) Saeed Nejatali * @date (original) 25-Feb-2020 @@ -21,6 +21,7 @@ #include "LoadCell.h" #include "MessageSupport.h" #include "NVDataMgmt.h" +#include "OperationModes.h" #include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "TaskPriority.h" @@ -51,7 +52,8 @@ #define EMPTY_RESERVOIR_WEIGHT_GRAMS 1600.0F ///< Reservoirs empty weight in grams. #define MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_FIRST_TARE_GRAMS 300.0F ///< Max allowed extra weight before first tare in grams. #define MAX_ALLOWED_EXTRA_WEIGHT_BEFORE_TARE_GRAMS 60.0F ///< Max allowed extra weight before tare in grams. -#define LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS 60.0F ///< Load cell primary and backup maximum allowed weight drift in grams. +#define LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS 80.0F ///< Load cell primary and backup maximum allowed weight drift in grams. +#define LOAD_CELL_PRIMARY_BACKUP_MAX_HEAT_DRIFT_GRAMS 200.0F ///< Load cell primary and backup maximum allowed weight drift in grams for heat disinfect mode. #define DATA_PUBLISH_COUNTER_START_COUNT 0 ///< Data publish counter start count. #define LOAD_CELL_FPGA_ERROR_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< Load cell FPGA error timeout in milliseconds. #define LOAD_CELL_FPGA_SIGN_BIT 0x800000 ///< Load cell FPGA sign bit. @@ -162,17 +164,16 @@ *************************************************************************/ void execLoadCell( void ) { - U32 ii; + LOAD_CELL_ID_T sensorId; + LOAD_CELL_ID_T sensorInAlarm = LOAD_CELL_FIRST; U32 a1 = getFPGALoadCellA1(); U32 a2 = getFPGALoadCellA2(); U32 b1 = getFPGALoadCellB1(); U32 b2 = getFPGALoadCellB2(); + F32 weight = 0.0F; + BOOL isLoadCellOutOfRange = FALSE; + F32 alarmLoadCell = 0.0F; - BOOL isLoadCellOutOfRange = FALSE; - BOOL isCurrentLoadCellOut = FALSE; - U08 loadCellOutID = 0; - F32 weight = 0.0F; - // Check error bits from new readings U32 a1Err = ( a1 >> SHIFT_BITS_BY_31 ); U32 a2Err = ( a2 >> SHIFT_BITS_BY_31 ); @@ -211,55 +212,58 @@ } // Rolling average of last 100 raw samples in small filter - for ( ii = 0; ii < NUM_OF_LOAD_CELLS; ++ii ) + for ( sensorId = LOAD_CELL_FIRST; sensorId < NUM_OF_LOAD_CELLS; ++sensorId ) { - F32 loadCell = (F32)loadcells[ ii ].rawReading * ADC2GRAM; + F32 loadCell = (F32)loadcells[ sensorId ].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[ sensorId ].weight.data = pow( loadCell, 4 ) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)sensorId ].fourthOrderCoeff + + pow( loadCell, 3 ) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)sensorId ].thirdOrderCoeff + + pow( loadCell, 2 ) * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)sensorId ].secondOrderCoeff + + loadCell * loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)sensorId ].gain + + loadCellsCalRecord.loadCells[ (CAL_DATA_DG_LOAD_CELLS_T)sensorId ].offset; // Monitor the load cells weight // Since there is a single alarm value for all 4 load cells the persistence check must be // executed if ANY of the load cell values are out of range. Only when all of the load // cells are in range should the persistence for the alarm be cleared. - weight = getLoadCellWeight( ( LOAD_CELL_ID_T) ii ); - isCurrentLoadCellOut = ( ( weight < LOAD_CELL_MIN_ALLOWED_WEIGHT_GRAMS ) || ( weight > LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ) ? TRUE : FALSE ); - isLoadCellOutOfRange |= isCurrentLoadCellOut; - loadCellOutID = ( TRUE == isCurrentLoadCellOut ? ii : loadCellOutID ); + weight = getLoadCellWeight( ( LOAD_CELL_ID_T) sensorId ); + if ( ( weight < LOAD_CELL_MIN_ALLOWED_WEIGHT_GRAMS ) || ( weight > LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ) ) + { + isLoadCellOutOfRange = TRUE; + sensorInAlarm = sensorId; + alarmLoadCell = weight; + } - loadcells[ ii ].loadCellVelocity_g_min = ( getLoadCellWeight( (LOAD_CELL_ID_T)ii ) - - loadcells[ ii ].smallFilterReadings[ smallReadingsIdx ] ) * (F32)SEC_PER_MIN; + loadcells[ sensorId ].loadCellVelocity_g_min = ( getLoadCellWeight( (LOAD_CELL_ID_T)sensorId ) - + loadcells[ sensorId ].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 ); + loadcells[ sensorId ].smallFilterTotal -= loadcells[ sensorId ].smallFilterReadings[ smallReadingsIdx ]; + loadcells[ sensorId ].smallFilterReadings[ smallReadingsIdx ] = getLoadCellWeight( (LOAD_CELL_ID_T)sensorId ); + loadcells[ sensorId ].smallFilterTotal += getLoadCellWeight( (LOAD_CELL_ID_T)sensorId ); // Calculate the load cell value before applying calibration to it - loadcells[ ii ].smallFilteredWeight.data = (F32)( loadcells[ ii ].smallFilterTotal / (F64)SIZE_OF_SMALL_LOAD_CELL_AVG ); + loadcells[ sensorId ].smallFilteredWeight.data = (F32)( loadcells[ sensorId ].smallFilterTotal / (F64)SIZE_OF_SMALL_LOAD_CELL_AVG ); // Apply the tare offset. NOTE: tare must be applied after checking the weight out of range. - loadcells[ ii ].smallFilteredWeight.data -= loadcells[ ii ].autoCalOffset; + loadcells[ sensorId ].smallFilteredWeight.data -= loadcells[ sensorId ].autoCalOffset; } - checkPersistentAlarm( ALARM_ID_DG_LOAD_CELL_WEIGHT_OUT_OF_RANGE, isLoadCellOutOfRange, loadCellOutID, weight ); + checkPersistentAlarm( ALARM_ID_DG_LOAD_CELL_WEIGHT_OUT_OF_RANGE, isLoadCellOutOfRange, sensorInAlarm, alarmLoadCell ); 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 ) + for ( sensorId = LOAD_CELL_FIRST; sensorId < NUM_OF_LOAD_CELLS; ++sensorId ) { // Update large filter with new small filter weight sample - loadcells[ ii ].largeFilterTotal -= loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ]; - loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ] = getLoadCellSmallFilteredWeight((LOAD_CELL_ID_T) ii); - loadcells[ ii ].largeFilterTotal += getLoadCellSmallFilteredWeight((LOAD_CELL_ID_T) ii); - loadcells[ ii ].largeFilteredWeight = (F32)( loadcells[ ii ].largeFilterTotal / (F64)SIZE_OF_LARGE_LOAD_CELL_AVG ); + loadcells[ sensorId ].largeFilterTotal -= loadcells[ sensorId ].largeFilterReadings[ largeReadingsIdx ]; + loadcells[ sensorId ].largeFilterReadings[ largeReadingsIdx ] = getLoadCellSmallFilteredWeight((LOAD_CELL_ID_T) sensorId); + loadcells[ sensorId ].largeFilterTotal += getLoadCellSmallFilteredWeight((LOAD_CELL_ID_T) sensorId); + loadcells[ sensorId ].largeFilteredWeight = (F32)( loadcells[ sensorId ].largeFilterTotal / (F64)SIZE_OF_LARGE_LOAD_CELL_AVG ); } loadCellFilterTimerCount = 0; @@ -512,8 +516,15 @@ F32 drift = 0.0; F32 loadCellADrift = 0.0; F32 loadCellBDrift = 0.0; + F32 maxDrift = LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS; BOOL isDriftOutOfRange = FALSE; + // Increase max drift limit if in heat disinfect mode + if ( DG_MODE_HEAT == getCurrentOperationMode() ) + { + maxDrift = LOAD_CELL_PRIMARY_BACKUP_MAX_HEAT_DRIFT_GRAMS; + } + // Test is valid after load cells are tared if ( isLoadCellTared( LOAD_CELL_RESERVOIR_1_PRIMARY ) && isLoadCellTared( LOAD_CELL_RESERVOIR_1_BACKUP ) ) { @@ -526,8 +537,7 @@ getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_BACKUP ) ); } - if ( ( loadCellADrift > LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS ) || - ( loadCellBDrift > LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS ) ) + if ( ( loadCellADrift > maxDrift ) || ( loadCellBDrift > maxDrift ) ) { isDriftOutOfRange = TRUE; } @@ -538,8 +548,7 @@ // Pick the biggest drift to log w/ alarm if triggered 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 ); + checkPersistentAlarm( ALARM_ID_DG_LOAD_CELL_PRIMARY_BACKUP_DRIFT_OUT_OF_RANGE, isDriftOutOfRange, drift, maxDrift ); }