Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a30cded074 | |||
| 0514940877 | |||
| aae23f53a6 |
+31
-2
@@ -109,7 +109,7 @@
|
||||
VALUE_ENTRY(c3udc, "V", 2035 ) \
|
||||
VALUE_ENTRY(c3idc, "A", 2036 ) \
|
||||
VALUE_ENTRY(tmpobcmax, "°C", 2051 ) \
|
||||
VALUE_ENTRY(vcustop, OFFON, 2052 ) \
|
||||
VALUE_ENTRY(vcucmd, VCUCMDS, 2052 ) \
|
||||
VALUE_ENTRY(test_time, "s", 3000 ) \
|
||||
VALUE_ENTRY(test_timer_flag, "X", 3001 ) \
|
||||
VALUE_ENTRY(test_timer_icvalue, "X", 3002 ) \
|
||||
@@ -119,6 +119,12 @@
|
||||
#define OPMODES "0=Off, 1=Run"
|
||||
#define CHARGERS "1=Charger1, 2=Charger2, 4=Charger3"
|
||||
#define OFFON "0=Off, 1=On"
|
||||
// SKUDAK-S12: VCU→charger control command on 0x212 byte 3. Mirrors the Dilong
|
||||
// path's OBC_ControlCMD vocabulary exactly so VCU code paths can be reused.
|
||||
// 0 = Charging — VCU wants current to flow; walk to / hold at EVSEACTIVATE
|
||||
// 1 = Stopped — VCU is not driving a cycle; sit in OFF/WAITSTART/ENABLE
|
||||
// 2 = Complete — VCU has terminated the cycle; drop to STOP
|
||||
#define VCUCMDS "0=Charging, 1=Stopped, 2=Complete"
|
||||
#define CHFLAGS "0=None, 1=Enabled, 2=Fault, 4=CheckAlive"
|
||||
#define STATES "0=Off, 1=WaitStart, 2=Enable, 3=Activate, 4=Run, 5=Stop"
|
||||
#define INPUTS "0=Type2, 1=Type2-3P, 2=Type1, 3=Manual, 4=Manual-3P, 5=Type2-Auto"
|
||||
@@ -151,7 +157,18 @@
|
||||
// it sets vcustop=1; ChargerStateMachine's EVSEACTIVATE case transitions to
|
||||
// STOP immediately, the J1772 CP signal drops, EVSE stops delivering AC,
|
||||
// VCU_IsCharging() returns 0, water pumps power down naturally.
|
||||
#define VERSTR STRINGIFY(4=VER-S11)
|
||||
// -S12 promotes byte 3 from a binary vcustop flag to the full 3-state
|
||||
// VCUCMDS vocabulary (0=Charging, 1=Stopped, 2=Complete), matching the
|
||||
// Dilong OBC_ControlCMD exactly. The internal FSM now obeys vcucmd for
|
||||
// lifecycle transitions: ENABLE only advances to ACTIVATE on Charging, STOP
|
||||
// returns to OFF when vcucmd flips back to Charging/Stopped (fixes the
|
||||
// "raise ChargeLimit mid-COMPLETE doesn't resume" wedge — teslacharger was
|
||||
// stuck in STOP because STOP only exited on CheckUnplugged). The self-
|
||||
// decided EVSEACTIVATE exits (CheckVoltage, CheckTimeout) are stripped; the
|
||||
// VCU is now authoritative for when charging starts and stops. The charger
|
||||
// retains hardware-level wisdom: CheckUnplugged() still forces OFF from any
|
||||
// state, and CheckStartCondition() still gates OFF→WAITSTART on EVSE pilot.
|
||||
#define VERSTR STRINGIFY(4=VER-S12)
|
||||
|
||||
/***** enums ******/
|
||||
|
||||
@@ -175,6 +192,18 @@ enum states
|
||||
STOP
|
||||
};
|
||||
|
||||
// SKUDAK-S12: VCU→charger command vocabulary on 0x212 byte 3. Must match
|
||||
// the values exposed via VCUCMDS enum string AND the VCU's
|
||||
// VCU_OIOBCCommand_t enum (stm32-hal-vcu Core/Inc/applications/charger/
|
||||
// charger_types.h). Updating one without the others silently breaks the
|
||||
// charge state machine, so keep them in lock-step.
|
||||
enum vcucmds
|
||||
{
|
||||
VCUCMD_CHARGING = 0,
|
||||
VCUCMD_STOPPED = 1,
|
||||
VCUCMD_COMPLETE = 2
|
||||
};
|
||||
|
||||
enum _canspeeds
|
||||
{
|
||||
CAN_PERIOD_100MS = 0,
|
||||
|
||||
+107
-12
@@ -299,10 +299,26 @@ void CalcAcCurrentLimit()
|
||||
Param::SetFloat(Param::aclim, iacLim);
|
||||
}
|
||||
|
||||
// SKUDAK-S12: VCU is authoritative for charge-cycle lifecycle. The charger
|
||||
// keeps hardware-level wisdom (EVSE pilot detection, plug-out safety,
|
||||
// fault detection, current ramping) but obeys vcucmd for "should I be
|
||||
// charging right now?" — exactly mirroring the Dilong OBC_ControlCMD
|
||||
// pattern so the VCU's two charger paths can share code/state.
|
||||
//
|
||||
// VCUCMDS vocabulary (see include/param_prj.h):
|
||||
// 0 Charging — VCU wants current; walk to / hold at EVSEACTIVATE
|
||||
// 1 Stopped — VCU has no active cycle; sit idle (no current)
|
||||
// 2 Complete — VCU has terminated the cycle; drop to STOP
|
||||
//
|
||||
// Self-decided exits from EVSEACTIVATE (CheckVoltage, CheckTimeout) are
|
||||
// gone — those were band-aids for the absence of an authoritative VCU
|
||||
// stop signal. CheckUnplugged() still owns hardware safety from every
|
||||
// state; CheckChargerFaults() still bails on real module faults.
|
||||
void ChargerStateMachine()
|
||||
{
|
||||
static states state = OFF;
|
||||
int configuredChargers = Param::GetInt(Param::chargerena);
|
||||
int vcucmd = Param::GetInt(Param::vcucmd);
|
||||
|
||||
if (!Param::GetBool(Param::enable))
|
||||
{
|
||||
@@ -316,6 +332,9 @@ void ChargerStateMachine()
|
||||
Param::SetInt(Param::opmode, 0);
|
||||
DisableAll();
|
||||
|
||||
// OFF→WAITSTART requires hardware-level start condition (EVSE pilot
|
||||
// or non-EVSE input). vcucmd is checked downstream — we still walk
|
||||
// up to ENABLE on plug-in so the modules are ready when VCU says go.
|
||||
if (CheckStartCondition())
|
||||
{
|
||||
startTime = rtc_get_counter_val();
|
||||
@@ -334,7 +353,15 @@ void ChargerStateMachine()
|
||||
DigIo::ch2ena_out.Set();
|
||||
if (configuredChargers & 4)
|
||||
DigIo::ch3ena_out.Set();
|
||||
state = ACTIVATE;
|
||||
|
||||
// Hold here until VCU says go (vcucmd=Charging) or terminates
|
||||
// (vcucmd=Complete). Stopped keeps us armed but idle.
|
||||
if (vcucmd == VCUCMD_COMPLETE)
|
||||
state = STOP;
|
||||
else if (vcucmd == VCUCMD_CHARGING)
|
||||
state = ACTIVATE;
|
||||
else if (CheckUnplugged())
|
||||
state = OFF;
|
||||
break;
|
||||
case ACTIVATE:
|
||||
Param::SetInt(Param::opmode, 1);
|
||||
@@ -353,21 +380,81 @@ void ChargerStateMachine()
|
||||
DigIo::evseact_out.Set();
|
||||
DigIo::acpres_out.Set();
|
||||
|
||||
// SKUDAK-S11: explicit VCU stop command takes priority over every
|
||||
// other exit condition. When the VCU's BMS-side gate determines the
|
||||
// charge session is complete (or the user lowered ChargeLimit below
|
||||
// current SOC), it sets vcustop=1 on 0x212 byte 3. We obey
|
||||
// immediately — drop AC pres + EVSE-active GPIOs and transition to
|
||||
// STOP. Mirrors how the Dilong path's OBC_ControlCMD = Stopped works.
|
||||
// Replaces the brittle udclim=200V mechanism from -S10.
|
||||
if (Param::GetInt(Param::vcustop))
|
||||
// VCU command is the primary exit. CheckUnplugged + CheckChargerFaults
|
||||
// remain for hardware safety; the self-decided CheckVoltage/CheckTimeout
|
||||
// exits are gone (S12 — VCU is authoritative).
|
||||
//
|
||||
// SKUDAK: ramp the DC-current reference toward zero across 3 ticks
|
||||
// (300 ms) before transitioning to STOP. Without this, the VCU
|
||||
// opening its HV contactor at the moment we go STOP triggers a hard
|
||||
// 23 A → 0 disconnect transient that floods the Tesla BMS state-
|
||||
// change CAN path and starves the VCU's task scheduler past its
|
||||
// IWDG window (bench-confirmed 2026-05-21 limit-lower 0x002A loop).
|
||||
// The VCU also defers its contactor open by ~1500 ms; together
|
||||
// they make the disconnect graceful even on a hard limit-lower
|
||||
// mid-RUN. completeRampTicks counts DOWN from 3 → 0; the original
|
||||
// reference is stashed so if vcucmd reverts to Charging mid-ramp
|
||||
// we restore cleanly.
|
||||
static uint8_t completeRampTicks = 0;
|
||||
static s32fp completeRampOriginalRef = 0;
|
||||
if (vcucmd == VCUCMD_COMPLETE)
|
||||
{
|
||||
if (completeRampTicks == 0)
|
||||
{
|
||||
completeRampOriginalRef = MIN(Param::Get(Param::idcspnt), Param::Get(Param::idclim));
|
||||
completeRampTicks = 3;
|
||||
}
|
||||
if (completeRampTicks > 1)
|
||||
{
|
||||
// Scale 2/3 → 1/3 → 0 across remaining ticks.
|
||||
s32fp scaled = (completeRampOriginalRef * (completeRampTicks - 1)) / 3;
|
||||
dcCurController.SetRef(scaled);
|
||||
completeRampTicks--;
|
||||
// Stay in EVSEACTIVATE this tick; current is decaying.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Final ramp tick: clear GPIOs (cuts AC enable; output decays
|
||||
// through filter caps) and restore the PI reference to the
|
||||
// current idcspnt/idclim so the NEXT EVSEACTIVATE entry
|
||||
// doesn't sip at 0 A. Param::Change only fires on parameter
|
||||
// changes; a normal restart cycle never touches idcspnt or
|
||||
// idclim, so without this restore the PI controller stays
|
||||
// pinned at 0 across the cycle. Iteration O bug — bench-
|
||||
// confirmed 0.1 kW sip on limit-raise from COMPLETE.
|
||||
DigIo::acpres_out.Clear();
|
||||
DigIo::evseact_out.Clear();
|
||||
dcCurController.SetRef(MIN(Param::Get(Param::idcspnt), Param::Get(Param::idclim)));
|
||||
completeRampTicks = 0;
|
||||
completeRampOriginalRef = 0;
|
||||
state = STOP;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// vcucmd not Complete — if we were mid-ramp the user reversed
|
||||
// (raised limit again); restore reference before the existing
|
||||
// branches take over.
|
||||
if (completeRampTicks != 0)
|
||||
{
|
||||
dcCurController.SetRef(completeRampOriginalRef);
|
||||
completeRampTicks = 0;
|
||||
completeRampOriginalRef = 0;
|
||||
}
|
||||
|
||||
if (vcucmd == VCUCMD_STOPPED)
|
||||
{
|
||||
// VCU revoked the charge command (e.g. user lowered limit below
|
||||
// current SOC but BMS isn't full yet). Park HV-armed in ENABLE
|
||||
// ready to resume on the next Charging command.
|
||||
DigIo::ch1act_out.Clear();
|
||||
DigIo::ch2act_out.Clear();
|
||||
DigIo::ch3act_out.Clear();
|
||||
DigIo::acpres_out.Clear();
|
||||
DigIo::evseact_out.Clear();
|
||||
state = STOP;
|
||||
Param::SetInt(Param::opmode, 0);
|
||||
state = ENABLE;
|
||||
}
|
||||
else if (CheckVoltage() || CheckTimeout())
|
||||
state = STOP;
|
||||
else if (CheckUnplugged())
|
||||
{
|
||||
DigIo::acpres_out.Clear();
|
||||
@@ -386,6 +473,14 @@ void ChargerStateMachine()
|
||||
|
||||
if (CheckUnplugged())
|
||||
state = OFF;
|
||||
// S12 fix for the "raise ChargeLimit mid-COMPLETE wedges in STOP"
|
||||
// bug: when VCU clears Complete (latch released because user raised
|
||||
// limit above current SOC), drop to OFF so the natural OFF→WAITSTART
|
||||
// →ENABLE→ACTIVATE→EVSEACTIVATE flow restarts charging next tick.
|
||||
// Previously STOP only exited on physical unplug — wedge on every
|
||||
// limit-raise.
|
||||
else if (vcucmd != VCUCMD_COMPLETE)
|
||||
state = OFF;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+13
-7
@@ -100,7 +100,7 @@ void ChargerCAN::MapMessages(CanMap* can)
|
||||
can->AddSend(Param::aclim, 0x44c, 16, 16, 1500);
|
||||
can->AddSend(Param::opmode, 0x44c, 32, 8, 154, 100);
|
||||
|
||||
/***** SKUDAK VCU command RX (0x212) — SKUDAK-516 chglim + S10 udclim + S11 stop *****/
|
||||
/***** SKUDAK VCU command RX (0x212) — SKUDAK-516 chglim + S10 udclim + S12 vcucmd *****/
|
||||
// Byte 0: ChargeLimit_pct (0-100). Param::Change(vcuchglim) translates
|
||||
// % to udcspnt setpoint (see src/main.cpp).
|
||||
// Bytes 1-2: udclim_V (uint16 LE). DEPRECATED in -S11 but mapping kept for
|
||||
@@ -111,17 +111,23 @@ void ChargerCAN::MapMessages(CanMap* can)
|
||||
// contactors, never accumulating 10 ticks > udclim. Defensive
|
||||
// clamp in Param::Change(udclim) ignores values < 50 V (would
|
||||
// otherwise turn an old-VCU bench into a never-charges brick).
|
||||
// Byte 3: vcustop (uint8, NEW in -S11). 0 = no override / normal state
|
||||
// machine. 1 = VCU commands STOP. Checked at the top of
|
||||
// EVSEACTIVATE in ChargerStateMachine; transition to STOP fires
|
||||
// within one 100 ms tick. Mirrors how the Dilong path uses
|
||||
// OBC_ControlCMD = Stopped to authoritatively halt charging.
|
||||
// Byte 3: vcucmd (uint8, S12 — was binary vcustop in S11). Three states
|
||||
// mirror the Dilong OBC_ControlCMD vocabulary 1:1 so VCU code
|
||||
// paths can be reused across charger families:
|
||||
// 0 = Charging — walk to / hold at EVSEACTIVATE, produce current
|
||||
// 1 = Stopped — sit idle in OFF/WAITSTART/ENABLE, no current
|
||||
// 2 = Complete — VCU declares cycle done; drop to STOP
|
||||
// VCU is now authoritative for lifecycle. Self-decided
|
||||
// EVSEACTIVATE exits (CheckVoltage, CheckTimeout) are gone;
|
||||
// STOP also exits to OFF when vcucmd flips back from Complete
|
||||
// to Stopped/Charging so the natural restart path runs (fixes
|
||||
// the "raise limit mid-COMPLETE doesn't resume" wedge in S11).
|
||||
// Bytes 4-7: reserved 0x00.
|
||||
// VCU broadcasts every 200 ms once teslacharger detected; charger holds
|
||||
// last received values if VCU goes silent.
|
||||
can->AddRecv(Param::vcuchglim, 0x212, 0, 8, 1);
|
||||
can->AddRecv(Param::udclim, 0x212, 8, 16, 1);
|
||||
can->AddRecv(Param::vcustop, 0x212, 24, 8, 1);
|
||||
can->AddRecv(Param::vcucmd, 0x212, 24, 8, 1);
|
||||
|
||||
/***** SKUDAK VCU telemetry (0x211) — SKUDAK-448 expanded charger telemetry *****/
|
||||
// Byte 0: state (0=Off, 1=WaitStart, 2=Enable, 3=Activate, 4=Run, 5=Stop)
|
||||
|
||||
Reference in New Issue
Block a user