📄 复件 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 + -