⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 inverter.c

📁 单相电机变频驱动原码,采用DISPIC30芯片
💻 C
📖 第 1 页 / 共 2 页
字号:
*/
			// 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 + -