📄 pfc.c
字号:
/****************************************************************
*
* Microchip 16-bit Embedded Control Design Contest
*
* Entry # MT2268
*
* Spa Pump Controller
*
*****************************************************************
*
* Power Factor Corrector control routines
*
*****************************************************************/
#include "PumpCtl.h"
#include "PFC.h"
#include "ADC.h"
#include "PWM.h"
#include "Inverter.h"
static SWORD duty_pfc_i;
/*
Initialize PFC variables
*/
void PFC_init (void)
{
pfc_state = P_STOPPED;
pfcp.v_gain_p = PFC_V_GAIN_P;
pfcp.v_gain_f = PFC_V_GAIN_F;
pfcp.i_gain_p = PFC_I_GAIN_P;
pfcp.i_gain_i = PFC_I_GAIN_I;
pfcp.i_gain_lim = PFC_I_GAIN_LIM;
pfcp.v_nom = _VBUS_NOM;
pfcp.v_low_thr = _VBUS_LOW_THR;
pfcp.v_high_thr = _VBUS_HIGH_THR;
pfcp.max_duty = _PWM_MAX_DUTY_PFC;
}
/*
Start up the PFC
*/
void PFC_start (void)
{
if (pfc_state != P_STOPPED)
return;
_ADIE = 0; // Disable ADC int while we initialize
duty_pfc_i = 0;
PWM_REG (FCLCON, PWM_CH_pfc) = 0x004D; // Set SFLT2 to non-latched fault mode
SET_PWM_TRIG (PWM_CH_pfc, _PWM_MIN_DUTY_PFC >> 1);
pfc_state = P_STARTWAIT;
_ADIF = 0; // Clear system
ADSTAT = 0; // and ADC module interrupt flags
_ADIE = 1; // Enable interrupt
}
/*
Stop the PFC
*/
void PFC_stop (void)
{
PWM_mode (PWM_CH_pfc, PWM_MODE_off);
pfc_state = P_STOPPED;
}
/*
PFC task handler, called by the main routine
Check for fault conditions
*/
void PFC_run (void)
{
/*
Check for PWM fault
*/
if (PWM_REG_BITS (PWMCON, bits.FLTSTAT, PWM_CH_pfc)) {
PWM_mode (PWM_CH_pfc, PWM_MODE_off);
pfc_state = STOPPED;
sys_fault |= FAULT_pfc_fault;
}
/*
Check temperature limit
*/
if (meas.v.temp_pfc >= _TEMP_LIMIT)
sys_fault |= FAULT_pfc_temp;
}
/*
The ADC interrupt occurs after all of the PFC conversions
are complete. It reads all the ADC values, computes the
duty cycle for the next period, and writes it out.
There are 4 conversions, which start in the middle of the
PWM period. The worst case latest start would be if the
duty cycle were near 100%, which would leave 25 usec for
the conversions and the interrupt routine, in order to
write out the new duty cycle in time for the next period.
The ADC is set up to take .75 usec per conversion, which
leaves us 22 usec for the interrupt. That's plenty of time,
since this is the highest priority interrupt and takes
about half that time.
*/
void _ISR _ADCInterrupt (void)
{
WORD temp;
SWORD stemp;
WORD duty_pfc;
SWORD v_error, i_error;
_ADIF = 0; // Clear interrupt flags
ADSTAT = 0;
/*
Get ADC values from current PFC cycle
*/
// Get all PFC vars from ADC, convert to Q.14 notation,
// and filter or average them as needed
temp = ADCBUF_isns_pfc;
if (temp >= offset.isns_pfc) // Subtract offset value to compensate
temp -= offset.isns_pfc; // for hardware offset voltage
else
temp = 0; // Don't let it go below zero
temp >>= 2;
meas.nv.isns_pfc = temp;
temp = ADCBUF_vsns_bus >> 2;
meas.nv.vsns_bus += (SWORD)(temp - meas.nv.vsns_bus) >> 1; // Fast average
if (pfc_state == P_RUNNING)
meas.nv.vsns_bus_avg += // Very slow average
(SLONG)(((LONG) meas.nv.vsns_bus << 16L) - meas.nv.vsns_bus_avg) >> 10;
else // If not running, store value without averaging
meas.nv.vsns_bus_avg = (LONG) meas.nv.vsns_bus << 16L;
temp = ADCBUF_vsns_ac >> 2;
meas.nv.vsns_ac += (SWORD)(temp - meas.nv.vsns_ac) >> 1;
// Keep track of the AC voltage peak, using a fast attack and slow decay time
if (meas.nv.vsns_ac >= (WORD) (meas.nv.vsns_ac_pk >> 16))
meas.nv.vsns_ac_pk +=
(SLONG)(((LONG) meas.nv.vsns_ac << 16L) - meas.nv.vsns_ac_pk) >> 3;
else
meas.nv.vsns_ac_pk +=
(SLONG)(((LONG) meas.nv.vsns_ac << 16L) - meas.nv.vsns_ac_pk) >> 12;
temp = ADCBUF_temp_pfc >> 2; // Slow average for temperature
meas.nv.temp_pfc += (SWORD)(temp - meas.nv.temp_pfc) >> 10;
if (pfc_state == P_STOPPED) // Stop here if we're not running
return;
/*
Compute the PWM value for the next cycle
*/
#define V_AC meas.nv.vsns_ac // Make formulas easier to read
#define V_AC_PEAK ((WORD) (meas.nv.vsns_ac_pk >> 16))
#define I_PFC meas.nv.isns_pfc
#define V_BUS meas.nv.vsns_bus
#define V_BUS_AVG ((WORD) (meas.nv.vsns_bus_avg >> 16))
v_error = pfcp.v_nom - V_BUS_AVG; // Get bus voltage error
#ifdef LOAD_COMP
if (sys_mode.b.load_comp) { // Apply feedforward term from inverter
if (inv_state == RAMP_UP) // to prepare for increasing
v_error += pfcp.v_gain_f;
if (inv_state == RAMP_DOWN) // or decreasing load current
v_error -= pfcp.v_gain_f;
}
#endif
if (V_AC >= V_AC_PEAK)
temp = Q14_ONE;
else
temp = DIV_Q14 (V_AC, V_AC_PEAK); // Scale AC voltage to get fraction of peak
i_error = MUL_Q14 (temp, v_error); // and use that to scale voltage error
temp = MUL_Q14 (pfcp.v_gain_p, V_AC_PEAK); // Compensate gain by scaling with peak
i_error -= MUL_Q14 (temp, I_PFC); // Now we have input current error term
duty_pfc_i += MUL_Q14 (i_error, pfcp.i_gain_i); // Update integral term
if (duty_pfc_i > (SWORD) pfcp.i_gain_lim) // Limit it to prevent
duty_pfc_i = (SWORD) pfcp.i_gain_lim; // too much overshoot
if (duty_pfc_i < (SWORD) - pfcp.i_gain_lim)
duty_pfc_i = (SWORD) - pfcp.i_gain_lim;
stemp = MUL_Q14 (i_error, pfcp.i_gain_p) + duty_pfc_i; // Add on proportional term
if (stemp > Q14_ONE) // Limit result to 0.0 - 1.0
stemp = Q14_ONE;
if (stemp < 0)
stemp = 0;
duty_pfc = MUL_Q14U (stemp, _PWM_PERIOD); // Scale by PWM period
if (duty_pfc > pfcp.max_duty) // Limit to valid range
duty_pfc = pfcp.max_duty;
if (duty_pfc < _PWM_MIN_DUTY_PFC)
duty_pfc = _PWM_MIN_DUTY_PFC;
/*
Write out the new PFC value
*/
// Set the duty cycle value
SET_PWM_DUTY (PWM_CH_pfc, duty_pfc);
// Set ADC trigger to occur in the center of the output pulse
SET_PWM_TRIG (PWM_CH_pfc, duty_pfc >> 1);
// Disable output override at startup
if (pfc_state == P_STARTWAIT) {
PWM_REG (IOCON, PWM_CH_pfc) &= ~0x0300;
pfc_state = P_STARTUP;
}
/*
Update status
*/
if (pfc_state == P_STARTUP) // If just starting up
if (V_BUS >= pfcp.v_nom - (pfcp.v_nom >> 3)) // and voltage near nominal
if (_RA9 == 1) { // and fault input is high
pfc_state = P_RUNNING; // then set state to running
duty_pfc_i >>= 2;
PWM_REG (PWMCON, PWM_CH_pfc) = 0x0080; // Clear and
PWM_REG (PWMCON, PWM_CH_pfc) = 0x1080; // re-enable fault latch
PWM_REG (FCLCON, PWM_CH_pfc) = 0x004C; // and set latched fault mode
}
#ifdef VBUS_HOLD
vbus_low = (V_BUS < pfcp.v_low_thr) ? 1 : 0; // Report status
vbus_high = (V_BUS > pfcp.v_high_thr) ? 1 : 0; // to inverter
#endif
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -