Index: firmware/App/Controllers/LoadCell.c =================================================================== diff -u -rf1cb4be5fb8201cec188589e683c8bf20d895ec0 -rd5f2ac4d84453feb1b782f35f9d69432977a95f6 --- firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision f1cb4be5fb8201cec188589e683c8bf20d895ec0) +++ firmware/App/Controllers/LoadCell.c (.../LoadCell.c) (revision d5f2ac4d84453feb1b782f35f9d69432977a95f6) @@ -7,8 +7,8 @@ * * @file LoadCell.c * -* @author (last) Dara Navaei -* @date (last) 13-Jul-2022 +* @author (last) Michael Garthwaite +* @date (last) 07-Sep-2022 * * @author (original) Saeed Nejatali * @date (original) 25-Feb-2020 @@ -48,23 +48,23 @@ #define LOAD_CELL_MIN_ALLOWED_WEIGHT_BEFORE_TARE_GRAMS 1600.0F ///< 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. +#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 DATA_PUBLISH_COUNTER_START_COUNT 0 ///< Data publish counter start count. /// Load cell data structure. typedef struct { U32 rawReading; ///< Latest raw load cell reading. - OVERRIDE_F32_T weight; ///< Latest load cell weight. + F32 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. + OVERRIDE_F32_T 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. @@ -76,12 +76,13 @@ 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 loadCellFilterTimerCount; ///< Load cell filtering timer count. static U32 loadCellDataPublicationTimerCounter; ///< 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 BOOL hasLoadCellBeenTared[ NUM_OF_LOAD_CELLS ]; ///< Flags indicating whether loadcells have been tared yet. // ********** private function prototypes ********** @@ -105,21 +106,24 @@ loadCellDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; loadCellFilterTimerCount = 0; + // Initialize load cell filter data and set calibration data as benign until loaded from non-volatile memory for ( i = 0; i < NUM_OF_LOAD_CELLS; i++ ) { benignPolynomialCalRecord( &loadCellsCalRecord.loadCells[ i ] ); - loadcells[ i ].rawReading = 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; - loadcells[ i ].largeFilteredWeight = 0.0; - loadcells[ i ].smallFilterTotal = 0.0; - loadcells[ i ].smallFilteredWeight = 0.0; + hasLoadCellBeenTared[ i ] = FALSE; + loadcells[ i ].rawReading = 0; + loadcells[ i ].weight = 0.0; + loadcells[ i ].autoCalOffset = 0.0; + loadcells[ i ].largeFilterTotal = 0.0; + loadcells[ i ].largeFilteredWeight = 0.0; + loadcells[ i ].smallFilterTotal = 0.0; + loadcells[ i ].smallFilteredWeight.data = 0.0; + loadcells[ i ].smallFilteredWeight.ovData = 0.0; + loadcells[ i ].smallFilteredWeight.ovInitData = 0.0; + loadcells[ i ].smallFilteredWeight.override = OVERRIDE_RESET; + for ( j = 0; j < SIZE_OF_SMALL_LOAD_CELL_AVG; j++ ) { loadcells[ i ].smallFilterReadings[ j ] = 0.0; @@ -198,11 +202,11 @@ // 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 ].weight = 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; @@ -213,13 +217,13 @@ 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 ); + loadcells[ ii ].smallFilteredWeight.data = (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; + loadcells[ ii ].smallFilteredWeight.data -= loadcells[ ii ].autoCalOffset; } smallReadingsIdx = INC_WRAP( smallReadingsIdx, 0, SIZE_OF_SMALL_LOAD_CELL_AVG - 1 ); @@ -231,8 +235,8 @@ { // Update large filter with new small filter weight sample loadcells[ ii ].largeFilterTotal -= loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ]; - loadcells[ ii ].largeFilterReadings[ largeReadingsIdx ] = loadcells[ ii ].smallFilteredWeight; - loadcells[ ii ].largeFilterTotal += loadcells[ ii ].smallFilteredWeight; + 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 ); } @@ -325,7 +329,7 @@ F32 weight = getLoadCellSmallFilteredWeight( loadCellID ); // Check if the load cell is being tared for the first time - if ( fabs( loadcells[ loadCellID ].autoCalOffset ) < NEARLY_ZERO ) + if ( hasLoadCellBeenTared[ loadCellID ] != TRUE ) { // 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 @@ -340,7 +344,8 @@ if ( FALSE == isWeightOutOfRange ) { // Add old auto calibration offset to get back to actual weight value - loadcells[ loadCellID ].autoCalOffset = ( loadcells[ loadCellID ].smallFilteredWeight + loadcells[ loadCellID ].autoCalOffset ); + loadcells[ loadCellID ].autoCalOffset = ( loadcells[ loadCellID ].smallFilteredWeight.data + loadcells[ loadCellID ].autoCalOffset ); + hasLoadCellBeenTared[ loadCellID ] = TRUE; } else { @@ -373,14 +378,11 @@ *************************************************************************/ F32 getLoadCellWeight( LOAD_CELL_ID_T loadCellID ) { - F32 result = loadcells[ loadCellID ].weight.data; + F32 result = 0.0; if ( loadCellID < NUM_OF_LOAD_CELLS ) { - if ( OVERRIDE_KEY == loadcells[ loadCellID ].weight.override ) - { - result = loadcells[ loadCellID ].weight.ovData; - } + result = loadcells[ loadCellID ].weight; } else { @@ -405,7 +407,12 @@ if ( loadCellID < NUM_OF_LOAD_CELLS ) { - result = loadcells[ loadCellID ].smallFilteredWeight; + result = loadcells[ loadCellID ].smallFilteredWeight.data; + + if ( OVERRIDE_KEY == loadcells[ loadCellID ].smallFilteredWeight.override ) + { + result = loadcells[ loadCellID ].smallFilteredWeight.ovData; + } } else { @@ -469,6 +476,8 @@ * @brief * The monitorLoadCellsWeightOutOfRange function monitors the weight of the * load cells and if they are not in range, it raises an alarm. + * Function must be called prior to applying tare so that un-tared weight + * is checked for out of range. * @details Inputs: none * @details Outputs: none * @param loadCell which is the load cell ID that its range is checked @@ -477,7 +486,7 @@ 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 ); + BOOL isWeightOutOfRange = ( ( weight < LOAD_CELL_MIN_ALLOWED_WEIGHT_GRAMS ) || ( weight > LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ) ? TRUE : FALSE ); checkPersistentAlarm( ALARM_ID_DG_LOAD_CELL_WEIGHT_OUT_OF_RANGE, isWeightOutOfRange, weight, LOAD_CELL_MAX_ALLOWED_WEIGHT_GRAMS ); } @@ -551,8 +560,8 @@ { result = TRUE; // 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; + loadcells[ loadCellID ].smallFilteredWeight.ovData = value; + loadcells[ loadCellID ].smallFilteredWeight.override = OVERRIDE_KEY; } } @@ -576,8 +585,8 @@ if ( TRUE == isTestingActivated() ) { result = TRUE; - loadcells[ loadCellID ].weight.override = OVERRIDE_RESET; - loadcells[ loadCellID ].weight.ovData = loadcells[ loadCellID ].weight.ovInitData; + loadcells[ loadCellID ].smallFilteredWeight.override = OVERRIDE_RESET; + loadcells[ loadCellID ].smallFilteredWeight.ovData = loadcells[ loadCellID ].smallFilteredWeight.ovInitData; } }