first commit

This commit is contained in:
HosuKim
2026-04-14 00:43:29 +09:00
commit 28740cdb3b
10 changed files with 7476 additions and 0 deletions

647
Source/Oper.c Normal file
View File

@@ -0,0 +1,647 @@
/* ========================================================================= */
/* 1. Includes */
/* ========================================================================= */
#include "main.h"
/* ========================================================================= */
/* 2. Local Macros & Constants (내부 전용 매크로 및 상수) */
/* ========================================================================= */
#define ENGINE_MAXIMUM_SPEED (2800U)
#define ENGINE_OPERATION_SPEED (2400U)
#define ENGINE_DIFF_SPEED (400U) // 2800 - 2400
// IDLE_SEQ_MOD
#define RPM_RAMP_START (1500U)
#define RPM_RAMP_END (2400U)
#define RPM_RAMP_RANGE (RPM_RAMP_END - RPM_RAMP_START) // 900
#define TIME_RAMP_UP (TIME_1SEC * 3UL) // 3초
#define LED_OFF (0U)
#define LED_ON (1U)
#define LED_BLINK (2U)
/* ========================================================================= */
/* 3. Local Typedefs & Structures (내부 전용 사용자 정의 자료형) */
/* ========================================================================= */
// No Code
/* ========================================================================= */
/* 4. Internal Linkage Function Declarations (내부 동작용 Static 함수 선언) */
/* ========================================================================= */
static void CInitialStandby(void);
static void CEmergencyStop(void);
static void CProcessApuStateReady(void);
static void CProcessApuStatePreheat(void);
static void CProcessApuStateCranking(void);
static void CProcessApuStateRetryCranking(void);
static void CProcessApuStateEngineIdle(void);
static void CProcessApuStateGenerating(void);
static void CProcessApuStateCooldown(void);
static void CProcessApuStateStopping(void);
static void CProcessApuStateTransition(void); // 비상/시동/정지 전이 판별용
static void CSetEngineActualRpm(Uint16 Rpm);
static float32 CGetGcuLoadPower(void);
static Uint16 CDynamicRpmControl(void);
static void CLedControl(Uint16 idx, Uint16 state);
/* ========================================================================= */
/* 5. Global Variables & Structure Initialization (전역 변수 및 구조체 초기화) */
/* ========================================================================= */
// No Code
/* ========================================================================= */
/* Function Definitions */
/* ========================================================================= */
static void CProcessApuStateReady(void)
{
// 냉각수 펌프 및 냉각팬 시작
CSetAuxCtrlPin(IDX_CS_COOLANT_PUMP, 1U);
CSetAuxCtrlPin(IDX_CS_FAN1, 1U);
CSetAuxCtrlPin(IDX_CS_FAN2, 1U);
// ECU 동작 명령 송신, 2400 RPM 설정
CSetEcuCommand((Uint16)IDX_ECU_CMD_START);
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_PREHEAT;
}
static void CProcessApuStatePreheat(void)
{
if (((Rx301.State >> 1U) & 0x07U) == (Uint16)IDX_ECU_STAT_STARTING)
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_CRANKING;
}
else
{
// PRE HEAT 상태가 60초 이상 지속 될 경우 알람처리
if (CSoftWaitCountProcedure(SOFTTIMER_WAIT_PREHEAT, TIME_60SEC) == (Uint16)TIME_OVER)
{
// 알람처리를 할지 무기한 대기 할 지 검토 필요
}
}
}
static void CProcessApuStateCranking(void)
{
CSetGcuCommand((Uint16)IDX_GCU_CMD_CRANKING);
if (((Rx301.State >> 1U) & 0x07U) == (Uint16)IDX_ECU_STAT_IDLE)
{
CSetGcuCommand((Uint16)IDX_GCU_CMD_STOP_CRANKING);
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_ENGINE_IDLE;
GeneralOperValue.uiRetryCrankingCount = 0U;
CSoftWaitCountClear(SOFTTIMER_WAIT_CRANKING);
}
else
{
// 10초간 시동 시도 → 5초 동안 휴식 → 3회 반복
if (CSoftWaitCountProcedure(SOFTTIMER_WAIT_CRANKING, TIME_10SEC) == (Uint16)TIME_OVER)
{
CSetGcuCommand((Uint16)IDX_GCU_CMD_STOP_CRANKING);
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_RETRY_CRANKING;
CSoftWaitCountClear(SOFTTIMER_WAIT_RETRY_CRANKING);
}
}
}
static void CProcessApuStateRetryCranking(void)
{
if (GeneralOperValue.uiRetryCrankingCount < 3U)
{
// 5초 대기 후 재시도
if (CSoftWaitCountProcedure(SOFTTIMER_WAIT_RETRY_CRANKING, (TIME_1SEC * 5UL)) == (Uint16)TIME_OVER)
{
GeneralOperValue.uiRetryCrankingCount++;
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_CRANKING;
CSoftWaitCountClear(SOFTTIMER_WAIT_CRANKING);
}
}
else
{
ulDcuTotalAlarm = (1UL << (Uint32)(Uint16)IDX_FAULT_DCU_CRANKING_FAIL);
}
}
static void CProcessApuStateEngineIdle(void)
{
#if 0 // '0' - 기존 코드, '1' - IDLE_SEQ_MOD
if (((Rx301.State >> 1U) & 0x07U) == (Uint16)IDX_ECU_STAT_OPERATION)
{
// 보조엔진제어기의 상태가 OPERATION이고 보조엔진의 속도가 2300 RPM 이상 5초 유지 시 발전상태로 전환
if (CGetEngineActualRpm() >= (ENGINE_OPERATION_SPEED - 100U)) // 2300 RPM
{
if (CSoftWaitCountProcedure(SOFTTIMER_WAIT_OPERATION, TIME_5SEC) == (Uint16)TIME_OVER)
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_GENERATING;
}
}
else
{
CSoftWaitCountClear(SOFTTIMER_WAIT_OPERATION);
}
}
#else
if (((Rx301.State >> 1U) & 0x07U) == (Uint16)IDX_ECU_STAT_OPERATION)
{
// ECU OPERATION 확인 즉시 발전 상태로 전환 (RPM Ramp는 CProcessApuStateGenerating에서 수행)
GeneralOperValue.ulRampStartClock = 0UL;
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_GENERATING;
}
#endif
}
static void CProcessApuStateGenerating(void)
{
#if 0 // '0' - 기존 코드, '1' - IDLE_SEQ_MOD
CSetGcuCommand((Uint16)IDX_GCU_CMD_GENERATING); // 발전 명령 송신
GeneralOperValue.uiDynamicRPM = CDynamicRpmControl();
CSetEngineActualRpm(GeneralOperValue.uiDynamicRPM); // RPM 가변 제어 시작
#else
Uint32 ulElapsed;
Uint16 uiRampRpm;
CSetGcuCommand((Uint16)IDX_GCU_CMD_GENERATING);
if (GeneralOperValue.uiRampComplete == 1U)
{
GeneralOperValue.uiDynamicRPM = CDynamicRpmControl();
CSetEngineActualRpm(GeneralOperValue.uiDynamicRPM);
}
else
{
if (GeneralOperValue.ulRampStartClock == 0UL)
{
GeneralOperValue.ulRampStartClock = CGetSoftClock();
CSetEngineActualRpm(RPM_RAMP_START);
}
else
{
ulElapsed = (CGetSoftClock() + SYSTEM_10MIN_TIME - GeneralOperValue.ulRampStartClock) % SYSTEM_10MIN_TIME;
if (ulElapsed < TIME_RAMP_UP)
{
uiRampRpm = (Uint16)(RPM_RAMP_START + (Uint16)(((Uint32)RPM_RAMP_RANGE * ulElapsed) / TIME_RAMP_UP));
CSetEngineActualRpm(uiRampRpm);
}
else
{
GeneralOperValue.uiRampComplete = 1U;
GeneralOperValue.uiDynamicRPM = CDynamicRpmControl();
CSetEngineActualRpm(GeneralOperValue.uiDynamicRPM);
}
}
}
#endif
}
static void CProcessApuStateCooldown(void)
{
Uint16 IsRpmZero;
Uint16 IsTimeout;
// 쿨다운: 발전 중지 -> 엔진 IDLE로 변경
CSetGcuCommand((Uint16)IDX_GCU_CMD_STOP);
CSetEcuCommand((Uint16)IDX_ECU_CMD_STOP);
IsRpmZero = (CGetEngineActualRpm() == 0U) ? 1U : 0U;
IsTimeout = (CSoftWaitCountProcedure(SOFTTIMER_WAIT_AFTER_COOLDOWN, TIME_60SEC) == 1U) ? 1U : 0U;
if ((IsRpmZero == 1U) || (IsTimeout == 1U))
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_STOPPING;
}
}
static void CProcessApuStateStopping(void)
{
if (GeneralOperValue.uiApuState == (Uint16)IDX_APU_OPER_STOPPING)
{
CInitialStandby();
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_STANDBY;
}
}
static void CProcessApuStateTransition(void)
{
if (GeneralOperValue.uiApuState == (Uint16)IDX_APU_OPER_EMERGENCY)
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_STANDBY;
CInitialStandby();
}
if (GeneralOperValue.uiApuState == (Uint16)IDX_APU_OPER_STANDBY)
{
if (KeyOperValue.KeyList.EngineStartStop == 1U)
{
GeneralOperValue.uiRetryCrankingCount = 0U;
if ((GeneralOperValue.Conection.Gcu == 1U) && (GeneralOperValue.Conection.Ecu == 1U))
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_READY;
}
else
{
CommCheck.Gcu = (GeneralOperValue.Conection.Gcu == 0U) ? COMM_TIME_OUT_COUNT : 0U;
CommCheck.Ecu = (GeneralOperValue.Conection.Ecu == 0U) ? COMM_TIME_OUT_COUNT : 0U;
}
}
}
else
{
if ((GeneralOperValue.uiApuState >= (Uint16)IDX_APU_OPER_READY) && (GeneralOperValue.uiApuState <= (Uint16)IDX_APU_OPER_GENERATING))
{
if (KeyOperValue.KeyList.EngineStartStop == 0U)
{
if (GeneralOperValue.uiApuState == (Uint16)IDX_APU_OPER_GENERATING)
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_COOLDOWN;
}
else
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_STOPPING;
}
}
}
}
}
void CApuOperProcedure(void)
{
// 입력 신호 Lo Active
Uint16 EngineHeaterSig = (GPIO_ENGINE_HEATER_ACTIVE() == false) ? 1U : 0U;
Uint16 FuelPumpSig = (GPIO_FUEL_PUMP_ACTIVE() == false) ? 1U : 0U;
Uint16 GlowPlugSig = (GPIO_GLOW_PLUG_ACTIVE() == false) ? 1U : 0U;
Uint16 SolenoidSig = (GPIO_SOLENOID_ACTIVE() == false) ? 1U : 0U;
Uint16 FailSafeSig = (GPIO_FAIL_SAFE_READ() == false) ? 1U : 0U;
// 비상 상황 체크
if ((GeneralOperValue.uiFaultOccured > 0U) || (KeyOperValue.KeyList.Emergency == 1U) || (FailSafeSig == 1U))
{
GeneralOperValue.uiApuState = (Uint16)IDX_APU_OPER_EMERGENCY;
CEmergencyStop();
}
else
{
// 외부 조작에 의한 상태 변경 확인
CProcessApuStateTransition();
// ECU Aux Bypass 제어
if (GeneralOperValue.uiApuState > (Uint16)IDX_APU_OPER_STANDBY)
{
CSetAuxCtrlPin(IDX_CS_ENG_HEATER, EngineHeaterSig);
CSetAuxCtrlPin(IDX_CS_GLOW_PLUG, GlowPlugSig);
CSetAuxCtrlPin(IDX_CS_SOLENOID, SolenoidSig);
CSetAuxCtrlPin(IDX_CS_FUEL_PUMP, FuelPumpSig);
}
// 각 상태별 동작 수행
switch (GeneralOperValue.uiApuState)
{
case (Uint16)IDX_APU_OPER_READY:
{
CProcessApuStateReady();
break;
}
case (Uint16)IDX_APU_OPER_PREHEAT:
{
CProcessApuStatePreheat();
break;
}
case (Uint16)IDX_APU_OPER_CRANKING:
{
CProcessApuStateCranking();
break;
}
case (Uint16)IDX_APU_OPER_RETRY_CRANKING:
{
CProcessApuStateRetryCranking();
break;
}
case (Uint16)IDX_APU_OPER_ENGINE_IDLE:
{
CProcessApuStateEngineIdle();
break;
}
case (Uint16)IDX_APU_OPER_GENERATING:
{
CProcessApuStateGenerating();
break;
}
case (Uint16)IDX_APU_OPER_COOLDOWN:
{
CProcessApuStateCooldown();
break;
}
default:
{
CProcessApuStateStopping();
break;
}
}
}
}
static Uint16 CDynamicRpmControl(void)
{
float32 TargetRPM;
Uint16 ReturnRpm;
if (GeneralOperValue.uiApuState == (Uint16)IDX_APU_OPER_GENERATING)
{
// 0.0kW~17.0kW 부하량에 따라 목표 RPM 실시간 계산
TargetRPM = (float32)ENGINE_OPERATION_SPEED + ((CGetGcuLoadPower() * 0.058823F) * (float32)ENGINE_DIFF_SPEED); // 0.058823 = 1/17kw
ReturnRpm = (Uint16)((float32)(TargetRPM + 0.5F)); // 소수점 반올림
}
else
{
// 발전 상태가 아닐 때는 기본 2400 RPM 반환
ReturnRpm = ENGINE_OPERATION_SPEED;
}
ReturnRpm = (ENGINE_MAXIMUM_SPEED > ReturnRpm) ? ReturnRpm : ENGINE_MAXIMUM_SPEED;
return ReturnRpm;
}
static void CInitialStandby(void)
{
CSetGcuCommand((Uint16)IDX_GCU_CMD_STOP);
CSetEcuCommand((Uint16)IDX_ECU_CMD_STOP);
COffChipSelect();
CSoftWaitCountClear(SOFTTIMER_WAIT_CRANKING);
CSoftWaitCountClear(SOFTTIMER_WAIT_RETRY_CRANKING);
CSoftWaitCountClear(SOFTTIMER_WAIT_PREHEAT);
CSoftWaitCountClear(SOFTTIMER_WAIT_PREHEAT);
CSoftWaitCountClear(SOFTTIMER_WAIT_OPERATION);
CSoftWaitCountClear(SOFTTIMER_WAIT_AFTER_COOLDOWN);
GeneralOperValue.uiEmergency = 0U;
GeneralOperValue.ulRampStartClock = 0UL; // IDLE_SEQ_MOD
GeneralOperValue.uiRampComplete = 0U; // IDLE_SEQ_MOD
GpioDataRegs.GPBCLEAR.bit.GPIO55 = 1U; // GPIO_FAULT_CMD
}
static void CEmergencyStop(void)
{
KeyOperValue.KeyList.EngineStartStop = 0U; // 비상정지 상황에서는 시동/정지 키 초기화
CSetGcuCommand((Uint16)IDX_GCU_CMD_STOP);
CSetEcuCommand((Uint16)IDX_ECU_CMD_EMERGENCY);
COffChipSelect();
CSoftWaitCountClear(SOFTTIMER_WAIT_CRANKING);
CSoftWaitCountClear(SOFTTIMER_WAIT_RETRY_CRANKING);
CSoftWaitCountClear(SOFTTIMER_WAIT_AFTER_COOLDOWN);
GeneralOperValue.uiEmergency = 1U;
GeneralOperValue.ulRampStartClock = 0UL; // IDLE_SEQ_MOD
GeneralOperValue.uiRampComplete = 0U; // IDLE_SEQ_MOD
GpioDataRegs.GPBSET.bit.GPIO55 = 1U; //GPIO_FAULT_CMD
}
static void CSetEngineActualRpm(Uint16 Rpm)
{
GeneralOperValue.EcuCommand.RpmSetPoint = Rpm;
}
Uint16 CGetEngineActualRpm(void)
{
return (Uint16)Rx320.ActualRpm;
}
static float32 CGetGcuLoadPower(void)
{
float32 power = ((float32)Rx220.Power * 0.1F);
// 범위를 0.0 ~ 17.0 으로 제한
if (power > 17.0F)
{
power = 17.0F;
}
else
{
if (power < 0.0F)
{
power = 0.0;
}
}
return power;
}
Uint16 CGetGeneratorRpm(void)
{
return Rx220.Rpm;
}
void CSetGcuCommand(Uint16 Command)
{
GeneralOperValue.GcuCommand.PlayCmd = Command;
}
void CSetEcuCommand(Uint16 Command)
{
if ((Command == (Uint16)IDX_ECU_CMD_STOP) || (Command == (Uint16)IDX_ECU_CMD_EMERGENCY))
{
GeneralOperValue.EcuCommand.EngineStart = 0U;
GeneralOperValue.EcuCommand.EngineStop = 1U;
CSetEngineActualRpm(0U);
}
else
{
// [ECU_OPER_CMD_START]
GeneralOperValue.EcuCommand.EngineStart = 1U;
GeneralOperValue.EcuCommand.EngineStop = 0U;
#if 0 // RPM 테스트
CSetEngineActualRpm(Rx400.SetRPM.PCAN_RPM);
#else
//CSetEngineActualRpm(2400U);
CSetEngineActualRpm(1500U); // Target Idle
#endif
}
}
int16 CGetEngCoolantTemperature(void)
{
return (int16) Rx321.CoolantTemperature - 40; // 온도 오프셋 -40도
}
void CDebugModeProcedure(void)
{
if (GeneralOperValue.Maintenance.ManualCranking == 1U)
{
if (GeneralOperValue.uiFaultOccured == 0U)
{
// 알람이 없을 경우만 동작 하도록 함.
CSetGcuCommand((Uint16)IDX_GCU_CMD_CRANKING);
}
}
else
{
CSetGcuCommand((Uint16)IDX_GCU_CMD_STOP);
}
if (GeneralOperValue.Maintenance.LampTest == 1U)
{
CLedControl(0U, 1U);
CLedControl(1U, 1U);
CLedControl(2U, 1U);
}
else
{
CLedControl(0U, 0U);
CLedControl(1U, 0U);
CLedControl(2U, 0U);
}
if (GeneralOperValue.Maintenance.KeyTest == 1U)
{
Uint16 uiKeyUp = (GPIO_KEY_UP() == 0U) ? 1U : 0U;
Uint16 uiKeyDn = (GPIO_KEY_DOWN() == 0U) ? 1U : 0U;
if ((uiKeyUp == 1U) && (uiKeyDn == 1U))
{
GeneralOperValue.Maintenance.KeyTest = 0U;
OledOperValue.uiPageNum = (Uint16)IDX_OLED_PAGE_MAINTENANCE;
}
}
}
void CLedControlProcedure(void)
{
static const CLedPattern APU_LED_TABLE[] = // LED 룩업 테이블
{
// FAULT, OPER, STOP
{LED_OFF, LED_OFF, LED_ON }, // 0: BOOT
{LED_OFF, LED_OFF, LED_ON }, // 1: INITIAL
{LED_OFF, LED_OFF, LED_ON }, // 2: POST
{LED_ON, LED_OFF, LED_ON }, // 3: EMERGENCY
{LED_OFF, LED_OFF, LED_ON }, // 4: STANDBY
// --- OPER 깜빡임 구간 (준비~예열) ---
{LED_OFF, LED_BLINK, LED_OFF }, // 5: READY
{LED_OFF, LED_BLINK, LED_OFF }, // 6: PREPARE_START
{LED_OFF, LED_BLINK, LED_OFF }, // 7: CRANKING
{LED_OFF, LED_BLINK, LED_OFF }, // 8: RETRY_CRANKING
{LED_OFF, LED_BLINK, LED_OFF }, // 9: ENGINE_WARM_UP
{LED_OFF, LED_ON, LED_OFF }, // 10: GENERATING (정상 운전)
// --- STOP 깜빡임 구간 (APU 정지 시) ---
{LED_OFF, LED_ON, LED_BLINK }, // 11: COOLDOWN (STOP 깜빡임, OPER는 켜둠)
{LED_OFF, LED_ON, LED_BLINK } // 12: STOPPING (COOLDWON이 순식간에 지나가기 때문에 점멸로 수정)
};
CLedPattern TargetLeds = {0, 0, 0};
Uint64 SoftClock = CGetSoftClock();
Uint16 IsBlinkOn = ((SoftClock % 10000U) < 5000U) ? 1U : 0U; // 0.5s 점멸을 위함
Uint16 WarningValue = 0U;
TargetLeds = APU_LED_TABLE[GeneralOperValue.uiApuState];
// 발전상태에서 경고 발생시 FAULT LED 깜빡이도록 설정
if (GeneralOperValue.uiApuState == (Uint16)IDX_APU_OPER_GENERATING)
{
WarningValue = ((((Uint16)Rx210.GcuWarning & (Uint16)MASK_LOW_NIBBLE) | ((Uint16)Rx310.EcuWarning & 0xFDU)) > 0U) ? 1U : 0U;
}
// 비상정지 상태가 아닌 경우에만 경고비트에 점멸 대응
if ((GeneralOperValue.uiApuState != (Uint16)IDX_APU_OPER_EMERGENCY) && (WarningValue == 1U))
{
TargetLeds.Fault = (Uint16)LED_BLINK;
}
// FAULT LED 제어
if (TargetLeds.Fault == (Uint16)LED_BLINK)
{
CLedControl(0U, IsBlinkOn);
}
else
{
CLedControl(0U, TargetLeds.Fault);
}
// OPERATION LED 제어
if (TargetLeds.Operation == (Uint16)LED_BLINK)
{
CLedControl(1U, IsBlinkOn);
}
else
{
CLedControl(1U, TargetLeds.Operation);
}
// STOP LED 제어
if (TargetLeds.Stop == (Uint16)LED_BLINK)
{
CLedControl(2U, IsBlinkOn);
}
else
{
CLedControl(2U, TargetLeds.Stop);
}
}
static void CLedControl(Uint16 idx, Uint16 state)
{
/*
* idx
* 0 : FAULT LED
* 1 : OPER LED
* 2 : STOP LED
*/
if (idx == 0U)
{
// GPIO_CPU_LED_FAULT
if (state == 0U)
{
GpioDataRegs.GPACLEAR.bit.GPIO14 = 1U;
}
else
{
GpioDataRegs.GPASET.bit.GPIO14 = 1U;
}
}
else if (idx == 1U)
{
// GPIO_CPU_LED_OPERATION
if (state == 0U)
{
GpioDataRegs.GPACLEAR.bit.GPIO13 = 1U;
}
else
{
GpioDataRegs.GPASET.bit.GPIO13 = 1U;
}
}
else
{
// GPIO_CPU_LED_STOP
if (state == 0U)
{
GpioDataRegs.GPACLEAR.bit.GPIO12 = 1U;
}
else
{
GpioDataRegs.GPASET.bit.GPIO12 = 1U;
}
}
}