first commit
This commit is contained in:
@@ -0,0 +1,864 @@
|
||||
#include "main.h"
|
||||
|
||||
void CInitAlarmOperValue(void);
|
||||
void CKeyMainPowerProcess(void);
|
||||
void CKeyArrowUpProcess(void);
|
||||
void CKeyArrowDownProcess(void);
|
||||
void CKeyEnterProcess(void);
|
||||
void CKeyMenuProcess(void);
|
||||
void CKeyEngineStartStopProcess(void);
|
||||
void CKeyEmergencyProcess(void);
|
||||
void CInitAdcStructure(void);
|
||||
Uint16 CAlarmCheck(ALARM_TYPE Idx, float32 fValue, Uint16 uiCheckDetectTime, Uint16 uiCheckType);
|
||||
static inline Uint16 CheckOpenFault(Uint16 isCsHigh, Uint16 isFuseHigh);
|
||||
Uint32 CGetKey(void);
|
||||
void CKeyCheck(Uint32 ulChangeKey, Uint32 ulKeyRead);
|
||||
static void MoveFocusLine(Uint16 maxLines, Uint16 direction);
|
||||
static void CChangePasswordDigit(Uint16 direction);
|
||||
static inline void CCalcAdcSum(CAdcCalcValue *AdcBuff);
|
||||
|
||||
CAdcCalcValue Adc_EngineHeater_I;
|
||||
CAdcCalcValue Adc_GlowPlug_I;
|
||||
CAdcCalcValue Adc_Solenoid_I;
|
||||
CAdcCalcValue Adc_FuelPump_I;
|
||||
CAdcCalcValue Adc_CoolantPump_I;
|
||||
CAdcCalcValue Adc_Fan1_I;
|
||||
CAdcCalcValue Adc_Fan2_I;
|
||||
|
||||
CAdcOperValue AdcOperValue;
|
||||
CAlarmOperValue AlarmOperValue[IDX_FAULT_MAX];
|
||||
CFaultBitValue FaultBitValue;
|
||||
CKeyOperValue KeyOperValue;
|
||||
|
||||
static const CKeyHandler KeyTable[IDX_KEY_MAX] =
|
||||
{
|
||||
{ IDX_KEY_MAIN_POWER, CKeyMainPowerProcess },
|
||||
{ IDX_KEY_ARR_UP, CKeyArrowUpProcess },
|
||||
{ IDX_KEY_ARR_DOWN, CKeyArrowDownProcess },
|
||||
{ IDX_KEY_ENTER, CKeyEnterProcess },
|
||||
{ IDX_KEY_MENU, CKeyMenuProcess },
|
||||
{ IDX_KEY_ENG_START_STOP, CKeyEngineStartStopProcess },
|
||||
{ IDX_KEY_EMERGENCY, CKeyEmergencyProcess }
|
||||
};
|
||||
|
||||
interrupt void CAdcInterrupt(void)
|
||||
{
|
||||
Uint16 uiTemp[IDX_ADC_MAX];
|
||||
Uint16 i;
|
||||
|
||||
const volatile Uint16 *pAdcAddress = &AdcRegs.ADCRESULT0;
|
||||
|
||||
for (i = 0U; i < IDX_ADC_MAX; i++)
|
||||
{
|
||||
uiTemp[i] = (*(pAdcAddress++) >> 4);
|
||||
}
|
||||
|
||||
Adc_EngineHeater_I.iAdcValue = (int16) uiTemp[IDX_ADC_ENGINE_HEATER_I];
|
||||
Adc_GlowPlug_I.iAdcValue = (int16) uiTemp[IDX_ADC_GLOW_PLUG_I];
|
||||
Adc_Solenoid_I.iAdcValue = (int16) uiTemp[IDX_ADC_SOLENOID_I];
|
||||
Adc_FuelPump_I.iAdcValue = (int16) uiTemp[IDX_ADC_FUEL_PUMP_I];
|
||||
Adc_CoolantPump_I.iAdcValue = (int16) uiTemp[IDX_ADC_COOLANT_PUMP_I];
|
||||
Adc_Fan1_I.iAdcValue = (int16) uiTemp[IDX_ADC_FAN1_I];
|
||||
Adc_Fan2_I.iAdcValue = (int16) uiTemp[IDX_ADC_FAN2_I];
|
||||
|
||||
CCalcAdcSum(&Adc_EngineHeater_I);
|
||||
CCalcAdcSum(&Adc_GlowPlug_I);
|
||||
CCalcAdcSum(&Adc_Solenoid_I);
|
||||
CCalcAdcSum(&Adc_FuelPump_I);
|
||||
CCalcAdcSum(&Adc_CoolantPump_I);
|
||||
CCalcAdcSum(&Adc_Fan1_I);
|
||||
CCalcAdcSum(&Adc_Fan2_I);
|
||||
|
||||
if (AdcOperValue.uiOffsetAdjustStart == 1U) // ADC Calibration
|
||||
{
|
||||
Adc_EngineHeater_I.fTempAdcOffset += Adc_EngineHeater_I.fSampledValue;
|
||||
Adc_GlowPlug_I.fTempAdcOffset += Adc_GlowPlug_I.fSampledValue;
|
||||
Adc_Solenoid_I.fTempAdcOffset += Adc_Solenoid_I.fSampledValue;
|
||||
Adc_FuelPump_I.fTempAdcOffset += Adc_FuelPump_I.fSampledValue;
|
||||
Adc_CoolantPump_I.fTempAdcOffset += Adc_CoolantPump_I.fSampledValue;
|
||||
Adc_Fan1_I.fTempAdcOffset += Adc_Fan1_I.fSampledValue;
|
||||
Adc_Fan2_I.fTempAdcOffset += Adc_Fan2_I.fSampledValue;
|
||||
|
||||
AdcOperValue.uiAdcOffsetIndex--;
|
||||
|
||||
if (AdcOperValue.uiAdcOffsetIndex == 0U)
|
||||
{
|
||||
Adc_EngineHeater_I.fOffset -= (Adc_EngineHeater_I.fTempAdcOffset / 10000.0f);
|
||||
Adc_GlowPlug_I.fOffset -= (Adc_GlowPlug_I.fTempAdcOffset / 10000.0f);
|
||||
Adc_Solenoid_I.fOffset -= (Adc_Solenoid_I.fTempAdcOffset / 10000.0f);
|
||||
Adc_FuelPump_I.fOffset -= (Adc_FuelPump_I.fTempAdcOffset / 10000.0f);
|
||||
Adc_CoolantPump_I.fOffset -= (Adc_CoolantPump_I.fTempAdcOffset / 10000.0f);
|
||||
Adc_Fan1_I.fOffset -= (Adc_Fan1_I.fTempAdcOffset / 10000.0f);
|
||||
Adc_Fan2_I.fOffset -= (Adc_Fan2_I.fTempAdcOffset / 10000.0f);
|
||||
|
||||
AdcOperValue.uiOffsetAdjustStart = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
// Reinitialize for next ADC sequence
|
||||
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1U; // Reset SEQ1
|
||||
AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1U; // Clear INT SEQ1 bit
|
||||
PieCtrlRegs.PIEACK.all |= PIEACK_GROUP1; // Acknowledge interrupt to PIE
|
||||
}
|
||||
|
||||
void CDisplayAlarmPopup(void)
|
||||
{
|
||||
Uint64 ullFaultValue = ((Uint64)FaultBitValue.ulTotal & 0x3FFFFUL) | (((Uint64)Rx210.GcuFault.uiTotal & 0xFFFFU) << 19UL) | (((Uint64)Rx310.EcuFault.uiTotal & 0x3FU) << 35UL);
|
||||
Uint32 ulWarningValue = ((Uint32)Rx210.GcuWarning.uiTotal & 0x7U) | (((Uint32)Rx310.EcuWarning.uiTotal & 0xFU) << 4U);
|
||||
Uint16 i;
|
||||
|
||||
if (OledOperValue.uiAlarmPopCheck == 0U)
|
||||
{
|
||||
if (ulWarningValue > 0U)
|
||||
{
|
||||
for (i = 0U; i < 16U; i++)
|
||||
{
|
||||
if ((ulWarningValue >> i) == 1U)
|
||||
{
|
||||
OledOperValue.uiAlarmPopCheck = 1U;
|
||||
OledOperValue.uiPrevAlarmPage = OledOperValue.uiPageNum;
|
||||
OledOperValue.uiPageNum = ((i / 9U) + OLED_PAGE_WARNING1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ullFaultValue > 0U)
|
||||
{
|
||||
for (i = 0U; i < 64U; i++)
|
||||
{
|
||||
if ((ullFaultValue >> i) == 1U)
|
||||
{
|
||||
OledOperValue.uiAlarmPopCheck = 1U;
|
||||
OledOperValue.uiPrevAlarmPage = OledOperValue.uiPageNum;
|
||||
OledOperValue.uiPageNum = (((i % 64U) / 8U) + OLED_PAGE_FAULT1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAlarmProcedure(void)
|
||||
{
|
||||
int16 iDiffRpm = 0U;
|
||||
|
||||
if (CGetApuOperIndex() == APU_OPER_IDX_EMERGENCY)
|
||||
{
|
||||
// 타임 아웃 발생 시 연결수립 비트는 클리어 한다.
|
||||
GeneralOperValue.Conection.CarComputer = (FaultBitValue.bit.CarCommTimeout == 1U) ? 0U : GeneralOperValue.Conection.CarComputer;
|
||||
GeneralOperValue.Conection.Gcu = (FaultBitValue.bit.GcuCommTimeout == 1U) ? 0U : GeneralOperValue.Conection.Gcu;
|
||||
GeneralOperValue.Conection.Ecu = (FaultBitValue.bit.EcuCommTimeOut == 1U) ? 0U : GeneralOperValue.Conection.Ecu;
|
||||
|
||||
if (GeneralOperValue.uiAlarmReset == 1U)
|
||||
{
|
||||
GeneralOperValue.uiAlarmReset = 0U;
|
||||
|
||||
CInitAlarmOperValue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GeneralOperValue.uiApuState > APU_OPER_IDX_EMERGENCY)
|
||||
{
|
||||
// Comm Timeout Checks
|
||||
FaultBitValue.bit.CarCommTimeout = CAlarmCheck(IDX_FAULT_CAR_COMM, (float32)CommCheck.CarComputer, AlarmOperValue[IDX_FAULT_CAR_COMM].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.GcuCommTimeout = CAlarmCheck(IDX_FAULT_GCU_COMM, (float32)CommCheck.Gcu, AlarmOperValue[IDX_FAULT_GCU_COMM].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.EcuCommTimeOut = CAlarmCheck(IDX_FAULT_ECU_COMM, (float32)CommCheck.Ecu, AlarmOperValue[IDX_FAULT_ECU_COMM].uiCheckTime, ALARM_OVER_CHECK);
|
||||
|
||||
if ((GeneralOperValue.Conection.Gcu == 1U) && (GeneralOperValue.Conection.Ecu == 1U))
|
||||
{
|
||||
// RPM오류는 보조발전기제어기와 보조엔진제어기의 통신이 연결되었을 때 검출한다.
|
||||
iDiffRpm = (int16)CGetGeneratorRpm() - (int16)CGetEngineActualRpm();
|
||||
iDiffRpm = ABS(iDiffRpm);
|
||||
FaultBitValue.bit.RpmError = CAlarmCheck(IDX_FAULT_RPM_ERR, (float32)iDiffRpm, AlarmOperValue[IDX_FAULT_RPM_ERR].uiCheckTime, ALARM_OVER_CHECK);
|
||||
}
|
||||
FaultBitValue.bit.EngineHeatOverCurrent = CAlarmCheck(IDX_FAULT_ENGINE_HEAT_OC, Adc_EngineHeater_I.fLpfValue, AlarmOperValue[IDX_FAULT_ENGINE_HEAT_OC].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.GlowPlugOverCurrent = CAlarmCheck(IDX_FAULT_GLOW_PLUG_OC, Adc_GlowPlug_I.fLpfValue, AlarmOperValue[IDX_FAULT_GLOW_PLUG_OC].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.SolenoidOverCurrent = CAlarmCheck(IDX_FAULT_SOLENOID_OC, Adc_Solenoid_I.fLpfValue, AlarmOperValue[IDX_FAULT_SOLENOID_OC].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.FuelPumpOverCurrent = CAlarmCheck(IDX_FAULT_FUEL_PUMP_OC, Adc_FuelPump_I.fLpfValue, AlarmOperValue[IDX_FAULT_FUEL_PUMP_OC].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.CoolantPumpOverCurrent = CAlarmCheck(IDX_FAULT_COOLANT_PUMP_OC, Adc_CoolantPump_I.fLpfValue, AlarmOperValue[IDX_FAULT_COOLANT_PUMP_OC].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.Fan1OverCurrent = CAlarmCheck(IDX_FAULT_FAN1_OC, Adc_Fan1_I.fLpfValue, AlarmOperValue[IDX_FAULT_FAN1_OC].uiCheckTime, ALARM_OVER_CHECK);
|
||||
FaultBitValue.bit.Fan2OverCurrent = CAlarmCheck(IDX_FAULT_FAN2_OC, Adc_Fan2_I.fLpfValue, AlarmOperValue[IDX_FAULT_FAN2_OC].uiCheckTime, ALARM_OVER_CHECK);
|
||||
|
||||
// Fuse 신호는 각 장치에 대응하는 CS가 ON 상태에서 작동하므로 CS가 HI일 때, Fuse 신호가 HI면 단선
|
||||
if (CGetApuOperIndex() > APU_OPER_IDX_STANDBY)
|
||||
{
|
||||
FaultBitValue.bit.EngineHeatOpen = CheckOpenFault(GPIO_ENGINE_HEATER_CS_READ(), GPIO_ENGINE_HEATER_FUSE());
|
||||
FaultBitValue.bit.GlowPlugOpen = CheckOpenFault(GPIO_GLOW_PLUG_CS_READ(), GPIO_GLOW_PLUG_FUSE());
|
||||
FaultBitValue.bit.SolenoidOpen = CheckOpenFault(GPIO_SOLENOID_CS_READ(), GPIO_SOLENOID_FUSE());
|
||||
FaultBitValue.bit.FuelPumpOpen = CheckOpenFault(GPIO_FUEL_PUMP_CS_READ(), GPIO_FUEL_PUMP_FUSE());
|
||||
FaultBitValue.bit.CoolantPumpOpen = CheckOpenFault(GPIO_COOLANT_PUMP_CS_READ(), GPIO_COOLANT_PUMP_FUSE());
|
||||
FaultBitValue.bit.Fan1Open = CheckOpenFault(GPIO_FAN1_CS_READ(), GPIO_FAN1_FUSE());
|
||||
FaultBitValue.bit.Fan2Open = CheckOpenFault(GPIO_FAN2_CS_READ(), GPIO_FAN2_FUSE());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Uint16 CAlarmCheck(ALARM_TYPE Idx, float32 fValue, Uint16 uiCheckDetectTime, Uint16 uiCheckType)
|
||||
{
|
||||
Uint16 uiCheckStatus = 0;
|
||||
|
||||
if (AlarmOperValue[Idx].uiCheck == 0U)
|
||||
{
|
||||
if (uiCheckType == ALARM_OVER_CHECK)
|
||||
{
|
||||
// Over Check !
|
||||
if (fValue >= AlarmOperValue[Idx].fCheckLimit)
|
||||
{
|
||||
uiCheckStatus = 1U;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Under Check !
|
||||
if (fValue <= AlarmOperValue[Idx].fCheckLimit)
|
||||
{
|
||||
uiCheckStatus = 1U;
|
||||
}
|
||||
}
|
||||
|
||||
if (uiCheckStatus == 1U)
|
||||
{
|
||||
if (AlarmOperValue[Idx].uiCheckCount < uiCheckDetectTime)
|
||||
{
|
||||
AlarmOperValue[Idx].uiCheckCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
AlarmOperValue[Idx].uiCheck = 1U;
|
||||
AlarmOperValue[Idx].uiCheckCount = 0U;
|
||||
AlarmOperValue[Idx].fFaultValue = fValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AlarmOperValue[Idx].uiCheckCount = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
return AlarmOperValue[Idx].uiCheck;
|
||||
}
|
||||
|
||||
static inline Uint16 CheckOpenFault(Uint16 isCsHigh, Uint16 isFuseHigh)
|
||||
{
|
||||
// 두 신호가 모두 1(High)일 때만 1(Fault) 반환
|
||||
return ((isCsHigh == 1U) && (isFuseHigh == 1U)) ? 1U : 0U;
|
||||
}
|
||||
|
||||
void CInitAlarmOperValue(void)
|
||||
{
|
||||
int16 i;
|
||||
|
||||
for (i = 0; i < IDX_FAULT_MAX; i++)
|
||||
{
|
||||
(void) memset(&AlarmOperValue[i], 0, sizeof(CAlarmOperValue));
|
||||
}
|
||||
|
||||
(void) memset(&FaultBitValue, 0, sizeof(CFaultBitValue));
|
||||
(void) memset(&CommCheck, 0, sizeof(CCommCheck));
|
||||
|
||||
// 체계/GCU/ECU 통신 및 신호 단선은 다른 함수에서 처리
|
||||
/*
|
||||
* Alarm Check Standard Value
|
||||
* Alarm Count per 1mS
|
||||
*/
|
||||
AlarmOperValue[IDX_FAULT_CAR_COMM].fCheckLimit = (float32)(COMM_TIME_OUT_COUNT); // 3 Seconds
|
||||
AlarmOperValue[IDX_FAULT_CAR_COMM].uiCheckTime = 1U; // 시간을 감지 하므로 즉시 검출
|
||||
|
||||
AlarmOperValue[IDX_FAULT_GCU_COMM].fCheckLimit = (float32)(COMM_TIME_OUT_COUNT); // 3 Seconds
|
||||
AlarmOperValue[IDX_FAULT_GCU_COMM].uiCheckTime = 1U; // 시간을 감지 하므로 즉시 검출
|
||||
|
||||
AlarmOperValue[IDX_FAULT_ECU_COMM].fCheckLimit = (float32)(COMM_TIME_OUT_COUNT); // 3 Seconds
|
||||
AlarmOperValue[IDX_FAULT_ECU_COMM].uiCheckTime = 1U; // 시간을 감지 하므로 즉시 검출
|
||||
|
||||
AlarmOperValue[IDX_FAULT_RPM_ERR].fCheckLimit = 300.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_RPM_ERR].uiCheckTime = 10U; // Value
|
||||
|
||||
AlarmOperValue[IDX_FAULT_ENGINE_HEAT_OC].fCheckLimit = 10.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_ENGINE_HEAT_OC].uiCheckTime = 10U; // Value
|
||||
|
||||
AlarmOperValue[IDX_FAULT_GLOW_PLUG_OC].fCheckLimit = 10.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_GLOW_PLUG_OC].uiCheckTime = 10U; // Value
|
||||
|
||||
AlarmOperValue[IDX_FAULT_SOLENOID_OC].fCheckLimit = 10.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_SOLENOID_OC].uiCheckTime = 10U; // Value
|
||||
|
||||
AlarmOperValue[IDX_FAULT_FUEL_PUMP_OC].fCheckLimit = 10.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_FUEL_PUMP_OC].uiCheckTime = 10U; // Value
|
||||
|
||||
AlarmOperValue[IDX_FAULT_COOLANT_PUMP_OC].fCheckLimit = 10.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_COOLANT_PUMP_OC].uiCheckTime = 10U; // Value
|
||||
|
||||
AlarmOperValue[IDX_FAULT_FAN1_OC].fCheckLimit = 10.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_FAN1_OC].uiCheckTime = 10U; // Value
|
||||
|
||||
AlarmOperValue[IDX_FAULT_FAN2_OC].fCheckLimit = 10.0f; // Value
|
||||
AlarmOperValue[IDX_FAULT_FAN2_OC].uiCheckTime = 10U; // Value
|
||||
|
||||
}
|
||||
|
||||
void CInitAdc(void)
|
||||
{
|
||||
InitAdc(); // ADC Initialize in DSP2833x_Adc.c
|
||||
|
||||
AdcRegs.ADCTRL3.bit.ADCCLKPS = 0x0; // No prescaler
|
||||
AdcRegs.ADCTRL1.bit.CPS = 0x1; // scaler 12.5Mhz
|
||||
AdcRegs.ADCTRL3.bit.SMODE_SEL = 0x0; // sequentail mode
|
||||
AdcRegs.ADCTRL1.bit.SEQ_OVRD = 0x0; // EOS
|
||||
AdcRegs.ADCTRL1.bit.SEQ_CASC = 0x1; // Cascade Sequence Mode
|
||||
|
||||
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; // Engine_Heater_I
|
||||
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; // Glow_Plug_I
|
||||
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x2; // Solenoid_I
|
||||
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x3; // Fuel_Pump_I
|
||||
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x4; // Cooling_Pump_I
|
||||
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x5; // Fan1_I
|
||||
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x6; // Fan2_I
|
||||
|
||||
AdcRegs.ADCMAXCONV.all = IDX_ADC_MAX; // Setup 16 channel conversion for cascade sequence mode
|
||||
|
||||
AdcRegs.ADCREFSEL.bit.REF_SEL = 0x1; // external Reference 2.048[V], 'b01 - 2.048, b10 - 1.500[V], 'b11 - 1.024[v]
|
||||
AdcRegs.ADCTRL2.bit.INT_MOD_SEQ1 = 0x0;
|
||||
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 0x1; // Enable SEQ1 interrupt (every EOS)
|
||||
AdcRegs.ADCTRL1.bit.ACQ_PS = 6; // Sample and hold duration(width) = (ACQ_PS + 1)
|
||||
|
||||
CInitAdcStructure();
|
||||
CInitAlarmOperValue();
|
||||
}
|
||||
|
||||
void CInitAdcStructure(void)
|
||||
{
|
||||
(void) memset(&AdcOperValue, 0, sizeof(CAdcOperValue));
|
||||
(void) memset(&Adc_EngineHeater_I, 0, sizeof(CAdcCalcValue));
|
||||
(void) memset(&Adc_GlowPlug_I, 0, sizeof(CAdcCalcValue));
|
||||
(void) memset(&Adc_Solenoid_I, 0, sizeof(CAdcCalcValue));
|
||||
(void) memset(&Adc_FuelPump_I, 0, sizeof(CAdcCalcValue));
|
||||
(void) memset(&Adc_CoolantPump_I, 0, sizeof(CAdcCalcValue));
|
||||
(void) memset(&Adc_Fan1_I, 0, sizeof(CAdcCalcValue));
|
||||
(void) memset(&Adc_Fan2_I, 0, sizeof(CAdcCalcValue));
|
||||
|
||||
AdcOperValue.uiAdcOffsetIndex = 10000U;
|
||||
|
||||
Adc_EngineHeater_I.fGain = 0.005637f;
|
||||
Adc_GlowPlug_I.fGain = 0.005637f;
|
||||
Adc_Solenoid_I.fGain = 0.005637f;
|
||||
Adc_FuelPump_I.fGain = 0.005637f;
|
||||
Adc_CoolantPump_I.fGain = 0.005637f;
|
||||
Adc_Fan1_I.fGain = 0.005637f;
|
||||
Adc_Fan2_I.fGain = 0.005637f;
|
||||
|
||||
Adc_EngineHeater_I.fOffset = -2.333f;
|
||||
Adc_GlowPlug_I.fOffset = -2.333f;
|
||||
Adc_Solenoid_I.fOffset = -2.333f;
|
||||
Adc_FuelPump_I.fOffset = -2.333f;
|
||||
Adc_CoolantPump_I.fOffset = -2.333f;
|
||||
Adc_Fan1_I.fOffset = -2.333f;
|
||||
Adc_Fan2_I.fOffset = -2.333f;
|
||||
}
|
||||
|
||||
static inline void CCalcAdcSum(CAdcCalcValue *AdcBuff)
|
||||
{
|
||||
#if 1
|
||||
AdcBuff->fSampledValue = ((float32) AdcBuff->iAdcValue * AdcBuff->fGain) + AdcBuff->fOffset;
|
||||
AdcBuff->fSampledSum += AdcBuff->fSampledValue;
|
||||
AdcBuff->uiSamplingCount++;
|
||||
if (AdcBuff->uiSamplingCount >= 100)
|
||||
{
|
||||
AdcBuff->uiSamplingCount = 0;
|
||||
AdcBuff->fSampledSum /= 100;
|
||||
AdcBuff->fLpfValue = (ADC_LPF_GAIN * AdcBuff->fSampledSum) + ((1.0f - ADC_LPF_GAIN) * AdcBuff->fLpfValue);
|
||||
AdcBuff->fSampledSum = 0.0f;
|
||||
}
|
||||
#else
|
||||
AdcBuff->fSampledValue = ((float32) AdcBuff->iAdcValue * AdcBuff->fGain) + AdcBuff->fOffset;
|
||||
AdcBuff->fLpfValue = (ADC_LPF_GAIN * AdcBuff->fSampledValue) + ((1.0f - ADC_LPF_GAIN) * AdcBuff->fLpfValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint32 CGetKey(void)
|
||||
{
|
||||
Uint16 i, ucDiv, ucMod;
|
||||
Uint32 ulGpioData = 0UL, ulKeyRead = 0UL;
|
||||
Uint16 ucKeyGpioList[7] = { 67, 39, 31, 30, 29, 66, 64};
|
||||
|
||||
for (i = 0; i < IDX_KEY_MAX; i++)
|
||||
{
|
||||
ucDiv = ucKeyGpioList[i] / 32;
|
||||
ucMod = ucKeyGpioList[i] % 32;
|
||||
|
||||
if (ucDiv == 0U) // GPIO-A
|
||||
{
|
||||
ulGpioData = GpioDataRegs.GPADAT.all;
|
||||
}
|
||||
else if (ucDiv == 1U)
|
||||
{
|
||||
ulGpioData = GpioDataRegs.GPBDAT.all;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulGpioData = GpioDataRegs.GPCDAT.all;
|
||||
}
|
||||
|
||||
if (((ulGpioData >> ucMod) & 0x01UL) == 0U) // Push Check
|
||||
{
|
||||
ulKeyRead |= (0x01UL << i);
|
||||
}
|
||||
}
|
||||
return ulKeyRead;
|
||||
}
|
||||
|
||||
void CKeyCheckProcedure(void)
|
||||
{
|
||||
static Uint32 ulLongKeyCnt = 0UL; // 롱키 카운트용 변수
|
||||
static Uint16 uiLongKeyProcessed = 0U; // 롱키 처리 완료 플래그 (중복 실행 방지)
|
||||
static Uint32 ulPrevKey = 0UL;
|
||||
Uint32 ulChangeKey;
|
||||
Uint32 ulReadKey = CGetKey();
|
||||
|
||||
ulChangeKey = (ulPrevKey ^ ulReadKey) & ~KEY_POWER_MASK; // 현재 키와 이전 키를 비교하되, 롱키(Bit 0)는 변화 감지에서 제외함 (& ~KEY_POWER_MASK)
|
||||
|
||||
if (ulChangeKey > 0UL)
|
||||
{
|
||||
if (KeyOperValue.uiKeyWait == 0U) // 채터링 무시 시작
|
||||
{
|
||||
KeyOperValue.uiKeyWait = 1U;
|
||||
KeyOperValue.uiKeyWaitCount = 20; // 20ms
|
||||
}
|
||||
else
|
||||
{
|
||||
// 전원키를 제외한 나머지 키들은 POST 단계가 넘어가야지 동작한다.
|
||||
if ((KeyOperValue.uiKeyWaitCount == 0U) && (CGetApuOperIndex() > APU_OPER_IDX_POST))
|
||||
{
|
||||
ulPrevKey = (ulPrevKey & KEY_POWER_MASK) | (ulReadKey & ~KEY_POWER_MASK); // ulPrevKey의 나머지 비트는 유지하고 변경된 비트만 갱신
|
||||
CKeyCheck(ulChangeKey, ulReadKey); // 일반 키 동작
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 변화가 없으면 채터링 대기 초기화 (일반 키용)
|
||||
// 단, 눌려있는 상태 유지를 위해 ulPrevKey 갱신은 필요 없음
|
||||
if ((KeyOperValue.uiKeyWait) != 0U && (KeyOperValue.uiKeyWaitCount == 0U))
|
||||
{
|
||||
KeyOperValue.uiKeyWait = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
// Bit 0이 눌려 있는지 확인 (1 = 눌림)
|
||||
if ((ulReadKey & KEY_POWER_MASK) == KEY_POWER_MASK)
|
||||
{
|
||||
// 이미 처리가 끝난 상태가 아니라면 카운트 증가
|
||||
if (uiLongKeyProcessed == 0U)
|
||||
{
|
||||
ulLongKeyCnt++;
|
||||
|
||||
// 1초(1000ms) 도달 시 동작 수행
|
||||
if (ulLongKeyCnt >= LONG_KEY_TIME)
|
||||
{
|
||||
CKeyCheck(KEY_POWER_MASK, ulReadKey); // 롱키 동작 수행 (CKeyCheck에 롱키 비트만 전달)
|
||||
uiLongKeyProcessed = 1U; // 떼기 전까지 다시 동작하지 않도록 플래그 설정
|
||||
ulLongKeyCnt = LONG_KEY_TIME; // 카운트 오버플로우 방지
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 키를 뗐을 때 변수들 초기화
|
||||
ulLongKeyCnt = 0UL;
|
||||
uiLongKeyProcessed = 0U;
|
||||
|
||||
// ulPrevKey의 Bit 0 상태도 0으로 동기화 (다음 비교를 위해)
|
||||
ulPrevKey &= ~KEY_POWER_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyWaitCount(void)
|
||||
{
|
||||
if (KeyOperValue.uiKeyWait == 1U)
|
||||
{
|
||||
if (KeyOperValue.uiKeyWaitCount > 0U)
|
||||
{
|
||||
KeyOperValue.uiKeyWaitCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyOperValue.uiKeyWait = 0U;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyCheck(Uint32 ulChangeKey, Uint32 ulKeyRead)
|
||||
{
|
||||
Uint16 i;
|
||||
|
||||
for (i = 0U; i < IDX_KEY_MAX; i++)
|
||||
{
|
||||
if ((ulChangeKey & (0x1UL << i)) > 0U)
|
||||
{
|
||||
if ((ulKeyRead & (0x1UL << i)) > 0U)
|
||||
{
|
||||
KeyTable[i].pAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyArrowUpProcess(void)
|
||||
{
|
||||
if (OledOperValue.uiPageNum == OLED_PAGE_APU2)
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_APU1;
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_MENU1)
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_1)
|
||||
{
|
||||
OledOperValue.uiFocusLine = OLED_LINE_FOCUS_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveFocusLine(4U, DIR_UP);
|
||||
}
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_MENU2)
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_1)
|
||||
{
|
||||
// Go back to Menu 1
|
||||
OledOperValue.uiFocusLine = OLED_LINE_FOCUS_4;
|
||||
OledOperValue.uiPageNum = OLED_PAGE_MENU1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveFocusLine(3U, DIR_UP);
|
||||
}
|
||||
}
|
||||
else if ((OledOperValue.uiPageNum > OLED_PAGE_SENSOR1) && (OledOperValue.uiPageNum <= OLED_PAGE_SENSOR4))
|
||||
{
|
||||
OledOperValue.uiPageNum = OledOperValue.uiPageNum - 1U;
|
||||
}
|
||||
else if ((OledOperValue.uiPageNum > OLED_PAGE_WARNING1) && (OledOperValue.uiPageNum <= OLED_PAGE_WARNING2))
|
||||
{
|
||||
OledOperValue.uiPageNum = OledOperValue.uiPageNum - 1U;
|
||||
}
|
||||
else if ((OledOperValue.uiPageNum > OLED_PAGE_FAULT1) && (OledOperValue.uiPageNum <= OLED_PAGE_FAULT6))
|
||||
{
|
||||
OledOperValue.uiPageNum = OledOperValue.uiPageNum - 1U;
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_PASSWORD)
|
||||
{
|
||||
CChangePasswordDigit(DIR_UP);
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_RESET_ALARM)
|
||||
{
|
||||
OledOperValue.uiResetAnswer = OledOperValue.uiResetAnswer ^ 1U; // toggle
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OledOperValue.uiPageNum == OLED_PAGE_MAINTENENCE)
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_1)
|
||||
{
|
||||
OledOperValue.uiFocusLine = OLED_LINE_FOCUS_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveFocusLine(3U, DIR_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyArrowDownProcess(void)
|
||||
{
|
||||
if (OledOperValue.uiPageNum == OLED_PAGE_APU1)
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_APU2;
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_MENU1)
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_4)
|
||||
{
|
||||
// Bottom of Menu 1 -> Go to Menu 2
|
||||
OledOperValue.uiFocusLine = OLED_LINE_FOCUS_1;
|
||||
OledOperValue.uiPageNum = OLED_PAGE_MENU2;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveFocusLine(4U, DIR_DOWN);
|
||||
}
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_MENU2)
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_3)
|
||||
{
|
||||
OledOperValue.uiFocusLine = OLED_LINE_FOCUS_3;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveFocusLine(3U, DIR_DOWN);
|
||||
}
|
||||
}
|
||||
else if ((OledOperValue.uiPageNum >= OLED_PAGE_SENSOR1) && (OledOperValue.uiPageNum < OLED_PAGE_SENSOR4))
|
||||
{
|
||||
OledOperValue.uiPageNum = OledOperValue.uiPageNum + 1U;
|
||||
}
|
||||
else if ((OledOperValue.uiPageNum >= OLED_PAGE_WARNING1) && (OledOperValue.uiPageNum < OLED_PAGE_WARNING2))
|
||||
{
|
||||
OledOperValue.uiPageNum = OledOperValue.uiPageNum + 1U;
|
||||
}
|
||||
else if ((OledOperValue.uiPageNum >= OLED_PAGE_FAULT1) && (OledOperValue.uiPageNum < OLED_PAGE_FAULT6))
|
||||
{
|
||||
OledOperValue.uiPageNum = OledOperValue.uiPageNum + 1U;
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_PASSWORD)
|
||||
{
|
||||
CChangePasswordDigit(DIR_DOWN);
|
||||
}
|
||||
else if (OledOperValue.uiPageNum == OLED_PAGE_RESET_ALARM)
|
||||
{
|
||||
OledOperValue.uiResetAnswer = OledOperValue.uiResetAnswer ^ 1U; // toggle
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OledOperValue.uiPageNum == OLED_PAGE_MAINTENENCE)
|
||||
{
|
||||
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_3)
|
||||
{
|
||||
OledOperValue.uiFocusLine = OLED_LINE_FOCUS_3;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveFocusLine(3U, DIR_DOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CChangePasswordDigit(Uint16 direction)
|
||||
{
|
||||
// Ensure the focus digit is within valid range to avoid out-of-bounds access
|
||||
if (OledOperValue.uiFocusDigit <= OLED_PASS_DIGIT_4)
|
||||
{
|
||||
Uint16 *pDigit = &GeneralOperValue.uiPassword[OledOperValue.uiFocusDigit];
|
||||
|
||||
if (direction == DIR_UP)
|
||||
{
|
||||
*pDigit = (*pDigit + 1U) % 10U;
|
||||
}
|
||||
else // DIR_DOWN
|
||||
{
|
||||
if (*pDigit == 0U)
|
||||
{
|
||||
*pDigit = 9U;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pDigit = (*pDigit - 1U) % 10U;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void MoveFocusLine(Uint16 maxLines, Uint16 direction)
|
||||
{
|
||||
if (direction == DIR_UP)
|
||||
{
|
||||
OledOperValue.uiFocusLine = (OledOperValue.uiFocusLine + maxLines - 1U) % maxLines;
|
||||
}
|
||||
else // DIR_DOWN
|
||||
{
|
||||
OledOperValue.uiFocusLine = (OledOperValue.uiFocusLine + 1U) % maxLines;
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyEnterProcess(void)
|
||||
{
|
||||
switch (OledOperValue.uiPageNum)
|
||||
{
|
||||
case OLED_PAGE_MENU1:
|
||||
{
|
||||
switch (OledOperValue.uiFocusLine)
|
||||
{
|
||||
case OLED_MENU_APU:
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_APU1;
|
||||
break;
|
||||
}
|
||||
case OLED_MENU_TEMP:
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_TEMP;
|
||||
break;
|
||||
}
|
||||
case OLED_MENU_SENSOR:
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_SENSOR1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_MENU_WARNING)
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_WARNING1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OLED_PAGE_MENU2:
|
||||
{
|
||||
switch (OledOperValue.uiFocusLine)
|
||||
{
|
||||
case OLED_LINE_FOCUS_1: // Fault
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_FAULT1;
|
||||
break;
|
||||
}
|
||||
case OLED_LINE_FOCUS_2: // Reset
|
||||
{
|
||||
OledOperValue.uiPrevFocusLine = OledOperValue.uiFocusLine;
|
||||
OledOperValue.uiPageNum = OLED_PAGE_RESET_ALARM;
|
||||
break;
|
||||
}
|
||||
case OLED_LINE_FOCUS_3: // Maintenence
|
||||
{
|
||||
OledOperValue.uiPrevFocusLine = OledOperValue.uiFocusLine;
|
||||
OledOperValue.uiPageNum = OLED_PAGE_PASSWORD;
|
||||
OledOperValue.uiFocusDigit = OLED_PASS_DIGIT_1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OLED_PAGE_PASSWORD:
|
||||
{
|
||||
if (OledOperValue.uiFocusDigit < OLED_PASS_DIGIT_4)
|
||||
{
|
||||
OledOperValue.uiFocusDigit = (OledOperValue.uiFocusDigit + 1U) % 4U;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check password
|
||||
const Uint16 uiPassword[4] = DEBUG_MENU_PASSWORD;
|
||||
|
||||
if (memcmp(GeneralOperValue.uiPassword, uiPassword, sizeof(uiPassword)) == 0U)
|
||||
{
|
||||
GeneralOperValue.uiMaintenence = 1U;
|
||||
OledOperValue.uiPageNum = OLED_PAGE_MAINTENENCE;
|
||||
OledOperValue.uiFocusLine = OLED_LINE_FOCUS_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
OledOperValue.uiFocusDigit = OLED_PASS_DIGIT_1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OLED_PAGE_RESET_ALARM:
|
||||
{
|
||||
// Selected "YES"
|
||||
if (OledOperValue.uiResetAnswer == 1U)
|
||||
{
|
||||
if (CApuSystemAlarmCheck() > 0)
|
||||
{
|
||||
GeneralOperValue.uiAlarmReset = 1U;
|
||||
OledOperValue.uiAlarmPopCheck = 0U;
|
||||
OledOperValue.uiAlreadyAlarm = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
OledOperValue.uiPageNum = OLED_PAGE_MENU2;
|
||||
break;
|
||||
}
|
||||
case OLED_PAGE_MAINTENENCE:
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_1)
|
||||
{
|
||||
GeneralOperValue.Maintenence.ManualCranking = GeneralOperValue.Maintenence.ManualCranking ^ 1U; // Toggle
|
||||
}
|
||||
else if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_2)
|
||||
{
|
||||
GeneralOperValue.Maintenence.LampTest = GeneralOperValue.Maintenence.LampTest ^ 1U; // Toggle
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OledOperValue.uiFocusLine == OLED_LINE_FOCUS_3)
|
||||
{
|
||||
GeneralOperValue.Maintenence.KeyTest = GeneralOperValue.Maintenence.KeyTest ^ 1U; // Toggle
|
||||
OledOperValue.uiPageNum = OLED_PAGE_KEY_TEST;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Handle Fault/Warning page return logic
|
||||
if ((OledOperValue.uiPageNum >= OLED_PAGE_WARNING1) && (OledOperValue.uiPageNum <= OLED_PAGE_FAULT6))
|
||||
{
|
||||
if (OledOperValue.uiAlarmPopCheck == 1U)
|
||||
{
|
||||
OledOperValue.uiAlreadyAlarm = 1U;
|
||||
OledOperValue.uiPageNum = OledOperValue.uiPrevAlarmPage;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyMenuProcess(void)
|
||||
{
|
||||
// Return to main menus from sub-pages
|
||||
if ((OledOperValue.uiPageNum == OLED_PAGE_MENU1) || (OledOperValue.uiPageNum == OLED_PAGE_MENU2))
|
||||
{
|
||||
OledOperValue.uiPageNum = OLED_PAGE_APU1;
|
||||
OledOperValue.uiFocusLine = 0U;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((OledOperValue.uiPageNum >= OLED_PAGE_FAULT1) && (OledOperValue.uiPageNum <= OLED_PAGE_MAINTENENCE))
|
||||
{
|
||||
// Return to Menu 2 from Faults or Debug
|
||||
if (OledOperValue.uiPageNum == OLED_PAGE_MAINTENENCE)
|
||||
{
|
||||
GeneralOperValue.uiMaintenence = 0U;
|
||||
OledOperValue.uiFocusLine = OledOperValue.uiPrevFocusLine;
|
||||
}
|
||||
OledOperValue.uiPageNum = OLED_PAGE_MENU2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return to Menu 1 from others (APU, Temp, Sensor, Warning)
|
||||
OledOperValue.uiPageNum = OLED_PAGE_MENU1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyMainPowerProcess(void)
|
||||
{
|
||||
if (CGetApuOperIndex() <= APU_OPER_IDX_STANDBY)
|
||||
{
|
||||
// APU가 정지 상태에서만 전원 스위치 입력 가능
|
||||
OledOperValue.uiPageNum = OLED_PAGE_SHUTDOWN;
|
||||
if (CSoftWaitCountProcedure(SOFTTIMER_WAIT_SHUTDOWN, TIME_1SEC) == TIME_OVER)
|
||||
{
|
||||
GPIO_POWER_HOLD(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CKeyEngineStartStopProcess(void)
|
||||
{
|
||||
KeyOperValue.KeyList.bit.EngineStartStop = KeyOperValue.KeyList.bit.EngineStartStop ^ 1U; // Toggle
|
||||
}
|
||||
|
||||
void CKeyEmergencyProcess(void)
|
||||
{
|
||||
// 비상정지 스위치를 클리어 하기 위해서는 APU 시스템에 알람이 없어야 한다.
|
||||
KeyOperValue.KeyList.bit.Emergency = KeyOperValue.KeyList.bit.Emergency ^ 1U; // Toggle
|
||||
}
|
||||
Reference in New Issue
Block a user