📄 isrs.c
字号:
break;
case 1: OVDCON=SECTOR1_OVERRIDE;
adc_channel_config=VPH_YELLOW;
break;
case 2: OVDCON=SECTOR2_OVERRIDE;
adc_channel_config=VPH_RED;
break;
case 3: OVDCON=SECTOR3_OVERRIDE;
adc_channel_config=VPH_BLUE;
break;
case 4: OVDCON=SECTOR4_OVERRIDE;
adc_channel_config=VPH_YELLOW;
break;
case 5: OVDCON=SECTOR5_OVERRIDE;
adc_channel_config=VPH_RED;
break;
}
// When running sensorless, the zero_crossing
// routine actually moves on the sector number
// in response to a zero crossing. Otherwise
// need to increment sector in the correct direction
// to ensure that the next time the routine is called
// the system commutates.
if (control_flags.SENSORLESS==FALSE)
{
if (control_flags.ACQUIRE1==FALSE)
{
if (control_flags.DIR==FORWARDS)
{
if (sector==5) sector=0;
else sector++;
}
else
{
if (sector==0) sector=5;
else sector--;
}
}
}
// When running sensorless need to reconfigure ADC to
// look at correct vph channel and initialize several
// variables in zero_crossing routine.
// This is triggered by ADCCONFIG flag being set.
// Also need to disable T2 interrupt as zero crossing
// routine to load up next commutation instant.
else
{
control_flags.ADCCONFIG=TRUE;
IEC0bits.T2IE = 0;
}
IFS0bits.T2IF = 0;
return;
}
// Timer 3 used as to provide simple PWM of
// brake switch while OC module not functioning
// It is used to time the On time and turns
// brake switch off when interrupt occurs
void __attribute__((__interrupt__)) _T3Interrupt(void)
{
IFS0bits.T3IF = 0;
IEC0bits.T3IE = 0;
return;
}
// check_zero_crossing is used to detect and act upon phase voltage
// zero crossings (when BEMF crosses vdc/2)
// It is called during normal running and when starting using
// acqusition method1.
void check_zero_crossing(void)
{
static unsigned int previous_sample;
static unsigned char level_counter;
static unsigned char blanking_counter;
static unsigned int half_vdc=0;
unsigned int new_timestamp;
// If a commutation just occured which reconfigured
// ADC to look at next vph feedack, need to clear
// previous sample and level counter as well as
// returning without checking for crossing and clearing
// the relevant flags.
// Also load up blanking counter to ignore first
// few samples to reject false crossings due to
// diode action at the end of the demag period
// Finally determine whether looking for RISING or FALLING X
if (control_flags.ADCCONFIG==TRUE)
{
control_flags.ADCCONFIG=FALSE;
previous_sample=0;
level_counter=0;
control_flags.LEVEL=FALSE;
control_flags.ZERO_CROSS=FALSE;
blanking_counter=(unsigned char)user_parameters[32];
// Decide whether rising or falling edge detect
if (control_flags.DIR==FORWARDS)
{
if ((sector % 2) == 0) control_flags2.FALLING_EDGE=TRUE;
else control_flags2.FALLING_EDGE=FALSE;
}
else
{
if ((sector % 2) == 0) control_flags2.FALLING_EDGE=FALSE;
else control_flags2.FALLING_EDGE=TRUE;
}
return;
}
// Blanking counter used to ignore samples just after
// commutation so that diode action doesn't cause false
// zero_crossing.
if (blanking_counter > 0)
{
blanking_counter--;
return;
}
// This locks out the possibility of a second zero crossing
// being detected in a particular sector.
if (control_flags.ZERO_CROSS==TRUE)
return;
// Half_vdc is a simple filtered version of vdc/2
half_vdc = ((half_vdc + (vdc>>1)) >> 1);
// To add robustness to the zero crossing detection a programmable
// number of samples of vph must be seen above (falling edge)
// or below (rising edge) before the actual crossing detection is enabled
// The level_counter variable along with the LEVEL flag implement this
if (level_counter==(unsigned char)user_parameters[33])
{
control_flags.LEVEL=TRUE;
}
if (control_flags2.FALLING_EDGE)
{
if ((level_counter < (unsigned char)user_parameters[33]) && (vph > half_vdc))
{
level_counter++;
}
if ((level_counter > 0) && (vph <= half_vdc))
{
level_counter--;
}
}
else
{
if ((level_counter < (unsigned char)user_parameters[33]) && (vph < half_vdc))
{
level_counter++;
}
if ((level_counter > 0) && (vph >= half_vdc))
{
level_counter--;
}
}
// Note that saved timestamp and timedelta data is stored in arrays with three
// elements corresponding to the three phases. The conventions used are as follows:
// Index 0 = Red
// Index 1 = Yellow
// Index 2 = Blue
// Sector 0,3 = Blue Phase Crossing
// Sector 1,4 = Yellow Phase Crossing
// Sector 2,5 = Red Phase Crossing
if (control_flags.LEVEL==TRUE) // Only check for the crossings once LEVEL is true
{
// If we have seen sufficient valid samples above or below vdc/2
// and the current and previous samples are <= vdc/2 or >= vdc/2 (edge dependant)
// then this is the valid crossing.
if (((control_flags2.FALLING_EDGE) && (previous_sample <= half_vdc)\
&& (vph <= half_vdc)) || \
((control_flags2.FALLING_EDGE==FALSE) && (previous_sample >= half_vdc)\
&& (vph >= half_vdc)))
{
// Grab latest value of QEI counter which is
// being used in the 16 bit timer mode
new_timestamp=POSCNT;
// Calculate time between this zero crossing and the last one
// The result of this calculation is correct irrespective
// of counter rollover due to the elegance of 2's comp
// arithmetic on unsigned variables.
// Note that to ensure we use the correct previous zero crossing
// timestamp the calculation is direction dependent
if (control_flags.DIR == FORWARDS)
{
switch (sector)
{
case 0:
case 3: latest_delta=new_timestamp-previous_timestamps[0];
break;
case 1:
case 4: latest_delta=new_timestamp-previous_timestamps[2];
break;
case 2:
case 5: latest_delta=new_timestamp-previous_timestamps[1];
break;
}
}
else // going BACKWARDS
{
switch (sector)
{
case 0:
case 3: latest_delta=new_timestamp-previous_timestamps[1];
break;
case 1:
case 4: latest_delta=new_timestamp-previous_timestamps[0];
break;
case 2:
case 5: latest_delta=new_timestamp-previous_timestamps[2];
break;
}
}
// Calculate period measurement used for speed feedback
// rather than just using 60 degree measurement use 180
// degrees to get better resolution at high speeds
// Only calculate period once per electrical cycle in sector 0
// Note if you calculate period more often than once per electrical
// cycle, you will get faster update rate but more ripple.
if (sector==0)
{
period_measurement=new_timestamp-previous_timestamps[2];
// Reset stall counter to denote a valid period measurement event has ocurred
stall_counter=0;
}
// Calculate the number of counts of TIMER2 until next commutation
// Note that TIMER2 and QEI are on same timebase
// The divison by 2 is because the diffence between the zero
// crossing of a phase and the previous one from another phase
// is 60 electrical degrees. The natural offset between zero_crossing
// and the correct commutation is 30 electrical degress without any
// phase advance.
commutation_time=((latest_delta)/2) - phase_advance;
// Now check that phase advance didn't cause too small a value for
// commutation time. This can occur because phase advance is only
// calculated in the medium speed event routine based on the period
// measurement for efficiency. The test is done at 1 because this is
// the smallest value that can be loaded into PR2 for the TIMER2 interrupt
// to function correctly as we reset TIMER2 to zero after writing to PR2
if (commutation_time < 1) commutation_time=1;
// Now check to see if commutation has failed. This is done
// by comparing latest_delta with previous_delta and applying a
// a tolerance check.
check_value=(latest_delta*100)/previous_delta;
if (((check_value > upper_tol) \
|| (check_value < lower_tol)) \
&& (check_counter==0))
{
// If sensorless failed then enter
// acquistion mode2 again if auto re-acquire (user param 31) is enabled
DISABLE_FIRING;
IEC0bits.T2IE=FALSE;
control_flags.SENSORLESS=FALSE;
if (user_parameters[31])
{
control_flags.ACQUIRE2=TRUE;
control_flags.ADCCONFIG=TRUE;
control_flags2.RETRY_FLAG=FALSE;
}
else
{
run_state=FAULT;
trip_state=LOST;
}
return;
}
else // Valid zero crossing detected
{
// Update sector
if (control_flags.DIR==FORWARDS)
{
if (sector==5) sector=0;
else sector++;
}
else
{
if (sector==0) sector=5;
else sector--;
}
// Commutate as sensorless working provided not acquiring
// using method1
if (control_flags.ACQUIRE1==FALSE)
{
// Load up period register
PR2 = (unsigned int) commutation_time;
TMR2=0;
// Now clear off interrupt flag and enable interrupt
IFS0bits.T2IF = 0;
IEC0bits.T2IE = 1;
// Load up guard timer implemented in Timer 1 with
// double the latest time delta between zero crossings
PR1=latest_delta<<1;
TMR1=0;
// Clear interrupt flag just in case writing to timer
// generated an interrupt.
IFS0bits.T1IF = 0;
// Now enable interrupt.
IEC0bits.T1IE = 1;
}
else
{
// Incrementing acquire counter indicates a valid zero x
// event has just occurred.
// acquire counter is reset to zero by code in T2ISR if
// a zero X event is missed during acquistion
acquire_counter++;
#ifdef DEBUG
asm("btg LATD,#15");
#endif
if (acquire_counter > 1)
{ // i.e. if have had two consequtive zero X events
// Disable T2 interrupts ready for transition to
// closed loop commutation
IEC0bits.T2IE = 0;
// Set flags to indicate now running sensorless
control_flags.ACQUIRE1=FALSE;
control_flags.SENSORLESS=TRUE;
run_state=RUNNING;
// load up check counter to disable zeor X tolerance
// checks for 1 elec cycle
check_counter=6;
// set up T2 to generate interrupt at correct time
PR2 = (unsigned int) commutation_time;
TMR2=0;
// Now clear off interrupt flag and enable interrupt
IFS0bits.T2IF = 0;
IEC0bits.T2IE = 1;
#ifdef DEBUG
asm("btg LATG,#1");
#endif
}
}
}
// End of else associated with a valid zero crossing
// Note that if you fail tolerance check code returns before
// this point.
// Save off timestamp info ready for next zero crossing
if (control_flags.DIR==FORWARDS)
{
switch (sector)
{
case 0:
case 3: previous_timestamps[0] = new_timestamp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -