?? 復件 main.c
字號:
// This file has been prepared for Doxygen automatic documentation generation.
/*! \file ********************************************************************
*
* Atmel Corporation
*
* - File : main.c
* - Compiler : IAR EWAAVR 2.28a/3.10c
*
* - Support mail : avr@atmel.com
*
* - Supported devices : ATmega48/88/168
*
* - AppNote : AVR444 - Sensorless control of three-phase brushless
* DC motors with ATmega48.
*
* - Description : Example of how to use the ATmega48 for sensorless
* control of a three phase brushless DC motor.
*
* $Revision: 1.1
* $Date: Monday, October 10, 2005 11:15:46 UTC
*****************************************************************************/
/*
編譯器:GCC
CPU:ATMEGA128
頻率:4M
編譯平臺:VC++
程序流程:
主程序:
1. 端口初始化
詳見P9頁
2. 定時器初始化
TC0:升序時,比較匹配發生時,OC0B清零,降序比較匹配發生時置位;相位修正PWM模式
TOP值為OCR0A=200
TC1:8分頻
3. ADC初始化
參考電壓AREF;轉換結果左對齊
定時器/計數器0溢出觸發ADC自動轉換
允許模擬比較器中斷(上升沿)
4. 換相控制數據表格
5. 首次換相輸出--共8次,然后打開TIMSK1的比較器A.
6. 全局中斷開,進入循環
7.循環程序.
BMEF檢測的思路:
**進入TC1的比較器A中斷,換相并復位換相定時器,設定HOLD-OFF定時器,打開TC1 的比較器B中斷;
打開TCO中斷,關閉TC1中斷,準備零點檢測
**進入TC0溢出中斷,取出ADC檢測的電壓值(周期中未通電相),根據上升或下降沿以及和標定值比較判斷
是否為過零點.
如果是過零點,則對換相數據進行處理,更新TC1的OCR1A,之后關閉TC0,打開TC1,轉入電流檢測通道,打開ADC轉換完成中斷.
如果不是過零點,檢測電流過后,恢復到BMEF電壓檢測狀態,打開ADC轉換完成中斷.(等待下一次TC0中斷)
*/
/*
疑問:
1.為何程序中多處出現清中斷標志位命令:TIFRN.
2.看門狗的GCC中斷程序怎么寫
3.為了快速存取,在GCC中如何定義寄存器變量
IAR是這樣定義的.
__regvar __no_init volatile unsigned int filteredTimeSinceCommutation @14;
4.感覺過零點檢測失敗應該恢復至定時器/計數器0溢出觸發ADC自動轉換
*/
#include "BLDC.h"
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
//! Array of power stage enable signals for each commutation step.
//驅動功率管的信號組
unsigned char driveTable[6];
//! Array of ADC channel selections for each commutation step.
//ADC通道選擇組
unsigned char ADMUXTable[6];
//! Array holding the inter commutation delays used during startup.
unsigned int startupDelays[STARTUP_NUM_COMMUTATIONS];//8 啟動期間的變換次數
/*! \brief Filtered commutation timer variable and speed indicator.
* This value equals half the time of one commutation step. It is filtered
* through an IIR filter, so the value stored is not the most recent measuremnt.
* The variable is stored in registers R14-R15 for quicker access.
*/
//該變量數值為變換步階時間的一半,是通過IIR得出的。所以儲存的值不是最新測量的數值
//為了快速存取,將這個變量被放入R14,R15寄存器中
//__regvar __no_init volatile unsigned int filteredTimeSinceCommutation @14;
volatile unsigned int filteredTimeSinceCommutation;
/*! \brief The power stage enable signals that will be output to the motor drivers
* at next commutation.
*
* This variable holds the pattern of enable signals that will be output to the
* power stage at next commutation. It is stored in register R13 for quick access.
*/
//該變量為下一時刻功率管的驅動信號
//為了快速存取,將這個變量被放入R13寄存器中
//__regvar __no_init volatile unsigned char nextDrivePattern @13;
volatile unsigned char nextDrivePattern;
/*! \brief Polarity of the expected zero crossing.
*
* The polarity of the expected zero crossing.
* Could be eiter \ref EDGE_FALLING or \ref EDGE_RISING.
*/
//過零點的斜率(極性)
//為了快速存取,將這個變量被放入R12寄存器中
//__regvar __no_init volatile unsigned char zcPolarity @ 12;
volatile unsigned char zcPolarity;
/*! \brief The commutation step that starts at next commutation.
*
* The commutation step that starts at next commutation. This is used to keep
* track on where in the commutation cycle we are. Stored in register R11 for
* quick access
*/
//下一個變換步階,用于追蹤現在所處的位置
//為了快速存取,將這個變量被放入R11寄存器中
//__regvar __no_init volatile unsigned char nextCommutationStep @11;
volatile unsigned char nextCommutationStep;
//! ADC reading of external analog speed reference.
//外部速度比較輸入信號
volatile unsigned char speedReferenceADC;
//! ADC reading of shunt voltage.
//外部分壓輸入信號
volatile unsigned char shuntVoltageADC = 0;
//! ADC reading of the known external reference voltage.
//外部參考電壓輸入信號
volatile unsigned char referenceVoltageADC;
//! Flag that specifies whether a new external speed reference
//! and a motor speed measurement is available.
//速度量測結束標志
volatile unsigned char speedUpdated = FALSE;
//! Flag that specifies whether a new current measurement is available.
// 電流量測結束標志
volatile unsigned char currentUpdated = FALSE;
/*! \brief Program entry point.
*
* Main initializes all peripheral units used and calls the startup procedure.
* All commutation control from that point is done in interrupt routines.
*/
void main(void)
{
// Initialize all sub-systems.
//ResetHandler();
InitPorts();
InitTimers();
InitADC();
MakeTables();
InitAnalogComparator();
// Run startup procedure.
StartMotor();
// Turn on watchdog for stall-detection.
//WatchdogTimerEnable();
//__enable_interrupt();
sei();
for(;;)
{
PWMControl();
}
}
/*! \brief Examines the reset source and acts accordingly.
*
* This function is called early in the program to disable watchdog timer and
* determine the source of reset.
*
* Actions can be taken, based on the reset source. When the watchdog is used as
* stall protection, a stall can be detected here. It is possible to e.g. store
* a variable in EEPROM that counts the number of failed restart attempts. After a
* certain number of attempts, the motor could simply refuse to continue until
* an external action happens, indicating that the rotor lock situation could be
* fixed.
*/
//將啟動失敗的嘗試次數寫入EEPROM,達到一定次數禁止啟動,等待處理。
/*static void ResetHandler(void)
{
__eeprom unsigned static int restartAttempts;
// Temporary variable to avoid unnecessary reading of volatile register MCUSR.
unsigned char tempMCUSR;
tempMCUSR = MCUSR;
MCUSR = tempMCUSR & ~((1 << WDRF) | (1 << BORF) | (1 << EXTRF) | (1 << PORF));
// Reset watchdog to avoid false stall detection before the motor has started.
__disable_interrupt();
__watchdog_reset();
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = 0x00;
// Examine the reset source and take action.
switch (tempMCUSR & ((1 << WDRF) | (1 << BORF) | (1 << EXTRF) | (1 << PORF)))
{
case (1 << WDRF):
restartAttempts++;
if (restartAttempts >= MAX_RESTART_ATTEMPTS)
{
// Do something here. E.g. wait for a button to be pressed.
//可在此輸入處理 程序,比如按鍵解除。
for (;;)
{
}
}
// Put watchdog reset handler here.
break;
case (1 << BORF):
//Put brownout reset handler here.
break;
case (1 << EXTRF):
restartAttempts = 0;
// Put external reset handler here.
break;
case (1 << PORF):
restartAttempts = 0;
// Put power-on reset handler here.
break;
}
}
*/
/*! \brief Initializes I/O ports.
*
* Initializes I/O ports.
*/
static void InitPorts(void)
{
// Init DRIVE_DDR for motor driving.
DRIVE_DDR = (1 << UL) | (1 << UH) | (1 << VL) | (1 << VH) | (1 << WL) | (1 << WH);
//DDRB=0b00111111;
// Init PORTD for PWM on PD5.
DDRD = (1 << PD5);
//DDRD=0b00100000;
// Disable digital input buffers on ADC channels.
//關閉ADC輸入數字緩沖器
DIDR0 = (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | (1 << ADC0D);
//DIDR0=0b00011111;
}
/*! \brief Initializes timers (for commutation timing and PWM).
*
* This function initializes Timer/counter0 for PWM operation for motor speed control
* and Timer/counter1 for commutation timing.
*/
static void InitTimers(void)
{
// Set up Timer/counter0 for PWM, output on OCR0B, OCR0A as TOP value, prescaler = 1.
//比較匹配A不與OC0A連接
//TCCROA=0x;//升序時,比較匹配發生時,OC0B清零,降序比較匹配發生時置位;相位修正PWM模式。
TCCR0A = (0 << COM0A1) | (0 << COM0A0) | (1 << COM0B1) | (0 << COM0B0) | (0 << WGM01) | (1 << WGM00);
TCCR0B = (1 << WGM02) | (0 << CS02) | (0 << CS01) | (1 << CS00);//CLK 無分頻
OCR0A = PWM_TOP_VALUE;//8m/20k/2=200 設置TOP值(TOP=OCR0A)
TIFR0 = TIFR0;//意義何在?
TIMSK0 = (0 << TOIE0);//TC0溢出中斷禁止
// Set up Timer/counter1 for commutation timing, prescaler = 8.
TCCR1B = (1 << CS11) | (0 << CS10);//為何沒有把CS12加進去?
}
/*! \brief Initializes the AD-converter.
*
* This function initializes the AD-converter and makes a reading of the external
* reference voltage.
*/
static void InitADC(void)
{
// First make a measurement of the external reference voltage.
//參考電壓AREF;轉換結果左對齊;選取電源通道5
ADMUX = ADMUX_REF_VOLTAGE;// ((0 << REFS1) | (0 << REFS0))|(1<<ADLAR)|0X05;
//ADC使能;ADC開始轉換;讀出電源電壓
ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADIF) | (ADC_PRESCALER_16);
while (ADCSRA & (1 << ADSC))
{
}
referenceVoltageADC = ADCH;//讀入VCC電壓值
// Initialize the ADC for autotriggered operation on PWM timer overflow.
//ADC使能|ADC開始轉換禁止|自動觸發使能|清中斷標志|ADC中斷禁止
ADCSRA = (1 << ADEN) | (0 << ADSC) | (1 << ADATE) | (1 << ADIF) | (0 << ADIE) | ADC_PRESCALER_8;
//定時器/計數器0溢出觸發ADC轉換
ADCSRB = ADC_TRIGGER_SOURCE;
}
/*! \brief Initializes the analog comparator.
*
* This function initializes the analog comparator for overcurrent detection.
*/
static void InitAnalogComparator(void)
{
#ifdef ANALOG_COMPARATOR_ENABLE
// Enable analog comparator interrupt on rising edge.
//允許模擬比較器中斷(上升沿)
ACSR = (0 << ACBG) | (1 << ACI) | (1 << ACIE) | (1 << ACIS1) | (1 << ACIS0);
#endif
}
/*! \brief Initializes the watchdog timer
*
* This function initializes the watchdog timer for stall restart.
*/
/*static void WatchdogTimerEnable(void)
{
__disable_interrupt();
__watchdog_reset();
WDTCSR |= (1 << WDCE) | (1 << WDE);
WDTCSR = (1 << WDIF) | (1 << WDIE) | (1 << WDE) | (1 << WDP2);
__enable_interrupt();
}
*/
/*! \brief Initializes arrays for motor driving and AD channel selection.
*
* This function initializes the arrays used for motor driving and AD channel
* selection that changes for each commutation step.
*/
static void MakeTables(void)
{
#if DIRECTION_OF_ROTATION == CCW//如果定義了方向為CCW,則執行CCW程序。
driveTable[0] = DRIVE_PATTERN_STEP1_CCW;//UY
driveTable[1] = DRIVE_PATTERN_STEP2_CCW;//UW
driveTable[2] = DRIVE_PATTERN_STEP3_CCW;//YW
driveTable[3] = DRIVE_PATTERN_STEP4_CCW;//YU
driveTable[4] = DRIVE_PATTERN_STEP5_CCW;//WY
driveTable[5] = DRIVE_PATTERN_STEP6_CCW;//WY
ADMUXTable[0] = ADMUX_W;
ADMUXTable[1] = ADMUX_V;
ADMUXTable[2] = ADMUX_U;
ADMUXTable[3] = ADMUX_W;
ADMUXTable[4] = ADMUX_V;
ADMUXTable[5] = ADMUX_U;
#else
driveTable[0] = DRIVE_PATTERN_STEP1_CW;
driveTable[1] = DRIVE_PATTERN_STEP2_CW;
driveTable[2] = DRIVE_PATTERN_STEP3_CW;
driveTable[3] = DRIVE_PATTERN_STEP4_CW;
driveTable[4] = DRIVE_PATTERN_STEP5_CW;
driveTable[5] = DRIVE_PATTERN_STEP6_CW;
ADMUXTable[0] = ADMUX_U;
ADMUXTable[1] = ADMUX_V;
ADMUXTable[2] = ADMUX_W;
ADMUXTable[3] = ADMUX_U;
ADMUXTable[4] = ADMUX_V;
ADMUXTable[5] = ADMUX_W;
#endif
startupDelays[0] = 200;//啟動延時數組附值
startupDelays[1] = 150;
startupDelays[2] = 100;
startupDelays[3] = 80;
startupDelays[4] = 70;
startupDelays[5] = 65;
startupDelays[6] = 60;
startupDelays[7] = 55;
}
/*! \brief Executes the motor startup sequence.
*
* This function locks the motor into a known position and fires off a
* commutation sequence controlled by the Timer/counter1 overflow interrupt.
*/
static void StartMotor(void)
{
unsigned char i;
//OCR0B =130;TOP=OCR0A=200--在定時器初始化中定義
SET_PWM_COMPARE_VALUE(STARTUP_PWM_COMPARE_VALUE);
nextCommutationStep = 0;
//Preposition.
DRIVE_PORT = driveTable[nextCommutationStep];
StartupDelay(STARTUP_LOCK_DELAY);//第一次變換持續時間
nextCommutationStep++;
nextDrivePattern = driveTable[nextCommutationStep];
for (i = 0; i < STARTUP_NUM_COMMUTATIONS; i++)
{
DRIVE_PORT = nextDrivePattern;
StartupDelay(startupDelays[i]);
//參考電壓VREF,左對齊,選取零點檢測通道
ADMUX = ADMUXTable[nextCommutationStep];
// Use LSB of nextCommutationStep to determine zero crossing polarity.
zcPolarity = nextCommutationStep & 0x01;//大概是判斷零點斜率
nextCommutationStep++;
if (nextCommutationStep >= 6)
{
nextCommutationStep = 0;
}
nextDrivePattern = driveTable[nextCommutationStep];
}
// Switch to sensorless commutation.
// 啟動結束,進入無傳感器模式正常運行
TCNT1 = 0;
//輸出比較匹配A中斷使能,行使換相功能,復位換相定時器,啟動HOLD-OFF準備定時比較器
TIMSK1 = (1 << OCIE1A);
// Set filteredTimeSinceCommutation to the time to the next commutation.
filteredTimeSinceCommutation = startupDelays[STARTUP_NUM_COMMUTATIONS - 1] * (STARTUP_DELAY_MULTIPLIER / 2);
//不清楚其含義
}
/*! \brief Timer/counter0 bottom overflow. Used for zero-cross detection.
*
* This interrupt service routine is called every time the up/down counting
* PWM counter reaches bottom. An ADC reading on the active channel is
* automatically triggered at the same time as this interrupt is triggered.
* This is used to detect a zero crossing.
*
* In the event of a zero crossing, the time since last commutation is stored
* and Timer/counter1 compare A is set up to trigger at the next commutation
* instant.
*/
//當PWM計數器歸零時引起中斷,同時觸發AD轉換,零點檢測服務程序
SIGNAL(SIG_OVERFLOW1)
{
unsigned char temp;
// Disable ADC auto-triggering. This must be done here to avoid wrong channel being sampled on manual samples later.
//ADC自動觸發禁止;ADC中斷禁止,開始手動轉換
ADCSRA &= ~((1 << ADATE) | (1 << ADIE));
// Wait for auto-triggered ADC sample to complete.
while (!(ADCSRA & (1 << ADIF)))
{// 等待自動ADC采樣結束
}
temp = ADCH;
if (((zcPolarity == EDGE_RISING) && (temp > ADC_ZC_THRESHOLD)) || ((zcPolarity == EDGE_FALLING) && (temp < ADC_ZC_THRESHOLD)))
{//上升沿且檢測點電壓高于設定零點極限電壓或處在下降沿且低于零點極限電壓時
unsigned int timeSinceCommutation;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -