// ================================================================================= // PROJECT : xxx // SUBSYSTEM : Digital Control Logic Block - 3-Leg Interleaved Totem-Pole PFC // MODULE : pfc_c_driver.cpp // FILE VERSION : R1.0.0 // RELEASE DATE : August 18, 2026 // ARCHITECTURE : Unified State-Space Averaged Cascaded Control Loop // COMPLIANCE : QSPICE C++ Native DLL Interface Standard // DESCRIPTION : Implements a 140 kHz interleaved high-frequency current tracking // loop synchronized with a 10 Hz bandwidth outer energy-balance // voltage loop. Features 120-degree phase shift modulation. // Author : Reda // ================================================================================= // ================================================================================= // Part 1: Architecture Specifications and Persistent Context Allocation // ================================================================================= #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // Data-interchange union structure mapping strictly to the QSPICE solver engine union uData { bool b; char c; unsigned char uc; short s; unsigned short us; int i; unsigned int ui; float f; double d; long long int i64; unsigned long long int ui64; char *str; unsigned char *bytes; }; // Persistent context allocation space mapping memory bounds across transient solver steps struct PfcCascadeContext { double v_loop_integrator; double i_loop_int1; double i_loop_int2; double i_loop_int3; double last_time; double last_reg_t; double t_on_h1; double t_on_h2; double t_on_h3; double carrier_time_A; double carrier_time_B; double carrier_time_C; // ADDED: Persistent modulation flags to bridge the scope boundary bool pwm_high_A; bool pwm_high_B; bool pwm_high_C; double kp_v; double ki_v; double kp_i; double ki_i; double p_max_limit; bool initialized; }; // Standard dynamic link library entry point definition int __stdcall DllMain(void *module, unsigned int reason, void *reserved) { return 1; } // ================================================================================= // Part 2: Pin Mapping with Complete Undefs (7 Inputs / 8 Outputs + 4 Telemetry) // ================================================================================= #undef V_DC_POS #undef V_DC_Minus #undef L #undef N #undef IL1_MES #undef IL2_MES #undef IL3_MES #undef G_M1 #undef G_M2 #undef G_M3 #undef G_M4 #undef G_M5 #undef G_M6 #undef G_M7 #undef G_M8 #undef P_COMM_PIN #undef I_TARGET_PIN #undef I_INT1_PIN #undef DUTY_ACT1_PIN extern "C" __declspec(dllexport) void pfc_c_driver(void **opaque, double t, union uData *data) { // 2.1 Extract real analog feedback signals from the 7-channel input matrix double V_DC_POS = data[0].d; // Sensed DC Bus bulk positive voltage rail double V_DC_Minus = data[1].d; // Sensed DC negative / absolute circuit mass reference (0V) double L = data[2].d; // Sensed AC grid incoming line phase wire voltage (L) double N = data[3].d; // Sensed AC grid incoming neutral wire reference voltage (N) double IL1_MES = data[4].d; // Sensed inductor tracking feedback current - Leg 1 [via B1] double IL2_MES = data[5].d; // Sensed inductor tracking feedback current - Leg 2 [via B2] double IL3_MES = data[6].d; // Sensed inductor tracking feedback current - Leg 3 [via B3] // 2.2 Bind logical output registers steering behavioral driver table sources (B4 to B11) double &G_M1 = data[7].d; // Gate Drive Output - Phase Leg 1 High-Side NMOS double &G_M2 = data[8].d; // Gate Drive Output - Phase Leg 1 Low-Side NMOS double &G_M3 = data[9].d; // Gate Drive Output - Phase Leg 2 High-Side NMOS double &G_M4 = data[10].d; // Gate Drive Output - Phase Leg 2 Low-Side NMOS double &G_M5 = data[11].d; // Gate Drive Output - Phase Leg 3 High-Side NMOS double &G_M6 = data[12].d; // Gate Drive Output - Phase Leg 3 Low-Side NMOS double &G_M7 = data[13].d; // Gate Drive Output - Return Leg 4 Synchronous High-Side NMOS double &G_M8 = data[14].d; // Gate Drive Output - Return Leg 4 Synchronous Low-Side NMOS // 2.3 Map diagnostic scope pins outputting live variables to the plot windows double &P_COMM_PIN = data[15].d; // Outputs raw differential line grid tracking voltage double &I_TARGET_PIN = data[16].d; // Outputs total scaled directional target current command double &I_INT1_PIN = data[17].d; // Outputs active current loop integrator tracking output double &DUTY_ACT1_PIN = data[18].d; // Outputs calculated continuous active duty cycle ratio // Map the external Phase A triangular carrier trace link double &saw_A = data[19].d; // Global switching grid constant declarations const double fSW = 140000.0; const double T = 1.0 / fSW; // Hard logical driver clamp limits matching the gate driver tables (-6V to +18V mapping scale) const double V_GATE_HIGH = 1.0; const double V_GATE_LOW = 0.0; const double V_REF = 800.0; // Initialization sequence executing during the initial time-step parameter (t=0) if (*opaque == nullptr) { PfcCascadeContext* ctx = (PfcCascadeContext*)malloc(sizeof(PfcCascadeContext)); if (ctx != nullptr) { double P_MAX = 11000.0; // Nominal 11 kW structural performance envelope // Mathematically calibrated current loop gains (Technical Optimum Tuning) ctx->kp_i = 0.0258; // Stabilizes the 14 kHz cross-over frequency boundaries ctx->ki_i = 4.397; // Exactly cancels the L/R time constant of behavioral chokes // Outer voltage loop operational coefficients (Gv tuned to 10 Hz band rejection) ctx->kp_v = 0.002; ctx->ki_v = 0.04; ctx->p_max_limit = P_MAX * 1.15; // Set safe 15% overload threshold limit ctx->v_loop_integrator = 0.0; // Forced safe null start tracking window ctx->i_loop_int1 = 0.0; ctx->i_loop_int2 = 0.0; ctx->i_loop_int3 = 0.0; // Seed stable initial duty registers preventing start-up converter collapse ctx->t_on_h1 = 0.5; ctx->t_on_h2 = 0.5; ctx->t_on_h3 = 0.5; // Flawless carrier phase displacement seeds tracking symmetric 120 / 240 interleaving angles ctx->carrier_time_A = 0.0; ctx->carrier_time_B = 1.0 / 3.0; ctx->carrier_time_C = 2.0 / 3.0; ctx->pwm_high_A = false; ctx->pwm_high_B = false; ctx->pwm_high_C = false; ctx->last_time = t; ctx->last_reg_t = t; ctx->initialized = true; *opaque = ctx; } } PfcCascadeContext* ctx = (PfcCascadeContext*)*opaque; // ================================================================================= // Part 3: Real Hardware Synchronization and Discrete Cascaded Loop Block // ================================================================================= double dt_global = t - ctx->last_time; ctx->last_time = t; if (dt_global <= 0.0) dt_global = 1e-10; // Eliminate low-voltage solver matrix noise during start-up operations double v_dc_act_safe = V_DC_POS - V_DC_Minus; if (v_dc_act_safe < 50.0) v_dc_act_safe = V_REF; // Extract real measured grid differential voltage profile double v_net_raw = L - N; // Direct hardware-referenced phase verification tracking the real mains voltage vector bool zd = (v_net_raw >= 0.0); // Execute core digital update equations strictly at 140 kHz fixed step boundaries double dt_reg = t - ctx->last_reg_t; if (dt_reg >= T || t == 0.0) { ctx->last_reg_t = t; // 3.1 Compute outer voltage loop controller with anti-windup clamping double v_error = V_REF - v_dc_act_safe; double absolute_p_max = ctx->p_max_limit; // Open the power boundaries cleanly from 11 kW base line up to maximum clamp double dynamic_p_max = absolute_p_max; if (t < 0.002) { dynamic_p_max = 11000.0 + (absolute_p_max - 11000.0) * (t / 0.002); } ctx->v_loop_integrator += v_error * T * ctx->ki_v; if (ctx->v_loop_integrator > absolute_p_max) ctx->v_loop_integrator = absolute_p_max; if (ctx->v_loop_integrator < 0.0) ctx->v_loop_integrator = 0.0; // Lock power command with a persistent 11 kW active feed-forward operating base double P_command = 11000.0 + (v_error * ctx->kp_v) + ctx->v_loop_integrator; if (P_command > dynamic_p_max) P_command = dynamic_p_max; if (P_command < 0.0) P_command = 0.0; // 3.2 Execute matching power-to-current conversion based on real line values double v_rms_sq = 220.0 * 220.0; // Anchored around nominal 220V grid standard double i_target_total_abs = (P_command * std::fabs(v_net_raw)) / v_rms_sq; double i_target_per_leg = i_target_total_abs / 3.0; // Absolute reference per channel // 3.3 Map feedback current vectors into absolute magnitude space double i_act1 = std::fabs(IL1_MES); double i_act2 = std::fabs(IL2_MES); double i_act3 = std::fabs(IL3_MES); // Error matrices tracking true negative feedback deviations double i_err1 = i_target_per_leg - i_act1; double i_err2 = i_target_per_leg - i_act2; double i_err3 = i_target_per_leg - i_act3; // Symmetric integrator saturation boundaries (-0.45 to +0.45 duty domain scale) double max_i_sat = 0.45; double min_i_sat = -0.45; ctx->i_loop_int1 += i_err1 * T * ctx->ki_i; if (ctx->i_loop_int1 > max_i_sat) ctx->i_loop_int1 = max_i_sat; if (ctx->i_loop_int1 < min_i_sat) ctx->i_loop_int1 = min_i_sat; ctx->i_loop_int2 += i_err2 * T * ctx->ki_i; if (ctx->i_loop_int2 > max_i_sat) ctx->i_loop_int2 = max_i_sat; if (ctx->i_loop_int2 < min_i_sat) ctx->i_loop_int2 = min_i_sat; ctx->i_loop_int3 += i_err3 * T * ctx->ki_i; if (ctx->i_loop_int3 > max_i_sat) ctx->i_loop_int3 = max_i_sat; if (ctx->i_loop_int3 < min_i_sat) ctx->i_loop_int3 = min_i_sat; // ================================================================================= // Part 4: Analytical State-Space Averaged Volt-Second Control Decoupling Loop // ================================================================================= // 4.1 Consolidate current loop proportional and integral responses double u_ctrl1 = (i_err1 * ctx->kp_i) + ctx->i_loop_int1; double u_ctrl2 = (i_err2 * ctx->kp_i) + ctx->i_loop_int2; double u_ctrl3 = (i_err3 * ctx->kp_i) + ctx->i_loop_int3; // 4.2 Universal boost-topology volt-second feedforward decoupling rule double v_ac_abs_local = std::fabs(v_net_raw); double duty_feedforward = v_ac_abs_local / v_dc_act_safe; // Form the active duty correction fields double duty_active1 = duty_feedforward - u_ctrl1; double duty_active2 = duty_feedforward - u_ctrl2; double duty_active3 = duty_feedforward - u_ctrl3; // Enforce strict deadband guard pads (5% to 85%) protecting M=3 parallel gate channels if (duty_active1 < 0.05) duty_active1 = 0.05; if (duty_active1 > 0.85) duty_active1 = 0.85; if (duty_active2 < 0.05) duty_active2 = 0.05; if (duty_active2 > 0.85) duty_active2 = 0.85; if (duty_active3 < 0.05) duty_active3 = 0.05; if (duty_active3 > 0.85) duty_active3 = 0.85; // Pass finalized control profiles to persistent modulation registers ctx->t_on_h1 = duty_active1; ctx->t_on_h2 = duty_active2; ctx->t_on_h3 = duty_active3; // Synchronize outgoing diagnostic signals to scope ports P_COMM_PIN = v_net_raw; I_TARGET_PIN = zd ? (i_target_per_leg * 3.0) : (-i_target_per_leg * 3.0); // Full 67A scaled sinus target I_INT1_PIN = ctx->i_loop_int1; DUTY_ACT1_PIN = duty_active1; } // Terminate discrete control loop execution window (dt_reg >= T) // ================================================================================= // Part 5: High-Frequency Center-Aligned Interleaving Modulation Engine // ================================================================================= // 5.1 Step the high-precision ePWM phase time registers forward at every single solver delta // Directly emulates a dual-slope hardware triangular carrier counter profile (TBCTR) ctx->carrier_time_A += fSW * dt_global; ctx->carrier_time_B += fSW * dt_global; ctx->carrier_time_C += fSW * dt_global; // Enforce clean modulo wrapping boundaries at the upper period limits ctx->carrier_time_A -= std::floor(ctx->carrier_time_A); ctx->carrier_time_B -= std::floor(ctx->carrier_time_B); ctx->carrier_time_C -= std::floor(ctx->carrier_time_C); // Extract perfectly balanced, center-aligned triangular carrier waves (0.0 to 1.0) double carrier_a = std::fabs(2.0 * ctx->carrier_time_A - 1.0); double carrier_b = std::fabs(2.0 * ctx->carrier_time_B - 1.0); double carrier_c = std::fabs(2.0 * ctx->carrier_time_C - 1.0); // Extract active duty ratios calculated by the Part 4 register loop double d1 = ctx->t_on_h1; double d2 = ctx->t_on_h2; double d3 = ctx->t_on_h3; // Pass continuous phase A reference wave to the scope trace output pin saw_A = carrier_a; // Execute the real-time triangle logic comparisons locally to prevent scope compilation errors ctx->pwm_high_A = (d1 > carrier_a); ctx->pwm_high_B = (d2 > carrier_b); ctx->pwm_high_C = (d3 > carrier_c); // Hard hardware-equivalent dead-band buffer (2%) managing large parallel capacitance loads double dt_offset = 0.02; if (zd) { // --- POSITIVE GRIDS QUADRANT: Low-Side NMOS modulates energy, High-Side returns charge --- // Phase Leg A G_M2 = (carrier_a < d1) ? V_GATE_HIGH : V_GATE_LOW; // Low-side active boost path G_M1 = (carrier_a > (d1 + dt_offset)) ? V_GATE_HIGH : V_GATE_LOW; // Active complementary sync rectifier // Phase Leg B G_M4 = (carrier_b < d2) ? V_GATE_HIGH : V_GATE_LOW; G_M3 = (carrier_b > (d2 + dt_offset)) ? V_GATE_HIGH : V_GATE_LOW; // Phase Leg C G_M6 = (carrier_c < d3) ? V_GATE_HIGH : V_GATE_LOW; G_M5 = (carrier_c > (d3 + dt_offset)) ? V_GATE_HIGH : V_GATE_LOW; // Low Frequency Leg 4 Configuration: Return line clamped hard to ground G_M7 = V_GATE_LOW; G_M8 = V_GATE_HIGH; } else { // --- NEGATIVE GRIDS QUADRANT: High-Side NMOS modulates energy, Low-Side returns charge --- // Phase Leg A G_M1 = (carrier_a < d1) ? V_GATE_HIGH : V_GATE_LOW; // High-side active boost path G_M2 = (carrier_a > (d1 + dt_offset)) ? V_GATE_HIGH : V_GATE_LOW; // Active complementary sync rectifier // Phase Leg B G_M3 = (carrier_b < d2) ? V_GATE_HIGH : V_GATE_LOW; G_M4 = (carrier_b > (d2 + dt_offset)) ? V_GATE_HIGH : V_GATE_LOW; // Phase Leg C G_M5 = (carrier_c < d3) ? V_GATE_HIGH : V_GATE_LOW; G_M6 = (carrier_c > (d3 + dt_offset)) ? V_GATE_HIGH : V_GATE_LOW; // Low Frequency Leg 4 Configuration: Return line clamped hard to active DC bus G_M7 = V_GATE_HIGH; G_M8 = V_GATE_LOW; } // 5.2 Absolute Software Interlock Guard (Zero Cross-Conduction Protection) if (G_M1 > 0.5 && G_M2 > 0.5) { G_M1 = V_GATE_LOW; G_M2 = V_GATE_LOW; } if (G_M3 > 0.5 && G_M4 > 0.5) { G_M3 = V_GATE_LOW; G_M4 = V_GATE_LOW; } if (G_M5 > 0.5 && G_M6 > 0.5) { G_M5 = V_GATE_LOW; G_M6 = V_GATE_LOW; } } // ================================================================================= // Part 6: Standard QSPICE DLL Export Interfacing API Blocks // ================================================================================= // Restrict solver time-steps around high-frequency clock bounds extern "C" __declspec(dllexport) double MaxExtStepSize(PfcCascadeContext *inst, double t) { // Forces internal solver sampling to exactly 1/40th of the 140kHz period (~178ns window match) return 1.0 / (140000.0 * 40.0); } // Transient state copy lookahead tracking for active step-size reduction extern "C" __declspec(dllexport) void Trunc(PfcCascadeContext *inst, double t, union uData *data, double *timestep) { const double ttol = 1e-9; // 1ns tracking lookahead threshold boundary if (*timestep > ttol) { PfcCascadeContext tmp = *inst; pfc_c_driver((void**)&tmp, t, data); } } // Clear persistent context allocation space upon active simulation termination extern "C" __declspec(dllexport) void Destroy(PfcCascadeContext *inst) { if (inst != nullptr) { free(inst); } }