Main Page | File List | Globals

main.c

Go to the documentation of this file.
00001 // This file has been prepared for Doxygen automatic documentation generation.
00038 #include <inavr.h>
00039 #include <ioavr.h>
00040 #include "motor.h"
00041 #include "tables.h"
00042 
00044 __no_init __regvar volatile unsigned char nextTCCR0A        @15;
00045 
00047 __no_init __regvar volatile unsigned char nextADMUX         @14;
00048 
00050 __no_init __regvar volatile unsigned char activePhase       @13;
00051 
00053 __no_init __regvar volatile unsigned char nextPhase         @12;
00054 
00056 __no_init __regvar volatile unsigned int comTick            @10;
00057 
00059 __no_init __regvar volatile unsigned char speedRefUpdated   @9;
00060 
00062 volatile unsigned char speedRef = 0;
00063 
00065 volatile unsigned int currentSpeedTick;
00066 
00068 __eeprom volatile unsigned int numWatchdogResets;
00069 
00076 void main( void )
00077 {
00078   unsigned char duty;
00079 
00080   CheckResetSource();
00081   WDTOff();
00082 
00083   // Initialize all subsystems
00084   InitPORTB();
00085   InitPWM();
00086   InitADC();
00087 
00088   // Run startup procedure
00089   StartMotor();
00090 
00091   InitWDT();
00092 
00093   __enable_interrupt();
00094 
00095   for(;;)
00096   {
00097     duty = CalculateDutyCycle(duty);
00098     SET_DUTY_BOTH_PHASES(duty);
00099 
00100     WaitMillisecs(1);
00101   }
00102 }
00103 
00104 
00118 static void CheckResetSource(void)
00119 {
00120   if (MCUSR & (1 << WDRF))
00121   {
00122     // Watchdog was the cause of the reset.
00123     // Error handling can be done here.
00124     numWatchdogResets++;
00125   }
00126   MCUSR = 0;
00127 }
00128 
00129 
00138 static void InitPORTB(void)
00139 {
00140   // Set PWM output pins to output.
00141   DDRB = (1 << WINDING1_PIN) | (1 << WINDING2_PIN);
00142 
00143   // Initialise tacho pin.
00144 #ifdef TACHO_ENABLED
00145   PORTB &= (1 << TACHO_OUT_PIN);
00146 #endif
00147 }
00148 
00149 
00155 static void InitPWM(void)
00156 {
00157   // Set up TCNT1 for PWM, phase correct mode.
00158   TCCR0A = PWM_OFF;
00159   TCCR0B = (0<<WGM02);
00160 
00161   // Start timer
00162   TCCR0B |= (1 << CS00);
00163 }
00164 
00165 
00170 static void InitADC(void)
00171 {
00172   // Initialize ADC
00173   ADCSRA = (1 << ADEN) | (1 << ADATE) | (1 << ADIF) | (0 << ADIE) | ADC_PRESCALER;
00174   ADCSRB = (1 << ADTS2);
00175 }
00176 
00177 
00184 static void WDTOff(void)
00185 {
00186   __disable_interrupt();
00187   __watchdog_reset();
00188   WDTCR |= (1 << WDCE) | (1 << WDE);
00189   WDTCR = 0x00;
00190   __enable_interrupt();
00191 }
00192 
00193 
00200 static void InitWDT(void)
00201 {
00202   __disable_interrupt();
00203   __watchdog_reset();
00204   WDTCR |= (1 <<WDCE) | (1 << WDE);
00205   WDTCR = (1 << WDE) | (0 << WDTIE) | (1 << WDP1) | (0 << WDP1);
00206   __enable_interrupt();
00207 }
00208 
00209 
00216 #pragma vector = TIM0_OVF_vect
00217 __interrupt void Timer0_Overflow_ISR(void)
00218 {
00219   unsigned char adcvalue;
00220   unsigned char tempADMUX;
00221 
00222   // Chance ADC channel to read analog reference once the zero-cross detection is done.
00223   tempADMUX = ADMUX;
00224   ADMUX = ADC_SPEED_REF;
00225 
00226   // Has the necessary time passed since last commutation?
00227   if (comTick >= ADC_WAIT_TICKS)
00228   {
00229     // Wait for AD conversion to complete
00230     while( (ADCSRA & (1 << ADSC)) )
00231     {
00232 
00233     }
00234 
00235     // Read ADC value
00236     adcvalue = ADCH;
00237 
00238     // Commutate if ADC value is lower than commutation threshold
00239     if (adcvalue < ADC_THRESHOLD)
00240     {
00241       TCCR0A = nextTCCR0A;
00242       tempADMUX = nextADMUX;
00243       activePhase = nextPhase;
00244 
00245       // Reset watchdog if the motor is not spinning suspicously fast.
00246       if (comTick > MAX_RPM_TICKS)
00247       {
00248         __watchdog_reset();
00249       }
00250       // Otherwise, turn off PWM outputs and wait for watchdog reset.
00251       else
00252       {
00253         TCCR0A = PWM_OFF;
00254         for(;;)
00255         {
00256           // Wait here until watchdog reset.
00257         }
00258       }
00259 
00260       currentSpeedTick = comTick;
00261       comTick = 0;
00262 
00263 
00264       // Prepare for next commutation and toggle tacho signal
00265       if (activePhase == PHASE1)
00266       {
00267         nextTCCR0A = PWM_PHASE2;
00268         nextADMUX = ADC_PHASE1;
00269         nextPhase = PHASE2;
00270 #ifdef TACHO_ENABLED
00271         DDRB |= (1 << TACHO_OUT_PIN);
00272 #endif
00273       }
00274       else
00275       {
00276         nextTCCR0A = PWM_PHASE1;
00277         nextADMUX = ADC_PHASE2;
00278         nextPhase = PHASE1;
00279 #ifdef TACHO_ENABLED
00280         DDRB &= ~(1 << TACHO_OUT_PIN);
00281 #endif
00282       }
00283     }
00284     // Start ADC conversion, analog speed reference.
00285     ADCSRA |= (1 << ADSC);
00286   }
00287   else
00288   {
00289     // Wait for first AD conversion
00290     while( (ADCSRA & (1 << ADSC)) )
00291     {
00292 
00293     }
00294     // Start ADC conversion, analog speed reference.
00295     ADCSRA |= (1 << ADSC);
00296   }
00297 
00298   // Wait for ADC reading (speed ref)
00299   while( (ADCSRA & (1<<ADSC)) )
00300   {
00301 
00302   }
00303   speedRef = ADCH;
00304   speedRefUpdated = 1;
00305 
00306   // Change ADC channel back for zero-cross detection.
00307   ADMUX = tempADMUX;
00308 
00309   comTick++;
00310 }
00311 
00312 
00321 static void WaitMillisecs(unsigned int milliSeconds)
00322 {
00323   unsigned int i;
00324   for (i = 0; i < milliSeconds; i++)
00325   {
00326     __delay_cycles(MILLISEC_NUM_CYCLES);
00327   }
00328 }
00329 
00330 
00331 /* \brief Starts the motor according to a predefined sequence.
00332  *
00333  *  This function prepositions the rotor and executes a predefined activation
00334  *  sequence. Sensorless commutation is then activated.
00335  */
00336 static void StartMotor(void)
00337 {
00338   unsigned char i;
00339   unsigned char delay_ms;
00340 
00341   // Lock into phase 1
00342   TCCR0A = PWM_PHASE1;
00343   for (i = 0; i < MAX_OCR_VALUE; i++)
00344   {
00345     SET_DUTY_PHASE1(i);
00346     WaitMillisecs(ROTOR_LOCK_RAMPUP_DELAY_MS);
00347   }
00348 
00349   // Wait for rotor to stabilize
00350   WaitMillisecs(ROTOR_STABILIZATION_TIME_MS);
00351 
00352   // Let rotor slip to detent position
00353   for (i = 0; i < MAX_OCR_VALUE; i++)
00354   {
00355     SET_DUTY_PHASE1(MAX_OCR_VALUE - i);
00356     WaitMillisecs(ROTOR_LOCK_RAMPDOWN_DELAY_MS);
00357   }
00358 
00359   // Disconnect PWM output for a short while
00360   TCCR0A = PWM_OFF;
00361 
00362   // Set 100% duty cycle on PWM channel A and B
00363   SET_DUTY_BOTH_PHASES(MAX_OCR_VALUE);
00364 
00365   activePhase = PHASE1;
00366 
00367   i = 0;
00368   while(delay_ms = startupSequenceDelay[i++])
00369   {
00370     if (activePhase == PHASE1)
00371     {
00372       TCCR0A = PWM_PHASE2;
00373       WaitMillisecs(delay_ms);
00374       activePhase = PHASE2;
00375     }
00376     else
00377     {
00378       TCCR0A = PWM_PHASE1;
00379       WaitMillisecs(delay_ms);
00380       activePhase = PHASE1;
00381     }
00382   }
00383 
00384   // initialize all variables needed for sensorless control.
00385   if (activePhase == PHASE1)
00386   {
00387     ADMUX = ADC_PHASE2;
00388     nextTCCR0A = PWM_PHASE2;
00389     nextADMUX = ADC_PHASE1;
00390     nextPhase = PHASE2;
00391   }
00392   else
00393   {
00394     ADMUX = ADC_PHASE1;
00395     nextTCCR0A = PWM_PHASE1;
00396     nextADMUX = ADC_PHASE2;
00397     nextPhase = PHASE1;
00398   }
00399 
00400   // Make sure a restart is not triggered.
00401   currentSpeedTick = (MAX_RPM_TICKS + MIN_RPM_TICKS) / 2;
00402   comTick = 0;
00403   speedRefUpdated = 0;
00404 
00405   // Enable timer/counter1 overflow interrupt
00406   TIMSK0 |= (1 << TOIE0);
00407 }
00408 
00409 
00421 static unsigned char CalculateDutyCycle(unsigned char duty)
00422 {
00423   static unsigned int tickSetPoint;
00424   unsigned long RPMSetPoint;
00425 
00426   // If the speed reference is updated, update the tick setpoint.
00427   if (speedRefUpdated)
00428   {
00429     RPMSetPoint = MAX_RPM * speedRef / 230;
00430     if (RPMSetPoint < MIN_RPM)
00431     {
00432       RPMSetPoint = MIN_RPM;
00433     }
00434     tickSetPoint = RPM2COMM_TICKS(RPMSetPoint);
00435     speedRefUpdated = 0;
00436   }
00437 
00438   // Very simple speed controller.
00439   // If current speed too slow -> increase duty cycle.
00440   // If current speed too fast -> decrease duty cycle.
00441   if (currentSpeedTick < tickSetPoint)
00442   {
00443     if (duty > MIN_OCR_VALUE)
00444     {
00445       duty--;
00446     }
00447   }
00448   else
00449   {
00450     if (duty < MAX_OCR_VALUE)
00451     {
00452       duty++;
00453     }
00454     // If speed is lower than critically low speed, maximize duty cycle.
00455     if (currentSpeedTick > CRITICALLY_LOW_RPM_TICKS)
00456     {
00457       duty = MAX_OCR_VALUE;
00458     }
00459   }
00460 
00461   return duty;
00462 }

Generated on Thu Sep 29 15:48:28 2005 for AVR440 - Sensorless Control of Two-Phase Brushless DC Motor by  doxygen 1.4.4