📄 medium_event.c
字号:
/**********************************************************************
* *
* Software License Agreement *
* *
* The software supplied herewith by Microchip Technology *
* Incorporated (the "Company") for its dsPIC controller *
* is intended and supplied to you, the Company's customer, *
* for use solely and exclusively on Microchip dsPIC *
* products. The software is owned by the Company and/or its *
* supplier, and is protected under applicable copyright laws. All *
* rights are reserved. Any use in violation of the foregoing *
* restrictions may subject the user to criminal sanctions under *
* applicable laws, as well as to civil liability for the breach of *
* the terms and conditions of this license. *
* *
* THIS SOFTWARE IS PROVIDED IN AN "AS IS" CONDITION. NO *
* WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, *
* BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND *
* FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE *
* COMPANY SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, *
* INCIDENTAL OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. *
* *
**********************************************************************/
/**********************************************************************
* *
* Author: Smart Power Soutions, LLP *
* *
* Filename: medium_event.c *
* Date: 4/21/04 *
* File Version: 4.00 *
* Project: 53 *
* Drawing: 2 *
* *
* Tools used: MPLAB C30 Compiler v 1.20.01 *
* *
* Linker File: p30f4012.gld *
* *
* *
***********************************************************************
* Code Description
*
* This file has the medium event handler and its associated functions,
* which execute every 10 msec. In particular, the voltage and speed
* control loops are executed here.
*
**********************************************************************/
#include "general.h"
#include "hardware.h"
#include "defs.h"
#include "extern_globals.h"
void medium_event_handler(void);
void speed_loop(void);
void voltage_control(void);
void starting_code(void);
// debounce_switches is within user_interface.c
extern void debounce_switches(void);
void medium_event_handler(void)
{
long ltemp;
int itemp;
// MED_EVENT_RATE set to 10ms in defs.h
if (medium_speed_event_count > MED_EVENT_RATE)
{
medium_speed_event_count=0;
debounce_switches();
// Check to see if in the lockout period after
// transitioning from acquistion to sensorless. If in the
// lockout period (check_counter>0) then assume speed is
// the same as end of ramp. Without this it is possible to
// get spurious overspeed trips or even overcurrent
// trips if speed loop active due to initial oscillation
// giving unreliable speed calculation
if (check_counter)
rpm=user_parameters[7];
else
// Calculate speed in RPM for display and control purposes
// Period used for speed measurement is 180 electrical degrees
// There are therefore 1*poles counter units per rev
// Thus to get to Revs per sec do
// COUNTER RATE / (poles*period_measurement)
// To get speed in RPM * this by 60
// First Calculate period*number of rotor poles as this is
// used more than once
{ // added this brace S. Bowling
asm("disi #5");
ltemp=((unsigned long)user_parameters[25] \
*(unsigned long)period_measurement);
// Make sure trap a zero period_measurement or any value
// that would cause an overflow
if (ltemp < (COUNTER_RATE*60/65535))
rpm=65535;
else
rpm = COUNTER_RATE*60/ltemp;
} // added this brace S. Bowling
// Now check that a stall has not ocurred which would mean
// that period_measurement is not being updated leading to
// incorrect calculation of speed. This is done by having a counter
// which increments every medium speed event and is reset in the
// sector 0 zero_crossing code where the period measurement is done
// If the counter exceeds the user parameter stall time then it is
// assumed we are in stall.
// Stall detection is disabled unless actually running
if (run_state!=RUNNING) stall_counter=0;
// Now check for overspeed and stall if not already
// in a fault condition
if (!trip_state)
{
if ((rpm>user_parameters[14]) && (control_flags.SENSORLESS))
{
DISABLE_FIRING;
run_state=FAULT;
trip_state=OVER_SPEED;
}
if (stall_counter>user_parameters[13])
{
DISABLE_FIRING;
run_state=FAULT;
trip_state=STALLED;
}
else
stall_counter++;
}
// Now calculate phase advance
// If before the start of phase advance then just load in the
// value required to account for the fact that zero crossing is
// always always detected at least 1 PWM cycle late
if (rpm < user_parameters[11])
{
phase_advance=TIME_CORRECTION;
}
// else within phase advance speed region
else
{
// Calculate the amount of phase advance required in thousandths
// of an electrical degree
ltemp=(long)(rpm-user_parameters[11])*user_parameters[12];
// Then use the current period_measurement (180 electrical degrees)
// To calculate the appropriate phase advance time
phase_advance=((ltemp*period_measurement)/180000)+TIME_CORRECTION;
}
// Call the speed loop if running sensorless
if (control_flags.SENSORLESS==TRUE) speed_loop();
voltage_control();
if (run_state==STARTING) starting_code();
}
return;
}
// This is the speed control loop which can be used in 4 ways
// When the speed control mode is selected to be open loop the PI
// control is bypassed and either the PWM duty is controlled directly
// from the pot (OPEN_VOLTS mode) or the demand for the current
// control loop is taken from the pot(OPEN_CURRENT mode).
// If the (CLOSED_VOLTS mode) is selected, then the output from the
// speed loop directly controls the PWM duty cycle.
// If the (CLOSED_CURRENT mode) is selected, then the output from the
// speed loop forms the demand for the current control loop
// The control mode is selectable via the user menu
void speed_loop(void)
{
static long speed_integral;
int speed_error;
long proportional_term, pos_wloop_limit, neg_wloop_limit;
int control_output,scaled_demand;
long temp;
#ifdef DEBUG
asm("bset LATG,#2");
#endif
// If either not RUNNING or during acquistion to sensorless
// transition then clear off the integral and return
if ((run_state != RUNNING) || (check_counter))
{
speed_integral=0;
return;
}
switch (user_parameters[1])
{
case OPEN_VOLTS: speed_integral=0;
// Disable updates of PWM duty registers to ensure
// All 3 values get loaded together
PWMCON2bits.UDIS=1;
scaled_demand=filtered_pot/user_parameters[37];
// Compensate for inverted firing as FULL_DUTY gives
// zero duty cycle.
// First make sure we don't underflow calculation of
// correct duty
if (scaled_demand > FULL_DUTY) PDC1=0;
else PDC1=FULL_DUTY-scaled_demand;
PDC2=PDC1;
PDC3=PDC1;
// Now enable updates
PWMCON2bits.UDIS=0;
return;
break;
case OPEN_CURRENT:speed_integral=0;
scaled_demand=filtered_pot/user_parameters[38];
if (scaled_demand >= (current_trip-ibus_offset))
current_demand=current_trip-ibus_offset;
else
current_demand=scaled_demand;
return;
break;
case CLOSED_VOLTS:// The *32 is because duty limits
// also used by current loop where
// scaling is *512, whereas here it is
// *16384 hence the 32 factor.
pos_wloop_limit=pos_duty_limit*32;
neg_wloop_limit=neg_duty_limit*32;
break;
case CLOSED_CURRENT:
pos_wloop_limit=pos_current_limit;
neg_wloop_limit=neg_current_limit;
break;
}
speed_demand=filtered_pot*user_parameters[39];
control_flags.TORQUE_LIMIT=FALSE;
// Form the speed error by subtracting the speed in rpm from the
// speed demand read from VR2.
speed_error=speed_demand-rpm;
// All calculated quantities will be shifted back down at the
// end by 512. P & I gains are effectively scaled up by 512.
// This is done to allow fractional values of P & I without
// severe rounding issues which causes quantization noise etc.
// Now calculate proportional term
proportional_term=((long)speed_error*(long)wloop_p_gain);
if (proportional_term > pos_wloop_limit)
{
control_flags.TORQUE_LIMIT=TRUE;
control_output=pos_wloop_limit/16384;
if (user_parameters[1]==CLOSED_VOLTS)
control_output+=ZERO_DUTY;
// If in limit due to just P term then clear
// off I term so that when come out of limit
// I term can smoothly take over to eliminate
// steady state error.
speed_integral=0;
}
else
{
if (proportional_term < neg_wloop_limit)
{
control_flags.TORQUE_LIMIT=TRUE;
control_output=neg_wloop_limit/16384;
if (user_parameters[1]==CLOSED_VOLTS)
control_output+=ZERO_DUTY;
// If in limit due to just P term then clear
// off I term so that when come out of limit
// I term can smoothly take over to eliminate
// steady state error.
speed_integral=0;
}
}
// Now do integral term if not in torque limit already due to P term
if (control_flags.TORQUE_LIMIT==FALSE)
{
// Update Intergral term here but this will be overwritten if
// found to be in limit later on in code
speed_integral+=((long)speed_error * (long)wloop_i_gain);
temp=speed_integral+proportional_term;
// If sum of P+I terms pushes you into positive limit
if (temp > pos_wloop_limit)
{
control_flags.TORQUE_LIMIT=TRUE;
control_output=pos_wloop_limit/16384;
if (user_parameters[1]==CLOSED_VOLTS)
control_output+=ZERO_DUTY;
// Now calculate integrator limit and clamp
speed_integral = pos_wloop_limit - proportional_term;
}
else
{
// If sum of P+I terms pushes you into negative limit
if (temp < neg_wloop_limit)
{
control_flags.TORQUE_LIMIT=TRUE;
control_output=(neg_wloop_limit/16384);
if (user_parameters[1]==CLOSED_VOLTS)
control_output+=ZERO_DUTY;
// Now calculate integrator limit and clamp
speed_integral=neg_wloop_limit - proportional_term;
}
else
// Calculate control output based on both unclamped P and I terms
{
// Temp still contains unclamped P+I term so no need to recalculate
control_output=(temp/16384);
if (user_parameters[1]==CLOSED_VOLTS)
control_output+=ZERO_DUTY;
}
}
}
if (user_parameters[1]==CLOSED_VOLTS)
{
// Do final check to ensure control_output is not < 0
// as this would cause large positive value to be loaded
// into duty cycle registers!
if (control_output < 0) control_output=0;
// Disable updates of PWM duty registers to ensure
// All 3 values get loaded together
PWMCON2bits.UDIS=1;
// Now load calculated value into duty cycle registers
// Compensate for inverted firing as FULL_DUTY gives
// zero duty cycle.
control_output=FULL_DUTY-control_output;
PDC1=(unsigned int)control_output;
PDC2=(unsigned int)control_output;
PDC3=(unsigned int)control_output;
// Now enable updates
PWMCON2bits.UDIS=0;
}
else
{
current_demand=control_output;
}
return;
}
// This routine does the voltage control of the DC bus
// As there is an issue with using OC modules, TIMER3
// is used to provide the PWM.
// This is done by directly setting the port pin here
// and loading PR3 with the on time. The TIMER3 ISR
// sets the port pin low.
void voltage_control(void)
{
static long voltage_integral;
int voltage_error;
long proportional_term;
long temp;
if ((run_state != RUNNING) && (run_state != STARTING))
{
voltage_integral=0;
return;
}
// If previous on pulse still running then disable the
// interrupt used to time the on time and turn switch off
if(IEC0bits.T3IE)
{
IEC0bits.T3IE=0;
// BRAKE_FIRE=0;
}
// Form the voltage error by subtracting the voltage demand
// in the user parameters from the filtered vdc feedback
// Note that error is of the opposite polarity than usual as
// if the volts are too high you want to fire the switch
voltage_error=filtered_vdc-voltage_demand;
// All calculated quantities will be shifted back down at the
// end by 512. P & I gains are effectively scaled up by 512.
// This is done to allow fractional values of P & I without
// severe rounding issues which causes quantization noise etc.
// Now calculate proportional term
proportional_term=((long)voltage_error*(long)vloop_p_gain);
if (proportional_term > POS_V_LIMIT)
{
voltage_integral=0;
return;
}
else
{
// Note that negative saturation is tested by looking at
// voltage error with -10 or approx 1% of full scale.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -