Started a gui menuflow
Dependencies: LCD_DISCO_F429ZI mbed TS_DISCO_F429ZI BSP_DISCO_F429ZI
Ventilation.txt
- Committer:
- ahaas92
- Date:
- 2020-06-11
- Revision:
- 3:b029a3f73a9e
- Parent:
- Ventilation.c@ 2:5828e6917e75
File content as of revision 3:b029a3f73a9e:
/* Includes ------------------------------------------------------------------*/ #define EXTERN extern #include <stdlib.h> #include <string.h> #include "SS.h" #include "main.h" #include "_SS_OnOffActioner.h" #include "_SS_Pwm.h" #include "Monitoring.h" #include "Safety.h" #include "_SS_Data_Logging.h" #include "_SS_Record_Settings.h" #include "_SS_I2CX_SDP600.h" #include "_SS_I2CX_X201641.h" #include "_SS_OptimaComm.h" #undef EXTERN #define INIT_VARIABLES #define EXTERN #include "Ventilation.h" #undef EXTERN #undef INIT_VARIABLES /* Internal constants --------------------------------------------------------*/ // ******************* Min-max PI or PDF *************************************** #define MAX_VOLTAGE_REF MAX_PI_OUTPUT // MAX_PI_OUTPUT = 1343 = 18.5micros = 93.3%, want 80% #define MIN_VOLTAGE_REF MIN_PI_OUTPUT #define INTEGRAL_MAX (2147483647) #define INTEGRAL_MIN (-2147483648) #ifndef C_M3_DEVICETEST_TARGET /* Internal variables --------------------------------------------------------*/ // ******************* MANAGED CONSTANT SPEED **************************** static u16 uiBlowerSpeedSetting; static int32_t lIntegral_Speed_I; static bool bMaxPIOutputSpeedI; static bool bMinPIOutputSpeedI; // ************* MANAGED BAROMETRIC VENTILATION ************************* static u16 uiIpapSettingTemp; static u16 uiTeBaroSet; static int32_t lIntegral_Pressure_I; static bool bMaxPIOutputPressureI; static bool bMinPIOutputPressureI; static u16 uiTiD; static u8 ucIndexTableSlope; #define TEMPO_TRIGGER_EXPI 30 static u8 ucTempoTriggerExpiAuto; #define MIN_THRESHOLD_EXPI_TRIGGER_AUTO 25 static u8 ucExpiTriggerTreshold; static u8 fFirstCycleInspi; // ----------- Main blower management during expiration ------------------- static u16 uiPWMatTheEndOfInspiration; static bool fFirstCycleExpi; static u16 uiEpapSettingTemp; static int32_t lIntegral_Pressure_E; static bool bMaxPIOutputPressureE; static bool bMinPIOutputPressureE; static u16 uiPWMatTheEndOfExpiration; static u16 uiTeD; static bool fBlockCloseLoop; static u16 uiTiTyp; // ************* MANAGED VOLUMETRIC VENTILATION ************************* static u32 ulMaxIFlowSet; static u32 ulMinIFlowSet; static u32 ulDecFlowStep; static u16 uiTeVoluSet; static u16 uiProximalPressureAtTheEndOfInspiration; static int32_t lIntegral_Flow_I; static bool bMaxPIOutputFlowI; static bool bMinPIOutputFlowI; static int16_t iVtAdjust=0; static u8 ucCounterHPAlarm=0; // ******************* MANAGED_CPAP_VENTILATION ****************************** #define EXPIRATORY_DISCONNECTION_TIME_OUT 500 // 500ms #define CPAP_DISCONNECTION_TEST_PERIODICITY 4000 // 4s static u16 uiDisconnectionTime; static u16 uiDisconnectionTimeInCPAP; static u16 uiMemoPWMDuringDisconnection; //static u8 ucStandByMode; // ***************** MANAGED INSPIRATORY TRIGGER ***************************** #define MAX_DELTA_FLOW 100 // 1 l/min #define SIZE_DELTA_INSPIRATORY_FLOW_BUFFER 40 #define NUMBER_TRIGGER_VALID 20 #define NUMBER_MIN_FLOW_VALUE 30 //static int16_t iBufferIFlowDelta[SIZE_DELTA_INSPIRATORY_FLOW_BUFFER]; static u8 ucInspiTriggerScheduler; static u8 ucMinFlowCounter; static int16_t iInspiratoryFlowMin; //static int16_t iOldInspiratoryFlowSmoothingMesForTrigger; #define SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER 250 static int16_t iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER]; // static int16_t iBufferConductance[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER]; static u8 ucCounterTriggerValid; static int16_t iFlowBaseLineReference; static bool bDetectionConstantFlow; // *********************** MANAGED MONITORING FOR MLT ************************ static u16 uiMLT_PressureSetPoint; #ifdef MOTOR_LIFE_TESTING #define MLT_AVERAGE_MONITORING 1000 static u16 uiAverageMonitoringMLT; static u32 ulMLT_SumSpeedMes; static u32 ulMLT_SumCurrentMes; static u32 ulMLT_SumTemperatureMes; static u32 ulMLT_SumBlowerVoltage; static u16 uiMLT_EnableAlarm; #endif // #ifdef MOTOR_LIFE_TESTING // ************************ MANAGED CALIBRATION ****************************** typedef enum { INIT_FLOW_CALIB_TEST=0, SET_GAS_TYPE, SET_GAS_STANDARD, START_FLOW_READING, SEND_FLOW_COMMAND, READ_FLOW_VALUE, SEND_HIGH_FLOW_CALIB_REQUEST, WAIT_FOR_HIGH_FLOW_CALIB_ACK, PREPARE_FAILURE_COMMAND, PREPARE_SUCCESS_COMMAND, SEND_COMMAND_TO_PF300, CHECK_COMMAND_FROM_PF300, END_OF_CALIBRATION, } type_enLowFlowCalibrationStep; typedef enum { INIT_PRESSURE_CALIB_TEST=0, START_PRESSURE_READING, SEND_PRESSURE_COMMAND, READ_PRESSURE_VALUE, PRESSURE_CALIB_FAILURE_COMMAND, PRESSURE_CALIB_SUCCESS_COMMAND, PRESSURE_CALIB_SEND_COMMAND_TO_PF300, PRESSURE_CALIB_CHECK_COMMAND_FROM_PF300, PRESSURE_CALIB_END_OF_CALIBRATION, } type_enPressureCalibrationStep; #define SIZE_BUFFER_FLOW_VALUE 15 typedef struct { unsigned char ucIndex; bool bFirstPartOfTheCRReceived; char szValue[SIZE_BUFFER_FLOW_VALUE]; } type_stFlow; // Flow and pressure calibration #define TIME_OUT_RECEPTION_PACKET_FROM_PF300 2000 // 2s #define SAMPLE_RATE_BETWEEN_TWO_FLOW 5000 // 5s #define SIZE_RX_BUFFER_PF300 20 static u16 uiCalibrationTimeOut; static unsigned char ucIndexAnswerFromPF300; static char szAnswerFromPF300[SIZE_RX_BUFFER_PF300]; static char szExpectedAnswerFromPF300[SIZE_RX_BUFFER_PF300]; static u8 ucNumberOfBytesToCheckFromPF300Answer; // Flow calibration static type_enLowFlowCalibrationStep enLowFlowCalibrationStep; static type_enLowFlowCalibrationStep enGoBackToStep; static unsigned long ulSumFlowTicks; static unsigned int uiFlowTicksSamplesCounter; //static type_stLUTFlowSensor stTemporayLUTFlowSensor; static bool bHighFlowCalibrationInProgress; static unsigned int uiMotorDutyCyleForFlowCalib; static unsigned int uiImHereMsgTimer; // Pressure calibration #define SAMPLE_RATE_BETWEEN_TWO_PRESSURE 10000 // 30s static type_enPressureCalibrationStep enPressureCalibrationStep; static type_enPressureCalibrationStep enPressureCalibGoBackToStep; static u32 ulSumPressureTicks; static u16 uiPressureTicksSamplesCounter; // List of command to PF300 static const char szPF300_CmdSwitchOffEcho[]={"%CM#5$0\r"}; // Command Echo from PF300 off static const char szPF300_AnswerSwitchOffEcho[]={"%CM#5"}; // Answer echo off from PF300 (WO \r) static const char szPF300_SetAirGasType[]={"%WS#1$0\r"}; // command Air static const char szPF300_AnswerAirGasType[]={"%WS#1$0"}; // Answer Air static const char szPF300_SetGasStandard[]={"%WS#3$1\r"}; // Command STPD static const char szPF300_AnswerGasStandard[]={"%WS#3$1"}; // Answer STPD static const char szPF300_ReadLowFlowCmd[]={"%RM#1\r"}; // Command read low flow static const char szPF300_ReadLowFlowAnswer[]={"%RM#1$"}; // Answer read low flow static const char szPF300_ReadHighFlowCmd[]={"%RM#0\r"}; // Command read high flow static const char szPF300_ReadHighFlowAnswer[]={"%RM#0$"}; // Answer read high flow static const char szPF300_ReadPdiffCmd[]={"%RM#3\r"}; // Command read pressure diff. static const char szPF300_ReadPdiffAnswer[]={"%RM#3$"}; // Answewr read pressure diff. // List of Command to Handset static const char szRequestHighFlowCalib[]={"@N"}; static const char szEndOfCalibOK[]={"@S"}; static const char szImHere[]={"@*"}; // ---------------- Management FRAM for settings ----------------------------- #ifdef RECORD_SETTINGS #endif // RECORD_SETTINGS // -------------------- Manage Motor starting -------------------------------- #ifndef TEMPERATURE_TRENDS static u8 ucStartVentilationScheduler=0; #endif static enMaroubraModes enPreviousMode; static u16 uiPreviousDeviceMode; #endif // C_M3_DEVICETEST_TARGET #ifndef C_M3_DEVICETEST_TARGET #define SIZE_SLOPE2_TABLE 21 const unsigned char ucSlope2Table[SIZE_SLOPE2_TABLE]= { 0, 35, 50, 59, 65, 70, 74, 77, 80, 83, 85, 87, 89, 91, 92, 94, 95, 96, 98, 99, 100 }; // Table pente inspiratoire 2 #define SIZE_SLOPE3_TABLE 41 const unsigned char ucSlope3Table[SIZE_SLOPE3_TABLE]= { 0, 20, 35, 44, 50, 55, 59, 62, 65, 68, 70, 72, 74, 76, 77, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 90, 91, 91, 92, 93, 94, 94, 95, 96, 96, 97, 98, 98, 99, 99, 100 }; // Table pente inspiratoire 3 #define SIZE_SLOPE4_TABLE 61 const unsigned char ucSlope4Table[SIZE_SLOPE4_TABLE]= { 0, 11, 26, 35, 41, 46, 50, 53, 56, 59, 61, 63, 65, 67, 68, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 88, 89 ,90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 95, 95, 96, 96, 96, 97, 97, 98, 98, 99, 99, 99, 100, 100 }; // Maximum allowed flow in expiration Flow=f(PEEP set) (example: flow-by max=29L/min @ 5hPa) const u16 uiMaximumFlowInExpiration[41]={ 0, 1300, 1830, 2250, 2600, 2900, 3180, 3430, 3670, 3900, 4110, 4310, 4500, 4680, 4860, 5030, 5200, 5360, 5510, 5660, 5810, 5950, 6090, 6230, 6360, 6500, 6620, 6750, 6870, 7000, 7120, 7230, 7350, 7460, 7580, 7690, 7800, 7900, 8010, 8110, 8220 }; #endif // C_M3_DEVICETEST_TARGET /* Internal functions --------------------------------------------------------*/ #ifndef C_M3_DEVICETEST_TARGET u16 ComputeTe(u16 uiF, u16 uiTi); void ComputeInspiratoryFlowSetPointInAVC(u16 uiTypeOfPatient, u16 uiVtc, u16 uiTi, u16 uiFlowShape, u32 *ulMaxFlow, u32 *ulMinFlow, u32 *ulFlowStep); unsigned char TestTriggerExpiratoire(unsigned char ucValeurSeuil, unsigned int uiPressureSetting); bool TestInspiratoryTrigger(bool bSpont, u16 uiTe, u16 uiFlowThreshold); int16_t UpdateInspiratoryFlowAverage(void); int16_t UpdateInspiratoryConductanceAverage(void); void ResetInspiratoryConductanceAverage(void); bool TestInspiratorySlowTrigger(int16_t iTheFlowBaseLine, u16 uiFlowTreshold); void ApplyDefaultValueToTemporaryVentilationSettings(void); void ApplyDefaultValueToTechnicalSettings(void); void ApplyAllDefaultValues(void); bool ApplyNewVentilationMode(void); void ApplyDefaultFlowLUT(void); void CheckFlowLUTRange(void); void ManageFlowCalibration(void); void ManagePressureCalibration(void); void ManageCstPressure(void); #endif // C_M3_DEVICETEST_TARGET #ifndef C_M3_DEVICETEST_TARGET /******************************************************************************* * Function Name : InitSettings * Description : Initialize parameters for ventilation * Input : None * Output : None * Return : None *******************************************************************************/ void InitSettings(void) { uiPreviousDeviceMode=0xFF; enPreviousMode=NUMBER_OF_MODE; // --- Init Settings uiTechnicalDataMes[SW_VERSION_TEC_MES]=MAROUBRA_BLOWER_SW_VERSION; bComputeOffsetSensors=FALSE; #ifdef RECORD_SETTINGS // Read and init FRAM if (InitSettingsZoneAndTechnicalZoneInFRAM()==OPSTATUS_OK) { if (ReadSettingsZoneAndTechnicalZoneInFRAM()==OPSTATUS_FAIL) { uiFlagsAlarm[ALARM_FLAGS2]|=FRAM_FAILURE_ALARM_MASK; ApplyAllDefaultValues(); } } else { uiFlagsAlarm[ALARM_FLAGS2]|=FRAM_FAILURE_ALARM_MASK; ApplyAllDefaultValues(); } #else ApplyAllDefaultValues(); #endif // Compute offsets sensors if necessary if (bComputeOffsetSensors==TRUE) { // ------ Update Blower Flow sensor offset before ventilation ------- #ifdef SDP600_USED_I2C1_BUS SDP600_ComputeOffsetFlowSensorOnI2C1(SDP600_BLOWER_FLOW_SENSOR); #endif // SDP600_USED_I2C1_BUS #ifdef X201641_USED_I2C1_BUS X201641_ComputeOffsetFlowSensorOnI2C1(X201641_BLOWER_FLOW_SENSOR); #endif // X201641_USED_I2C1_BUS // ----- Update Proximal Pressure sensor offset before ventilation ------- ComputeOffsetProximalPressureSensor(); } // Check Technical settings range CheckTechnicalSettingsRange(); // Check Flow LUT CheckFlowLUTRange(); // Check temporary ventilation setting range if (CheckTemporaryVentilationSettingRange()==OPSTATUS_FAIL) { ApplyDefaultValueToTemporaryVentilationSettings(); uiFlagsAlarm[ALARM_FLAGS2]|=VENTILATION_SETTINGS_RANGE_ALARM_MASK; } // --- Update computed settings (Te,...) UpdateSettings(); #ifdef MOTOR_LIFE_TESTING uiTechnicalDataSet[DEVICE_MODE_TEC]=PRESSURE_CST_MODE; uiTechnicalDataSet[START_STOP_VENTILATION]=1; #endif } /******************************************************************************* * Function Name : InitVentilation * Description : Initialize variables for ventilation * Function called : * - when we start ventilation * - when we change of ventilation mode * - when we change of device mode * Input : None * Output : None * Return : None *******************************************************************************/ void InitVentilation(void) { ucVentilationCycle=EXPIRATION_CYCLE; uiTiD=0; uiTeD=0; uiTiTyp = 1000; bManualBreath=FALSE; if (enPreviousMode==NUMBER_OF_MODE) { // Start ventilation uiDisconnectionTime=EXPIRATORY_DISCONNECTION_TIME_OUT; uiDisconnectionTimeInCPAP=CPAP_DISCONNECTION_TEST_PERIODICITY; uiMemoPWMDuringDisconnection=0; fBlockCloseLoop=FALSE; } ClearAllVentilationAlarms(); ClearAllMeasures(); switch(uiTechnicalDataSet[DEVICE_MODE_TEC]) { default: case VENTILATION_MODE: { if (enVentilationMode==APCV_MODE || enVentilationMode==PS_MODE) { if (enPreviousMode==APCV_MODE || enPreviousMode==PS_MODE) { // --- We are already in pressure mode } else { // --- Start a pressure mode fFirstCycleInspi=TRUE; uiPWMatTheEndOfInspiration=MIN_VOLTAGE_REF; if (enPreviousMode!=AVC_MODE) { uiPWMatTheEndOfExpiration=MIN_VOLTAGE_REF; lIntegral_Pressure_E=0; bMaxPIOutputPressureE=FALSE; bMinPIOutputPressureE=FALSE; fFirstCycleExpi=TRUE; } } } else if (enVentilationMode==AVC_MODE) { iVtAdjust=0; fFirstCycleInspi=TRUE; uiPWMatTheEndOfInspiration=MIN_VOLTAGE_REF; if (enPreviousMode!=APCV_MODE && enPreviousMode!=PS_MODE) { uiPWMatTheEndOfExpiration=MIN_VOLTAGE_REF; lIntegral_Pressure_E=0; bMaxPIOutputPressureE=FALSE; bMinPIOutputPressureE=FALSE; fFirstCycleExpi=TRUE; } } else if (enVentilationMode==CPAP_MODE) { Ki_Pressure_I=1000; uiPWMatTheEndOfInspiration=MIN_VOLTAGE_REF; lIntegral_Pressure_I=0; bMaxPIOutputPressureI=FALSE; bMinPIOutputPressureI=FALSE; } break; } case ONE_CST_PWM_MODE: { uiInspirationBlowerPWMTec=250; break; } case TWO_CST_PWM_MODE: { uiInspirationBlowerPWMTec=350; uiExpirationBlowerPWMTec=150; break; } case CST_SPEED_MODE: { uiBlowerSpeedSet=15000; uiBlowerSpeedSetting=5000; break; } case FLOW_CAL_MODE: { enLowFlowCalibrationStep=INIT_FLOW_CALIB_TEST; bHighFlowCalibrationInProgress=FALSE; stTemporayLUTFlowSensor.uiLUT_TableSize=0; uiCalibrationTimeOut=(TIME_OUT_BLOWER_HANDSET_COMM*20); // 2s break; } case PRESSURE_CAL_MODE: { enPressureCalibrationStep=INIT_PRESSURE_CALIB_TEST; uiCalibrationTimeOut=(TIME_OUT_BLOWER_HANDSET_COMM*20); // 2s break; } case PRESSURE_CST_MODE: { uiMLT_PressureSetPoint=10; lIntegral_Pressure_I=0; bMaxPIOutputPressureI=FALSE; bMinPIOutputPressureI=FALSE; #ifdef MOTOR_LIFE_TESTING uiMLT_EnableAlarm=5000; // 5s before enabling alarm uiAverageMonitoringMLT=MLT_AVERAGE_MONITORING; ulMLT_SumSpeedMes=0; ulMLT_SumCurrentMes=0; ulMLT_SumTemperatureMes=0; ulMLT_SumBlowerVoltage=0; #endif // #ifdef MOTOR_LIFE_TESTING break; } } enPreviousMode=enVentilationMode; } /******************************************************************************* * Function Name : StopAllActuators * Description : Stop all actuators * Input : None * Output : None * Return : None *******************************************************************************/ void StopAllActuators(void) { // Stop main blower SS_Xclose(mdrv); // Valve OFF SS_Xputdw(act, EV_CTL|FLAG_ACTIONER_OFF); } /******************************************************************************* * Function Name : CalculateHoseDrop * Description : Manage Ti, Te, DAC, Blower, PID * Input : None * Output : None * Return : None *******************************************************************************/ u16 CalculateHoseDrop(void) { u16 hoseDrop = (u16)(10*(iBlowerFlowSmoothingMes/100.0f/60.0f * 0.5f)); return hoseDrop; } /******************************************************************************* * Function Name : ManageCPAPVentilation * Description : Manage Ti, Te, DAC, Blower, PID * Input : None * Output : None * Return : None *******************************************************************************/ void ManageCPAPVentilation(void) { // *************************** UPDATE SETTINGS ******************************* if (ApplyNewVentilationMode()==TRUE) return; // **************************** Cycle detection ****************************** // Test Synchronisation /*if (iBlowerFlowSmoothingMesForTrigger<(-500) && ucVentilationCycle==INSPIRATION_CYCLE) ucVentilationCycle=EXPIRATION_CYCLE; */ int32_t peakP = 2;//(int32_t)uiVentilationSet[PS_CPAP_SET]; // Cycle detection if (ucVentilationCycle==INSPIRATION_CYCLE) { uiTiD++; if(uiTiD < (0.1f * uiTiTyp)) { peakP =(int32_t)( uiVentilationSet[PS_CPAP_SET] * ((uiTiD* 1.0f)/(0.1f * uiTiTyp))); } else if(uiTiD < (1.0f * uiTiTyp)) { peakP = ((int32_t)uiVentilationSet[PS_CPAP_SET] - ((int32_t)(uiVentilationSet[PS_CPAP_SET] * (uiTiD * 1.0f)/(1.1f * uiTiTyp)))) + 20; } else { peakP = 2; } if (uiTiD>5000 || (uiTiD>TiBaroSet_P_MIN && TestTriggerExpiratoire(20, uiVentilationSet[PS_CPAP_SET])==TRUE)) { ucVentilationCycle=EXPIRATION_CYCLE; uiPWMatTheEndOfInspiration = uiInspirationBlowerPWMTec; if(uiTiD < 2000) { uiTiTyp = uiTiD; } else { uiTiTyp = 2000; } uiTeD=0; } } else { uiTeD++; if (uiTeD>TE_SET_MAX) uiTeD=TE_SET_MAX; if (TestInspiratoryTrigger(TRUE, uiTeD, 15)==TRUE) { ucVentilationCycle=INSPIRATION_CYCLE; uiTiD=0; } } // Barometric Mode : error computation ControlPressure((int32_t)(peakP + CalculateHoseDrop())); } /******************************************************************************* * Function Name : ControlPressure * Description : Manage Ti, Te, DAC, Blower, PID * Input : None * Output : None * Return : None *******************************************************************************/ int32_t previousError = 0; int32_t lIntegralSum = 0; void ControlPressure(int32_t setPressure)//pressure in cmH2O*10 { int32_t lPropTerm; int32_t lIntegralTerm; int32_t lDerivativeTerm; int32_t lOutputPressure; int32_t lError = setPressure - uiProximalPressureMes; lPropTerm = (int32_t)uiTechnicalDataSet[KP_CPAP_VENTED_PRESSURE_I] * lError; lIntegralTerm += (int32_t)(uiTechnicalDataSet[KI_CPAP_VENTED_PRESSURE_I] * lError); lDerivativeTerm = (int32_t)(uiTechnicalDataSet[KP_FLOW_I]*(lError - previousError/1000)); previousError = lError; // Close loop in pressure if ((lIntegralTerm + lIntegralSum) > INTEGRAL_MAX) { lIntegralSum = INTEGRAL_MAX; } else if ((lIntegralTerm - lIntegralSum)< INTEGRAL_MIN) { lIntegralSum = INTEGRAL_MIN; } else { lIntegralSum += lIntegralTerm; } lOutputPressure = (lDerivativeTerm>>19) + (lIntegralSum>>16) + (lPropTerm>>13) ; lOutputPressure+=uiPWMatTheEndOfInspiration; // Limit and Update blower PWM if (lOutputPressure>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputPressure<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputPressureI = FALSE; bMinPIOutputPressureI = FALSE; uiInspirationBlowerPWMTec = lOutputPressure; } SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); } /******************************************************************************* * Function Name : DavidCControlPressure * Description : Manage Ti, Te, DAC, Blower, PID * Input : None * Output : None * Return : None *******************************************************************************/ void DavidCControlPressure(int32_t setPressure)//pressure in cmH2O*10 { int32_t lPropTerm; int32_t lIntegralTerm; int32_t lOutputPressure; u16 uiFlowMaxInExpi; int32_t lError = setPressure - uiProximalPressureMes; lIntegralTerm = (int32_t)Ki_Pressure_I * lError; if (Ki_Pressure_I<uiTechnicalDataSet[KI_CPAP_VENTED_PRESSURE_I]) { Ki_Pressure_I++; } // Close loop in pressure if ((lIntegral_Pressure_I>=0) && (lIntegralTerm>=0) && (bMaxPIOutputPressureI==FALSE)) { if ((lIntegral_Pressure_I + lIntegralTerm) <0) lIntegral_Pressure_I = INTEGRAL_MAX; else lIntegral_Pressure_I += lIntegralTerm; } else if ((lIntegral_Pressure_I<=0) && (lIntegralTerm<=0) && (bMinPIOutputPressureI==FALSE)) { if ((lIntegral_Pressure_I + lIntegralTerm)>0) lIntegral_Pressure_I = INTEGRAL_MIN; else lIntegral_Pressure_I += lIntegralTerm; } else if ((lIntegral_Pressure_I<=0) && (lIntegralTerm>=0)) lIntegral_Pressure_I += lIntegralTerm; else if ((lIntegral_Pressure_I>=0) && (lIntegralTerm<=0)) lIntegral_Pressure_I += lIntegralTerm; lPropTerm = (int32_t)uiTechnicalDataSet[KP_CPAP_VENTED_PRESSURE_I] * lError; lOutputPressure = (lIntegral_Pressure_I>>16) + (lPropTerm>>13); lOutputPressure+=uiPWMatTheEndOfInspiration; // Flow limiting uiFlowMaxInExpi=uiMaximumFlowInExpiration[uiVentilationSet[PS_CPAP_SET]/10]; if (lError>5 && iBlowerFlowSmoothingMes>(int16_t)uiFlowMaxInExpi) { // Impossible to maintain the peep (patient disconnection) if (uiDisconnectionTime==0) { if (fBlockCloseLoop==FALSE) uiDisconnectionTimeInCPAP=CPAP_DISCONNECTION_TEST_PERIODICITY; fBlockCloseLoop=TRUE; // Block the close loop if (uiMemoPWMDuringDisconnection!=0) // Apply a fixed PWM value uiInspirationBlowerPWMTec=uiMemoPWMDuringDisconnection; else uiInspirationBlowerPWMTec=MIN_VOLTAGE_REF; } else uiDisconnectionTime--; } else if (iBlowerFlowSmoothingMes<(int16_t)uiFlowMaxInExpi && (lError>(-2) && lError<2)) { // Good pressure : no disconnection uiDisconnectionTime=EXPIRATORY_DISCONNECTION_TIME_OUT; if (fBlockCloseLoop==TRUE) { // The close loop was block before => unblock it fBlockCloseLoop=FALSE; lIntegral_Pressure_I=0; bMaxPIOutputPressureI = FALSE; bMinPIOutputPressureI = FALSE; uiPWMatTheEndOfInspiration=uiInspirationBlowerPWMTec; return; } else if (iBlowerFlowSmoothingMes>=0) { // Record current PWM uiMemoPWMDuringDisconnection=uiInspirationBlowerPWMTec; } } // Unblock the close loop every 4s in case of none recorded PWM value if (fBlockCloseLoop==TRUE && uiMemoPWMDuringDisconnection==0 && uiDisconnectionTimeInCPAP==0) { uiDisconnectionTime=EXPIRATORY_DISCONNECTION_TIME_OUT; fBlockCloseLoop=FALSE; lIntegral_Pressure_I=0; bMaxPIOutputPressureI = FALSE; bMinPIOutputPressureI = FALSE; uiPWMatTheEndOfInspiration=uiInspirationBlowerPWMTec; return; } // Update Time counters if (uiDisconnectionTimeInCPAP!=0) uiDisconnectionTimeInCPAP--; // Update blower PWM if (fBlockCloseLoop==TRUE) { bMaxPIOutputPressureI = TRUE; } else if (lOutputPressure>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputPressure<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputPressureI = FALSE; bMinPIOutputPressureI = FALSE; uiInspirationBlowerPWMTec = lOutputPressure; } SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); } /******************************************************************************* * Function Name : ManageOneCstPWM * Description : Manage Ti, Te, DAC, Blower, PID * Input : None * Output : None * Return : None *******************************************************************************/ void ManageOneCstPWM(void) { // *************************** UPDATE SETTINGS ******************************* if (ApplyNewVentilationMode()==TRUE) return; // Update Blower Speed SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); } /******************************************************************************* * Function Name : Manage2CstPWM * Description : Manage Ti, Te, DAC, Blower, PID * Input : None * Output : None * Return : None *******************************************************************************/ void Manage2CstPWM(void) { if (ucVentilationCycle==INSPIRATION_CYCLE) { // ------------- INSPIRATION ------------ SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); // Inspiratoy time uiTiD++; if (uiTiD>=uiVentilationSet[TI_BARO_SET]) { ucVentilationCycle=EXPIRATION_CYCLE; uiTiD=0; } } else { // ------------- EXPIRATION ------------ // *************************** UPDATE SETTINGS ******************************* if (ApplyNewVentilationMode()==TRUE) return; SS_Xputdw(mdrv, uiExpirationBlowerPWMTec); // Expiratoy time uiTeD++; if (uiTeD>=uiTeBaroSet) { ucVentilationCycle=INSPIRATION_CYCLE; uiTeD=0; } } } /******************************************************************************* * Function Name : ManageCstSpeed * Description : Manage constant speed ventilation * Input : None * Output : None * Return : None *******************************************************************************/ void ManageCstSpeed(void) { int32_t lError; int32_t lPropTerm; int32_t lIntegralTerm; int32_t lOutputSpeed; // *************************** UPDATE SETTINGS ******************************* if (ApplyNewVentilationMode()==TRUE) return; // Update settings if (uiBlowerSpeedSetting<uiBlowerSpeedSet) uiBlowerSpeedSetting+=10; else if (uiBlowerSpeedSetting>uiBlowerSpeedSet) uiBlowerSpeedSetting-=10; uiTiD++; if ((uiTiD%10)==0) { // Barometric Mode : error computation lError=(int32_t)uiBlowerSpeedSetting-(int32_t)uiBlowerSpeedMes; lIntegralTerm = (int32_t)uiTechnicalDataSet[KI_SPEED_I_TEC] * lError; // Close loop in pressure if ((lIntegral_Speed_I>=0) && (lIntegralTerm>=0) && (bMaxPIOutputSpeedI==FALSE)) { if ((lIntegral_Speed_I + lIntegralTerm) <0) lIntegral_Speed_I = INTEGRAL_MAX; else lIntegral_Speed_I += lIntegralTerm; } else if ((lIntegral_Speed_I<=0) && (lIntegralTerm<=0) && (bMinPIOutputSpeedI==FALSE)) { if ((lIntegral_Speed_I + lIntegralTerm)>0) lIntegral_Speed_I = INTEGRAL_MIN; else lIntegral_Speed_I += lIntegralTerm; } else if ((lIntegral_Speed_I<=0) && (lIntegralTerm>=0)) lIntegral_Speed_I += lIntegralTerm; else if ((lIntegral_Speed_I>=0) && (lIntegralTerm<=0)) lIntegral_Speed_I += lIntegralTerm; lPropTerm = (int32_t)uiTechnicalDataSet[KP_SPEED_I_TEC] * lError; lOutputSpeed = (lIntegral_Speed_I>>16) + (lPropTerm>>13); if (lOutputSpeed>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputSpeedI = TRUE; uiInspirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputSpeed<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputSpeedI = TRUE; uiInspirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputSpeedI = FALSE; bMinPIOutputSpeedI = FALSE; uiInspirationBlowerPWMTec = lOutputSpeed; } SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); } } /******************************************************************************* * Function Name : ManageFlowCalibration * Description : Manage Low flow calibration * Input : None * Output : None * Return : None *******************************************************************************/ void ManageFlowCalibration(void) { char cCharValue; char szTempBuf[SIZE_RX_BUFFER_PF300]; /* #ifdef USE_TSI_4040 switch(enTSICommScheduler) { default: case TSI_SET_TYPE_OF_GAS: { // Send Type of Gas command ucTSICommand=TSI_SEND_GAS_TYPE_AIR_CMD; if (SS_Xputdw(tsi, &ucTSICommand)==TRUE) { enTSICommScheduler=TSI_CHECK_ANSWER; enTSIGoBackToStep=TSI_SET_FLOW_UNIT; } break; } case TSI_SET_FLOW_UNIT: { // Send Type of Gas command ucTSICommand=TSI_SEND_FLOW_UNIT_STANDARD_CMD; if (SS_Xputdw(tsi, &ucTSICommand)==TRUE) { enTSICommScheduler=TSI_CHECK_ANSWER; enTSIGoBackToStep=TSI_SET_SAMPLE_TIME; } break; } case TSI_SET_SAMPLE_TIME: { // Send sample time ucTSICommand=TSI_SEND_SAMPLE_TIME_10MS_CMD; if (SS_Xputdw(tsi, &ucTSICommand)==TRUE) { enTSICommScheduler=TSI_CHECK_ANSWER; enTSIGoBackToStep=TSI_READ_FLOW; } break; } case TSI_READ_FLOW: { // Send sample time ucTSICommand=TSI_SEND_READ_FLOW_CMD; if (SS_Xputdw(tsi, &ucTSICommand)==TRUE) { enTSICommScheduler=TSI_CHECK_ANSWER; enTSIGoBackToStep=TSI_CYCLE_DETECTION; } break; } case TSI_CYCLE_DETECTION: { uiBlowerFlow=iFlowFromTSI; break; } case TSI_CHECK_ANSWER: { opstatus=SS_Xgetw(tsi, &uiDummy); if (opstatus==OPSTATUS_OK) { enTSICommScheduler=enTSIGoBackToStep; } else if (opstatus==OPSTATUS_FAIL) { enTSICommScheduler=TSI_SET_TYPE_OF_GAS; } break; } } #endif // #ifdef USE_TSI_4040 */ switch(enLowFlowCalibrationStep) { case INIT_FLOW_CALIB_TEST: { // Init comm with PF300 => wait 2s before sending first command to PF300 // We need to wait for this delay in order to the handset to activate its transparent mode if (uiCalibrationTimeOut==0) { bDisableMaroubraCommCommunication=TRUE; // Prepare command to PF300 UpdateBufferToSend((char*)szPF300_CmdSwitchOffEcho); enLowFlowCalibrationStep=SEND_COMMAND_TO_PF300; strcpy(szExpectedAnswerFromPF300, szPF300_AnswerSwitchOffEcho); ucNumberOfBytesToCheckFromPF300Answer=strlen(szPF300_AnswerSwitchOffEcho); enGoBackToStep=SET_GAS_TYPE; } else { uiCalibrationTimeOut--; } break; } case SET_GAS_TYPE: { // Prepare command to PF300 UpdateBufferToSend((char*)szPF300_SetAirGasType); enLowFlowCalibrationStep=SEND_COMMAND_TO_PF300; strcpy(szExpectedAnswerFromPF300, szPF300_AnswerAirGasType); ucNumberOfBytesToCheckFromPF300Answer=strlen(szPF300_AnswerAirGasType); enGoBackToStep=SET_GAS_STANDARD; break; } case SET_GAS_STANDARD: { // Prepare command to PF300 UpdateBufferToSend((char*)szPF300_SetGasStandard); enLowFlowCalibrationStep=SEND_COMMAND_TO_PF300; strcpy(szExpectedAnswerFromPF300, szPF300_AnswerGasStandard); ucNumberOfBytesToCheckFromPF300Answer=strlen(szPF300_AnswerGasStandard); enGoBackToStep=START_FLOW_READING; break; } case START_FLOW_READING: { // Set Min blower speed uiMotorDutyCyleForFlowCalib=MIN_PI_OUTPUT; SS_Xputdw(mdrv, uiMotorDutyCyleForFlowCalib); // Init variable for flow sensor uiCalibrationTimeOut=SAMPLE_RATE_BETWEEN_TWO_FLOW; ulSumFlowTicks=0; uiFlowTicksSamplesCounter=0; // Next step enLowFlowCalibrationStep=SEND_FLOW_COMMAND; break; } case SEND_FLOW_COMMAND: { if (uiCalibrationTimeOut!=0) { uiCalibrationTimeOut--; if (uiCalibrationTimeOut<(SAMPLE_RATE_BETWEEN_TWO_FLOW>>1)) { ulSumFlowTicks+=uiBlowerFlowRAWMes; uiFlowTicksSamplesCounter++; } } else { // Prepare next command (Read Low Flow) enLowFlowCalibrationStep=SEND_COMMAND_TO_PF300; if (bHighFlowCalibrationInProgress==FALSE) { UpdateBufferToSend((char*)szPF300_ReadLowFlowCmd); // %RM#1\r strcpy(szExpectedAnswerFromPF300, szPF300_ReadLowFlowAnswer); // %RM#1$ ucNumberOfBytesToCheckFromPF300Answer=strlen(szPF300_ReadLowFlowAnswer); } else { UpdateBufferToSend((char*)szPF300_ReadHighFlowCmd); // %RM#0\r strcpy(szExpectedAnswerFromPF300, szPF300_ReadHighFlowAnswer); // %RM#0$ ucNumberOfBytesToCheckFromPF300Answer=strlen(szPF300_ReadHighFlowAnswer); } enGoBackToStep=READ_FLOW_VALUE; } break; } case READ_FLOW_VALUE: { if (uiFlowTicksSamplesCounter!=0) { // Read PF300 flow (l/min) strcpy(szTempBuf, &szAnswerFromPF300[ucNumberOfBytesToCheckFromPF300Answer]); // Store value of flow if (stTemporayLUTFlowSensor.uiLUT_TableSize<LOW_FLOW_CALIB_NUMBER_OF_SAMPLES) stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize]=(unsigned int)atoi(szTempBuf); else stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize]=(unsigned int)atoi(szTempBuf)*10; // Store ticks value stTemporayLUTFlowSensor.uiFlowSensorTicks[stTemporayLUTFlowSensor.uiLUT_TableSize]=ulSumFlowTicks/uiFlowTicksSamplesCounter; uiCalibrationTimeOut=SAMPLE_RATE_BETWEEN_TWO_FLOW; ulSumFlowTicks=0; uiFlowTicksSamplesCounter=0; // Check if current flow <0.1l/min if (stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize]<10) { enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; break; } // Check if the last low flow is higher than 20l/min or lower than 2l/min if (stTemporayLUTFlowSensor.uiLUT_TableSize==(LOW_FLOW_CALIB_NUMBER_OF_SAMPLES-1)) { // Check if the last low flow is higher than 20l/min or lower than 2l/min if (stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize]>2000 || stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize]<200) { enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; break; } } // Next flow sample if (stTemporayLUTFlowSensor.uiLUT_TableSize==LOW_FLOW_CALIB_NUMBER_OF_SAMPLES && stTemporayLUTFlowSensor.uiFlowValue[LOW_FLOW_CALIB_NUMBER_OF_SAMPLES]<(stTemporayLUTFlowSensor.uiFlowValue[LOW_FLOW_CALIB_NUMBER_OF_SAMPLES-1]+500)) { // No enough flow margin between the last value of the low flow and the first value of the high flow } else if (stTemporayLUTFlowSensor.uiLUT_TableSize!=0 && stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize]<(stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize-1]+50)) { // No enough margin between two consecutives flow } else if (stTemporayLUTFlowSensor.uiLUT_TableSize!=0 && stTemporayLUTFlowSensor.uiFlowSensorTicks[stTemporayLUTFlowSensor.uiLUT_TableSize]<=stTemporayLUTFlowSensor.uiFlowSensorTicks[stTemporayLUTFlowSensor.uiLUT_TableSize-1]) { // Same or less ticks between two consecutives flow enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; break; } else { stTemporayLUTFlowSensor.uiLUT_TableSize++; if (stTemporayLUTFlowSensor.uiFlowValue[stTemporayLUTFlowSensor.uiLUT_TableSize-1]>=14000) { // End of calib => check the number of samples if (stTemporayLUTFlowSensor.uiLUT_TableSize>(FLOW_CALIB_NUMBER_OF_SAMPLES>>1)) { enLowFlowCalibrationStep=PREPARE_SUCCESS_COMMAND; // Transfer the temporary LUT in the definitive LUT stLUTFlowSensor=stTemporayLUTFlowSensor; } else { enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; } break; } else if (stTemporayLUTFlowSensor.uiLUT_TableSize>FLOW_CALIB_NUMBER_OF_SAMPLES) { enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; break; } } // Prepare next flow reading if (stTemporayLUTFlowSensor.uiLUT_TableSize<LOW_FLOW_CALIB_NUMBER_OF_SAMPLES) { // New motor speed uiMotorDutyCyleForFlowCalib+=20; enLowFlowCalibrationStep=SEND_FLOW_COMMAND; } else if (stTemporayLUTFlowSensor.uiLUT_TableSize==LOW_FLOW_CALIB_NUMBER_OF_SAMPLES && bHighFlowCalibrationInProgress==FALSE) { // Min speed for motor uiMotorDutyCyleForFlowCalib=MIN_PI_OUTPUT; SS_Xputdw(mdrv, uiMotorDutyCyleForFlowCalib); enLowFlowCalibrationStep=SEND_HIGH_FLOW_CALIB_REQUEST; break; } else { // New motor speed uiMotorDutyCyleForFlowCalib+=10; enLowFlowCalibrationStep=SEND_FLOW_COMMAND; } // Update motor speed if (uiMotorDutyCyleForFlowCalib<MAX_PI_OUTPUT) { SS_Xputdw(mdrv, uiMotorDutyCyleForFlowCalib); } else { enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; break; } } else // if (uiFlowTicksSamplesCounter!=0) { enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; } break; } case SEND_HIGH_FLOW_CALIB_REQUEST: { if (TransferCommandToHandset((char*)szRequestHighFlowCalib)==TRUE) enLowFlowCalibrationStep=WAIT_FOR_HIGH_FLOW_CALIB_ACK; break; } case WAIT_FOR_HIGH_FLOW_CALIB_ACK: { // Wait for acknownledge from handset if (ReadByteReceive(&cCharValue)==TRUE) { if (cCharValue=='@') { // Continue flow calibration bHighFlowCalibrationInProgress=TRUE; enLowFlowCalibrationStep=SEND_FLOW_COMMAND; } else enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; } break; } case PREPARE_FAILURE_COMMAND: { if (TransferCommandToHandset((char*)szEndOfCalibFailed)==TRUE) enLowFlowCalibrationStep=END_OF_CALIBRATION; break; } case PREPARE_SUCCESS_COMMAND: { if (TransferCommandToHandset((char*)szEndOfCalibOK)==TRUE) enLowFlowCalibrationStep=END_OF_CALIBRATION; break; } case SEND_COMMAND_TO_PF300: { // Send a byte every 1ms if (TransferBufferToPF300()==TRUE) { // Buffer sent !! if (ucNumberOfBytesToCheckFromPF300Answer!=0) { // Check answer from PF300 ucIndexAnswerFromPF300=0; uiCalibrationTimeOut=TIME_OUT_RECEPTION_PACKET_FROM_PF300; enLowFlowCalibrationStep=CHECK_COMMAND_FROM_PF300; } else enLowFlowCalibrationStep=enGoBackToStep; } break; } case CHECK_COMMAND_FROM_PF300: { if (uiCalibrationTimeOut==0) { // Time-out in reception !! ucIndexAnswerFromPF300=0; enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; } else if (ReadByteReceive(&cCharValue)==TRUE) { // Check last byte received if (cCharValue=='\r') { // End of answer => check buffer content ucIndexAnswerFromPF300=0; strcpy(szTempBuf, szAnswerFromPF300); szTempBuf[ucNumberOfBytesToCheckFromPF300Answer]=0; if (strcmp(szTempBuf, szExpectedAnswerFromPF300)==0) { // Buffer OK !! enLowFlowCalibrationStep=enGoBackToStep; } else { // Buffer wrong !! enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; } } else if (cCharValue=='?') { // PF300 is lost => end of calibration ucIndexAnswerFromPF300=0; enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; } else { // Store byte from PF300 into buffer szAnswerFromPF300[ucIndexAnswerFromPF300++]=cCharValue; if (ucIndexAnswerFromPF300>=SIZE_RX_BUFFER_PF300) { // Answer too long !! ucIndexAnswerFromPF300=0; enLowFlowCalibrationStep=PREPARE_FAILURE_COMMAND; } else { // Add 0 at the end of the string szAnswerFromPF300[ucIndexAnswerFromPF300]=0; } } } else { // No byte received !! uiCalibrationTimeOut--; } break; } default: case END_OF_CALIBRATION: { uiTechnicalDataSet[START_STOP_VENTILATION]=0; bDisableMaroubraCommCommunication=FALSE; break; } } // switch // Every 300ms, send '@*' to inform the handset that the blower works properly if (uiImHereMsgTimer==0) { if (enLowFlowCalibrationStep!=SEND_COMMAND_TO_PF300) { uiImHereMsgTimer=300; TransferCommandToHandset((char*)szImHere); } } else uiImHereMsgTimer--; } /******************************************************************************* * Function Name : ManagePressureCalibration * Description : Manage pressure calibration * Input : None * Output : None * Return : None *******************************************************************************/ void ManagePressureCalibration(void) { char cCharValue; char szTempBuf[SIZE_RX_BUFFER_PF300]; u16 uiCalibPressureADCValue; u16 uiCalibPressureValue; u16 uiCalibPressureGain; switch(enPressureCalibrationStep) { case INIT_PRESSURE_CALIB_TEST: { // Init comm with PF300 => wait 2s before sending first command to PF300 // We need to wait for this delay in order to the handset to activate its transparent mode if (uiCalibrationTimeOut==0) { bDisableMaroubraCommCommunication=TRUE; // Prepare command to PF300 UpdateBufferToSend((char*)szPF300_CmdSwitchOffEcho); strcpy(szExpectedAnswerFromPF300, szPF300_AnswerSwitchOffEcho); ucNumberOfBytesToCheckFromPF300Answer=strlen(szPF300_AnswerSwitchOffEcho); enPressureCalibrationStep=PRESSURE_CALIB_SEND_COMMAND_TO_PF300; enPressureCalibGoBackToStep=START_PRESSURE_READING; } else { uiCalibrationTimeOut--; } break; } case START_PRESSURE_READING: { // Set blower speed SS_Xputdw(mdrv, 600); // Init variable for flow sensor uiCalibrationTimeOut=SAMPLE_RATE_BETWEEN_TWO_PRESSURE; ulSumPressureTicks=0; uiPressureTicksSamplesCounter=0; enPressureCalibrationStep=SEND_PRESSURE_COMMAND; break; } case SEND_PRESSURE_COMMAND: { if (uiCalibrationTimeOut!=0) { uiCalibrationTimeOut--; if (uiCalibrationTimeOut<2000) { ulSumPressureTicks+=uiProximalPressureADCMes; uiPressureTicksSamplesCounter++; } } else { // Prepare next command UpdateBufferToSend((char*)szPF300_ReadPdiffCmd); strcpy(szExpectedAnswerFromPF300, szPF300_ReadPdiffAnswer); ucNumberOfBytesToCheckFromPF300Answer=strlen(szPF300_ReadPdiffAnswer); enPressureCalibrationStep=PRESSURE_CALIB_SEND_COMMAND_TO_PF300; enPressureCalibGoBackToStep=READ_PRESSURE_VALUE; } break; } case READ_PRESSURE_VALUE: { if (uiPressureTicksSamplesCounter!=0) { // Read PF300 pressure strcpy(szTempBuf, &szAnswerFromPF300[ucNumberOfBytesToCheckFromPF300Answer]); // Store value of Pressure uiCalibPressureValue=(unsigned int)atoi(szTempBuf); // Store ticks value uiCalibPressureADCValue=ulSumPressureTicks/uiPressureTicksSamplesCounter; uiCalibrationTimeOut=SAMPLE_RATE_BETWEEN_TWO_PRESSURE; ulSumPressureTicks=0; uiPressureTicksSamplesCounter=0; // Check if current pressure<10cmH20 if (uiCalibPressureValue<1000) { enPressureCalibrationStep=PRESSURE_CALIB_FAILURE_COMMAND; break; } // Compute pressure gain uiCalibPressureGain=(10000UL*(uiCalibPressureADCValue-uiTechnicalDataSet[PPROX_OFFSET_TEC]))/uiCalibPressureValue; uiCalibPressureGain+=5; uiCalibPressureGain/=10; // Check pressure gain range if (uiCalibPressureGain<Tec_GainPprox_MIN || uiCalibPressureGain>Tec_GainPprox_MAX) { enPressureCalibrationStep=PRESSURE_CALIB_FAILURE_COMMAND; } else { enPressureCalibrationStep=PRESSURE_CALIB_SUCCESS_COMMAND; // Transfer the temporary LUT in the definitive LUT uiTechnicalDataSet[PPROX_GAIN_TEC]=uiCalibPressureGain; } } else // if (uiPressureTicksSamplesCounter!=0) { enPressureCalibrationStep=PRESSURE_CALIB_FAILURE_COMMAND; } break; } case PRESSURE_CALIB_FAILURE_COMMAND: { if (TransferCommandToHandset((char*)szEndOfCalibFailed)==TRUE) enPressureCalibrationStep=PRESSURE_CALIB_END_OF_CALIBRATION; break; } case PRESSURE_CALIB_SUCCESS_COMMAND: { if (TransferCommandToHandset((char*)szEndOfCalibOK)==TRUE) enPressureCalibrationStep=PRESSURE_CALIB_END_OF_CALIBRATION; break; } case PRESSURE_CALIB_SEND_COMMAND_TO_PF300: { // Send a byte every 1ms if (TransferBufferToPF300()==TRUE) { // Buffer sent !! if (ucNumberOfBytesToCheckFromPF300Answer!=0) { // Check answer from PF300 ucIndexAnswerFromPF300=0; uiCalibrationTimeOut=TIME_OUT_RECEPTION_PACKET_FROM_PF300; enPressureCalibrationStep=PRESSURE_CALIB_CHECK_COMMAND_FROM_PF300; } else enPressureCalibrationStep=enPressureCalibGoBackToStep; } break; } case PRESSURE_CALIB_CHECK_COMMAND_FROM_PF300: { if (uiCalibrationTimeOut==0) { // Time-out in reception !! ucIndexAnswerFromPF300=0; enPressureCalibrationStep=PRESSURE_CALIB_FAILURE_COMMAND; } else if (ReadByteReceive(&cCharValue)==TRUE) { // Check last byte received if (cCharValue=='\r') { // End of answer => check buffer content ucIndexAnswerFromPF300=0; strcpy(szTempBuf, szAnswerFromPF300); szTempBuf[ucNumberOfBytesToCheckFromPF300Answer]=0; if (strcmp(szTempBuf, szExpectedAnswerFromPF300)==0) { // Buffer OK !! enPressureCalibrationStep=enPressureCalibGoBackToStep; } else { // Buffer wrong !! enPressureCalibrationStep=PRESSURE_CALIB_FAILURE_COMMAND; } } else if (cCharValue=='?') { // PF300 is lost => end of calibration ucIndexAnswerFromPF300=0; enPressureCalibrationStep=PRESSURE_CALIB_FAILURE_COMMAND; } else { // Store byte from PF300 into buffer szAnswerFromPF300[ucIndexAnswerFromPF300++]=cCharValue; if (ucIndexAnswerFromPF300>=SIZE_RX_BUFFER_PF300) { // Answer too long !! ucIndexAnswerFromPF300=0; enPressureCalibrationStep=PRESSURE_CALIB_FAILURE_COMMAND; } else { // Add 0 at the end of the string szAnswerFromPF300[ucIndexAnswerFromPF300]=0; } } } else { // No byte received !! uiCalibrationTimeOut--; } break; } default: case PRESSURE_CALIB_END_OF_CALIBRATION: { uiTechnicalDataSet[START_STOP_VENTILATION]=0; bDisableMaroubraCommCommunication=FALSE; break; } } // switch // Every 300ms, send '@*' to inform the handset that the blower works properly if (uiImHereMsgTimer==0) { if (enPressureCalibrationStep!=PRESSURE_CALIB_SEND_COMMAND_TO_PF300) { uiImHereMsgTimer=300; TransferCommandToHandset((char*)szImHere); } } else uiImHereMsgTimer--; } /******************************************************************************* * Function Name : ManageCstPressure * Description : Manage constant pressure * Input : None * Output : None * Return : None *******************************************************************************/ void ManageCstPressure(void) { int32_t lError; int32_t lPropTerm; int32_t lIntegralTerm; int32_t lOutputPressure; // Barometric Mode : error computation lError=(int32_t)uiMLT_PressureSetPoint-(int32_t)uiProximalPressureMes; lIntegralTerm = (int32_t)uiTechnicalDataSet[KI_CPAP_VENTED_PRESSURE_I] * lError; if (uiMLT_PressureSetPoint<uiVentilationSet[PI_SET]) uiMLT_PressureSetPoint++; // Close loop in pressure if ((lIntegral_Pressure_I>=0) && (lIntegralTerm>=0) && (bMaxPIOutputPressureI==FALSE)) { if ((lIntegral_Pressure_I + lIntegralTerm) <0) lIntegral_Pressure_I = INTEGRAL_MAX; else lIntegral_Pressure_I += lIntegralTerm; } else if ((lIntegral_Pressure_I<=0) && (lIntegralTerm<=0) && (bMinPIOutputPressureI==FALSE)) { if ((lIntegral_Pressure_I + lIntegralTerm)>0) lIntegral_Pressure_I = INTEGRAL_MIN; else lIntegral_Pressure_I += lIntegralTerm; } else if ((lIntegral_Pressure_I<=0) && (lIntegralTerm>=0)) lIntegral_Pressure_I += lIntegralTerm; else if ((lIntegral_Pressure_I>=0) && (lIntegralTerm<=0)) lIntegral_Pressure_I += lIntegralTerm; lPropTerm = (int32_t)uiTechnicalDataSet[KP_CPAP_VENTED_PRESSURE_I] * lError; lOutputPressure = (lIntegral_Pressure_I>>16) + (lPropTerm>>13); // Add constante value (PWM at the end of the previous inspiration) //lOutputPressure+=300; if (lOutputPressure>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputPressure<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputPressureI = FALSE; bMinPIOutputPressureI = FALSE; uiInspirationBlowerPWMTec = lOutputPressure; } // Update Blower Speed SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); // Monitoring and Alarm (every 1s) #ifdef MOTOR_LIFE_TESTING if (uiAverageMonitoringMLT==0) { uiAverageBlowerSpeedMes=ulMLT_SumSpeedMes/MLT_AVERAGE_MONITORING; uiBreathBlowerCurrentMes=ulMLT_SumCurrentMes/MLT_AVERAGE_MONITORING; uiAverateMotorTempMes=ulMLT_SumTemperatureMes/MLT_AVERAGE_MONITORING; uiAverageMotorVoltage=ulMLT_SumBlowerVoltage/MLT_AVERAGE_MONITORING; ulMLT_SumSpeedMes=0; ulMLT_SumCurrentMes=0; ulMLT_SumTemperatureMes=0; ulMLT_SumBlowerVoltage=0; uiAverageMonitoringMLT=MLT_AVERAGE_MONITORING; } else { ulMLT_SumSpeedMes+=uiBlowerSpeedMes; ulMLT_SumCurrentMes+=uiBlowerCurrentMes; ulMLT_SumTemperatureMes+=uiTechnicalDataMes[MOTOR_TEMPERATURE_TEC_MES]; ulMLT_SumBlowerVoltage+=uiTechnicalDataMes[MOTOR_VOLTAGE_TEC_MES]; uiAverageMonitoringMLT--; } // Alarms management if (uiMLT_EnableAlarm==0) { // High Speed alarm if (uiAverageBlowerSpeedMes>uiTechnicalDataSet[MLT_HIGH_SPEED_TEC]) uiFlagsAlarm[ALARM_FLAGS2]|=HIGH_BLOWER_SPEED_ALARM_MASK; // Low Speed alarm if (uiAverageBlowerSpeedMes<uiTechnicalDataSet[MLT_LOW_SPEED_TEC]) uiFlagsAlarm[ALARM_FLAGS2]|=LOW_BLOWER_SPEED_ALARM_MASK; // High blower current alarm if (uiBreathBlowerCurrentMes>uiTechnicalDataSet[MLT_HIGH_CURRENT_TEC]) uiFlagsAlarm[ALARM_FLAGS2]|=HIGH_BLOWER_CURRENT_ALARM_MASK; // High Blower temperature if (uiAverateMotorTempMes>uiTechnicalDataSet[MLT_HIGH_TEMPERATURE_TEC]) uiFlagsAlarm[ALARM_FLAGS2]|=HIGH_BLOWER_TEMP_ALARM_MASK; // Low Blower temperature if (uiAverateMotorTempMes<100) uiFlagsAlarm[ALARM_FLAGS2]|=LOW_BLOWER_TEMP_ALARM_MASK; // High Blower Flow if (iBlowerFlowSmoothingMesForTrigger>uiTechnicalDataSet[MLT_HIGH_FLOW_TEC] || iBlowerFlowSmoothingMesForTrigger<uiTechnicalDataSet[MLT_LOW_FLOW_TEC]) uiFlagsAlarm[ALARM_FLAGS2]|=OOR_BLOWER_FLOW_ALARM_MASK; } else uiMLT_EnableAlarm--; #endif // MOTOR_LIFE_TESTING } /******************************************************************************* * Function Name : ManagePACVVentilation * Description : Manage Bilevel ventilation * Input : bPSMode=true in PS mode, false in PI mode * Output : None * Return : None *******************************************************************************/ void ManagePACVVentilation(bool bPSMode) { int32_t lError; int32_t lPropTerm; int32_t lIntegralTerm; int32_t lOutputPressure; u16 uiPressureSetPoint; u16 uiTiMax; u16 uiTiMin; //u16 uiTeMax; u16 uiFlowMaxInExpi; if (ucVentilationCycle==INSPIRATION_CYCLE) { // --------------------------------------------------------------------------- // - INSPIRATION - // -------------------------------------------------------------------------- // Ti et pressure set point management if (bPSMode==TRUE) { uiPressureSetPoint=uiVentilationSet[PS_SET]; uiTiMax=uiVentilationSet[TI_MAX_SET]; } else { uiPressureSetPoint=uiVentilationSet[PI_SET]; uiTiMax=uiVentilationSet[TI_BARO_SET]; } // Compute Timin if (uiPatientType==PATIENT_ADULT) uiTiMin=TiBaroSet_A_MIN; else uiTiMin=TiBaroSet_P_MIN; // Limit the pressure setting if (uiVentilationSet[HIGH_PRESSURE_ALARM_SET]<uiPressureSetPoint) uiPressureSetPoint=uiVentilationSet[HIGH_PRESSURE_ALARM_SET]; if (uiTiD==0) { // Management Expiratory Trigger ucTempoTriggerExpiAuto=TEMPO_TRIGGER_EXPI; ucExpiTriggerTreshold=MIN_THRESHOLD_EXPI_TRIGGER_AUTO; // Management Inspiratory slope switch(uiVentilationSet[SLOPE_BARO_SET]) { default: case 1: { // Slope of 5ms/hPa => 0.2hPa/ms uiIpapSettingTemp=uiVentilationSet[PEEP_SET]+2; break; } case 2: { ucIndexTableSlope=0; uiIpapSettingTemp=(uiPressureSetPoint*ucSlope2Table[ucIndexTableSlope++])/100; break; } case 3: { ucIndexTableSlope=0; uiIpapSettingTemp=(uiPressureSetPoint*ucSlope3Table[ucIndexTableSlope++])/100; break; } case 4: { ucIndexTableSlope=0; uiIpapSettingTemp=(uiPressureSetPoint*ucSlope4Table[ucIndexTableSlope++])/100; break; } } } else { // Management Inspiratory slope switch(uiVentilationSet[SLOPE_BARO_SET]) { default: case 1: { // Slope of 5ms/hPa => 0.2hPa/ms uiIpapSettingTemp+=2; if (uiIpapSettingTemp>uiPressureSetPoint) uiIpapSettingTemp=uiPressureSetPoint; break; } case 2: { if (ucIndexTableSlope<SIZE_SLOPE2_TABLE && (uiTiD%10)==0) uiIpapSettingTemp=(uiPressureSetPoint*ucSlope2Table[ucIndexTableSlope++])/100; break; } case 3: { if (ucIndexTableSlope<SIZE_SLOPE3_TABLE && (uiTiD%10)==0) uiIpapSettingTemp=(uiPressureSetPoint*ucSlope3Table[ucIndexTableSlope++])/100; break; } case 4: { if (ucIndexTableSlope<SIZE_SLOPE4_TABLE && (uiTiD%10)==0) uiIpapSettingTemp=(uiPressureSetPoint*ucSlope4Table[ucIndexTableSlope++])/100; break; } } } // Barometric Mode : error computation lError=(int32_t)uiIpapSettingTemp-(int32_t)uiProximalPressureMes; if (fFirstCycleInspi==FALSE) lIntegralTerm = (int32_t)uiTechnicalDataSet[KI_VENTED_PRESSURE_I] * lError; else { lIntegralTerm = (int32_t)(uiTechnicalDataSet[KI_VENTED_PRESSURE_I]>>2) * lError; uiPWMatTheEndOfInspiration=uiExpirationBlowerPWMTec; } // Close loop in pressure if ((lIntegral_Pressure_I>=0) && (lIntegralTerm>=0) && (bMaxPIOutputPressureI==FALSE)) { if ((lIntegral_Pressure_I + lIntegralTerm) <0) lIntegral_Pressure_I = INTEGRAL_MAX; else lIntegral_Pressure_I += lIntegralTerm; } else if ((lIntegral_Pressure_I<=0) && (lIntegralTerm<=0) && (bMinPIOutputPressureI==FALSE)) { if ((lIntegral_Pressure_I + lIntegralTerm)>0) lIntegral_Pressure_I = INTEGRAL_MIN; else lIntegral_Pressure_I += lIntegralTerm; } else if ((lIntegral_Pressure_I<=0) && (lIntegralTerm>=0)) lIntegral_Pressure_I += lIntegralTerm; else if ((lIntegral_Pressure_I>=0) && (lIntegralTerm<=0)) lIntegral_Pressure_I += lIntegralTerm; lPropTerm = (int32_t)uiTechnicalDataSet[KP_VENTED_PRESSURE_I] * lError; lOutputPressure = (lIntegral_Pressure_I>>16) + (lPropTerm>>13); // Add constante value (PWM at the end of the previous inspiration) lOutputPressure+=uiPWMatTheEndOfInspiration; if (fAlarmPmax==TRUE || fAlarmVtiMax==TRUE) { bMaxPIOutputPressureI = TRUE; } else if (lOutputPressure>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputPressure<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputPressureI = TRUE; uiInspirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputPressureI = FALSE; bMinPIOutputPressureI = FALSE; uiInspirationBlowerPWMTec = lOutputPressure; } // Update Blower Speed SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); // ************************ CYCLE MANAGEMENT ********************************* uiTiD++; if ((uiTiD>=uiTiMax) || (TestTriggerExpiratoire(uiVentilationSet[TRIG_E_SET], uiVentilationSet[PS_SET])==TRUE && uiTiD>uiVentilationSet[TI_MIN_SET] && bPSMode==TRUE) || (fAlarmPmax==TRUE && uiTiD>uiTiMin) || (fAlarmVtiMax==TRUE && uiTiD>uiTiMin)) { // Beginning of the expiration if (fAlarmPmax==FALSE && fAlarmVtiMax==FALSE) { fFirstCycleInspi=FALSE; uiPWMatTheEndOfInspiration=uiInspirationBlowerPWMTec; } else { fFirstCycleInspi=TRUE; uiPWMatTheEndOfInspiration=MIN_VOLTAGE_REF; } ucVentilationCycle=EXPIRATION_CYCLE; uiTiD=0; // Reset PI variables for next inspiration lIntegral_Pressure_E=0; bMaxPIOutputPressureE=FALSE; bMinPIOutputPressureE=FALSE; fFirstCycleExpi=FALSE; } } else { // --------------------------------------------------------------------------- // - EXPIRATION - // --------------------------------------------------------------------------- // *************************** UPDATE SETTINGS ******************************* if (ApplyNewVentilationMode()==TRUE) return; // ******************** MAIN BLOWER MANAGEMENT ******************************* if (uiVentilationSet[PEEP_SET]==0) { uiExpirationBlowerPWMTec=MIN_VOLTAGE_REF; SS_Xputdw(mdrv, uiExpirationBlowerPWMTec); } else { if (uiTeD==0) { // First blower current setting uiEpapSettingTemp=uiVentilationSet[PI_SET]-2; // Slope of 5ms/hPa => 0.2hPa/ms } else { // we increase the speed of the blower every 1ms uiEpapSettingTemp-=2; // Slope of 5ms/hPa => 0.2hPa/ms if (uiEpapSettingTemp<uiVentilationSet[PEEP_SET]) uiEpapSettingTemp=uiVentilationSet[PEEP_SET]; } // Barometric Mode : error computation if (fFirstCycleExpi==FALSE) { lError=(int32_t)uiEpapSettingTemp-(int32_t)uiProximalPressureMes; lIntegralTerm = (int32_t)uiTechnicalDataSet[KI_VENTED_PRESSURE_E] * lError; } else { lError=(int32_t)uiVentilationSet[PEEP_SET]-(int32_t)uiProximalPressureMes; lIntegralTerm = (int32_t)(uiTechnicalDataSet[KI_VENTED_PRESSURE_E]>>2) * lError; } // Close loop in pressure if ((lIntegral_Pressure_E>=0) && (lIntegralTerm>=0) && (bMaxPIOutputPressureE==FALSE)) { if ((lIntegral_Pressure_E + lIntegralTerm) <0) lIntegral_Pressure_E = INTEGRAL_MAX; else lIntegral_Pressure_E += lIntegralTerm; } else if ((lIntegral_Pressure_E<=0) && (lIntegralTerm<=0) && (bMinPIOutputPressureE==FALSE)) { if ((lIntegral_Pressure_E + lIntegralTerm)>0) lIntegral_Pressure_E = INTEGRAL_MIN; else lIntegral_Pressure_E += lIntegralTerm; } else if ((lIntegral_Pressure_E<=0) && (lIntegralTerm>=0)) lIntegral_Pressure_E += lIntegralTerm; else if ((lIntegral_Pressure_E>=0) && (lIntegralTerm<=0)) lIntegral_Pressure_E += lIntegralTerm; lPropTerm = (int32_t)uiTechnicalDataSet[KP_VENTED_PRESSURE_E] * lError; lOutputPressure = (lIntegral_Pressure_E>>16) + (lPropTerm>>13); // Add constante value (PWM at the end of the previous expiration) lOutputPressure+=uiPWMatTheEndOfExpiration; // Flow limiting uiFlowMaxInExpi=uiMaximumFlowInExpiration[uiVentilationSet[PS_CPAP_SET]/10]; if (lError>5 && iBlowerFlowSmoothingMes>(int16_t)uiFlowMaxInExpi) { // Impossible to maintain the peep (patient disconnection) if (uiDisconnectionTime==0) { fBlockCloseLoop=TRUE; // Block the close loop if (uiMemoPWMDuringDisconnection!=0) // Apply a fixed PWM value uiExpirationBlowerPWMTec=uiMemoPWMDuringDisconnection; else uiExpirationBlowerPWMTec=MIN_VOLTAGE_REF; } else uiDisconnectionTime--; } else if (iBlowerFlowSmoothingMes<(int16_t)uiFlowMaxInExpi && (lError>(-2) && lError<2)) { // Good pressure : no disconnection uiDisconnectionTime=EXPIRATORY_DISCONNECTION_TIME_OUT; if (fBlockCloseLoop==TRUE) { // The close loop was block before => unblock it fBlockCloseLoop=FALSE; lIntegral_Pressure_E=0; bMaxPIOutputPressureE = FALSE; bMinPIOutputPressureE = FALSE; lOutputPressure=uiPWMatTheEndOfExpiration=uiExpirationBlowerPWMTec; } else if (iBlowerFlowSmoothingMes>=0) { // Record current PWM uiMemoPWMDuringDisconnection=uiExpirationBlowerPWMTec; } } // Unblock the close loop every 4s in case of none recorded PWM value if (fBlockCloseLoop==TRUE && uiMemoPWMDuringDisconnection==0 && uiTeD==0) { uiDisconnectionTime=EXPIRATORY_DISCONNECTION_TIME_OUT; fBlockCloseLoop=FALSE; lIntegral_Pressure_E=0; bMaxPIOutputPressureE = FALSE; bMinPIOutputPressureE = FALSE; lOutputPressure=uiPWMatTheEndOfExpiration=uiExpirationBlowerPWMTec; } // Update blower PWM if (fBlockCloseLoop==TRUE) { bMaxPIOutputPressureE = TRUE; } else if (lOutputPressure>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputPressureE = TRUE; uiExpirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputPressure<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputPressureE = TRUE; uiExpirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputPressureE = FALSE; bMinPIOutputPressureE = FALSE; uiExpirationBlowerPWMTec = lOutputPressure; } SS_Xputdw(mdrv, uiExpirationBlowerPWMTec); } // Expiratory time uiTeD++; if (uiTeD>TE_SET_MAX) uiTeD=TE_SET_MAX; if ((uiTeD>=uiTeBaroSet && bPSMode==FALSE) || TestInspiratoryTrigger(bPSMode, uiTeD, uiVentilationSet[TRIG_I_FLOW_SET])==TRUE) { ucVentilationCycle=INSPIRATION_CYCLE; uiTeD=0; // Record PWM value at the end of the expiration uiPWMatTheEndOfExpiration=uiExpirationBlowerPWMTec; // Reset PI variables for next inspiration lIntegral_Pressure_I=0; bMaxPIOutputPressureI=FALSE; bMinPIOutputPressureI=FALSE; } } } /******************************************************************************* * Function Name : ManageVolumetricVentilation * Description : Manage Volumetric Ventilation * Input : None * Output : None * Return : None *******************************************************************************/ void ManageVolumetricVentilation(void) { int32_t lError; int32_t lPropTerm; int32_t lIntegralTerm; int32_t lOutputPressure; int16_t iMaxVtAdjust; u16 uiFlowMaxInExpi; u16 uiTiMin; if (ucVentilationCycle==INSPIRATION_CYCLE) { // --------------------------------------------------------------------------- // - INSPIRATION - // --------------------------------------------------------------------------- // Compute Timin if (uiPatientType==PATIENT_ADULT) uiTiMin=TiVoluSet_A_MIN; else uiTiMin=TiVoluSet_P_MIN; // ************************** Main Blower MANAGEMENT ************************* if (uiTiD==0) { // Compute Volume adjustment if (fFirstCycleInspi==FALSE) { if ((uiFlagsAlarm[ALARM_FLAGS1]&HIGH_PRESSURE_ALARM_MASK)==0) { if (ucCounterHPAlarm==0) { iVtAdjust+=(((int16_t)uiVentilationSet[VT_SET]-(int16_t)uiRecordVtiMes>>1)); if (iVtAdjust>0) { iMaxVtAdjust=(int16_t)(uiVentilationSet[VT_SET]>>1); // >0 if (iVtAdjust>iMaxVtAdjust) iVtAdjust=iMaxVtAdjust; } else if (iVtAdjust<0) { iMaxVtAdjust=(int16_t)(~(uiVentilationSet[VT_SET]>>1)+1); // <0 if (iVtAdjust<iMaxVtAdjust) iVtAdjust=iMaxVtAdjust; } } else ucCounterHPAlarm--; } else ucCounterHPAlarm=1; } else iVtAdjust=0; ComputeInspiratoryFlowSetPointInAVC(uiPatientType, (u16)((int16_t)uiVentilationSet[VT_SET]+iVtAdjust), uiVentilationSet[TI_VOLU_SET], uiVentilationSet[SHAPE_SET], &ulMaxIFlowSet, &ulMinIFlowSet, &ulDecFlowStep); } else { if (ulMaxIFlowSet>=ulDecFlowStep) ulMaxIFlowSet-=ulDecFlowStep; if (ulMaxIFlowSet<ulMinIFlowSet) ulMaxIFlowSet=ulMinIFlowSet; } // Volumetric Mode : error computation //lError=(int32_t)(ulMaxIFlowSet/100)-iBlowerFlowMes; lError=(int32_t)(ulMaxIFlowSet/100)-iBlowerFlowSmoothingMes; if (fFirstCycleInspi==FALSE) { if (ulMaxIFlowSet<150000 && uiTiD>30000) lIntegralTerm = (int32_t)(uiTechnicalDataSet[KI_FLOW_I]>>1) * lError; else lIntegralTerm = (int32_t)uiTechnicalDataSet[KI_FLOW_I] * lError; } else lIntegralTerm = (int32_t)(uiTechnicalDataSet[KI_FLOW_I]>>2) * lError; // Close loop in pressure or flow (PI) if ((lIntegral_Flow_I>=0) && (lIntegralTerm>=0) && (bMaxPIOutputFlowI==FALSE)) { if ((lIntegral_Flow_I + lIntegralTerm) <0) lIntegral_Flow_I = INTEGRAL_MAX; else lIntegral_Flow_I += lIntegralTerm; } else if ((lIntegral_Flow_I<=0) && (lIntegralTerm<=0) && (bMinPIOutputFlowI==FALSE)) { if ((lIntegral_Flow_I + lIntegralTerm)>0) lIntegral_Flow_I = INTEGRAL_MIN; else lIntegral_Flow_I += lIntegralTerm; } else if ((lIntegral_Flow_I<=0) && (lIntegralTerm>=0)) lIntegral_Flow_I += lIntegralTerm; else if ((lIntegral_Flow_I>=0) && (lIntegralTerm<=0)) lIntegral_Flow_I += lIntegralTerm; lPropTerm = (int32_t)uiTechnicalDataSet[KP_FLOW_I] * lError; lOutputPressure = (lIntegral_Flow_I>>16) + (lPropTerm>>13); // Add constante value (PWM at the end of the previous inspiration) lOutputPressure+=uiPWMatTheEndOfExpiration; if (lOutputPressure>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputFlowI = TRUE; uiInspirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputPressure<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputFlowI = TRUE; uiInspirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputFlowI = FALSE; bMinPIOutputFlowI = FALSE; uiInspirationBlowerPWMTec = lOutputPressure; } // Update Blower Speed SS_Xputdw(mdrv, uiInspirationBlowerPWMTec); // ************************ CYCLE MANAGEMENT ********************************* uiTiD++; if ((uiTiD>=uiVentilationSet[TI_VOLU_SET]) || (fAlarmPmax==TRUE && uiTiD>uiTiMin)) { // Beginning of the expiration uiPWMatTheEndOfInspiration=uiInspirationBlowerPWMTec; uiProximalPressureAtTheEndOfInspiration=uiProximalPressureMes; ucVentilationCycle=EXPIRATION_CYCLE; uiTiD=0; // Reset PI variables for next inspiration lIntegral_Pressure_E=0; bMaxPIOutputPressureE=FALSE; bMinPIOutputPressureE=FALSE; fFirstCycleExpi=FALSE; fFirstCycleInspi=FALSE; } } else { // --------------------------------------------------------------------------- // - EXPIRATION - // --------------------------------------------------------------------------- // *************************** UPDATE SETTINGS ******************************* if (ApplyNewVentilationMode()==TRUE) return; // ******************** MAIN BLOWER MANAGEMENT ******************************* if (uiVentilationSet[PEEP_SET]==0) { uiExpirationBlowerPWMTec=MIN_VOLTAGE_REF; SS_Xputdw(mdrv, uiExpirationBlowerPWMTec); } else { if (uiTeD==0) { // First blower pressure setting if (uiProximalPressureAtTheEndOfInspiration<uiVentilationSet[PEEP_SET]) uiEpapSettingTemp=uiVentilationSet[PEEP_SET]; else if (uiProximalPressureAtTheEndOfInspiration>=2) uiEpapSettingTemp=uiProximalPressureAtTheEndOfInspiration-2; // Slope of 5ms/hPa => 0.2hPa/ms else uiEpapSettingTemp=uiVentilationSet[PEEP_SET]; } else { // we decrease the speed of the blower every 1ms if (uiEpapSettingTemp>=2) uiEpapSettingTemp-=2; // Slope of 5ms/hPa => 0.2hPa/ms } // Pressure set point limitation if (uiEpapSettingTemp<uiVentilationSet[PEEP_SET]) uiEpapSettingTemp=uiVentilationSet[PEEP_SET]; // Barometric Mode : error computation if (fFirstCycleExpi==FALSE) { lError=(int32_t)uiEpapSettingTemp-(int32_t)uiProximalPressureMes; lIntegralTerm = (int32_t)uiTechnicalDataSet[KI_VENTED_PRESSURE_E] * lError; } else { lError=(int32_t)uiVentilationSetTemp[PEEP_SET]-(int32_t)uiProximalPressureMes; lIntegralTerm = (int32_t)(uiTechnicalDataSet[KI_VENTED_PRESSURE_E]>>2) * lError; } // Close loop in pressure if ((lIntegral_Pressure_E>=0) && (lIntegralTerm>=0) && (bMaxPIOutputPressureE==FALSE)) { if ((lIntegral_Pressure_E + lIntegralTerm) <0) lIntegral_Pressure_E = INTEGRAL_MAX; else lIntegral_Pressure_E += lIntegralTerm; } else if ((lIntegral_Pressure_E<=0) && (lIntegralTerm<=0) && (bMinPIOutputPressureE==FALSE)) { if ((lIntegral_Pressure_E + lIntegralTerm)>0) lIntegral_Pressure_E = INTEGRAL_MIN; else lIntegral_Pressure_E += lIntegralTerm; } else if ((lIntegral_Pressure_E<=0) && (lIntegralTerm>=0)) lIntegral_Pressure_E += lIntegralTerm; else if ((lIntegral_Pressure_E>=0) && (lIntegralTerm<=0)) lIntegral_Pressure_E += lIntegralTerm; lPropTerm = (int32_t)uiTechnicalDataSet[KP_VENTED_PRESSURE_E] * lError; lOutputPressure = (lIntegral_Pressure_E>>16) + (lPropTerm>>13); // Add constante value (PWM at the end of the previous expiration) lOutputPressure+=uiPWMatTheEndOfExpiration; // Flow limiting uiFlowMaxInExpi=uiMaximumFlowInExpiration[uiVentilationSet[PS_CPAP_SET]/10]; if (lError>5 && iBlowerFlowSmoothingMes>(int16_t)uiFlowMaxInExpi) { // Impossible to maintain the peep (patient disconnection) if (uiDisconnectionTime==0) { fBlockCloseLoop=TRUE; // Block the close loop if (uiMemoPWMDuringDisconnection!=0) // Apply a fixed PWM value uiExpirationBlowerPWMTec=uiMemoPWMDuringDisconnection; else uiExpirationBlowerPWMTec=MIN_VOLTAGE_REF; } else uiDisconnectionTime--; } else if (iBlowerFlowSmoothingMes<(int16_t)uiFlowMaxInExpi && (lError>(-2) && lError<2)) { // Good pressure : no disconnection uiDisconnectionTime=EXPIRATORY_DISCONNECTION_TIME_OUT; if (fBlockCloseLoop==TRUE) { // The close loop was block before => unblock it fBlockCloseLoop=FALSE; lIntegral_Pressure_E=0; bMaxPIOutputPressureE = FALSE; bMinPIOutputPressureE = FALSE; lOutputPressure=uiPWMatTheEndOfExpiration=uiExpirationBlowerPWMTec; } else if (iBlowerFlowSmoothingMes>=0) { // Record current PWM uiMemoPWMDuringDisconnection=uiExpirationBlowerPWMTec; } } // Unblock the close loop every 4s in case of none recorded PWM value if (fBlockCloseLoop==TRUE && uiMemoPWMDuringDisconnection==0 && uiTeD==0) { uiDisconnectionTime=EXPIRATORY_DISCONNECTION_TIME_OUT; fBlockCloseLoop=FALSE; lIntegral_Pressure_E=0; bMaxPIOutputPressureE = FALSE; bMinPIOutputPressureE = FALSE; lOutputPressure=uiPWMatTheEndOfExpiration=uiExpirationBlowerPWMTec; } // Update blower PWM if (fBlockCloseLoop==TRUE) { bMaxPIOutputPressureE = TRUE; } else if (lOutputPressure>=((int32_t)MAX_VOLTAGE_REF)) { bMaxPIOutputPressureE = TRUE; uiExpirationBlowerPWMTec=MAX_VOLTAGE_REF; } else if (lOutputPressure<((int32_t)MIN_VOLTAGE_REF)) { bMinPIOutputPressureE = TRUE; uiExpirationBlowerPWMTec=MIN_VOLTAGE_REF; } else { bMaxPIOutputPressureE = FALSE; bMinPIOutputPressureE = FALSE; uiExpirationBlowerPWMTec = lOutputPressure; } // Update Blower Speed SS_Xputdw(mdrv, uiExpirationBlowerPWMTec); } // Expiratory time uiTeD++; if (uiTeD>=uiTeVoluSet || TestInspiratoryTrigger(FALSE, uiTeD, uiVentilationSet[TRIG_I_FLOW_SET])==TRUE) { ucVentilationCycle=INSPIRATION_CYCLE; uiTeD=0; // Record PWM value at the end of the expiration if (fBlockCloseLoop==FALSE) uiPWMatTheEndOfExpiration=uiExpirationBlowerPWMTec; else uiPWMatTheEndOfExpiration=0; fBlockCloseLoop=FALSE; // Reset PI variables for next inspiration lIntegral_Flow_I=0; bMaxPIOutputFlowI=FALSE; bMinPIOutputFlowI=FALSE; } } } /******************************************************************************* * Function Name : LaunchTherapyEngine * Description : Select and launch the therapy engine * Input : None * Output : None * Return : None *******************************************************************************/ void LaunchTherapyEngine(void) { if (bMemoStartVentilation==TRUE) { // ---- Check new configuration if (uiTechnicalDataSet[DEVICE_MODE_TEC]!=uiPreviousDeviceMode) { UpdateSettings(); enPreviousMode=NUMBER_OF_MODE; // Force full init of the variables InitVentilation(); uiPreviousDeviceMode=uiTechnicalDataSet[DEVICE_MODE_TEC]; } switch(uiTechnicalDataSet[DEVICE_MODE_TEC]) { default: case VENTILATION_MODE: { if (enVentilationMode==PS_MODE) ManagePACVVentilation(TRUE); else if (enVentilationMode==APCV_MODE) ManagePACVVentilation(FALSE); else if (enVentilationMode==AVC_MODE) ManageVolumetricVentilation(); else if (enVentilationMode==CPAP_MODE) ManageCPAPVentilation(); break; } case ONE_CST_PWM_MODE: { ManageOneCstPWM(); break; } case TWO_CST_PWM_MODE: { Manage2CstPWM(); break; } case CST_SPEED_MODE: { ManageCstSpeed(); break; } case FLOW_CAL_MODE: { ManageFlowCalibration(); break; } case PRESSURE_CAL_MODE: { ManagePressureCalibration(); break; } case PRESSURE_CST_MODE: { ManageCstPressure(); break; } } } } /******************************************************************************* * Function Name : StartStopVentilation * Description : Detect a start/stop ventilation request * Input : None * Output : None * Return : None *******************************************************************************/ void StartStopVentilation(void) { #ifndef TEMPERATURE_TRENDS opstatus_t byOpStatus; #endif // Check if an alarm must stop the ventilation ucStopVentilationAlarmNumber=ReadStopVentilationAlarmNumber(); #ifndef TEMPERATURE_TRENDS if (uiTechnicalDataSet[START_STOP_VENTILATION]==1 && ucStopVentilationAlarmNumber==SIZE_BLOWER_ALARM) { // *************** VENTILATION is ON ************** if (bMemoStartVentilation==FALSE) { switch(ucStartVentilationScheduler) { default: case 0: { // Active the HW safety system ControlHW(BLOWER_ON__PAT_CPLD); ucStartVentilationScheduler++; break; } case 1: { // Pat CPLD (WDI_CPLD), STOP_BLOWER=1 ControlHW(BLOWER_ON__PAT_CPLD); // Read Offset of the Blower current ComputeADCBlowerCurrent(TRUE); ucStartVentilationScheduler++; break; } case 2: { // Pat CPLD (WDI_CPLD), STOP_BLOWER=1 ControlHW(BLOWER_ON__PAT_CPLD); // Open motor driver byOpStatus=SS_Xopen(mdrv); if (byOpStatus==OPSTATUS_FAIL) { uiFlagsAlarm[ALARM_FLAGS2]|=MOTOR_FAILURE_ALARM_MASK; ucStartVentilationScheduler=0; } else if (byOpStatus==OPSTATUS_OK) { // Force init settings and variables uiPreviousDeviceMode=0xFF; uiTempo1min=TEMPO_1MIN; #ifdef DATA_LOGGING uiTrendsSampleTime=RECORD_TRENDS_SAMPLE_TIME; #endif // DATA_LOGGING // Check if ventilation mode is activated if (uiTechnicalDataSet[DEVICE_MODE_TEC]!=VENTILATION_MODE) uiFlagsAlarm[ALARM_FLAGS2]|=DEVICE_MODE_ALARM_MASK; bMemoStartVentilation=TRUE; ucStartVentilationScheduler=0; } break; } } } // --- Check Safety system for Motor transistors ---- else if (IsMotorOK()==FALSE) { ControlHW(BLOWER_OFF__STOP_PAT_CPLD); uiFlagsAlarm[ALARM_FLAGS2]|=MOTOR_FAILURE_ALARM_MASK; } // --- Test safety system thanks to the "STOP_BLOWER" Input pin else if (TestStopBlowerInputPin()==OPSTATUS_OK) { ControlHW(BLOWER_ON__PAT_CPLD); uiFlagsAlarm[ALARM_FLAGS2]&=(~HW_SAFETY_ALARM_MASK); } else { uiFlagsAlarm[ALARM_FLAGS2]|=HW_SAFETY_ALARM_MASK; } } else #endif // #ifndef TEMPERATURE_TRENDS { // *************** VENTILATION is OFF ************** uiFlagsAlarm[ALARM_FLAGS2]&=(~DEVICE_MODE_ALARM_MASK); if (ucStopVentilationAlarmNumber!=SIZE_BLOWER_ALARM) { // Stop patting CPLD (WDI_CPLD=0), STOP_BLOWER=0 ControlHW(BLOWER_OFF__STOP_PAT_CPLD); } else { // Pat CPLD (WDI_CPLD), STOP_BLOWER=1 ControlHW(BLOWER_ON__PAT_CPLD); // ---- Test STOP_ACTUATOR Input pin Status if (TestStopBlowerInputPin()==OPSTATUS_FAIL) uiFlagsAlarm[ALARM_FLAGS2]|=HW_SAFETY_ALARM_MASK; else uiFlagsAlarm[ALARM_FLAGS2]&=(~HW_SAFETY_ALARM_MASK); } if (bMemoStartVentilation==TRUE) { StopAllActuators(); ClearAllVentilationAlarms(); ClearAllMeasures(); } uiTechnicalDataSet[START_STOP_VENTILATION]=0; bMemoStartVentilation=FALSE; #ifndef TEMPERATURE_TRENDS ucStartVentilationScheduler=0; #endif bDisableMaroubraCommCommunication=FALSE; } } #endif // #ifndef C_M3_DEVICETEST_TARGET /******************************************************************************* * Function Name : ComputeTe * Description : Compute expiratory time * Input : uiF = Breath frequency (bpm) uiTi = Inspiratory time (ms) * Output : Expiratory time (ms) * Return : None *******************************************************************************/ #ifndef C_M3_DEVICETEST_TARGET u16 ComputeTe(u16 uiF, u16 uiTi) { u16 uiTtot; u16 uiTeTemp=TE_SET_MIN; if (uiF!=0) { // Compute Ttotal uiTtot=60000/uiF; if (uiTtot>uiTi) { uiTeTemp=uiTtot-uiTi; if (uiTeTemp<TE_SET_MIN) uiTeTemp=TE_SET_MIN; } } else { // F=0 => Te=2xTi uiTeTemp=uiTi<<1; } return(uiTeTemp); } /******************************************************************************* * Function Name : UpdateSettings * Description : Update the settings zone with the temporary settings zone * Input : None * Output : None * Return : TRUE if the ventilation mode has changed *******************************************************************************/ bool UpdateSettings(void) { u8 ucIndex; // Take into account a modification of the PEEP if (uiVentilationSet[PEEP_SET]!=uiVentilationSetTemp[PEEP_SET] || uiVentilationSet[PS_CPAP_SET]!=uiVentilationSetTemp[PS_CPAP_SET]) uiMemoPWMDuringDisconnection=0; // Transfert data from the setting temporary zone to the settings zone for (ucIndex=0; ucIndex<SIZE_LRS_GROUP; ucIndex++) uiVentilationSet[ucIndex]=uiVentilationSetTemp[ucIndex]; // Update barometric expiratory time uiTeBaroSet=ComputeTe(uiVentilationSet[BREATH_RATE_BARO_SET], uiVentilationSet[TI_BARO_SET]); // Update volumetric expiratory time uiTeVoluSet=ComputeTe(uiVentilationSet[BREATH_RATE_VOLU_SET], uiVentilationSet[TI_VOLU_SET]); // Update Tube configuration type enTubeConfigType=enTubeConfigTypeTemp; // Update Patient type uiPatientType=uiPatientTypeTemp; // Update Ventilation mode if (enVentilationMode!=enVentilationModeTemp) { enVentilationMode=enVentilationModeTemp; InitVentilation(); return(TRUE); } return(FALSE); } /******************************************************************************* * Function Name : ApplyNewVentilationMode * Description : Update settings and init variables if the ventilation mode has changed * Input : None * Output : None * Return : TRUE if new ventilation mode is applied *******************************************************************************/ bool ApplyNewVentilationMode(void) { if (fUpdateTheSettings==TRUE) { fUpdateTheSettings=FALSE; if (UpdateSettings()==TRUE) { InitVentilation(); return(TRUE); } } return(FALSE); } /******************************************************************************* * Function Name : ComputeInspiratoryFlowSetPointInAVC * Description : Compute: - the inspiratory flow max set point - the inspiratory flow min set point - the flow step between the flow max and the flow min during Ti * Input : uiTypeOfPatient = Adult or Pedia uiVtc = set point in volume (ml) uiTi = Ti set point (ms) uiFlowShape = Flow shape (0 to 4) * Output : uiMaxFlow = the inspiratory flow max set point (cl/min => 100=1l/min) uiMinFlow = the inspiratory flow min set point (cl/min => 100=1l/min) uiFlowStep = the flow step between the flow max and the flow min during Ti (cl/min => 100=1l/min) * Return : None *******************************************************************************/ void ComputeInspiratoryFlowSetPointInAVC(u16 uiTypeOfPatient, u16 uiVtc, u16 uiTi, u16 uiFlowShape, u32 *ulMaxFlow, u32 *ulMinFlow, u32 *ulFlowStep) { u32 ulFlowMax; u32 ulFlowMin; u32 ulMaxMaxFlow; u32 ulMinMinFlow; u16 uiTiMin; u32 ulFlowStepTemp; // Define min/max flow if (uiTypeOfPatient==PATIENT_ADULT) { ulMinMinFlow=InspiratoryFlowSet_A_MIN; ulMaxMaxFlow=InspiratoryFlowSet_A_MAX; uiTiMin=TiVoluSet_A_MIN; } else { ulMinMinFlow=InspiratoryFlowSet_P_MIN; ulMaxMaxFlow=InspiratoryFlowSet_P_MAX; uiTiMin=TiVoluSet_P_MIN; } // Check Ti if (uiTi==0) uiTi=uiTiMin; // Compute flow max switch(uiFlowShape) { default: case 0: { ulFlowMax=((u32)uiVtc*6000)/uiTi; break; } case 1: { ulFlowMax=((u32)uiVtc*7500)/uiTi; break; } case 2: { ulFlowMax=((u32)uiVtc*9000)/uiTi; break; } case 3: { ulFlowMax=((u32)uiVtc*10500)/uiTi; break; } case 4: { ulFlowMax=((u32)uiVtc*12000)/uiTi; break; } } // Limitation of the max flow if (ulFlowMax>ulMaxMaxFlow) ulFlowMax=ulMaxMaxFlow; // Limitation of the min flow if (ulFlowMax<ulMinMinFlow) ulFlowMax=ulMinMinFlow; // Factor 100 on flow max (and flow min and flow step) ulFlowMax*=100; // Compute flow min and flow step switch(uiFlowShape) { default: case 0: { ulFlowMin=ulFlowMax; break; } case 1: { ulFlowMin=(ulFlowMax*3)>>2; break; } case 2: { ulFlowMin=ulFlowMax>>1; break; } case 3: { ulFlowMin=ulFlowMax>>2; break; } case 4: { ulFlowMin=0; break; } } // Max flow *ulMaxFlow=ulFlowMax; // Min flow *ulMinFlow=ulFlowMin; // Flow Step ulFlowStepTemp=(ulFlowMax-ulFlowMin)/uiTi; //if (uiFlowStepTemp==0 && uiFlowShape!=0) uiFlowStepTemp=1; *ulFlowStep=ulFlowStepTemp; } /******************************************************************************* * Function Name : TestTriggerExpiratoire * Description : Manage the expiratory trigger * Input : ucValeurSeuil = threshold (% of the flow max) to reach in order to cycle * Output : None * Return : TRUE if cycling active *******************************************************************************/ unsigned char TestTriggerExpiratoire(unsigned char ucValeurSeuil, unsigned int uiPressureSetting) { unsigned char ucSeuil=ucValeurSeuil; unsigned int uiCalculTemp; int16_t iConductanceBaseLine=UpdateInspiratoryConductanceAverage(); // Management automatic expiratory trigger ucTempoTriggerExpiAuto--; if (ucTempoTriggerExpiAuto==0) { ucTempoTriggerExpiAuto=TEMPO_TRIGGER_EXPI; ucExpiTriggerTreshold++; } // Test si flow<0?? if (iBlowerFlowSmoothingMes<0) { return(TRUE); } // Test if deacring flow?? else if (iBlowerFlowSmoothingMes<iBlowerFlowSmoothingMaxMes) { // Test if Trigger Expi=AUTO ?? if ((ucValeurSeuil==ExpiTriggerSet_A_MAX && uiPatientType==PATIENT_ADULT) || (ucValeurSeuil==ExpiTriggerSet_P_MAX && uiPatientType==PATIENT_PEDIA)) ucSeuil=ucExpiTriggerTreshold; u16 conducThresh = 2000; if(enTubeConfigType == 1 ) conducThresh = 750; else if(enTubeConfigType == 2) conducThresh = 1500; // Test if cycling or not uiCalculTemp=((unsigned long)iBlowerFlowSmoothingMaxMes*ucSeuil)/100; if (uiCalculTemp>iBlowerFlowSmoothingMes) return(TRUE); else if (uiProximalPressureMes>(uiPressureSetting+20)) return(TRUE); else if (uiConductanceCalc < (iConductanceBaseLine - conducThresh)) { ResetInspiratoryConductanceAverage(); return(TRUE); } } return(FALSE); } /******************************************************************************* * Function Name : TestInspiratoryTrigger * Description : Manage the inspiratory trigger * Input : bSpont = TRUE if spontaneous mode * uiTe = expiration time (ms) * uiFlowTreshold = flow threshold (dl/min) * Output : None * Return : TRUE if cycling active *******************************************************************************/ bool TestInspiratoryTrigger(bool bSpont, u16 uiTe, u16 uiFlowTreshold) { //int16_t iInspiratoryFlowDeltaAverage; //int16_t iDeltaOfTheDelta1, iDeltaOfTheDelta2, iDeltaOfTheDelta3, iDeltaOfTheDelta4; bool bTrigger=FALSE; //int16_t iVirtualFlow; int16_t iConductanceBaseLine; // Manual breath management if (uiTe>TE_SET_MIN) { if (bManualBreath==TRUE) { bManualBreath=FALSE; bDisplayInspiratoryTrigger=TRUE; return(TRUE); } } // Trigger management if ((bSpont==TRUE) || (bSpont==FALSE && uiFlowTreshold<InspiTriggerFlowSet_A_MAX && uiPatientType==PATIENT_ADULT) || (bSpont==FALSE && uiFlowTreshold<InspiTriggerFlowSet_P_MAX && uiPatientType==PATIENT_PEDIA)) { // Init scheduler if (uiTe==1) { ucInspiTriggerScheduler=0; iInspiratoryFlowMin=0; ucMinFlowCounter=NUMBER_MIN_FLOW_VALUE; ucCounterTriggerValid=NUMBER_TRIGGER_VALID; bDetectionConstantFlow=FALSE; } iConductanceBaseLine=UpdateInspiratoryConductanceAverage(); // Manage detection phase switch(ucInspiTriggerScheduler) { default: case 0: { // Update the buffer on the Inspiratory flow average // Detect min flow if (iBlowerFlowSmoothingMesForTrigger<iInspiratoryFlowMin) { iInspiratoryFlowMin=iBlowerFlowSmoothingMesForTrigger; ucMinFlowCounter=NUMBER_MIN_FLOW_VALUE; } else { ucMinFlowCounter--; if (ucMinFlowCounter==0) ucInspiTriggerScheduler=1; } break; } case 1: { //ucTriggerNumber=0; if (uiTe>TE_SET_MIN) { // // Fast trigger management // if (iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-50]<=iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-25]) // { // iVirtualFlow=(iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-25]<<1)-iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-50]; // if (iBlowerFlowSmoothingMesForTrigger>=(iVirtualFlow+(int16_t)(uiFlowTreshold*7))) // { // //ucTriggerNumber=1; // //if (fBlockCloseLoop==FALSE) // bTrigger=TRUE; // } // } // if (bTrigger==FALSE && iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-100]<=iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-50]) // { // iVirtualFlow=(iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-50]<<1)-iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-100]; // if (iBlowerFlowSmoothingMesForTrigger>=(iVirtualFlow+(int16_t)(uiFlowTreshold*15))) // { // //ucTriggerNumber=2; // //if (fBlockCloseLoop==FALSE) // bTrigger=TRUE; // } // } // // // Slow trigger management // if (TestInspiratorySlowTrigger(iFlowBaseLine, uiFlowTreshold*10)==TRUE) // { // //ucTriggerNumber=3; // bTrigger=TRUE; // } //Conductance Trigger if(uiConductanceCalc >= (iConductanceBaseLine + 2000)) { bTrigger = TRUE; } // Trigger occurs?? if (bTrigger==TRUE) { ucCounterTriggerValid--; if (ucCounterTriggerValid==0) { bDisplayInspiratoryTrigger=TRUE; ResetInspiratoryConductanceAverage(); return(TRUE); } } else ucCounterTriggerValid=NUMBER_TRIGGER_VALID; } break; } } } return(FALSE); } /******************************************************************************* * Function Name : UpdateInspiratoryConductanceAverage * Description : Fill the buffer with the expiratory conductance and compute the average * Input : None * Output : None * Return : Inspiratory flow average *******************************************************************************/ u16 conducIndex = 0; int32_t lSumConductance = 0; bool bufFilled = FALSE; int16_t UpdateInspiratoryConductanceAverage(void) { int16_t oldValue = iBufferIFlow[conducIndex]; iBufferIFlow[conducIndex] = uiConductanceCalc; lSumConductance += uiConductanceCalc; conducIndex++; if(conducIndex > SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER) { conducIndex = 0; bufFilled = TRUE; } if(bufFilled) { lSumConductance -= oldValue; return (lSumConductance/SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER); } else { return (lSumConductance/conducIndex); } } /******************************************************************************* * Function Name : ResetInspiratoryConductanceAverage * Description : Fill the buffer with the expiratory conductance and compute the average * Input : None * Output : None * Return : Inspiratory flow average *******************************************************************************/ void ResetInspiratoryConductanceAverage(void) { conducIndex = 0; lSumConductance = 0; bufFilled = FALSE; } /******************************************************************************* * Function Name : UpdateInspiratoryFlowAverage * Description : Fill the buffer with the inspiratory flow and compute the average * Input : None * Output : None * Return : Inspiratory flow average *******************************************************************************/ int16_t UpdateInspiratoryFlowAverage(void) { u16 uiIndex; int32_t lSumIFlow=0; // Roll the buffer for(uiIndex=1; uiIndex<SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER; uiIndex++) { iBufferIFlow[uiIndex-1]=iBufferIFlow[uiIndex]; // Upate the Inspiratory Flow buffer lSumIFlow+=iBufferIFlow[uiIndex-1]; // Sum the Inspiratory Flow } // Add new flow value iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-1]=iBlowerFlowSmoothingMesForTrigger; // Upate the Inspiratory Flow buffer lSumIFlow+=iBlowerFlowSmoothingMesForTrigger; // Sum the Inspiratory Flow return(lSumIFlow/SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER); } /******************************************************************************* * Function Name : TestInspiratorySlowTrigger * Description : Compute flow base line and test trigger * Input : FlowTreshold = flow threshold (cl/min) * Output : None * Return : TRUE if trigger occurs *******************************************************************************/ bool TestInspiratorySlowTrigger(int16_t iTheFlowBaseLine, u16 uiFlowTreshold) { int16_t iFlowDelta1, iFlowDelta2, iFlowDelta3; if (bDetectionConstantFlow==FALSE) { // Detection constant flow if (iBufferIFlow[0]<iTheFlowBaseLine) iFlowDelta1=iTheFlowBaseLine-iBufferIFlow[0]; else iFlowDelta1=iBufferIFlow[0]-iTheFlowBaseLine; if (iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER>>1]<iTheFlowBaseLine) iFlowDelta2=iTheFlowBaseLine-iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER>>1]; else iFlowDelta2=iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER>>1]-iTheFlowBaseLine; if (iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-1]<iTheFlowBaseLine) iFlowDelta3=iTheFlowBaseLine-iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-1]; else iFlowDelta3=iBufferIFlow[SIZE_INSPIRATORY_FLOW_AVERAGE_BUFFER-1]-iTheFlowBaseLine; if (iFlowDelta1<60 && iFlowDelta2<60 && iFlowDelta3<60) { iFlowBaseLineReference=iTheFlowBaseLine; bDetectionConstantFlow=TRUE; } } else { // Detect trigger if (iBlowerFlowSmoothingMesForTrigger>=(iFlowBaseLineReference+(int16_t)uiFlowTreshold)) { return(TRUE); } } return(FALSE); } /******************************************************************************* * Function Name : ApplyDefaultValueToTemporaryVentilationSettings * Description : Init temporary ventilation settings with default values * Input : None * Output : None * Return : None *******************************************************************************/ void ApplyDefaultValueToTemporaryVentilationSettings(void) { u8 ucIndex; enVentilationModeTemp=VentilationMode_DEF; uiPatientTypeTemp=PatientType_DEF; enTubeConfigTypeTemp=(enMaroubraTubeConfigurationList)ReturnDefaultTubeConfiguration(VentilationMode_DEF); // Init all temporary settings if (uiPatientType==PATIENT_ADULT) { for (ucIndex=0; ucIndex<SIZE_LRS_GROUP; ucIndex++) uiVentilationSetTemp[ucIndex]=sLRSGroup[ucIndex].uiDefault_Adult; } else { for (ucIndex=0; ucIndex<SIZE_LRS_GROUP; ucIndex++) uiVentilationSetTemp[ucIndex]=sLRSGroup[ucIndex].uiDefault_Pedia; } } /******************************************************************************* * Function Name : CheckTemporaryVentilationSettingRange * Description : Check all the temporary settings range * Input : None * Output : None * Return : FALSE if one the settings is out of range *******************************************************************************/ opstatus_t CheckTemporaryVentilationSettingRange(void) { u8 ucIndex; if ((uiPatientTypeTemp==PATIENT_PEDIA || uiPatientTypeTemp==PATIENT_ADULT) && enVentilationModeTemp<NUMBER_OF_MODE && enTubeConfigTypeTemp<NUMBER_OF_TUBE_CONFIGURATION) { for (ucIndex=0; ucIndex<SIZE_LRS_GROUP; ucIndex++) { // Check data range if (uiPatientTypeTemp==PATIENT_PEDIA) { if (uiVentilationSetTemp[ucIndex]<sLRSGroup[ucIndex].uiMin_Pedia) return(OPSTATUS_FAIL); else if (uiVentilationSetTemp[ucIndex]>sLRSGroup[ucIndex].uiMax_Pedia) return(OPSTATUS_FAIL); } else { if (uiVentilationSetTemp[ucIndex]<sLRSGroup[ucIndex].uiMin_Adult) return(OPSTATUS_FAIL); else if (uiVentilationSetTemp[ucIndex]>sLRSGroup[ucIndex].uiMax_Adult) return(OPSTATUS_FAIL); } } return(OPSTATUS_OK); } return(OPSTATUS_FAIL); } /******************************************************************************* * Function Name : ApplyDefaultValueToTechnicalSettings * Description : Init technical settings with default values * Input : None * Output : None * Return : None *******************************************************************************/ void ApplyDefaultValueToTechnicalSettings(void) { u8 ucIndex; // Init all temporary settings for (ucIndex=0; ucIndex<SIZE_LRTS_GROUP; ucIndex++) uiTechnicalDataSet[ucIndex]=sLRTSGroup[ucIndex].uiDefault; } /******************************************************************************* * Function Name : CheckTechnicalSettingsRange * Description : Check all the technical settings range * Input : None * Output : None * Return : None *******************************************************************************/ void CheckTechnicalSettingsRange(void) { u8 ucIndex; bool fError=FALSE; for (ucIndex=0; ucIndex<SIZE_LRTS_GROUP; ucIndex++) { if (uiTechnicalDataSet[ucIndex]<sLRTSGroup[ucIndex].uiMin || uiTechnicalDataSet[ucIndex]>sLRTSGroup[ucIndex].uiMax) { uiTechnicalDataSet[ucIndex]=sLRTSGroup[ucIndex].uiDefault; fError=TRUE; //break; } } if (fError==TRUE) uiFlagsAlarm[ALARM_FLAGS2]|=TECHNICAL_SETTINGS_RANGE_ALARM_MASK; else uiFlagsAlarm[ALARM_FLAGS2]&=(~TECHNICAL_SETTINGS_RANGE_ALARM_MASK); } /******************************************************************************* * Function Name : CheckFlowLUTRange * Description : Check the flow LUT * Input : None * Output : None * Return : None *******************************************************************************/ void CheckFlowLUTRange(void) { u16 uiIndex; bool fError=FALSE; if (stLUTFlowSensor.uiLUT_TableSize<(FLOW_CALIB_NUMBER_OF_SAMPLES>>1) || stLUTFlowSensor.uiLUT_TableSize>FLOW_CALIB_NUMBER_OF_SAMPLES) { ApplyDefaultFlowLUT(); fError=TRUE; } else { for (uiIndex=1; uiIndex<stLUTFlowSensor.uiLUT_TableSize; uiIndex++) { if (stLUTFlowSensor.uiFlowValue[uiIndex]<=stLUTFlowSensor.uiFlowValue[uiIndex-1] || stLUTFlowSensor.uiFlowSensorTicks[uiIndex]<=stLUTFlowSensor.uiFlowSensorTicks[uiIndex-1]) { ApplyDefaultFlowLUT(); fError=TRUE; break; } } } if (fError==TRUE) uiFlagsAlarm[ALARM_FLAGS2]|=NO_FLOW_LUT_ALARM_MASK; else uiFlagsAlarm[ALARM_FLAGS2]&=(~NO_FLOW_LUT_ALARM_MASK); } /******************************************************************************* * Function Name : ApplyDefaultFlowLUT * Description : Apply default value for the flow LUT * Input : None * Output : None * Return : None *******************************************************************************/ void ApplyDefaultFlowLUT(void) { u16 uiIndex; stLUTFlowSensor.uiLUT_TableSize=stDefaultLUTFlowSensor.uiLUT_TableSize; for (uiIndex=0; uiIndex<FLOW_CALIB_NUMBER_OF_SAMPLES; uiIndex++) { stLUTFlowSensor.uiFlowValue[uiIndex]=stDefaultLUTFlowSensor.uiFlowValue[uiIndex]; stLUTFlowSensor.uiFlowSensorTicks[uiIndex]=stDefaultLUTFlowSensor.uiFlowSensorTicks[uiIndex]; } } /******************************************************************************* * Function Name : ReturnDefaultTubeConfiguration * Description : The default tubing configuration according to the current ventilation mode * Input : a ventilation mode * Output : None * Return : The tube configuration *******************************************************************************/ u16 ReturnDefaultTubeConfiguration(u16 uiMode) { u8 ucIndex; for (ucIndex=0; ucIndex<NUMBER_OF_TUBE_CONFIGURATION; ucIndex++) { if (ucTubeConfigurationTable[ucIndex][uiMode]==TUBE_DEFAULT) { return(ucIndex); } } return(0); } /******************************************************************************* * Function Name : ApplyAllDefaultValues * Description : Apply all default values in the technical and settings zones * : BE CARFUL this function call "ControlHW" function * Input : None * Output : None * Return : None *******************************************************************************/ void ApplyAllDefaultValues(void) { // Init ventilation settings ApplyDefaultValueToTemporaryVentilationSettings(); // Init technical settings ApplyDefaultValueToTechnicalSettings(); // Init flow LUT ApplyDefaultFlowLUT(); // Default value for the device/patient time counter ulDeviceTimeCounter=0; ulPatientTimeCounter=0; // Default value for the blower revolution counter ulBlowerRevolutionCounter=0; bComputeOffsetSensors=TRUE; } #endif // C_M3_DEVICETEST_TARGET /******************* (C) COPYRIGHT 2007 STMicroelectronics *****END OF FILE****/