Index: Accel.c =================================================================== diff -u --- Accel.c (revision 0) +++ Accel.c (revision 6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5) @@ -0,0 +1,751 @@ +/**********************************************************************//** + * + * Copyright (c) 2019-2020 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 Accel.c + * + * @date 25-Sep-2019 + * @author S. Nash + * + * @brief Accelerometer monitor module. Monitors accelerometer sensor. + * + **************************************************************************/ + +#include + +#include "Accel.h" +#include "FPGA.h" +#include "NVDataMgmt.h" +#include "SystemCommMessages.h" +#include "TaskPriority.h" + +/** + * @addtogroup Accel + * @{ + */ + +// ********** private definitions ********** + +#define ACCEL_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< interval (ms/task time) at which the accelerometer data is published on the CAN bus +#define SIZE_OF_ROLLING_AVG ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< vector for tilt is filtered w/ moving average +#define G_PER_LSB ( 0.004 ) ///< conversion from counts (LSB) to gravities +#define NO_NEW_ACCEL_SAMPLES_TIMEOUT ( 100 / TASK_PRIORITY_INTERVAL ) ///< maximum time w/o new accelerometer sample from FPGA. +#define NOMINAL_ACCEL_VECTOR_LENGTH ( 1.0 ) ///< expect unit vector length when system is stable +#define MAX_ACCEL_VECTOR_LENGTH_ERROR ( 0.1 ) ///< POST test looks at vector length at presumably stable moment - should be 1 +/- 0.1 +#define MAX_TILT_ANGLE ( 7.0 ) ///< maximum tilt of system before alarm. +#define MAX_TILT_PERSISTENCE ( 2 * MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< maximum time (in task intervals) that a tilt in excess of limit can persist before alarm +#define MAX_SHOCK_ACCELERATION ( 10.0 ) ///< maximum shock (acceleration) measured on any axis before alarm. + +/// Enumeration of accelerometer monitor states. +typedef enum Accelerometer_States +{ + ACCELEROMETER_START_STATE = 0, ///< Accelerometer start state. + ACCELEROMETER_MONITOR_STATE, ///< Accelerometer monitor state. + NUM_OF_ACCELEROMETER_STATES ///< Number of accelerometer states. +} ACCEL_STATE_T; + +/// Enumeration of accelerometer self test states. +typedef enum Accelerometer_Self_Test_States +{ + ACCELEROMETER_SELF_TEST_STATE_START = 0, ///< Accelerometer self test start state. + ACCELEROMETER_SELF_TEST_STATE_IN_PROGRESS, ///< Accelerometer self test in progress state. + ACCELEROMETER_SELF_TEST_STATE_COMPLETE, ///< Accelerometer self test completed state. + NUM_OF_ACCELEROMETER_SELF_TEST_STATES ///< Number of accelerometer self test states. +} ACCELEROMETER_SELF_TEST_STATE_T; + +// ********** private data ********** + +static ACCEL_STATE_T accelState = ACCELEROMETER_START_STATE; ///< current state of accelerometer monitor state machine +static U32 accelDataPublicationTimerCounter = 0; ///< used to schedule accelerometer data publication to CAN bus +static S32 accelCalOffsets[ NUM_OF_ACCEL_AXES ] = { 0, 0, 0 }; ///< accelerometer calibration offsets. + +static OVERRIDE_U32_T accelDataPublishInterval = { ACCEL_DATA_PUB_INTERVAL, ACCEL_DATA_PUB_INTERVAL, 0, 0 }; ///< interval (in ms/task interval) at which to publish accelerometer data to CAN bus. +static OVERRIDE_F32_T accelAxes[ NUM_OF_ACCEL_AXES ]; ///< Measured accelerometer axis readings (calibrated, converted to gravities). +static OVERRIDE_F32_T accelMaxs[ NUM_OF_ACCEL_AXES ]; ///< Maximum axis readings since last sample (calibrated, converted to gravities). +static U16 accelFPGAFaultReg = 0; ///< FPGA accelerometer fault register value indicates whether issues with reading accelerometer. +static U16 accelFPGASampleCtr = 0; ///< Sample counter from FPGA indicates when new sample(s) are available. +static U32 accelNoNewSampleTimerCounter = 0; ///< used to enforce timeout on no new samples. + +static F32 accelReadings[ NUM_OF_ACCEL_AXES ][ SIZE_OF_ROLLING_AVG ]; ///< holds flow samples for a rolling average +static F32 accelReadingsTotal[ NUM_OF_ACCEL_AXES ]; ///< rolling total - used to calc average +static U32 accelReadingsIdx = 0; ///< index for next sample in rolling average array +static U32 accelReadingsCount = 0; ///< number of samples in flow rolling average buffer +static F32 accelAvgVector[ NUM_OF_ACCEL_AXES ]; ///< Filtered accelerometer vector for tilt. +static F32 accelTilt[ NUM_OF_ACCEL_AXES ]; ///< Axis angles for tilt determination (filtered and converted to degrees). +static U32 accelTiltErrorTimerCounter = 0; ///< used for persistence requirement on tilt error + +static ACCELEROMETER_SELF_TEST_STATE_T accelSelfTestState = ACCELEROMETER_SELF_TEST_STATE_START; ///< current accelerometer self test state + +// ********** private function prototypes ********** + +static ACCEL_STATE_T handleAccelMonitorState( void ); +static void publishAccelData( void ); +static void resetAccelMovingAverage( void ); +static void filterAccelReadings( void ); +static F32 calcVectorLength( F32 x, F32 y, F32 z ); +static void checkForTiltError( void ); +static void checkForShockError( void ); + +/*********************************************************************//** + * @brief + * The initAccel function initializes the Accel module. + * @details + * Inputs : none + * Outputs : Accel module initialized. + * @return none + *************************************************************************/ +void initAccel( void ) +{ + ACCEL_AXIS_T axis; + + // zero rolling average buffer on accelerometer readings + resetAccelMovingAverage(); + + // zero accel readings + for ( axis = ACCEL_AXIS_X; axis < NUM_OF_ACCEL_AXES; axis++ ) + { + accelAxes[ axis ].data = 0.0; + accelAxes[ axis ].ovData = 0.0; + accelAxes[ axis ].ovInitData = 0.0; + accelAxes[ axis ].override = OVERRIDE_RESET; + accelMaxs[ axis ].data = 0.0; + accelMaxs[ axis ].ovData = 0.0; + accelMaxs[ axis ].ovInitData = 0.0; + accelMaxs[ axis ].override = OVERRIDE_RESET; + } +} + +/*********************************************************************//** + * @brief + * The execAccel function executes the accelerometer monitor state machine. + * @details + * Inputs : accelState + * Outputs : accelState + * @return none + *************************************************************************/ +void execAccel( void ) +{ + switch ( accelState ) + { + case ACCELEROMETER_START_STATE: + accelState = ACCELEROMETER_MONITOR_STATE; + break; + + case ACCELEROMETER_MONITOR_STATE: + accelState = handleAccelMonitorState(); + break; + + default: + // TODO - s/w fault + break; + } + + // publish accelerometer data on interval + publishAccelData(); +} + +/*********************************************************************//** + * @brief + * The handleAccelMonitorState function handles the accelerometer monitor state \n + * of the accelerometer monitor state machine. + * @details + * Inputs : + * Outputs : + * @return next state + *************************************************************************/ +static ACCEL_STATE_T handleAccelMonitorState( void ) +{ + ACCEL_STATE_T result = ACCELEROMETER_MONITOR_STATE; + S16 x, y, z; // axis readings + S16 xm, ym, zm; // max axis readings since last time we read FPGA registers + U16 cnt; // FPGA read counter + + // read FPGA accelerometer registers + getFPGAAccelAxes( &x, &y, &z ); + getFPGAAccelMaxes( &xm, &ym, &zm ); + getFPGAAccelStatus( &cnt, &accelFPGAFaultReg ); + + // check fresh sample + if ( cnt != accelFPGASampleCtr ) + { + accelNoNewSampleTimerCounter = 0; + accelFPGASampleCtr = cnt; + } + else + { + if ( ++accelNoNewSampleTimerCounter > NO_NEW_ACCEL_SAMPLES_TIMEOUT ) + { + // TODO - accelerometer fault + } + } + + // check error status + if ( accelFPGAFaultReg != 0 ) + { + // TODO - accelerometer fault + } + + // apply calibration (axis offsets) + x += accelCalOffsets[ ACCEL_AXIS_X ]; + y += accelCalOffsets[ ACCEL_AXIS_Y ]; + z += accelCalOffsets[ ACCEL_AXIS_Z ]; + xm += accelCalOffsets[ ACCEL_AXIS_X ]; + ym += accelCalOffsets[ ACCEL_AXIS_Y ]; + zm += accelCalOffsets[ ACCEL_AXIS_Z ]; + + // convert to gravities + accelAxes[ ACCEL_AXIS_X ].data = (F32)x * G_PER_LSB; + accelAxes[ ACCEL_AXIS_Y ].data = (F32)y * G_PER_LSB; + accelAxes[ ACCEL_AXIS_Z ].data = (F32)z * G_PER_LSB; + accelMaxs[ ACCEL_AXIS_X ].data = (F32)xm * G_PER_LSB; + accelMaxs[ ACCEL_AXIS_Y ].data = (F32)ym * G_PER_LSB; + accelMaxs[ ACCEL_AXIS_Z ].data = (F32)zm * G_PER_LSB; + + // filter readings to get a stable vector for tilt + filterAccelReadings(); + + // check tilt and shock + checkForTiltError(); + checkForShockError(); + + return result; +} + +/*********************************************************************//** + * @brief + * The getPublishAccelDataInterval function gets the accelerometer vector data \n + * publication interval. + * @details + * Inputs : accelDataPublishInterval + * Outputs : none + * @return the current accelerometer vector data publication interval (in priority task periods). + *************************************************************************/ +U32 getPublishAccelDataInterval( void ) +{ + U32 result = accelDataPublishInterval.data; + + if ( OVERRIDE_KEY == accelDataPublishInterval.override ) + { + result = accelDataPublishInterval.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getMeasuredAccelAxis function gets the current magnitude for the given \n + * accelerometer axis. + * @details + * Inputs : accelAxes[] + * Outputs : accelAxes[] + * @return the current magnitude for the given accelerometer axis (in g). + *************************************************************************/ +F32 getMeasuredAccelAxis( U32 axis ) +{ + F32 result = 0.0; + + if ( axis < NUM_OF_ACCEL_AXES ) + { + if ( OVERRIDE_KEY == accelAxes[ axis ].override ) + { + result = accelAxes[ axis ].ovData; + } + else + { + result = accelAxes[ axis ].data; + } + } + else + { +#ifdef _DG_ + activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); +#else + activateAlarmNoData( ALARM_ID_HD_SOFTWARE_FAULT ); +#endif + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getMaxAccelAxis function gets the current max magnitude for the given \n + * accelerometer axis. + * @details + * Inputs : accelMaxs[] + * Outputs : accelMaxs[] + * @return the current maximum magnitude for the given accelerometer axis (in g). + *************************************************************************/ +F32 getMaxAccelAxis( U32 axis ) +{ + F32 result = 0.0; + + if ( axis < NUM_OF_ACCEL_AXES ) + { + if ( OVERRIDE_KEY == accelMaxs[ axis ].override ) + { + result = accelMaxs[ axis ].ovData; + } + else + { + result = accelMaxs[ axis ].data; + } + } + else + { +#ifdef _DG_ + activateAlarmNoData( ALARM_ID_DG_SOFTWARE_FAULT ); +#else + activateAlarmNoData( ALARM_ID_HD_SOFTWARE_FAULT ); +#endif + } + + return result; +} + +/*********************************************************************//** + * @brief + * The publishAccelData function publishes accelerometer data at the set \n + * interval. + * @details + * Inputs : accelAxes[] + * Outputs : Accelerometer data is published to CAN bus. + * @return none + *************************************************************************/ +static void publishAccelData( void ) +{ + // publish accelerometer data on interval + if ( ++accelDataPublicationTimerCounter >= getPublishAccelDataInterval() ) + { + F32 x = getMeasuredAccelAxis( ACCEL_AXIS_X ); + F32 y = getMeasuredAccelAxis( ACCEL_AXIS_Y ); + F32 z = getMeasuredAccelAxis( ACCEL_AXIS_Z ); + F32 xm = getMaxAccelAxis( ACCEL_AXIS_X ); + F32 ym = getMaxAccelAxis( ACCEL_AXIS_Y ); + F32 zm = getMaxAccelAxis( ACCEL_AXIS_Z ); + F32 xt = accelTilt[ ACCEL_AXIS_X ]; + F32 yt = accelTilt[ ACCEL_AXIS_Y ]; + F32 zt = accelTilt[ ACCEL_AXIS_Z ]; + + broadcastAccelData( x, y, z, xm, ym, zm, xt, yt, zt ); + accelDataPublicationTimerCounter = 0; + } +} + +/*********************************************************************//** + * @brief + * The resetAccelMovingAverage function re-initializes the accelerometer \n + * moving average sample buffer. + * @details + * Inputs : none + * Outputs : accelReadingsTotal, accelReadingsIdx, accelReadingsCount all set to zero. + * @return none + *************************************************************************/ +static void resetAccelMovingAverage( void ) +{ + U32 axis; + + for ( axis = ACCEL_AXIS_X; axis < NUM_OF_ACCEL_AXES; axis++ ) + { + accelReadingsTotal[ axis ] = 0.0; + accelAvgVector[ axis ] = 0.0; + accelTilt[ axis ] = 0.0; + } + accelReadingsIdx = 0; + accelReadingsCount = 0; +} + +/*********************************************************************//** + * @brief + * The filterAccelReadings function adds a new axis samples to the filter. \n + * @details + * Inputs : none + * Outputs : flowReadings[], flowReadingsIdx, flowReadingsCount + * @return none + *************************************************************************/ +static void filterAccelReadings( void ) +{ + U32 axis; + + for ( axis = ACCEL_AXIS_X; axis < NUM_OF_ACCEL_AXES; axis++ ) + { + F32 rdg = getMeasuredAccelAxis( axis ); + + if ( accelReadingsCount >= SIZE_OF_ROLLING_AVG ) + { + accelReadingsTotal[ axis ] -= accelReadings[ axis ][ accelReadingsIdx ]; + } + accelReadings[ axis ][ accelReadingsIdx ] = rdg; + accelReadingsTotal[ axis ] += rdg; + } + accelReadingsIdx = INC_WRAP( accelReadingsIdx, 0, SIZE_OF_ROLLING_AVG - 1 ); + accelReadingsCount = INC_CAP( accelReadingsCount, SIZE_OF_ROLLING_AVG ); + // calculate average vector axes + accelAvgVector[ ACCEL_AXIS_X ] = accelReadingsTotal[ ACCEL_AXIS_X ] / (F32)accelReadingsCount; + accelAvgVector[ ACCEL_AXIS_Y ] = accelReadingsTotal[ ACCEL_AXIS_Y ] / (F32)accelReadingsCount; + accelAvgVector[ ACCEL_AXIS_Z ] = accelReadingsTotal[ ACCEL_AXIS_Z ] / (F32)accelReadingsCount; + // calculate axis angles + accelTilt[ ACCEL_AXIS_X ] = RAD2DEG( asin( accelAvgVector[ ACCEL_AXIS_X ] ) ); + accelTilt[ ACCEL_AXIS_Y ] = RAD2DEG( asin( accelAvgVector[ ACCEL_AXIS_Y ] ) ); + accelTilt[ ACCEL_AXIS_Z ] = RAD2DEG( asin( accelAvgVector[ ACCEL_AXIS_Z ] ) ); +} + +/*********************************************************************//** + * @brief + * The calcVectorLength function calculates the length of a vector with \n + * given vector axis magnitudes. + * @details + * Inputs : none + * Outputs : none + * @param x : X axis magnitude of vector + * @param y : Y axis magnitude of vector + * @param z : Z axis magnitude of vector + * @return the length of the given vector. + *************************************************************************/ +static F32 calcVectorLength( F32 x, F32 y, F32 z ) +{ + F32 result = sqrt( x * x + y * y + z * z ); + + return result; +} + +/*********************************************************************//** + * @brief + * The checkForTiltError function checks for a tilt error. + * @details + * Inputs : accelTilt[], accelTiltErrorTimerCounter + * Outputs : alarm if persistent excessive tilt detected + * @return none + *************************************************************************/ +static void checkForTiltError( void ) +{ + F32 x = accelTilt[ ACCEL_AXIS_X ]; + F32 y = accelTilt[ ACCEL_AXIS_Y ]; + + // is system tilted too much? + if ( ( FABS( x ) > MAX_TILT_ANGLE ) || ( FABS( y ) > MAX_TILT_ANGLE ) ) + { + // excessive tilt must persist before triggering alarm + if ( ++accelTiltErrorTimerCounter > MAX_TILT_PERSISTENCE ) + { + // TODO - tilt alarm + } + else + { + accelTiltErrorTimerCounter = 0; + } + } +} + +/*********************************************************************//** + * @brief + * The checkForShockError function checks for a shock error. + * @details + * Inputs : accelMaxs[] + * Outputs : alarm if excessive shock detected + * @return none + *************************************************************************/ +static void checkForShockError( void ) +{ + F32 maxX = getMaxAccelAxis( ACCEL_AXIS_X ); + F32 maxY = getMaxAccelAxis( ACCEL_AXIS_X ); + F32 maxZ = getMaxAccelAxis( ACCEL_AXIS_X ) - 1.0; // when system level, expect Z axis to be at 1 g normally + + // has system just experienced an excessive shock? + if ( ( FABS( maxX ) > MAX_SHOCK_ACCELERATION ) || + ( FABS( maxY ) > MAX_SHOCK_ACCELERATION ) || + ( FABS( maxZ ) > MAX_SHOCK_ACCELERATION ) ) + { + // TODO - shock alarm + } +} + +/*********************************************************************//** + * @brief + * The execAccelTest function executes the state machine for the \n + * accelerometer self test. + * @details + * Inputs : accelSelfTestState + * Outputs : accelSelfTestState + * @return the current state of the accelerometer self test. + *************************************************************************/ +SELF_TEST_STATUS_T execAccelTest( void ) +{ + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; + + switch ( accelSelfTestState ) + { + case ACCELEROMETER_SELF_TEST_STATE_START: + { + CALIBRATION_DATA_T cal; + + // retrieve blood flow sensor calibration data + if ( TRUE == getCalibrationData( &cal ) ) + { + accelCalOffsets[ ACCEL_AXIS_X ] = cal.accelXOffset; + accelCalOffsets[ ACCEL_AXIS_Y ] = cal.accelYOffset; + accelCalOffsets[ ACCEL_AXIS_Z ] = cal.accelZOffset; + accelSelfTestState = ACCELEROMETER_SELF_TEST_STATE_IN_PROGRESS; + } + else + { + result = SELF_TEST_STATUS_FAILED; + accelSelfTestState = ACCELEROMETER_SELF_TEST_STATE_COMPLETE; + } + } + break; + + case ACCELEROMETER_SELF_TEST_STATE_IN_PROGRESS: + { + F32 vectorLen = calcVectorLength( accelAxes[ ACCEL_AXIS_X ].data, accelAxes[ ACCEL_AXIS_Y ].data, accelAxes[ ACCEL_AXIS_Z ].data ); + + if ( FABS(NOMINAL_ACCEL_VECTOR_LENGTH - vectorLen) < MAX_ACCEL_VECTOR_LENGTH_ERROR ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } + accelSelfTestState = ACCELEROMETER_SELF_TEST_STATE_COMPLETE; + } + break; + + case ACCELEROMETER_SELF_TEST_STATE_COMPLETE: + break; + + default: + // TODO - s/w fault + break; + } + + return result; +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The setAccelCalibration function sets the accelerometer calibration \n + * factors and has them stored in non-volatile memory. + * @details + * Inputs : none + * Outputs : accelCalOffsets[] + * @param offsetX : offset calibration factor for X axis + * @param offsetY : offset calibration factor for Y axis + * @param offsetZ : offset calibration factor for Z axis + * @return TRUE if calibration factors successfully set/stored, FALSE if not + *************************************************************************/ +BOOL setAccelCalibration( S32 offsetX, S32 offsetY, S32 offsetZ ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + CALIBRATION_DATA_T cal; + + if ( TRUE == getCalibrationData( &cal ) ) + { // keep locally and apply immediately + accelCalOffsets[ ACCEL_AXIS_X ] = offsetX; + accelCalOffsets[ ACCEL_AXIS_Y ] = offsetY; + accelCalOffsets[ ACCEL_AXIS_Z ] = offsetZ; + // also update calibration record in non-volatile memory + cal.accelXOffset = offsetX; + cal.accelYOffset = offsetY; + cal.accelZOffset = offsetZ; + if ( TRUE == setCalibrationData( cal ) ) + { + result = TRUE; + } + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getAccelCalibration function retrieves the current accelerometer \n + * calibration factors. + * @details + * Inputs : accelCalOffsets[] + * Outputs : none + * @param offsetX : value to populate with X axis offset calibration factor + * @param offsetY : value to populate with Y axis offset calibration factor + * @param offsetZ : value to populate with Z axis offset calibration factor + * @return none + *************************************************************************/ +void getAccelCalibration( S32 *offsetX, S32 *offsetY, S32 *offsetZ ) +{ + *offsetX = accelCalOffsets[ ACCEL_AXIS_X ]; + *offsetY = accelCalOffsets[ ACCEL_AXIS_Y ]; + *offsetZ = accelCalOffsets[ ACCEL_AXIS_Z ]; +} + +/*********************************************************************//** + * @brief + * The testSetAccelDataPublishIntervalOverride function overrides the \n + * accelerometer data publish interval. + * @details + * Inputs : none + * Outputs : accelDataPublishInterval + * @param value : override accelerometer data publish interval with (in ms) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetAccelDataPublishIntervalOverride( U32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = value / TASK_PRIORITY_INTERVAL; + + result = TRUE; + accelDataPublishInterval.ovData = intvl; + accelDataPublishInterval.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetAccelDataPublishIntervalOverride function resets the override \n + * of the accelerometer data publish interval. + * @details + * Inputs : none + * Outputs : accelDataPublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetAccelDataPublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + accelDataPublishInterval.override = OVERRIDE_RESET; + accelDataPublishInterval.ovData = accelDataPublishInterval.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetAccelAxisOverride function overrides the value of the \n + * specified accelerometer axis with a given value. + * Inputs : none + * Outputs : accelAxes[] + * @param axis : ID of sensor axis to override for + * @param value : override value for the given axis + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetAccelAxisOverride( U32 axis, F32 value ) +{ + BOOL result = FALSE; + + if ( axis < NUM_OF_ACCEL_AXES ) + { + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + accelAxes[ axis ].ovData = value; + accelAxes[ axis ].override = OVERRIDE_KEY; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetAccelAxisOverride function resets the override of the \n + * specified accelerometer axis. + * @details + * Inputs : none + * Outputs : accelAxes[] + * @param axis : ID of accelerometer axis to reset override for + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetAccelAxisOverride( U32 axis ) +{ + BOOL result = FALSE; + + if ( axis < NUM_OF_ACCEL_AXES ) + { + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + accelAxes[ axis ].override = OVERRIDE_RESET; + accelAxes[ axis ].ovData = accelAxes[ axis ].ovInitData; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetAccelMaxOverride function overrides the max. value of the \n + * specified accelerometer axis with a given value. + * Inputs : none + * Outputs : accelMaxs[] + * @param axis : ID of sensor axis to override for + * @param value : override value for the given axis maximum + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetAccelMaxOverride( U32 axis, F32 value ) +{ + BOOL result = FALSE; + + if ( axis < NUM_OF_ACCEL_AXES ) + { + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + accelMaxs[ axis ].ovData = value; + accelMaxs[ axis ].override = OVERRIDE_KEY; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetAccelMaxOverride function resets the override of the \n + * specified accelerometer axis maximum. + * @details + * Inputs : none + * Outputs : accelMaxs[] + * @param axis : ID of accelerometer axis to reset override for + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetAccelMaxOverride( U32 axis ) +{ + BOOL result = FALSE; + + if ( axis < NUM_OF_ACCEL_AXES ) + { + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + accelMaxs[ axis ].override = OVERRIDE_RESET; + accelMaxs[ axis ].ovData = accelMaxs[ axis ].ovInitData; + } + } + + return result; +} + +/**@}*/ + Index: Accel.h =================================================================== diff -u --- Accel.h (revision 0) +++ Accel.h (revision 6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5) @@ -0,0 +1,62 @@ +/**********************************************************************//** + * + * Copyright (c) 2019-2020 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 Accel.h + * + * @date 25-Sep-2019 + * @author S. Nash + * + * @brief header file for Accelerometer monitor module. + * + **************************************************************************/ + +#ifndef __ACCEL_H__ +#define __ACCEL_H__ + +#include "Common.h" + +/** + * @defgroup Accel Accel + * @brief Accelerometer monitor module. Monitors the accelerometer sensor (Analog Devices ADXL345). + * + * @addtogroup Accel + * @{ + */ + +// ********** public definitions ********** + +/// Enumeration of accelerometer axes. +typedef enum Accelerometer_Axes +{ + ACCEL_AXIS_X = 0, ///< X axis of accelerometer + ACCEL_AXIS_Y, ///< Y axis of accelerometer + ACCEL_AXIS_Z, ///< Z axis of accelerometer + NUM_OF_ACCEL_AXES ///< Number of accelerometer axes +} ACCEL_AXIS_T; + +// ********** public function prototypes ********** + +void initAccel( void ); +void execAccel( void ); + +DATA_ARRAY_GET_PROTOTYPE( F32, getMeasuredAccelAxis, axis ); +DATA_ARRAY_GET_PROTOTYPE( F32, getMaxAccelAxis, axis ); + +SELF_TEST_STATUS_T execAccelTest( void ); + +BOOL setAccelCalibration( S32 offsetX, S32 offsetY, S32 offsetZ ); +void getAccelCalibration( S32 *offsetX, S32 *offsetY, S32 *offsetZ ); +BOOL testSetAccelDataPublishIntervalOverride( U32 value ); +BOOL testResetAccelDataPublishIntervalOverride( void ); +BOOL testSetAccelAxisOverride( U32 axis, F32 value ); +BOOL testResetAccelAxisOverride( U32 axis ); +BOOL testSetAccelMaxOverride( U32 axis, F32 value ); +BOOL testResetAccelMaxOverride( U32 axis ); + +/**@}*/ + +#endif Index: Common.h =================================================================== diff -u -r404f66cb61ba4bd71ea5af97c9b3e6eef9fa6743 -r6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5 --- Common.h (.../Common.h) (revision 404f66cb61ba4bd71ea5af97c9b3e6eef9fa6743) +++ Common.h (.../Common.h) (revision 6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5) @@ -104,6 +104,7 @@ #define SEC_PER_MIN 60 ///< # of seconds in a minute. #define FRACTION_TO_PERCENT_FACTOR 100.0 ///< Percentage factor (100). #define MIN_PER_HOUR 60 ///< # of minutes in an hour. +#define PI 3.1415927 ///< PI. // **** Common Macros **** @@ -125,6 +126,7 @@ #define MAKE_LONG_OF_WORDS(h, l) ((((U32)(h) << SHIFT_16_BITS_FOR_WORD_SHIFT) & MASK_OFF_LSW) | ((U32)(l) & MASK_OFF_MSW)) ///< Macro merges two 2-byte words into a 4-byte word. #define GET_TOGGLE(v, l, h) ((v) == (l) ? (h) : (l)) ///< Macro toggles a value. #define BIT_BY_POS(p) (1U << (p)) ///< Macro returns a bit mask for a bit of given position. +#define RAD2DEG(r) ((r) * 180.0 / PI) ///< Macro converts radians to degrees /**@}*/ Index: NVDataMgmt.c =================================================================== diff -u -r1e1578eeb5f1c1dd837283aab216ec50108f0754 -r6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5 --- NVDataMgmt.c (.../NVDataMgmt.c) (revision 1e1578eeb5f1c1dd837283aab216ec50108f0754) +++ NVDataMgmt.c (.../NVDataMgmt.c) (revision 6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5) @@ -71,7 +71,7 @@ #define LAST_DISINFECTION_DATE_ADDRESS 0x00000060 // 96 ///< Last disinfection date start address in RTC RAM (96) // Data addresses in EEPROM -#define CALIBRATION_RECORD_START_ADDRESS (BANK7_SECTOR0_START_ADDRESS + 0x100) ///< Calibration record start address in EEPROM +#define CALIBRATION_RECORD_START_ADDRESS ( BANK7_SECTOR0_START_ADDRESS + sizeof(MFG_RECORD_T) ) ///< Calibration record start address in EEPROM #define COMMAND_TIME_OUT 500U // time in ms ///< Timeout for an EEPROM or RTC command in ms @@ -171,6 +171,7 @@ { MFG_DATA_T mfgData; ///< Manufacturing data struct U16 crc; ///< Manufacturing data CRC + U08 Padding[ 0x400 - sizeof(MFG_DATA_T) - sizeof(U16) ]; ///< } MFG_RECORD_T; /// Service record struct @@ -185,6 +186,7 @@ { CALIBRATION_DATA_T calData; ///< Calibration data struct U16 crc; ///< Calibration data CRC + U08 Padding[ 0x400 - sizeof(CALIBRATION_DATA_T) - sizeof(U16) ]; ///< } CALIBRATION_RECORD_T; /// Last disinfection record struct @@ -215,6 +217,7 @@ static U32 bootloaderFlag = 0; ///< Bootloader flag static BOOL hasCommandTimedout = FALSE; ///< Boolean flag for timeout of the commands static U32 currentTime = 0; ///< Current time +static BOOL calRecordIsValid = FALSE; ///< Flag indicates whether stored calibration record was found to be valid // Private functions @@ -357,7 +360,7 @@ { BOOL status = FALSE; - if ( buffer != NULL ) + if ( ( buffer != NULL ) && ( TRUE == calRecordIsValid ) ) { memcpy ( buffer, (U08*)&calibrationRecord.calData, sizeof(CALIBRATION_DATA_T) ); status = TRUE; @@ -1097,11 +1100,13 @@ #ifdef _HD_ calcCRC = crc16 ( (U08*)&treatmentTimeRecord.treatmentTime, sizeof(U32) ); recordCRC = treatmentTimeRecord.crc; +#ifndef LIMITED_NVDATA_CRC_CHECKS if ( calcCRC != recordCRC ) { hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_HW_USAGE_DATA_CRC_ERROR, recordCRC, calcCRC ); } +#endif #else calcCRC = crc16 ( (U08*)&waterConsumptionRecord.waterConsumption, sizeof(U32) ); recordCRC = waterConsumptionRecord.crc; @@ -1119,6 +1124,7 @@ SET_ALARM_WITH_2_U32_DATA( AlARM_ID_NVDATA_DISINFECTION_DATE_CRC_ERROR, recordCRC, calcCRC ); } #endif +#ifndef LIMITED_NVDATA_CRC_CHECKS // Check log header record calcCRC = crc16 ( (U08*)&logRecord.logHeader, sizeof(LOG_HEADER_T) ); recordCRC = logRecord.crc; @@ -1142,6 +1148,7 @@ hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_MFG_RECORD_CRC_ERROR, recordCRC, calcCRC ); } +#endif // Check CRC for calibration record calcCRC = crc16 ( (U08*)&calibrationRecord.calData, sizeof(CALIBRATION_DATA_T) ); recordCRC = calibrationRecord.crc; @@ -1150,6 +1157,11 @@ hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_CAL_RECORD_CRC_ERROR, recordCRC, calcCRC ); } + else + { + calRecordIsValid = TRUE; + } +#ifndef LIMITED_NVDATA_CRC_CHECKS // Check CRC for service record calcCRC = crc16 ( (U08*)&serviceRecord.serviceData, sizeof(SERVICE_DATA_T) ); recordCRC = serviceRecord.crc; @@ -1158,8 +1170,8 @@ hasCRCPassed = FALSE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_NVDATA_SRVC_RECORD_CRC_ERROR, recordCRC, calcCRC ); } - // There should be no failed CRCs or no command should - // timeout for the self test to pass +#endif + // There should be no failed CRCs and no command should timeout for the self test to pass if ( hasCRCPassed && !hasCommandTimedout ) { NVDataMgmtSelfTestResult = SELF_TEST_STATUS_PASSED; Index: NVDataMgmt.h =================================================================== diff -u -rb46d288230d4e6aa32a7a2764c3d392c4dce0121 -r6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5 --- NVDataMgmt.h (.../NVDataMgmt.h) (revision b46d288230d4e6aa32a7a2764c3d392c4dce0121) +++ NVDataMgmt.h (.../NVDataMgmt.h) (revision 6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5) @@ -19,6 +19,8 @@ #include "Common.h" +// ********** public definitions ********** + #define MAX_SYS_SERIAL_NUMBER_CHARACTERS 7U ///< Max number of characters for SYS serial number #define MAX_HW_SERIAL_NUMBER_CHARACTERS 5U ///< Max number of characters for HD serial number #define MAX_DATE_CHARACTERS 10U ///< Max number of characters for date @@ -52,21 +54,31 @@ char mfgDate [ MAX_DATE_CHARACTERS ]; ///< Manufacturing date } MFG_DATA_T; +/// Calibration data struct +typedef struct calibration_Data +{ + U32 calRecordRevision; ///< Revision of calibration record (rev when structure changes to determine compatibility with f/w version) + S32 accelXOffset; ///< Accelerometer X axis offset + S32 accelYOffset; ///< Accelerometer Y axis offset + S32 accelZOffset; ///< Accelerometer Z axis offset +#ifdef _HD_ + F32 bloodFlowGain; ///< Gain for blood flow sensor + F32 bloodFlowOffset_mL_min; ///< Offset for blood flow sensor + char calDateBloodFlow[ MAX_DATE_CHARACTERS ]; ///< Last calibration date of blood flow sensor + F32 dialysateFlowGain; ///< + F32 dialysateFlowOffset_mL_min; ///< + char calDateDialysateFlow[ MAX_DATE_CHARACTERS ]; ///< Last calibration date of blood flow sensor +#endif + U16 crc; ///< CRC for this calibration record +} CALIBRATION_DATA_T; + /// Service dates struct typedef struct service_dates { char currentServiceDate [ MAX_DATE_CHARACTERS ]; ///< Current service date char nextServiceDate [ MAX_DATE_CHARACTERS ]; ///< Next service date } SERVICE_DATA_T; -/// Calibration data struct -typedef struct calibration_Data -{ - F32 occSensorOffset; ///< Item 1 - F32 tempSensorOffset; ///< Item 2 - char calibrationDate [ MAX_DATE_CHARACTERS ]; ///< Calibration date -} CALIBRATION_DATA_T; - /// Read data status typedef struct get_data { Index: Utilities.h =================================================================== diff -u -rf9846110e1d1f9f6fc183b428b9469768c3636eb -r6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5 --- Utilities.h (.../Utilities.h) (revision f9846110e1d1f9f6fc183b428b9469768c3636eb) +++ Utilities.h (.../Utilities.h) (revision 6a00bc5632cfdb84cf72e7dbbc55ff115e3481d5) @@ -32,13 +32,13 @@ /// Enumeration of time-windowed counts. typedef enum TimeWindowedCounts { - TIME_WINDOWED_COUNT_BAD_MSG_CRC = 0, ///< Bad message CRC. - TIME_WINDOWED_COUNT_CAN_WARNING, ///< CAN warning. - TIME_WINDOWED_COUNT_CAN_PASSIVE, ///< CAN passive mode. - TIME_WINDOWED_COUNT_CAN_OFF, ///< CAN off. - TIME_WINDOWED_COUNT_CAN_PARITY, ///< CAN parity error. - TIME_WINDOWED_COUNT_FPGA_UART_FRAME_ERROR, ///< FPGA UART frame error. - TIME_WINDOWED_COUNT_FPGA_UART_OVERRUN, ///< FPGA UART overrun error. + TIME_WINDOWED_COUNT_BAD_MSG_CRC = 0, ///< Bad message CRC + TIME_WINDOWED_COUNT_CAN_WARNING, ///< CAN warning + TIME_WINDOWED_COUNT_CAN_PASSIVE, ///< CAN passive mode + TIME_WINDOWED_COUNT_CAN_OFF, ///< CAN off + TIME_WINDOWED_COUNT_CAN_PARITY, ///< CAN parity error + TIME_WINDOWED_COUNT_FPGA_UART_FRAME_ERROR, ///< FPGA UART frame error + TIME_WINDOWED_COUNT_FPGA_UART_OVERRUN, ///< FPGA UART overrun error #ifdef _HD_ #endif #ifdef _DG_