📄 inverter.c
字号:
/****************************************************************
*
* Microchip 16-bit Embedded Control Design Contest
*
* Entry # MT2268
*
* Spa Pump Controller
*
*****************************************************************
*
* Motor drive inverter routines
*
*****************************************************************/
#include "PumpCtl.h"
#include "Inverter.h"
#include "Periph.h"
#include "ADC.h"
#include "PWM.h"
#include "PFC.h"
static BYTE inv_freq; // Inverter output frequency (Hz * 4)
static BYTE target_freq; // Requested inverter output frequency (Hz * 4)
static WORD ampl_scale; // Overall amplitude scaling factor (Q.14)
static WORD ampl_freq; // Amplitude scaling from output frequency
static WORD ampl_ph; // Amplitude scaling from output phase offset
static WORD phase_accum; // Phase accumulator (0 - 65535)
static WORD phase_step; // Phase step increment value
static SWORD phase_offset; // Phase offset between com and aux/run channels
static WORD phase_delta; // Phase difference between aux and run channels
static WORD ampl_com, ampl_aux, ampl_run; // Individual amplitude values (Q.14)
static WORD duty_com, duty_aux, duty_run; // PWM duty cycle values
struct IPHASE_S { // Data for computing motor current phase delta
SWORD min; // Min motor current value for this cycle
SWORD max; // Max motor current value for this cycle
SWORD last_min; // Min motor current value for last cycle
SWORD last_max; // Max motor current value for last cycle
LONG time; // Average time of zero crossing
BYTE sign; // Sign of motor current
BYTE flag; // Data valid flag
};
#define SIGN_INIT 0
#define SIGN_POS 1
#define SIGN_NEG 2
static volatile struct IPHASE_S iphase_aux, iphase_run;
static volatile WORD iphase_delta;
static volatile BYTE inv_period_flag; // PWM interrupt occurred flag
static volatile WORD inv_period_count; // PWM period counter
static volatile WORD inv_cycle_flag; // Output waveform cycle completed flag
// First two quadrants of cos(x) in Q.15 format
// This is equivalent to Q.14 format * 2
const SWORD cos_tbl[129] = {
32767, 32757, 32728, 32678, 32609, 32521, 32412, 32285,
32137, 31971, 31785, 31580, 31356, 31113, 30852, 30571,
30273, 29956, 29621, 29268, 28898, 28510, 28105, 27683,
27245, 26790, 26319, 25832, 25329, 24811, 24279, 23731,
23170, 22594, 22005, 21403, 20787, 20159, 19519, 18868,
18204, 17530, 16846, 16151, 15446, 14732, 14010, 13279,
12539, 11793, 11039, 10278, 9512, 8739, 7962, 7179,
6393, 5602, 4808, 4011, 3212, 2410, 1608, 804,
0, -804, -1608, -2410, -3212, -4011, -4808, -5602,
-6393, -7179, -7962, -8739, -9512, -10278, -11039, -11793,
-12539, -13279, -14010, -14732, -15446, -16151, -16846, -17530,
-18204, -18868, -19519, -20159, -20787, -21403, -22005, -22594,
-23170, -23731, -24279, -24811, -25329, -25832, -26319, -26790,
-27245, -27683, -28105, -28510, -28898, -29268, -29621, -29956,
-30273, -30571, -30852, -31113, -31356, -31580, -31785, -31971,
-32137, -32285, -32412, -32521, -32609, -32678, -32728, -32757,
-32767
};
// Compute cos(x) over all 4 quadrants, for x = 0 to 255,
// by accessing the table in reverse for x >= 128
#define COS_Q15(x) (((x) & 0x80) ? cos_tbl[256 - (x)] : cos_tbl[(x)])
/*
Initialize inverter variables
*/
void Inv_init (void)
{
inv_state = STOPPED;
invp.ramp.up = RAMP_RATE_UP;
invp.ramp.down = RAMP_RATE_DOWN;
invp.ampl_var = AMPL_VBUS_VAR;
invp.delta_nom = DELTA_NOM;
invp.delta_thr = DELTA_THRESH;
invp.offset_nom = OFFSET_NOM;
invp.offset_thr = OFFSET_THRESH;
}
/*
Get inverter frequency, or 0 if stopped
*/
BYTE Inv_get_freq (void)
{
if (inv_state == STOPPED)
return (0);
else
return (inv_freq);
}
/*
Set inverter frequency, after checking against valid limits
*/
void Inv_set_freq (BYTE freq)
{
if (freq < INV_FREQ_MIN)
freq = INV_FREQ_MIN;
if (freq > INV_FREQ_MAX)
freq = INV_FREQ_MAX;
target_freq = freq;
}
/*
Stop inverter
*/
void Inv_stop (void)
{
target_freq = 0;
}
/*
Start inverter (does nothing if already running)
*/
void Inv_start (void)
{
if (inv_state != STOPPED)
return;
_PSEMIE = 0; // Disable PWM int while we initialize
Inv_set_freq (target_freq); // Make sure target freq is valid
phase_accum = 0x0000; // Init phase accumulator
phase_offset = invp.offset_nom; // and phase offset
phase_delta = invp.delta_nom; // and phase delta
duty_com = _PWM_PERIOD / 2; // Init duty cycles
duty_aux = _PWM_PERIOD / 2;
duty_run = _PWM_PERIOD / 2;
iphase_aux.sign = SIGN_INIT; // Init motor current sense phase vars
iphase_aux.min = 32767;
iphase_aux.max = -32768;
iphase_aux.last_min = 0;
iphase_aux.last_max = 0;
iphase_run.sign = SIGN_INIT;
iphase_run.min = 32767;
iphase_run.max = -32768;
iphase_run.last_min = 0;
iphase_run.last_max = 0;
ampl_ph = Q14_ONE; // Init amplitude
inv_period_count = 0; // Init interrupt flags
inv_cycle_flag = 0;
inv_update_timer = INV_UPDATE_RATE - 2;
inv_freq = INV_FREQ_INIT; // Init starting freq
inv_state = STARTINIT; // and startup state
inv_update_flag = 1; // Force a freq update
inv_period_flag = 1; // and a PWM update
Inv_run (); // Do first freq update
_SESTAT = 0; // Clear PWM module
_PSEMIF = 0; // and system interrupt flags
_PSEMIE = 1; // Enable PWM interrupt
}
/*
Inverter task handler, called by the main routine
This executes the inverter state machine, and handles
all foreground tasks for inverter operation.
Its main tasks are to deal with startup and shutdown
sequencing, frequency change requests, automatic motor
waveform phase and amplitude tuning, and checking for
fault conditions.
*/
void Inv_run (void)
{
static WORD new_step;
WORD new_delta;
SWORD new_offset;
#ifdef PHASE_ADJ
SWORD stemp;
#endif
SBYTE inv_freq_adj;
/*
Check for PWM fault
*/
if (PWM_REG_BITS (PWMCON, bits.FLTSTAT, PWM_CH_inv)) {
PWM_mode (PWM_CH_inv, PWM_MODE_off);
target_freq = 0;
inv_state = STOPPED;
sys_fault |= FAULT_inv_fault;
}
/*
Check temperature limit
*/
if (meas.v.temp_inv >= _TEMP_LIMIT)
sys_fault |= FAULT_inv_temp;
/*
Check inverter state and frequency, and adjust if necessary
This section of code runs at regular intervals, set by INV_UPDATE_RATE
*/
if (inv_update_flag) {
inv_update_flag = 0;
inv_freq_adj = 0;
switch (inv_state) {
case STOPPED: // Nothing to do here
break;
case STARTINIT: // Prepare for startup by forcing a frequency update
inv_freq --;
inv_freq_adj = 1;
inv_state = STARTWAIT1;
break;
case STARTWAIT1: // Wait for vars to settle
inv_state = STARTWAIT2;
break;
case STARTWAIT2: // Wait for interrupt to occur
break;
case STARTUP: // Now we're ramping up to starting frequency
if (inv_freq < INV_FREQ_START)
inv_freq_adj = invp.ramp.up; // Bump freq up a step
else
inv_state = RAMP_UP; // Reached minimum, go to normal ramp-up
break;
case RUNNING: // Motor is running
if (target_freq == 0)
inv_state = STOPPING; // Freq set to 0 - go shut down
else {
if (inv_freq < target_freq) // Freq increased - go ramp up
inv_state = RAMP_UP;
if (inv_freq > target_freq) // Freq decreased - go ramp down
inv_state = RAMP_DOWN;
}
break;
case RAMP_UP: // Ramp up speed
if (inv_freq < target_freq) {
#ifdef VBUS_HOLD
if (!vbus_low || !sys_mode.b.vbus_hold)
#endif
inv_freq_adj = invp.ramp.up; // Bump freq up a step
}
else if (inv_freq > target_freq)
inv_state = DELAY; // Freq decreased - delay before ramping down
else
inv_state = RUNNING; // All done - go back to running
break;
case RAMP_DOWN: // Ramp down speed
if (target_freq == 0)
inv_state = STOPPING; // Freq set to 0 - go shut down
else {
if (inv_freq > target_freq) {
#ifdef VBUS_HOLD
if (!vbus_high || !sys_mode.b.vbus_hold)
#endif
inv_freq_adj = - invp.ramp.down; // Bump freq down a step
}
else if (inv_freq < target_freq)
inv_state = DELAY; // Freq increased - delay before ramping up
else
inv_state = RUNNING; // All done - go back to running
}
break;
case STOPPING: // Ramp down to stopping frequency
if (inv_freq > INV_FREQ_STOP) {
#ifdef VBUS_HOLD
if (!vbus_high || !sys_mode.b.vbus_hold)
#endif
inv_freq_adj = - invp.ramp.down; // Bump freq down a step
}
else if (inv_freq > 0)
inv_freq = 0; // Below minimum, go right down to 0
else {
PWM_mode (PWM_CH_inv, PWM_MODE_off);
inv_state = STOPPED; // and then shut down PWM outputs
}
break;
case ABORT: // Ramp down to 0
if (inv_freq > 0)
inv_freq_adj = - invp.ramp.down; // Bump freq down a step
else {
PWM_mode (PWM_CH_inv, PWM_MODE_off);
target_freq = 0; // Reached 0, shut down PWM outputs
inv_state = STOPPED;
}
break;
case DELAY: // Short delay when changing ramp directions
case DELAY1:
case DELAY2:
case DELAY3:
inv_state ++;
break;
case DELAY4: // Delay complete - go back to running
inv_state = RUNNING;
break;
}
/*
If the state machine requested a frequency change,
compute the new phase accumulator step size and
frequency-based amplitude
*/
if (inv_freq_adj != 0) {
inv_freq += inv_freq_adj;
new_step = ((LONG) inv_freq // output frequency (Hz * 4)
<< (16UL - 2UL)) // * counts per cycle (to get counts per second)
/ PWM_UPDATE_RATE; // / PWM periods per second (to get counts per period)
// Set amplitude proportional to frequency for V/F control
ampl_freq = AMPL_SLOPE * inv_freq / INV_FREQ_MAX;
}
}
new_delta = phase_delta; // Init temporary tuning vars
new_offset = phase_offset;
/*
This code runs once per cycle of the output waveform.
It checks the relative phase and amplitude of the motor
run and aux winding currents. It adjusts the PWM signals
(which control the motor voltages) if the phase and/or
amplitude are outside of a tolerance threshold, but not
so far outside that the measurements might be suspect.
*/
if (inv_cycle_flag) {
if (inv_state == RUNNING) {
#ifdef PHASE_ADJ
if (sys_mode.b.delta_adj) {
/*
Check the phase difference between the aux and run motor currents
and adjust the phase difference of the output drive voltage if needed
to bring the motor current phase difference close to its nominal value
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -