Index: firmware/App/Controllers/BloodFlow.c =================================================================== diff -u -r5070f8552a200e15dcc2ca0532db10fba9dc8c6b -r02fa169f02dd5a8caf27d1ea4441c74ba38317e8 --- firmware/App/Controllers/BloodFlow.c (.../BloodFlow.c) (revision 5070f8552a200e15dcc2ca0532db10fba9dc8c6b) +++ firmware/App/Controllers/BloodFlow.c (.../BloodFlow.c) (revision 02fa169f02dd5a8caf27d1ea4441c74ba38317e8) @@ -71,6 +71,7 @@ #define BP_MAX_MOTOR_SPEED_ERROR_RPM 300.0 ///< Maximum difference in speed between measured and commanded RPM. #ifdef USE_FMB_FLOW_SENSOR #define BP_MAX_FLOW_VS_SPEED_DIFF_RPM 200.0 ///< Maximum difference between measured speed and speed implied by measured flow. + /// Persist time (task intervals) for flow vs. motor speed error condition. static const U32 BP_FLOW_VS_SPEED_PERSIST = ( 5 * MS_PER_SECOND ); #endif @@ -87,33 +88,34 @@ /// Persist time (task intervals) blood flow rate out of range error condition. static const U32 BP_MAX_FLOW_RATE_OUT_OF_RANGE_PERSIST = ((1 * MS_PER_SECOND) / TASK_PRIORITY_INTERVAL); -#define BP_MAX_CURR_WHEN_STOPPED_MA 150.0 ///< Motor controller current should not exceed this when pump should be stopped -#define BP_MAX_CURR_WHEN_RUNNING_MA 2000.0 ///< Motor controller current should not exceed this when pump should be running -#define BP_MAX_CURR_ERROR_DURATION_MS 2000 ///< Motor controller current errors persisting beyond this duration will trigger an alarm +#define BP_MAX_CURR_WHEN_STOPPED_MA 150.0 ///< Motor controller current should not exceed this when pump should be stopped. +#define BP_MAX_CURR_WHEN_RUNNING_MA 2000.0 ///< Motor controller current should not exceed this when pump should be running. +#define BP_MAX_CURR_ERROR_DURATION_MS 2000 ///< Motor controller current errors persisting beyond this duration will trigger an alarm. -#define BP_SPEED_ADC_TO_RPM_FACTOR 1.751752 ///< Conversion factor from ADC counts to RPM for blood pump motor -#define BP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.000238 ///< ~42 BP motor RPM = 1% PWM duty cycle -#define BP_CURRENT_ADC_TO_MA_FACTOR 3.002 ///< Conversion factor from ADC counts to mA for blood pump motor +#define BP_SPEED_ADC_TO_RPM_FACTOR 1.751752 ///< Conversion factor from ADC counts to RPM for blood pump motor. +#define BP_MOTOR_RPM_TO_PWM_DC_FACTOR 0.000238 ///< ~42 BP motor RPM = 1% PWM duty cycle. +#define BP_CURRENT_ADC_TO_MA_FACTOR 3.002 ///< Conversion factor from ADC counts to mA for blood pump motor. -#define BP_REV_PER_LITER 144.7 ///< Rotor revolutions per liter +#define BP_REV_PER_LITER 146.84 ///< Rotor revolutions per liter. +#define BP_ML_PER_ROTOR_REV 6.81 ///< Milliliters per rotor revolusion. #define BP_ML_PER_MIN_TO_PUMP_RPM_FACTOR ( BP_REV_PER_LITER / ML_PER_LITER ) ///< Conversion factor from mL/min to motor RPM. -#define BP_GEAR_RATIO 32.0 ///< Blood pump motor to blood pump gear ratio -#define BP_PWM_ZERO_OFFSET 0.1 ///< 10% PWM duty cycle = zero speed +#define BP_GEAR_RATIO 32.0 ///< Blood pump motor to blood pump gear ratio. +#define BP_PWM_ZERO_OFFSET 0.1 ///< 10% PWM duty cycle = zero speed. /// Conversion macro from mL/min to estimated PWM duty cycle %. #define BP_PWM_FROM_ML_PER_MIN(rate) ( (rate) * BP_ML_PER_MIN_TO_PUMP_RPM_FACTOR * BP_GEAR_RATIO * BP_MOTOR_RPM_TO_PWM_DC_FACTOR + BP_PWM_ZERO_OFFSET ) /// Conversion from PWM duty cycle % to commanded pump motor speed. #define BP_PWM_TO_MOTOR_SPEED_RPM(pwm) ( ((pwm) - BP_PWM_ZERO_OFFSET) * 4000.0 ) -#define BLOODPUMP_ADC_FULL_SCALE_V 3.0 ///< BP analog signals are 0-3V (while int. ADC ref may be different) +#define BLOODPUMP_ADC_FULL_SCALE_V 3.0 ///< BP analog signals are 0-3V (while int. ADC ref may be different). #define BLOODPUMP_ADC_ZERO 1998 ///< Blood pump ADC channel zero offset. /// Macro converts 12 bit ADC value to signed 16-bit value. #define SIGN_FROM_12_BIT_VALUE(v) ( (S16)(v) - (S16)BLOODPUMP_ADC_ZERO ) #ifndef USE_FMB_FLOW_SENSOR -/// Measured blood flow is filtered w/ moving average +/// Measured blood flow is filtered w/ moving average. #define SIZE_OF_ROLLING_AVG ( ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) * 1 ) #else -/// Measured blood flow is filtered w/ moving average +/// Measured blood flow is filtered w/ moving average. #define SIZE_OF_ROLLING_AVG ( ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) * 10 ) /// Blood flow sensor signal strength low alarm persistence. @@ -134,17 +136,10 @@ #define PUMP_DIR_ERROR_COUNT_MASK 0x3F ///< Bit mask for pump direction error counter. #ifndef USE_FMB_FLOW_SENSOR -#define BLOOD_PC2 -0.0000090267 ///< Pressure compensation coefficient (2nd order) for blood pump. -#define BLOOD_PC1 -0.00071147 ///< Pressure compensation coefficient (1st order) for blood pump. -#define BLOOD_NSV 3.4555 ///< Nominal stroke (1/2 rotor revolution) volume (in mL) for blood pump. -#define BLOOD_SC2 -0.0000000718 ///< Speed compensation coefficient (2nd order) for blood pump. -#define BLOOD_SC1 0.0000851 ///< Speed compensation coefficient (1st order) for blood pump. -#define BLOOD_SC0 -0.027 ///< Speed compensation coefficient (offset) for blood pump. -#define BLOOD_PSC2 -0.00000382 ///< Additional pressure compensation coefficient (2nd order) for blood pump. -#define BLOOD_PSC1 0.000197 ///< Additional pressure compensation coefficient (1st order) for blood pump. -#define BLOOD_PSC0 0.17 ///< Additional pressure compensation coefficient (offset) for blood pump. -#define BLOOD_TS0 1000.0 ///< Lower boundary of weighted speed transition (in RPM). -#define BLOOD_TS1 1500.0 ///< Upper boundary of weighted speed transition (in RPM). +#define BP_FLOW_ALPHA_Y_INTERCEPT 1.218 ///< Y intercept used for alpha flow coefficient calculation. +#define BP_FLOW_WEAR_A_TERM 0.000000007474 ///< A term used for wear portion of alpha flow coefficient. +#define BP_FLOW_WEAR_B_TERM 0.0006053 ///< B term used for wear portion of alpha flow coefficient. +#define BP_MAX_ROTOR_COUNT_FOR_WEAR 25000 ///< Maximum rotor count for determining wear of the cartridge (negligible affect beyond this threshold). #endif /// Enumeration of blood pump controller states. @@ -190,7 +185,7 @@ /// Interval (in task intervals) at which to publish blood flow data to CAN bus. static OVERRIDE_U32_T bloodFlowDataPublishInterval = { BLOOD_FLOW_DATA_PUB_INTERVAL, BLOOD_FLOW_DATA_PUB_INTERVAL, BLOOD_FLOW_DATA_PUB_INTERVAL, 0 }; static S32 targetBloodFlowRate = 0; ///< Requested blood flow rate. -static OVERRIDE_F32_T measuredBloodFlowRate = { 0.0, 0.0, 0.0, 0 }; ///< Measured blood flow rate. +static OVERRIDE_F32_T measuredBloodFlowRate = { 0.0, 0.0, 0.0, 0 }; ///< Measured (calculated now) blood flow rate. static OVERRIDE_F32_T bloodPumpRotorSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< Measured blood pump rotor speed. static OVERRIDE_F32_T bloodPumpSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< Measured blood pump motor speed. static OVERRIDE_F32_T adcBloodPumpMCSpeedRPM = { 0.0, 0.0, 0.0, 0 }; ///< Measured blood pump motor controller speed. @@ -209,11 +204,18 @@ static U08 lastBloodPumpDirectionCount = 0; ///< Previous pump direction error count reported by FPGA. +#ifndef USE_FMB_FLOW_SENSOR +static F32 rpmReadings[ SIZE_OF_ROLLING_AVG ]; ///< Holds RPM samples for a rolling average. +static U32 rpmReadingsIdx = 0; ///< Index for next sample in rolling average array. +static F32 rpmReadingsTotal = 0.0; ///< Rolling total - used to calc average. +static U32 rpmReadingsCount = 0; ///< Number of samples in RPM rolling average buffer. +static F32 filteredBloodPumpSpeed = 0.0; ///< Filtered blood pump speed used in blood flow estimation. +#else static F32 flowReadings[ SIZE_OF_ROLLING_AVG ]; ///< Holds flow samples for a rolling average. static U32 flowReadingsIdx = 0; ///< Index for next sample in rolling average array. static F32 flowReadingsTotal = 0.0; ///< Rolling total - used to calc average. static U32 flowReadingsCount = 0; ///< Number of samples in flow rolling average buffer. - +#endif static U32 bpControlTimerCounter = 0; ///< Determines when to perform control on blood flow static U32 bpRotorRevStartTime = 0; ///< Blood pump rotor rotation start time (in ms) @@ -226,15 +228,6 @@ static U32 bpMotorSpeedCalcIdx = 0; ///< Index into 1 second buffer of motor speed hall sensor counts static U32 bpMotorSpeedCalcTimerCtr = 0; ///< Counter determines interval for calculating blood pump motor speed from hall sensor count. -#ifndef USE_FMB_FLOW_SENSOR -static F32 bloodPCSV; ///< Calculated pressure corrected blood pump stroke volume. -static F32 bloodSCSV; ///< Calculated speed corrected blood pump stroke volume. -static F32 bloodPSCSV; ///< Calculated additional pressure corrected blood pump stroke volume. -static F32 bloodHSWF; ///< High speed weighting factor for blood pump stroke volume calculation. -static F32 bloodLSWF; ///< Low speed weighting factor for blood pump stroke volume calculation. -static F32 bloodPSCSVT; ///< Pressure corrected, speed corrected blood pump stroke volume with weighted transition. -#endif - // ********** private function prototypes ********** static BLOOD_PUMP_STATE_T handleBloodPumpOffState( void ); @@ -246,8 +239,6 @@ static void releaseBloodPumpStop( void ); static void setBloodPumpDirection( MOTOR_DIR_T dir ); static void publishBloodFlowData( void ); -static void resetBloodFlowMovingAverage( void ); -static void filterBloodFlowReadings( F32 flow ); static void updateBloodPumpSpeedAndDirectionFromHallSensors( void ); static void checkBloodPumpRotor( void ); static void checkBloodPumpDirection( void ); @@ -256,41 +247,16 @@ static void checkBloodPumpFlowRate( void ); #ifdef USE_FMB_FLOW_SENSOR +static void resetBloodFlowMovingAverage( void ); +static void filterBloodFlowReadings( F32 flow ); static void checkBloodFlowSensorSignalStrength( void ); static BOOL processCalibrationData( void ); #else static F32 calcBloodFlow( void ); - -/*********************************************************************//** - * @brief - * The calcBloodFlow function calculates an estimated blood flow rate from - * blood pump speed and arterial pressure. - * @details Inputs: BP set speed, arterial pressure - * @details Outputs: calcBloodFlowRate - * @return calculated blood flow rate (mL/min) - *************************************************************************/ -static F32 calcBloodFlow( void ) -{ - F32 artPres = getLongFilteredArterialPressure(); - F32 artPres_2 = pow( artPres, 2.0 ); - F32 motSpd = getMeasuredBloodPumpMCSpeed(); //BP_PWM_TO_MOTOR_SPEED_RPM( bloodPumpPWMDutyCyclePctSet ); - F32 motSpd_2 = pow( motSpd, 2.0 ); - F32 rotSpd = motSpd / BP_GEAR_RATIO; - F32 strokeSpd = rotSpd * 2.0; // 1 rotor revolution = 2 strokes - - // Calculate compensated stroke volume (in mL). - bloodPCSV = BLOOD_PC2 * artPres_2 + BLOOD_PC1 * artPres + BLOOD_NSV; - bloodSCSV = bloodPCSV * ( 1.0 + BLOOD_SC2 * motSpd_2 + BLOOD_SC1 * motSpd + BLOOD_SC0 ); - bloodPSCSV = bloodSCSV * ( 1.0 + BLOOD_PSC2 * artPres_2 + BLOOD_PSC1 * artPres + BLOOD_PSC0 ); - bloodHSWF = ( ( motSpd - BLOOD_TS0 > 0.0 ? motSpd - BLOOD_TS0 : 0.0 ) - ( motSpd - BLOOD_TS1 > 0.0 ? motSpd - BLOOD_TS1 : 0.0 ) ) / ( BLOOD_TS1 - BLOOD_TS0 ); - bloodLSWF = 1.0 - bloodHSWF; - bloodPSCSVT = bloodLSWF * bloodPCSV + bloodHSWF * bloodPSCSV; - - // Blood flow = stroke speed (strokes/min) x stroke volume (mL/stroke) = mL/min - return ( strokeSpd * bloodPSCSVT ); -} +static void resetBloodPumpRPMMovingAverage( void ); +static void filterBloodPumpRPMReadings( F32 rpm ); #endif - + /*********************************************************************//** * @brief * The initBloodFlow function initializes the BloodFlow module. @@ -305,15 +271,21 @@ stopBloodPump(); setBloodPumpDirection( MOTOR_DIR_FORWARD ); +#ifndef USE_FMB_FLOW_SENSOR + // Zero rolling pump speed average buffer + resetBloodPumpRPMMovingAverage(); +#else // Zero rolling flow average buffer resetBloodFlowMovingAverage(); +#endif // Zero motor hall sensors counts buffer bpMotorSpeedCalcIdx = 0; for ( i = 0; i < BP_SPEED_CALC_BUFFER_LEN; i++ ) { bpLastMotorHallSensorCounts[ i ] = 0; } + resetBloodPumpRotorCount(); // Initialize blood flow PI controller initializePIController( PI_CONTROLLER_ID_BLOOD_FLOW, MIN_BLOOD_PUMP_PWM_DUTY_CYCLE, @@ -360,7 +332,11 @@ if ( flowRate <= MAX_SET_BLOOD_FLOW_RATE ) #endif { - resetBloodFlowMovingAverage(); +#ifndef USE_FMB_FLOW_SENSOR + resetBloodPumpRPMMovingAverage(); +#else + resetBloodFlowMovingAverage(); +#endif targetBloodFlowRate = ( dir == MOTOR_DIR_FORWARD ? (S32)flowRate : (S32)flowRate * -1 ); bloodPumpDirection = dir; bloodPumpControlMode = mode; @@ -374,13 +350,15 @@ { bloodPumpState = BLOOD_PUMP_RAMPING_DOWN_STATE; } - break; + break; + case BLOOD_PUMP_RAMPING_DOWN_STATE: // See if we need to reverse direction of ramp if ( bloodPumpPWMDutyCyclePct > bloodPumpPWMDutyCyclePctSet ) { bloodPumpState = BLOOD_PUMP_RAMPING_UP_STATE; } - break; + break; + case BLOOD_PUMP_CONTROL_TO_TARGET_STATE: // Start ramp to new target in appropriate direction if ( bloodPumpPWMDutyCyclePctSet > bloodPumpPWMDutyCyclePct ) { @@ -390,7 +368,8 @@ { bloodPumpState = BLOOD_PUMP_RAMPING_UP_STATE; } - break; + break; + default: // Ok - not all states need to be handled here break; @@ -408,6 +387,29 @@ return result; } +#ifndef USE_FMB_FLOW_SENSOR +/*********************************************************************//** + * @brief + * The calcBloodFlow function calculates an estimated blood flow rate from + * blood pump speed, arterial pressure and tubing wear (measured from count + * of rotor revolutions since cartridge install). + * @details Inputs: bloodPumpRotorCounter, arterial pressure + * @details Outputs: none + * @return calculated blood flow rate (mL/min) + *************************************************************************/ +static F32 calcBloodFlow( void ) +{ + F32 artPres = getLongFilteredArterialPressure(); + F32 rotSpd = filteredBloodPumpSpeed / BP_GEAR_RATIO; + U32 rotCnt = CAP( bloodPumpRotorCounter, BP_MAX_ROTOR_COUNT_FOR_WEAR ); + F32 wear = BP_FLOW_WEAR_A_TERM * (F32)rotCnt + BP_FLOW_WEAR_B_TERM; + F32 alpha = wear * artPres + BP_FLOW_ALPHA_Y_INTERCEPT; + F32 flow = alpha * BP_ML_PER_ROTOR_REV * rotSpd; + + return flow; +} +#endif + /*********************************************************************//** * @brief * The signalBloodPumpHardStop function stops the blood pump immediately. @@ -512,6 +514,20 @@ BOOL isBloodPumpRunning( void ) { return isBloodPumpOn; +} + +/*********************************************************************//** + * @brief + * The resetBloodPumpRotorCount function resets the blood pump rotor counter + * that is a proxy for cartridge wear. Call this function after a new cartridge + * has been installed. + * @details Inputs: none + * @details Outputs: bloodPumpRotorCounter + * @return none + *************************************************************************/ +void resetBloodPumpRotorCount( void ) +{ + bloodPumpRotorCounter = 0; } /*********************************************************************//** @@ -531,8 +547,8 @@ U08 spReadCtr = getFPGABloodFlowSlowPacketReadCounter(); U08 flowErrorCtr = getFPGABloodFlowErrorCounter(); U08 flowStatus = getFPGABloodFlowMeterStatus(); - F32 bpFlow; #ifdef USE_FMB_FLOW_SENSOR + F32 bpFlow; F32 fpgaBloodFlow = getFPGABloodFlow(); // Check if a new calibration is available @@ -573,16 +589,17 @@ adcBloodPumpMCCurrentmA.data = (F32)(SIGN_FROM_12_BIT_VALUE(bpmA)) * BP_CURRENT_ADC_TO_MA_FACTOR; #ifndef USE_FMB_FLOW_SENSOR - bpFlow = calcBloodFlow(); + filterBloodPumpRPMReadings( getMeasuredBloodPumpMCSpeed() ); + measuredBloodFlowRate.data = calcBloodFlow(); // Pressure and rotor speed already filtered as inputs to calc, so no need to filter flow any further #else bloodFlowSignalStrength.data = getFPGABloodFlowSignalStrength(); bpFlow = pow(fpgaBloodFlow, 4) * bloodFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_BLOOD_FLOW_SENSOR ].fourthOrderCoeff + pow(fpgaBloodFlow, 3) * bloodFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_BLOOD_FLOW_SENSOR ].thirdOrderCoeff + pow(fpgaBloodFlow, 2) * bloodFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_BLOOD_FLOW_SENSOR ].secondOrderCoeff + fpgaBloodFlow * bloodFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_BLOOD_FLOW_SENSOR ].gain + bloodFlowCalRecord.hdFlowSensors[ CAL_DATA_HD_BLOOD_FLOW_SENSOR ].offset; + filterBloodFlowReadings( bpFlow ); #endif - filterBloodFlowReadings( bpFlow ); // TODO - do we need to filter flow if calculated from filtered pressure? // Calculate blood pump motor speed/direction from hall sensor count updateBloodPumpSpeedAndDirectionFromHallSensors(); @@ -693,7 +710,11 @@ // Have we reached end of ramp up? else if ( bloodPumpPWMDutyCyclePctSet >= bloodPumpPWMDutyCyclePct ) { - resetBloodFlowMovingAverage(); +#ifndef USE_FMB_FLOW_SENSOR + resetBloodPumpRPMMovingAverage(); +#else + resetBloodFlowMovingAverage(); +#endif bloodPumpPWMDutyCyclePctSet = bloodPumpPWMDutyCyclePct; resetPIController( PI_CONTROLLER_ID_BLOOD_FLOW, bloodPumpPWMDutyCyclePctSet ); bloodPumpControlModeSet = bloodPumpControlMode; @@ -732,7 +753,11 @@ // Have we reached end of ramp down? else if ( bloodPumpPWMDutyCyclePctSet <= bloodPumpPWMDutyCyclePct ) { - resetBloodFlowMovingAverage(); +#ifndef USE_FMB_FLOW_SENSOR + resetBloodPumpRPMMovingAverage(); +#else + resetBloodFlowMovingAverage(); +#endif bloodPumpPWMDutyCyclePctSet = bloodPumpPWMDutyCyclePct; resetPIController( PI_CONTROLLER_ID_BLOOD_FLOW, bloodPumpPWMDutyCyclePctSet ); bloodPumpControlModeSet = bloodPumpControlMode; @@ -1006,6 +1031,45 @@ } } +#ifndef USE_FMB_FLOW_SENSOR +/*********************************************************************//** + * @brief + * The resetBloodPumpRPMMovingAverage function re-initializes the pump speed + * moving average sample buffer. + * @details Inputs: none + * @details Outputs: rpmReadingsTotal, rpmReadingsIdx, rpmReadingsCount all set to zero. + * @return none + *************************************************************************/ +static void resetBloodPumpRPMMovingAverage( void ) +{ + rpmReadingsIdx = 0; + rpmReadingsCount = 0; + rpmReadingsTotal = 0.0; + filteredBloodPumpSpeed = 0.0; +} + +/*********************************************************************//** + * @brief + * The filterBloodPumpRPMReadings function adds a new pump speed sample to + * the filter. + * @details Inputs: none + * @details Outputs: rpmReadings[], rpmReadingsIdx, rpmReadingsCount, rpmReadingsTotal + * @param rpm newest blood pump speed (in RPM) sample to add to filter + * @return none + *************************************************************************/ +static void filterBloodPumpRPMReadings( F32 rpm ) +{ + if ( rpmReadingsCount >= SIZE_OF_ROLLING_AVG ) + { + rpmReadingsTotal -= rpmReadings[ rpmReadingsIdx ]; + } + rpmReadings[ rpmReadingsIdx ] = rpm; + rpmReadingsTotal += rpm; + rpmReadingsIdx = INC_WRAP( rpmReadingsIdx, 0, SIZE_OF_ROLLING_AVG - 1 ); + rpmReadingsCount = INC_CAP( rpmReadingsCount, SIZE_OF_ROLLING_AVG ); + filteredBloodPumpSpeed = rpmReadingsTotal / (F32)rpmReadingsCount; +} +#else /*********************************************************************//** * @brief * The resetBloodFlowMovingAverage function re-initializes the blood flow @@ -1020,7 +1084,7 @@ flowReadingsCount = 0; flowReadingsTotal = 0.0; bpControlTimerCounter = 0; -} +} /*********************************************************************//** * @brief @@ -1032,7 +1096,6 @@ *************************************************************************/ static void filterBloodFlowReadings( F32 flow ) { -#ifndef RAW_FLOW_SENSOR_DATA if ( flowReadingsCount >= SIZE_OF_ROLLING_AVG ) { flowReadingsTotal -= flowReadings[ flowReadingsIdx ]; @@ -1041,11 +1104,9 @@ flowReadingsTotal += flow; flowReadingsIdx = INC_WRAP( flowReadingsIdx, 0, SIZE_OF_ROLLING_AVG - 1 ); flowReadingsCount = INC_CAP( flowReadingsCount, SIZE_OF_ROLLING_AVG ); - measuredBloodFlowRate.data = flowReadingsTotal / (F32)flowReadingsCount; -#else - measuredBloodFlowRate.data = flow; -#endif + measuredBloodFlowRate.data = flowReadingsTotal / (F32)flowReadingsCount; } +#endif /*********************************************************************//** * @brief