?? 復件 main.c
字號:
// Find time since last commutation
//紀錄最后一次換相的時間
timeSinceCommutation = TCNT1;
TCNT1 = COMMUTATION_CORRECTION;//修正零點采樣頻率
// Filter the current ZC detection with earlier measurements through an IIR filter.
filteredTimeSinceCommutation = (COMMUTATION_TIMING_IIR_COEFF_A * timeSinceCommutation
+ COMMUTATION_TIMING_IIR_COEFF_B * filteredTimeSinceCommutation)
/ (COMMUTATION_TIMING_IIR_COEFF_A + COMMUTATION_TIMING_IIR_COEFF_B);
OCR1A = filteredTimeSinceCommutation;
speedUpdated = TRUE;
SET_TIMER1_INT_COMMUTATION;//(TIMSK1 = (1 << OCIE1A))//允許TC1中斷
CLEAR_ALL_TIMER1_INT_FLAGS;//(TIFR1 = TIFR1)
// Disable Timer/Counter0 overflow ISR.//(TIMSK0 = 0)//禁止TC0中斷
DISABLE_ALL_TIMER0_INTS;
// Read speed reference.
// Make sure that a sample is not in progress.
//確保沒有AD轉換在過程中
while (ADCSRA & (1 << ADSC))
{
}
// Change channel
ADMUX = ADMUX_SPEED_REF;//選取參考速度輸入通道0X04
// Start conversion manually.//手動開始單次轉換
ADCSRA |= (1 << ADSC);
// Wait for conversion to complete.
while(ADCSRA & (1 << ADSC))
{
}
speedReferenceADC = ADCH;
// Read voltage reference.
// Change ADC channel.
ADMUX = ADMUX_REF_VOLTAGE;//選取參考電壓輸入通道0X05
// Start conversion manually.
ADCSRA |= (1 << ADSC);
// Wait for conversion to complete.
while((ADCSRA & (1 << ADSC)))
{
}
referenceVoltageADC = ADCH;
// Enable current measurements in ADC ISR.
ADMUX = ADMUX_CURRENT;//選取電流輸入通道0X03
ADCSRA |= (1 << ADATE) | (1 << ADIE) | ADC_PRESCALER;//ADC自動觸發使能,中斷使能
}
else
{//零點采樣失敗,檢測電流并更新電流標志位
unsigned char tempADMUX;
tempADMUX = ADMUX;
// Read current
// Make sure that a sample is not in progress
while (ADCSRA & (1 << ADSC))
{
}
// Change channel
ADMUX = ADMUX_CURRENT;
// Start conversion manually.
ADCSRA |= (1 << ADSC);
// Wait for conversion to complete.
while((ADCSRA & (1 << ADSC)))
{
}
shuntVoltageADC = ADCH;
currentUpdated = TRUE;
// Restore ADC channel.
//恢復ADMUX寄存器
ADMUX = tempADMUX;
//ADC自動觸發使能,中斷使能
ADCSRA |= (1 << ADATE) | (1 << ADIE) | ADC_PRESCALER;
}
}
/*! \brief Commutates and prepares for new zero-cross detection.
*
* This interrupt service routine is triggered exactly when a commutation
* is scheduled. The commutation is performed instantly and Timer/counter0
* is reset to measure the delay between commutation and zero-cross detection.
*
* Commutation causes large transients on all phases for a short while that could
* cause false zero-cross detections. A zero cross detection hold-off period is
* therefore used to avoid any false readings. This is performed by using Timer/counter1
* compare B. The compare is set to happen after the specified hold-off period.
* Timer/counter1 compare B interrupt handler then enables the zero-cross detection.
*/
// HOLD OFF 之前的操作,之后可以使能零點檢測開始
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
// Commutate and clear commutation timer.
//換相并復位換相定時器
DRIVE_PORT = nextDrivePattern;
TCNT1 = 0;
zcPolarity = nextCommutationStep & 0x01;
// Set zero-cross detection holdoff time.
CLEAR_ALL_TIMER1_INT_FLAGS;
OCR1B = ZC_DETECTION_HOLDOFF_TIME_US;//==OCR1A/2
SET_TIMER1_INT_HOLDOFF;//輸出比較器B中斷使能//使能ADC-HOLD OFF中斷
//__watchdog_reset();
}
/*! \brief Enables zero-cross detection.
*
* This interrupt service routine is triggered when the zero cross detection
* hold-off time after commutation is over. All Timer/counter1 interrupts are
* disabled and Timer/counter0 (PWM) overflow interrupt is enabled to allow
* the ADC readings to be used for zero-cross detection.
*/
// 檢測HOLD OFF完成,禁止TIMER1,使能TIMER0
SIGNAL(SIG_OUTPUT_COMPARE1B)
{
// Enable TCNT0 overflow ISR.
CLEAR_ALL_TIMER0_INT_FLAGS;
CLEAR_ALL_TIMER1_INT_FLAGS;
SET_TIMER0_INT_ZC_DETECTION;//TC0溢出中斷使能,準備過零點檢測
DISABLE_ALL_TIMER1_INTS; //TC1禁止
// Set up ADC for zero-cross detection
//測量FLOAING相的電壓
ADMUX = ADMUXTable[nextCommutationStep];
// Wait for ADC to complete
while (!(ADCSRA & (1 << ADIF)))//等待ADIF置位(轉換完成,數據寄存器更新)
{
}
ADCSRA &= ~(1 << ADIE);//ADC中斷禁止
ADCSRA |= (1 << ADSC) | (1 << ADATE);//自動觸發和轉換開始使能
// Rotate commutation step counter.
nextCommutationStep++;
if (nextCommutationStep >= 6)
{
nextCommutationStep = 0;
}
nextDrivePattern = driveTable[nextCommutationStep];
}
/* \brief ADC complete interrupt service routine, used for current measurements.
* ADC完成中斷服務程序,用于電流測量
* This interrupt service routine is only enabled when current measurements are
* 只用與在PWM定時器溢出時,電流檢測自動觸發的情況下。
* auto-triggered by the PWM counter overflow. The measured value is simply
* copied to \ref shuntVoltageADC, the \ref currentUpdated flag is set and
* Timer0 (PWM timer) interrupt flags are cleared.
*/
SIGNAL(SIG_ADC)
{
shuntVoltageADC = ADCH;
currentUpdated = TRUE;
CLEAR_ALL_TIMER0_INT_FLAGS;
}
/*! \brief Watchdog interrupt 看門狗中斷服務程序
*
* This ISR is called before the watchdog timer resets the device because of a stall.
* It simply disables driving, but other tasks that must be done before a watchdog reset,
* such as storage of variables in non-volatile memory can be done here.
*/
/*
#pragma vector=WDT_vect
__interrupt void WatchdogISR()
{
DISABLE_DRIVING;
for(;;)
{
;
}
}
*/
/*! \brief Overcurrent interrupt 過電流檢測中斷服務程序
*
* This interrupt service routine cuts power to the motor when an overcurrent situation
* is detected.
*/
SIGNAL(SIG_COMPARATOR)
{
DISABLE_DRIVING;
for(;;)
{
;
}
}
/*! \brief Generates a delay used during startup
*
* This functions is used to generate a delay during the startup procedure.
* The length of the delay equals delay * STARTUP_DELAY_MULTIPLIER microseconds.
* Since Timer/Counter1 is used in this function, it must never be called when
* sensorless operation is running.
*/
//只用在啟動時,正常運轉時不調用。
void StartupDelay(unsigned int delay)
{
CLEAR_ALL_TIMER1_INT_FLAGS;
do
{
TCNT1 = 0xffff - STARTUP_DELAY_MULTIPLIER;
// Wait for timer to overflow.
while (!(TIFR1 & (1 << TOV1)))//如果TC1溢出則清中斷標志位
{
}
CLEAR_ALL_TIMER1_INT_FLAGS;
delay--;
} while (delay);
}
#ifdef SPEED_CONTROL_CLOSED_LOOP
/*! \brief Controls the PWM duty cycle based on speed set-point and current consumption.
*
* This function controls the PWM duty cycle by calling a speed controller and a
* current controller. The speed controller signal is directly applied to the duty
* cycle. The current controller signal is used to limit the maximum duty cycle.
*/
//通過調用速度控制信號調節PWM占空比;電流信號限制PWM最大占空比。
static void PWMControl(void)
{
signed int speedCompensation;//速度改變量
static unsigned char currentCompensation = 0;
static signed int duty = STARTUP_PWM_COMPARE_VALUE;
// Run speed control only if a new speed measurement is available.
if (speedUpdated) //如果有新的速度給定
{
speedCompensation = SpeedControl();
speedUpdated = FALSE;
duty += speedCompensation;
}
// Run current control only if a new current measurement is available.
if (currentUpdated) //如果有新的電流轉換完成
{
currentCompensation = CurrentControl();
currentUpdated = FALSE;
}
// Keep duty cycle within limits.//控制占空比在范圍內
if (duty < MIN_PWM_COMPARE_VALUE)
{
duty = MIN_PWM_COMPARE_VALUE;
}
if (duty > (MAX_PWM_COMPARE_VALUE - currentCompensation))
{
duty = MAX_PWM_COMPARE_VALUE - currentCompensation;
}
SET_PWM_COMPARE_VALUE((unsigned char)duty);//OCR0B=DUTY更改TOP值
}
#endif
#ifdef SPEED_CONTROL_OPEN_LOOP
static void PWMControl(void)
{
// Only update duty cycle if a new speed reference measurement has been made. (Done right after speed measurement is ready)
//只有在建立一個新的速度參考值時改變DUTY,可能是指通過調節SPEED REFERENCE來調節DUTY
if (speedUpdated)
{
// Calculate duty cycle from speed reference value.
SET_PWM_COMPARE_VALUE(MIN_PWM_COMPARE_VALUE + speedReferenceADC * (MAX_PWM_COMPARE_VALUE - MIN_PWM_COMPARE_VALUE) / ADC_RESOLUTION);
}
}
#endif
/*! \brief Calculates the current speed in electrical RPM.
*
* This function calculates the current speed in electrical rotations per
* minute from the global variable \ref filteredTimeSinceCommutation.
*/
//讀出當前的速度
static unsigned long CalculateSpeed()
{
// Copy used to minimize period where interrupts are disabled.
unsigned int filteredTimeSinceCommutationCopy;
unsigned long rotationPeriod;
unsigned long speed;
/*
Disable interrupts to ensure that \ref filteredTimeSinceCommutation is accessed in
an atomic operation.
*/
//禁止中斷確保在極短的時間讀出該變量
cli();
filteredTimeSinceCommutationCopy = filteredTimeSinceCommutation;
sei();
/*
filteredTimeSinceCommutation is one half commutation time. Must be multiplied by 12 to get
one full rotation.
*/
//要得到一個完整電周期需乘以12
rotationPeriod = (unsigned long)filteredTimeSinceCommutationCopy * 12;
speed = (TICKS_PER_MINUTE / rotationPeriod);
return speed;
}
/*! \brief Calculates the speed set-point in electrical RPM.
*
* This function calculates the speed set-point from the global variable
* speedReferenceADC.
*
* In this implementation, the speed reference values from 0x00 to 0xff are
* linearly mapped into the allowable speed range, set by \ref MIN_SPEED and
* \ref MAX_SPEED.
*/
static unsigned long CalculateSpeedSetpoint()
{
return (MIN_SPEED + ((MAX_SPEED - MIN_SPEED) * (unsigned int)speedReferenceADC) / ADC_RESOLUTION);
}
/*! \brief Calculates current consumption.
*
* This function calculates the current consumption in milliAmperes from the
* global variable \ref shuntVoltageADC. The external know reference voltage
* is used to compensate for varying AREF voltage.
*/
static unsigned int CalculateCurrent()
{
unsigned long ADCref;
unsigned int current;
// Calculate the voltage at AREF pin (scaled down motor supply voltage),
// using the known reference voltage. (In milliVolts)
ADCref = EXTERNAL_REF_VOLTAGE * 256UL / referenceVoltageADC;
// Calculate the current through the shunt. (In milliAmperes)
current = (unsigned int)((shuntVoltageADC * ADCref * 1000UL / 256UL) / SHUNT_RESISTANCE);
return current;
}
/*! \brief Speed control loop
*
* This function runs a simple P-regulator speed control loop. The duty cycle
* is only updated each time a new speed measurement is ready (on each zero-crossing).
* The time step is thus variable, so the P-gain of the P-regulator corresponds to
* a speed-varying P-gain for the continous system.
*/
static signed int SpeedControl(void)
{
unsigned long speedSetpoint;
unsigned long currentSpeed;
signed long speedError;
signed long dutyChange;
speedSetpoint = CalculateSpeedSetpoint();
currentSpeed = CalculateSpeed();
speedError = (speedSetpoint - currentSpeed);
dutyChange = speedError * P_REG_K_P / P_REG_SCALING;
return dutyChange;
}
/*! \brief Current control loop
*
* This function is called after the speed control loop. The desired duty cycle
* calculated by the speed control loop is available, and this function is
* responsible for adjusting the duty cycle to ensure that the current stays
* within limits.
*/
//電流控制程序
static unsigned char CurrentControl(void)
{
unsigned int current;
unsigned int overCurrentCorrection = 0;
current = CalculateCurrent();
// Cut power to motor if current is critically high.
if (current > CURRENT_LIMITER_CRITICAL)
{
DRIVE_PORT = 0x00;
for (;;)
{
// Stop and let watchdog timer reset part.
}
}
if (current > CURRENT_LIMITER_START)
{
overCurrentCorrection = (current - CURRENT_LIMITER_START) * CURRENT_LIMITER_FACTOR;
}
if (overCurrentCorrection > 255)
{
return 255;
}
return overCurrentCorrection;
}
/*! \mainpage
* \section Intro Introduction
* This documents data structures, functions, variables, defines, enums, and
* typedefs in the software for application note AVR444.
*
* \section CI Compilation Info
* This software was written for the IAR Embedded Workbench 4.11A.
*
* To make project:
* <ol>
* <li> Add the file main.c to project.
* <li> Under processor configuration, select device ATmega48, ATmega88
* or ATmega168
* <li> Enable bit definitions in I/O include files
* <li> Under "C/C++ compiler->Code->Register utilization", select 5
* registers (R11..R15) locked for global variables.
* <li> High optimization on speed is recommended for best performance
* </ol>
*
* \section DI Device Info
* The included source code is written for ATmega48/88/168.
*/
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -