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 }