// Automatically generated C++ file on Sun Jul 19 16:56:30 2026 // // To build with Digital Mars C++ Compiler: // // dmc -mn -WD -o totempole_pfc.cpp kernel32.lib #include #include extern "C" __declspec(dllexport) void (*Display)(const char *format, ...) = 0; // works like printf() extern "C" __declspec(dllexport) void (*EXIT)(const char *format, ...) = 0; // print message like printf() but exit(0) afterward extern "C" __declspec(dllexport) const double *DegreesC = 0; // pointer to current circuit temperature extern "C" __declspec(dllexport) const int *StepNumber = 0; // pointer to current step number extern "C" __declspec(dllexport) const int *NumberSteps = 0; // pointer to estimated number of steps extern "C" __declspec(dllexport) const char* const *InstanceName = 0; // pointer to address of instance name extern "C" __declspec(dllexport) const char *QUX = 0; // path to QUX.exe extern "C" __declspec(dllexport) const bool *ForKeeps = 0; // pointer to whether being evaluated non-hypothetically extern "C" __declspec(dllexport) const bool *HoldICs = 0; // pointer to whether instance initial conditions are being held extern "C" __declspec(dllexport) const void *GUI_HWND = 0; // pointer to Window handle of QUX.exe extern "C" __declspec(dllexport) const double *CKTtime = 0; extern "C" __declspec(dllexport) const double *CKTdelta = 0; extern "C" __declspec(dllexport) const int *IntegrationOrder = 0; extern "C" __declspec(dllexport) const char *InstallDirectory = 0; extern "C" __declspec(dllexport) double (*EngAtof)(const char **string) = 0; extern "C" __declspec(dllexport) const char *(*BinaryFormat)(unsigned int data) = 0; // BinaryFormat(0x1C) returns "0b00011100" extern "C" __declspec(dllexport) const char *(*EngFormat )(double x, const char *units, int numDgts) = 0; // EngFormat(1e-6, "s", 6) returns "1µs" extern "C" __declspec(dllexport) int (*DFFT)(struct sComplex *u, bool inv, unsigned int N, double scale) = 0; // Discrete Fast Fourier Transform extern "C" __declspec(dllexport) void (*bzero)(void *ptr, unsigned int count) = 0; 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; }; // int DllMain() must exist and return 1 for a process to load the .DLL // See https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain for more information. int __stdcall DllMain(void *module, unsigned int reason, void *reserved) { return 1; } // #undef pin names lest they collide with names in any header file(s) you might include. #undef Vinp #undef Vinn #undef Iin #undef Iset #undef duty #undef sawtooth #undef Gate1 #undef Gate2 #include double carrier(int type,double t, double fsw, double phase){ // Generate periodic carrier waveforms for PWM modulation // Inputs: // type : Waveform type selector // t : Simulation time [seconds] // fsw : Switching frequency [Hz] (carrier frequency) // phase : Phase shift [radians] (offset applied to carrier waveform) // type 0: Sinusoidal Carrier if (type == 0) return sin(2*M_PI*fsw*(t-phase/2/M_PI/fsw)); // type 1: Sawtooth wave (0 to 1V) - increases linearly then resets else if (type == 1) return fmod(t-phase/2/M_PI/fsw,1/fsw)*fsw; // type 2: Inverted Sawtooth wave (1 to 0V) - decreases linearly then resets else if (type == 2) return 1-fmod(t-+phase/2/M_PI/fsw,1/fsw)*fsw; // type 3: Triangle wave (0 to 1V) - symmetrical about 0.5V else if (type == 3) return fabs(remainder(t-phase/2/M_PI/fsw,1/fsw))*2*fsw; // type 4: Triangle wave (-1 to +1V) - bipolar symmetrical else if (type == 4) return fabs(remainder(t-phase/2/M_PI/fsw,1/fsw))*4*fsw-1; // Unknown type: return 0 (safe default to prevent unintended behavior) else return 0; } struct sTOTEMPOLE_PFC { // declare the structure here double maxstep; // Maximum simulation timestep (controls simulation resolution) double lastT; // Previous simulation time (for delta time calculation) double cumsumIA; // Accumulated integral term of the PI controller (with anti-windup clamping) double cumsumIB; // Accumulated integral term of the PI controller (with anti-windup clamping) double cumsumIC; // Accumulated integral term of the PI controller (with anti-windup clamping) }; extern "C" __declspec(dllexport) void totempole_pfc(struct sTOTEMPOLE_PFC **opaque, double t, union uData *data) { double Vinp = data[ 0].d; // input double Vinn = data[ 1].d; // input double IinA = data[ 2].d; // input double IinB = data[ 3].d; // input double IinC = data[ 4].d; // input double Iset = data[ 5].d; // input double NominalRMS = data[ 6].d; // input parameter double Kp = data[ 7].d; // input parameter double Ki = data[ 8].d; // input parameter double CLK = data[ 9].d; // input parameter double &dutyA = data[10].d; // output double &sawtoothA = data[11].d; // output bool &Gate1 = data[12].b; // output bool &Gate2 = data[13].b; // output bool &Gate3 = data[14].b; // output bool &Gate4 = data[15].b; // output bool &Gate5 = data[16].b; // output bool &Gate6 = data[17].b; // output double &debug1 = data[18].d; // output double &debug2 = data[19].d; // output if(!*opaque) { *opaque = (struct sTOTEMPOLE_PFC *) malloc(sizeof(struct sTOTEMPOLE_PFC)); bzero(*opaque, sizeof(struct sTOTEMPOLE_PFC)); (*opaque)->maxstep = 1/double(CLK)/100; // limit maxstep according to fsw } struct sTOTEMPOLE_PFC *inst = *opaque; // Implement module evaluation code here: // Calculate time step since last iteration (for integration) const double dT = t - inst->lastT; // Generate synchronized reference current from input voltage (PFC synch signal) const double Vsync = (Vinp-Vinn)/NominalRMS/sqrt(2.0); // Normalized input voltage (peak=1 at nominal RMS) const double Iref = Vsync * Iset; // Reference current shape (in-phase with voltage) // Current error: magnitude control (absolute values for PFC current shaping) const double IerrA = fabs(Iref)-fabs(IinA); const double IerrB = fabs(Iref)-fabs(IinB); const double IerrC = fabs(Iref)-fabs(IinC); // Anti-windup integration : Gain = Ki inst->cumsumIA = Ki*IerrA*dT + inst->cumsumIA; if (inst->cumsumIA > 1) inst->cumsumIA=1; if (inst->cumsumIA < 0) inst->cumsumIA=0; inst->cumsumIB = Ki*IerrB*dT + inst->cumsumIB; if (inst->cumsumIB > 1) inst->cumsumIB=1; if (inst->cumsumIB < 0) inst->cumsumIB=0; inst->cumsumIC = Ki*IerrC*dT + inst->cumsumIC; if (inst->cumsumIC > 1) inst->cumsumIC=1; if (inst->cumsumIC < 0) inst->cumsumIC=0; // Calculate duty : PI controller (Proportional + Integral) dutyA = Kp*IerrA + inst->cumsumIA; if (dutyA > 0.95) dutyA = 0.95; // Clamp duty to max (95% for dead-time margin) if (dutyA < 0.05) dutyA = 0.05; // Clamp duty to min (5% for dead-time margin) double dutyB = Kp*IerrB + inst->cumsumIB; if (dutyB > 0.95) dutyB = 0.95; // Clamp duty to max (95% for dead-time margin) if (dutyB < 0.05) dutyB = 0.05; // Clamp duty to min (5% for dead-time margin) double dutyC = Kp*IerrC + inst->cumsumIC; if (dutyC > 0.95) dutyC = 0.95; // Clamp duty to max (95% for dead-time margin) if (dutyC < 0.05) dutyC = 0.05; // Clamp duty to min (5% for dead-time margin) // Sawtooth generation (PWM carrier) sawtoothA = carrier(1,t,CLK,0.0); const double sawtoothB = carrier(1,t,CLK,1*(2*M_PI)/3); const double sawtoothC = carrier(1,t,CLK,2*(2*M_PI)/3); debug1 = sawtoothB; debug2 = sawtoothC; // Compare duty with sawtooth to generate PWM switching signal const bool LSA = dutyA > sawtoothA; // Low-side gate signal (high when duty > carrier) const bool LSB = dutyB > sawtoothB; const bool LSC = dutyC > sawtoothC; // Gate drive with sign selection - Totempole PFC if (Vsync>0){ // Vin Positive half-cycle // Leg A Gate1 = !LSA; Gate2 = LSA; // Leg B Gate3 = !LSB; Gate4 = LSB; // Leg C Gate5 = !LSC; Gate6 = LSC; }else{ // Vin Negative half-cycle // Leg A Gate1 = LSA; Gate2 = !LSA; // Leg B Gate3 = LSB; Gate4 = !LSB; // Leg C Gate5 = LSC; Gate6 = !LSC; } // store previous status inst->lastT = t; } extern "C" __declspec(dllexport) double MaxExtStepSize(struct sTOTEMPOLE_PFC *inst, double t) { return inst->maxstep; // implement a good choice of max timestep size that depends on struct sTOTEMPOLE_PFC } extern "C" __declspec(dllexport) void Trunc(struct sTOTEMPOLE_PFC *inst, double t, union uData *data, double *timestep) { // limit the timestep to a tolerance if the circuit causes a change in struct sTOTEMPOLE_PFC const double ttol = 1e-9; // 1ns default tolerance if(*timestep > ttol) { struct sTOTEMPOLE_PFC tmp = *inst; totempole_pfc(&(&tmp), t, data); //if(tmp.lastLS != inst->lastLS) // *timestep=ttol; } } extern "C" __declspec(dllexport) void Destroy(struct sTOTEMPOLE_PFC *inst) { free(inst); }