📄 inverter.c
字号:
*/
// Check if I delta is over threshold, but not too far over
if (iphase_delta > (WORD) (invp.delta_nom + invp.delta_thr)
&& iphase_delta < (WORD) (invp.delta_nom + DELTA_LIMIT)) {
// If not at the lower limit, decr the V delta
if (new_delta >= DELTA_MIN + DELTA_INCR) {
new_delta -= DELTA_INCR;
// Now adjust the amplitude to compensate for the phase shift
ampl_ph = COS_Q15 ((new_delta >> 1) - (90 Deg));
if (ampl_ph >= Q14_ONE) // Don't let amplitude get too large
ampl_ph = DIV_Q14 (Q14_SQRT2, ampl_ph);
else
ampl_ph = Q14_SQRT2;
}
}
// Check if I delta is under threshold, but not too far under
else if (iphase_delta < (WORD) (invp.delta_nom - invp.delta_thr)
&& iphase_delta > (WORD) (invp.delta_nom - DELTA_LIMIT)) {
// If not at the upper limit, incr the V delta
if (new_delta <= DELTA_MAX - DELTA_INCR) {
new_delta += DELTA_INCR;
// Now adjust the amplitude to compensate for the phase shift
ampl_ph = COS_Q15 ((new_delta >> 1) - (90 Deg));
if (ampl_ph >= Q14_ONE) // Don't let amplitude get too large
ampl_ph = DIV_Q14 (Q14_SQRT2, ampl_ph);
else
ampl_ph = Q14_SQRT2;
}
}
}
if (sys_mode.b.offset_adj) {
/*
Check the difference between the aux and run RMS currents
and adjust the output drive voltage phase offset if needed
to bring the difference close to its nominal value
*/
// Divide by sample count to get RMS ** 2
meas.v.isns_aux_rms = meas.v.isns_aux_sum / inv_cycle_flag;
meas.v.isns_run_rms = meas.v.isns_run_sum / inv_cycle_flag;
meas.v.isns_aux_sum = 0; // Reset sums for next cycle
meas.v.isns_run_sum = 0;
stemp = meas.v.isns_run_rms - meas.v.isns_aux_rms;
// Check if positive difference is over threshold, but not too far over
if (stemp > (SWORD) (invp.offset_nom + invp.offset_thr)
&& stemp < (SWORD) (invp.offset_nom + OFFSET_LIMIT)) {
if (new_offset >= (SWORD) (OFFSET_MIN + OFFSET_INCR)) // If not at limit,
new_offset -= OFFSET_INCR; // decr the offset
}
// Check if negative difference is over threshold, but not too far over
else if (stemp < (SWORD) (invp.offset_nom - invp.offset_thr)
&& stemp > (SWORD) (invp.offset_nom - OFFSET_LIMIT)) {
if (new_offset <= (SWORD) (OFFSET_MAX - OFFSET_INCR)) // If not at limit,
new_offset += OFFSET_INCR; // incr the offset
}
}
#endif
}
inv_cycle_flag = 0;
}
/*
This code runs after each PWM interrupt. It computes the
output signal amplitude, and updates all the phase and
amplitude variables with interrupts disabled, to maintain
coherency.
The amplitude is computed from 3 different sources:
ampl_freq is the frequency-dependent value from
the V/F algorithm, which is simply a value
that is proportional to the waveform frequency.
ampl_ph is used to compensate for changes in the
delta angle between the run and aux waveforms,
since the winding voltage will increase as the
delta angle decreases.
meas.v.vsns_bus_avg is the average DC bus voltage,
and is used to compensate for voltage variations.
These are all combined, then checked against limits,
and multiplied by half the PWM period to get the actual
duty cycle multiplier.
*/
if (inv_period_flag) {
inv_period_flag = 0;
ampl_scale = MUL_Q14 (ampl_ph, ampl_freq); // Get initial ampl
#ifdef VBUS_COMP
if (sys_mode.b.vbus_comp) { // Compensate for bus voltage
if (ampl_scale > AMPL_MAX - invp.ampl_var)
ampl_scale = AMPL_MAX - invp.ampl_var;
if ((WORD) (meas.v.vsns_bus_avg >> 16) >= (Q14_ONE / 2))
ampl_scale = DIV_Q14 (ampl_scale, (WORD) (meas.v.vsns_bus_avg >> 16));
}
#endif
if (ampl_scale > AMPL_MAX) // Check against limits
ampl_scale = AMPL_MAX;
if (ampl_scale < AMPL_MIN)
ampl_scale = AMPL_MIN;
// Scale amplitude by period / 2 to get PWM duty cycle value
ampl_scale = MUL_Q14U (ampl_scale, _PWM_PERIOD / 2);
DISABLE_INTS;
phase_step = new_step; // Update all the interrupt vars
phase_delta = new_delta;
phase_offset = new_offset;
ampl_com = ampl_scale;
ampl_aux = ampl_scale;
ampl_run = ampl_scale;
ENABLE_INTS;
}
}
/*
The PWM special event match interrupt occurs just after
the beginning of each inverter PWM cycle. It writes out
the previously computed duty cycle and variable phase
offsets for the next cycle, reads the ADC values captured
during the previous cycle, and computes new values for
the following cycle.
This level of pipelining is needed since the PWM phase
offsets must be written while the PWM output is inactive,
which occurs at the beginning of each PWM cycle.
(This requirement is specified in the datasheet, although
there is no clear explanation of why this is necessary.)
This adds two extra cycles of update latency, which isn't
a problem since the motor control feedback loop operates
very slowly. But it leaves us the entire PWM period to do
the calculations. This gives lots of margin, since this
interrupt routine could be interrupted by the higher
priority PFC interrupt handler.
*/
void _ISR _PWMSpEventMatchInterrupt (void)
{
WORD temp;
SWORD stemp;
_SESTAT = 0; // Clear interrupt flags
_PSEMIF = 0;
if (inv_state == STOPPED)
return;
/*
First we write out the pre-computed PWM values
*/
// Shift com and aux outputs to be at the end of a PWM cycle
SET_PWM_PHASE (PWM_CH_com, duty_com);
SET_PWM_PHASE (PWM_CH_aux, duty_aux);
// Set the duty cycle values
SET_PWM_DUTY (PWM_CH_com, duty_com);
SET_PWM_DUTY (PWM_CH_aux, duty_aux);
SET_PWM_DUTY (PWM_CH_run, duty_run);
// Set ADC triggers to occur centered between output pulses
SET_PWM_TRIG (PWM_CH_aux, (WORD) (_PWM_PERIOD - duty_aux) >> 1);
SET_PWM_TRIG (PWM_CH_run, (WORD) (_PWM_PERIOD - duty_run) >> 1);
// Disable output overrides at startup
if (inv_state == STARTWAIT2) {
PWM_REG (IOCON, PWM_CH_com) &= ~0x0300;
PWM_REG (IOCON, PWM_CH_aux) &= ~0x0300;
PWM_REG (IOCON, PWM_CH_run) &= ~0x0300;
inv_state = STARTUP;
}
/*
Now get ADC values from previous PWM cycle
*/
// Get all Inverter vars from ADC, convert to Q.14 notation,
// and filter or average them as needed
// Subtract offsets to convert from offset binary to 2's complement.
stemp = (SWORD)(ADCBUF_isns_aux - offset.isns_aux) >> 1;
meas.nv.isns_aux += (SWORD)(stemp - meas.nv.isns_aux) >> 1;
meas.nv.isns_aux_sum += SQUARE (meas.nv.isns_aux) >> (8L - 2L + 8L); // Do RMS sum
stemp = (SWORD)(ADCBUF_isns_run - offset.isns_run) >> 1;
meas.nv.isns_run += (SWORD)(stemp - meas.nv.isns_run) >> 1;
meas.nv.isns_run_sum += SQUARE (meas.nv.isns_run) >> (8L - 2L + 8L); // Do RMS sum
// Kirchoff's current law says the sum of the three motor currents has to be zero,
// so we can compute the common current from the other two.
meas.nv.isns_com = - (meas.nv.isns_aux + meas.nv.isns_run);
stemp = ADCBUF_temp_inv >> 2;
meas.nv.temp_inv += (SWORD)(stemp - meas.nv.temp_inv) >> 10;
/*
Find the relative location of the current sense zero crossing points
*/
#define I_AUX meas.nv.isns_aux // Make formulas easier to read
#define I_RUN meas.nv.isns_run
#define I_COM meas.nv.isns_com
if (I_AUX > iphase_aux.max) // Find min and max
iphase_aux.max = I_AUX;
if (I_AUX < iphase_aux.min)
iphase_aux.min = I_AUX;
if (I_AUX > ((iphase_aux.last_max >> 1) + (iphase_aux.last_min >> 1))) {
if (iphase_aux.sign == SIGN_NEG) { // Positive crossing through centerline
stemp = phase_accum - (WORD)(iphase_aux.time >> 16L);
iphase_aux.time += (SLONG)stemp << 12L; // Update relative time of transition
iphase_aux.flag = 1; // Set data valid flag
iphase_aux.last_min = iphase_aux.min; // Save lower and upper limits
iphase_aux.last_max = iphase_aux.max;
iphase_aux.min = 32767; // Re-init values for next cycle
iphase_aux.max = -32768;
}
iphase_aux.sign = SIGN_POS;
}
else if (I_AUX < ((iphase_aux.last_max >> 2) + ((iphase_aux.last_min >> 2) * 3))) {
iphase_aux.sign = SIGN_NEG; // Set flag if below 25% point
}
if (I_RUN > iphase_run.max) // Find min and max
iphase_run.max = I_RUN;
if (I_RUN < iphase_run.min)
iphase_run.min = I_RUN;
if (I_RUN > ((iphase_run.last_max >> 1) + (iphase_run.last_min >> 1))) {
if (iphase_run.sign == SIGN_NEG) { // If positive crossing through centerline,
stemp = phase_accum - (WORD)(iphase_run.time >> 16L);
iphase_run.time += (SLONG)stemp << 12L; // Update relative time of transition
iphase_run.flag = 1; // and set data valid flag
iphase_run.last_min = iphase_run.min; // Save lower and upper limits
iphase_run.last_max = iphase_run.max;
iphase_run.min = 32767; // Re-init values for next cycle
iphase_run.max = -32768;
}
iphase_run.sign = SIGN_POS;
}
else if (I_RUN < ((iphase_run.last_max >> 2) + ((iphase_run.last_min >> 2) * 3))) {
iphase_run.sign = SIGN_NEG; // Set flag if below 25% point
}
/*
Finally, compute the PWM values for the next cycle
*/
if (inv_state != STARTWAIT1)
phase_accum += phase_step; // Update the phase accumulator
// Compute the duty cycle for the COM output
// Get the cosine value and scale it by the amplitude
stemp = - COS_Q15 (phase_accum >> 8);
stemp = MUL_Q15 (stemp, ampl_com); // stemp = (stemp * ampl_com) >> 15
// Shift duty cycle value by half the PWM period
// so that zero corresponds to a 50% duty cycle.
duty_com = stemp + (_PWM_PERIOD / 2);
#ifdef DT_COMP
if (sys_mode.b.dt_comp) {
// Compensate for the PWM deadtime to reduce distortion
// With positive current flow, the output voltage will go low
// as soon as the high PWM driver turns off. We can add the
// deadtime to the duty cycle, which means the high driver
// will have the correct on-time, and the low driver will
// have twice the normal deadtime (which doesn't matter,
// since the output voltage will go low on its own anyway.)
if (I_COM > 0)
duty_com += _PWM_DEADTIME;
// With negative current flow, the output voltage will go high
// as soon as the low PWM driver turns off, so we subtract the
// deadtime (so the low driver gets the correct on-time).
else
if (duty_com >= _PWM_DEADTIME) // Don't let duty go below 0
duty_com -= _PWM_DEADTIME;
}
#endif
// Check bounds to keep it within valid PWM range
if (duty_com > _PWM_MAX_DUTY_INV)
duty_com = _PWM_MAX_DUTY_INV;
if (duty_com < _PWM_MIN_DUTY_INV)
duty_com = _PWM_MIN_DUTY_INV;
// Repeat everything for the AUX output
temp = phase_accum + phase_offset + phase_delta;
stemp = COS_Q15 (temp >> 8);
stemp = MUL_Q15 (stemp, ampl_aux); // stemp = (stemp * ampl_aux) >> 15
duty_aux = stemp + (_PWM_PERIOD / 2);
#ifdef DT_COMP
if (sys_mode.b.dt_comp) {
if (I_AUX > 0)
duty_aux += _PWM_DEADTIME;
else
if (duty_aux >= _PWM_DEADTIME)
duty_aux -= _PWM_DEADTIME;
}
#endif
if (duty_aux > _PWM_MAX_DUTY_INV)
duty_aux = _PWM_MAX_DUTY_INV;
if (duty_aux < _PWM_MIN_DUTY_INV)
duty_aux = _PWM_MIN_DUTY_INV;
// And once more for the RUN output
temp = phase_accum + phase_offset - phase_delta;
stemp = COS_Q15 (temp >> 8);
stemp = MUL_Q15 (stemp, ampl_run); // stemp = (stemp * ampl_run) >> 15
duty_run = stemp + (_PWM_PERIOD / 2);
#ifdef DT_COMP
if (sys_mode.b.dt_comp) {
if (I_RUN > 0)
duty_run += _PWM_DEADTIME;
else
if (duty_run >= _PWM_DEADTIME)
duty_run -= _PWM_DEADTIME;
}
#endif
if (duty_run > _PWM_MAX_DUTY_INV)
duty_run = _PWM_MAX_DUTY_INV;
if (duty_run < _PWM_MIN_DUTY_INV)
duty_run = _PWM_MIN_DUTY_INV;
/*
Handle processing that's done once each cycle of the output waveform
*/
if (phase_accum < phase_step) { // Phase accumulator has just overflowed
if (iphase_aux.flag && iphase_run.flag) { // Save phase delta
iphase_delta = (iphase_run.time >> 16) - (iphase_aux.time >> 16);
iphase_aux.flag = 0;
iphase_run.flag = 0;
}
else
iphase_delta = invp.delta_nom;
inv_cycle_flag = inv_period_count; // Save count for higher level routines
inv_period_count = 0; // Reset counter for next cycle
}
else
inv_period_count ++; // Keep count of PWM interrupts
inv_period_flag = 1; // Set interrupt occurred flag
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -